diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8f52b6..6982e65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,8 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + id-token: write # to sign the attestation with Sigstore + attestations: write # to store the attestation with GitHub steps: # GoReleaser skips signing when MACOS_SIGN_P12 is empty, and a missing # secret arrives as an empty string. Fail rather than publish unsigned @@ -78,3 +80,13 @@ jobs: MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }} HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + + # Sign a SLSA build provenance attestation for every file in + # SHA256SUMS, each binary and its SBOM: it records that this workflow + # built the file from the tagged commit. "gh attestation verify" + # checks it; see the README. The release is already published by now, + # so if this step fails, delete the GitHub release (not the tag) and + # re-run the workflow. + - uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2 + with: + subject-checksums: dist/SHA256SUMS diff --git a/README.md b/README.md index c439e05..eb467b8 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,24 @@ that `go install` records. To check a release or `make dist` output, run (`shasum -a 256 -c --ignore-missing SHA256SUMS` on macOS) in the directory that holds it. +### Verify a release + +From v0.2.0 on, every release file has a signed build provenance +attestation, which shows that this repository's release workflow built it +from the tagged commit. A checksum only shows that a file matches +`SHA256SUMS`, which is published beside it; the attestation shows where both +came from. With the [GitHub CLI](https://cli.github.com): + +```sh +gh attestation verify rypt-linux-amd64 \ + --repo x6c-co/rypt-cli \ + --signer-workflow x6c-co/rypt-cli/.github/workflows/release.yml +``` + +The same command checks the `rypt` that Homebrew installed: give it +`"$(command -v rypt)"` instead of a file name. Each binary's SBOM +(`rypt-linux-amd64.cdx.json` and so on) is attested the same way. + ## Usage ``` diff --git a/docs/releasing.md b/docs/releasing.md index ad1aa24..d132d69 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -28,7 +28,10 @@ so on. Each binary has a CycloneDX SBOM beside it, named like it (`rypt-linux-amd64.cdx.json`), which GoReleaser makes with Syft from the binary's own build information: the Go version and every module compiled in. -The SBOMs are listed in `SHA256SUMS` too. The release workflow builds with +The SBOMs are listed in `SHA256SUMS` too, and the workflow's last step signs a +build provenance attestation for every file in `SHA256SUMS` with +[actions/attest](https://github.com/actions/attest); the README shows how to +verify one. The release workflow builds with the newest Go release, whatever go.mod's `go` line says, so that binaries get the standard library's security fixes.