Sign our releases (#1347)

* Sign our releases

* Use secrets instead of artifacts

* And the universal

* Apparently we can’t use secrets like that?

* Minor fixes to doc

* Private key requires untrusted comment

* Dogfood one deeper
This commit is contained in:
Félix Saparelli 2023-09-23 20:07:19 +12:00 committed by GitHub
parent 32beba507b
commit ee7fcb3210
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 88 additions and 14 deletions

16
.github/scripts/ephemeral-gen.sh vendored Executable file
View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euxo pipefail
cargo binstall -y rsign2
rsign generate -f -W -p minisign.pub -s minisign.key
cat >> crates/bin/Cargo.toml <<EOF
[package.metadata.binstall.signing]
algorithm = "minisign"
pubkey = "$(tail -n1 minisign.pub)"
EOF
set +x
echo "::add-mask::$(tail -n1 minisign.key)"
echo "private=$(tail -n1 minisign.key)" >> "$GITHUB_OUTPUT"

19
.github/scripts/ephemeral-sign.sh vendored Executable file
View file

@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
echo "untrusted comment: rsign encrypted secret key" > minisign.key
cat >> minisign.key <<< "$SIGNING_KEY"
set -x
cargo binstall -y rsign2
ts=$(date --utc --iso-8601=seconds)
git=$(git rev-parse HEAD)
comment="gh=$GITHUB_REPOSITORY git=$git ts=$ts run=$GITHUB_RUN_ID"
for file in "$@"; do
rsign sign -W -s minisign.key -x "$file.sig" -t "$comment" "$file"
done

View file

@ -16,6 +16,10 @@ on:
description: "Set to override default release profile codegen-units settings" description: "Set to override default release profile codegen-units settings"
required: false required: false
type: string type: string
secrets:
signingkey:
description: "Minisign private key. Required when publishing"
required: false
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
@ -84,6 +88,14 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- if: inputs.publish
uses: cargo-bins/cargo-binstall@main
- if: inputs.publish
env:
SIGNING_KEY: ${{ secrets.signingkey }}
run: .github/scripts/ephemeral-sign.sh packages/cargo-binstall-*
- if: inputs.publish - if: inputs.publish
name: Upload to release name: Upload to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
@ -140,6 +152,14 @@ jobs:
- run: just repackage-lipo - run: just repackage-lipo
- run: ls -shal packages/ - run: ls -shal packages/
- if: inputs.publish
uses: cargo-bins/cargo-binstall@main
- if: inputs.publish
env:
SIGNING_KEY: ${{ secrets.signingkey }}
run: .github/scripts/ephemeral-sign.sh packages/cargo-binstall-universal-*
- if: inputs.publish - if: inputs.publish
name: Upload to release name: Upload to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2

View file

@ -22,8 +22,8 @@ jobs:
event-data: ${{ toJSON(github.event) }} event-data: ${{ toJSON(github.event) }}
extract-notes-under: '### Release notes' extract-notes-under: '### Release notes'
tag: libtag:
if: needs.info.outputs.is-release == 'true' if: needs.info.outputs.is-release == 'true' && needs.info.outputs.crate != 'cargo-binstall'
needs: info needs: info
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -35,24 +35,43 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
custom_tag: ${{ needs.info.outputs.version }} custom_tag: ${{ needs.info.outputs.version }}
tag_prefix: ${{ needs.info.outputs.crate }}-v tag_prefix: ${{ needs.info.outputs.crate }}-v
- name: Push cli release tag
if: needs.info.outputs.crate == 'cargo-binstall'
uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
custom_tag: ${{ needs.info.outputs.version }}
tag_prefix: v
- name: Publish to crates.io - name: Publish to crates.io
run: | run: |
cargo publish -p '${{ needs.info.outputs.crate }}' cargo publish -p '${{ needs.info.outputs.crate }}'
env: env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
clitag:
if: needs.info.outputs.is-release == 'true' && needs.info.outputs.crate == 'cargo-binstall'
needs: info
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Push cli release tag
uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
custom_tag: ${{ needs.info.outputs.version }}
tag_prefix: v
- uses: cargo-bins/cargo-binstall@main
- name: Create ephemeral keypair
id: keypair
run: .github/scripts/ephemeral-gen.sh
- name: Publish to crates.io
env:
crate: ${{ needs.info.outputs.crate }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish -p "$crate"
outputs:
signingkey: ${{ steps.keypair.outputs.private }}
package: package:
if: needs.info.outputs.is-release == 'true' && needs.info.outputs.crate == 'cargo-binstall' if: needs.info.outputs.is-release == 'true' && needs.info.outputs.crate == 'cargo-binstall'
needs: needs:
- info - info
- tag - clitag
uses: ./.github/workflows/release-build.yml uses: ./.github/workflows/release-build.yml
with: with:
publish: ${{ toJSON(needs.info.outputs) }} publish: ${{ toJSON(needs.info.outputs) }}
secrets:
signingkey: ${{ needs.clitag.signingkey }}

View file

@ -59,7 +59,7 @@ It may or may not include the untrusted comment; it's ignored by Binstall so we
## Just-in-time signing ## Just-in-time signing
To reduce the risk of a key being stolen, this scheme supports just-in-time signing. To reduce the risk of a key being stolen, this scheme supports just-in-time or "keyless" signing.
The idea is to generate a keypair when releasing, use it for signing the packages, save the key in the Cargo.toml before publishing to a registry, and then discard the private key when it's done. The idea is to generate a keypair when releasing, use it for signing the packages, save the key in the Cargo.toml before publishing to a registry, and then discard the private key when it's done.
That way, there's no key to steal nor to store securely, and every release is signed by a different key. That way, there's no key to steal nor to store securely, and every release is signed by a different key.
And because crates.io is immutable, it's impossible to overwrite the key. And because crates.io is immutable, it's impossible to overwrite the key.
@ -73,7 +73,7 @@ There is one caveat to keep in mind: with the scheme as described above, Binstal
The solution here is either: The solution here is either:
- Commit the Cargo.toml with the ephemeral public key to the repo when publishing. - Commit the Cargo.toml with the ephemeral public key to the repo when publishing.
- Omit the `[...signing]` section in the source, and write the entire section on publish instead of just filling in the `pubkey`; signatures won't be checked for `--git` installs. - Omit the `[...signing]` section in the source, and write the entire section on publish instead of just filling in the `pubkey`; signatures won't be checked for `--git` installs. Binstall uses this approach.
- Instruct your users to use `--skip-signatures` if they want to install with `--git`. - Instruct your users to use `--skip-signatures` if they want to install with `--git`.
## Why not X? (Sigstore, GPG, signify, with SSH keys, ...) ## Why not X? (Sigstore, GPG, signify, with SSH keys, ...)
@ -84,10 +84,10 @@ We chose minisign as the first supported algorithm as it's lightweight, fairly p
## There's a competing project that does package signature verification differently! ## There's a competing project that does package signature verification differently!
[Tell use about it](https://github.com/cargo-bins/cargo-binstall/issues/1)! [Tell us about it](https://github.com/cargo-bins/cargo-binstall/issues/1)!
We're not looking to fracture the ecosystem here, and will gladly implement support if something exists already. We're not looking to fracture the ecosystem here, and will gladly implement support if something exists already.
We'll also work with others in the space to eventually formalise this beyond Binstall, for example around the `cargo-dist.json` metadata format. We'll also work with others in the space to eventually formalise this beyond Binstall, for example around the [`dist-manifest.json`](https://crates.io/crates/cargo-dist-schema) metadata format.
## What's the relationship to crate/registry signing? ## What's the relationship to crate/registry signing?