release: sign assets#53
Conversation
8b609d8 to
2d759e9
Compare
avoids issues when using e.g. macOS to develop and using linux sandboxes for agentic development
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 | ||
| with: | ||
| context: . | ||
| platforms: linux/amd64 |
There was a problem hiding this comment.
Old version had PLATFORMS=linux/amd64,linux/arm64 - is this intentional? I think linux/arm64 is important for kind clusters on Apple silicon.
| 3. **Verify the GitHub release** has all artifacts attached (manifests, checksums, | ||
| signature, SBOM) |
There was a problem hiding this comment.
What do you mean bi signature? And there is a Release attestation (json). Is this what you mean?
Please clarify.
| - name: Set up QEMU | ||
| uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 | ||
|
|
There was a problem hiding this comment.
possible stale, if linux/arm64 was really dropped intentionally.
| ## Verifying a Release | ||
|
|
||
| All release artifacts are signed with keyless [Sigstore](https://www.sigstore.dev/) | ||
| signatures using GitHub Actions OIDC. The signing | ||
| identity is this repository's release workflow at the release tag, i.e. | ||
| `https://github.com/cloudscale-ch/cluster-api-provider-cloudscale/.github/workflows/release.yml@refs/tags/<tag>` | ||
| issued by `https://token.actions.githubusercontent.com`. | ||
|
|
||
| Requires [`cosign`](https://docs.sigstore.dev/cosign/system_config/installation/) | ||
| and the [GitHub CLI](https://cli.github.com/) (`gh`). | ||
|
|
||
| ### Container image signature | ||
|
|
||
| ```bash | ||
| IMG=quay.io/cloudscalech/cluster-api-cloudscale-controller:v1.0.0 | ||
| cosign verify "$IMG" \ | ||
| --certificate-identity-regexp '^https://github.com/cloudscale-ch/cluster-api-provider-cloudscale/\.github/workflows/release\.yml@refs/tags/' \ | ||
| --certificate-oidc-issuer https://token.actions.githubusercontent.com | ||
| ``` | ||
|
|
||
| ### Build provenance & SBOM attestations | ||
|
|
||
| ```bash | ||
| # SLSA provenance + SBOM attestation attached to the image: | ||
| gh attestation verify oci://$IMG --owner cloudscale-ch | ||
|
|
||
| # List everything attached to the image (signature + attestations): | ||
| cosign tree "$IMG" | ||
| ``` | ||
|
|
||
| The SBOM is also attached to the GitHub release as `sbom.spdx.json`. | ||
|
|
||
| ### Release manifests | ||
|
|
||
| Download `checksums.txt`, `checksums.txt.bundle` and the | ||
| `*.yaml` manifests into the same directory, then (run from that directory): | ||
|
|
||
| ```bash | ||
| # Verify the manifest digests. `shasum` ships by default on both macOS and Linux | ||
| # and reads the checksum file; GNU coreutils users can use `sha256sum -c` instead. | ||
| # (Do not use BSD `sha256sum -c` — there `-c` compares against a single digest string.) | ||
| shasum -a 256 -c checksums.txt | ||
|
|
||
| # The bundle is a self-describing Sigstore bundle carrying the signature, | ||
| # signing certificate, and transparency-log proof. | ||
| cosign verify-blob \ | ||
| --bundle checksums.txt.bundle \ | ||
| --certificate-identity-regexp '^https://github.com/cloudscale-ch/cluster-api-provider-cloudscale/\.github/workflows/release\.yml@refs/tags/' \ | ||
| --certificate-oidc-issuer https://token.actions.githubusercontent.com \ | ||
| checksums.txt | ||
|
|
||
| # Each manifest also carries a SLSA provenance attestation: | ||
| gh attestation verify infrastructure-components.yaml --owner cloudscale-ch | ||
| ``` |
There was a problem hiding this comment.
All my testing and learning summarized in a slightly modified version of this section. Would appreciate if you applied it the file. Modifications welcome.
| ## Verifying a Release | |
| All release artifacts are signed with keyless [Sigstore](https://www.sigstore.dev/) | |
| signatures using GitHub Actions OIDC. The signing | |
| identity is this repository's release workflow at the release tag, i.e. | |
| `https://github.com/cloudscale-ch/cluster-api-provider-cloudscale/.github/workflows/release.yml@refs/tags/<tag>` | |
| issued by `https://token.actions.githubusercontent.com`. | |
| Requires [`cosign`](https://docs.sigstore.dev/cosign/system_config/installation/) | |
| and the [GitHub CLI](https://cli.github.com/) (`gh`). | |
| ### Container image signature | |
| ```bash | |
| IMG=quay.io/cloudscalech/cluster-api-cloudscale-controller:v1.0.0 | |
| cosign verify "$IMG" \ | |
| --certificate-identity-regexp '^https://github.com/cloudscale-ch/cluster-api-provider-cloudscale/\.github/workflows/release\.yml@refs/tags/' \ | |
| --certificate-oidc-issuer https://token.actions.githubusercontent.com | |
| ``` | |
| ### Build provenance & SBOM attestations | |
| ```bash | |
| # SLSA provenance + SBOM attestation attached to the image: | |
| gh attestation verify oci://$IMG --owner cloudscale-ch | |
| # List everything attached to the image (signature + attestations): | |
| cosign tree "$IMG" | |
| ``` | |
| The SBOM is also attached to the GitHub release as `sbom.spdx.json`. | |
| ### Release manifests | |
| Download `checksums.txt`, `checksums.txt.bundle` and the | |
| `*.yaml` manifests into the same directory, then (run from that directory): | |
| ```bash | |
| # Verify the manifest digests. `shasum` ships by default on both macOS and Linux | |
| # and reads the checksum file; GNU coreutils users can use `sha256sum -c` instead. | |
| # (Do not use BSD `sha256sum -c` — there `-c` compares against a single digest string.) | |
| shasum -a 256 -c checksums.txt | |
| # The bundle is a self-describing Sigstore bundle carrying the signature, | |
| # signing certificate, and transparency-log proof. | |
| cosign verify-blob \ | |
| --bundle checksums.txt.bundle \ | |
| --certificate-identity-regexp '^https://github.com/cloudscale-ch/cluster-api-provider-cloudscale/\.github/workflows/release\.yml@refs/tags/' \ | |
| --certificate-oidc-issuer https://token.actions.githubusercontent.com \ | |
| checksums.txt | |
| # Each manifest also carries a SLSA provenance attestation: | |
| gh attestation verify infrastructure-components.yaml --owner cloudscale-ch | |
| ``` | |
| ## Verifying a Release | |
| Every artifact is signed and attested by the same identity: this repository's | |
| `release.yml` workflow, running on the release tag, via keyless | |
| [Sigstore](https://www.sigstore.dev/)/GitHub Actions OIDC signing. Requires | |
| [`cosign`](https://docs.sigstore.dev/cosign/system_config/installation/) and the | |
| [GitHub CLI](https://cli.github.com/) (`gh`). | |
| Set these once. Every command below reuses them, so there's a single place to update | |
| if the tag, repo, or org ever changes: | |
| ```bash | |
| export TAG=v1.0.0 | |
| export IMG=quay.io/cloudscalech/cluster-api-cloudscale-controller:$TAG | |
| export ID_REGEXP='^https://github.com/cloudscale-ch/cluster-api-provider-cloudscale/\.github/workflows/release\.yml@refs/tags/' | |
| export ISSUER=https://token.actions.githubusercontent.com | |
| ``` | |
| ### Docker Image | |
| No `.bundle` file here: `cosign sign` pushes the signature into the registry | |
| alongside the image itself, not to a separate file. | |
| ```bash | |
| # Signature (predicate type sigstore.dev/cosign/sign/v1) | |
| cosign verify "$IMG" \ | |
| --certificate-identity-regexp "$ID_REGEXP" \ | |
| --certificate-oidc-issuer "$ISSUER" | |
| # Build provenance (predicate type slsa.dev/provenance/v1, this happens to be | |
| # gh's default if --predicate-type is omitted, but named explicitly here anyway) | |
| gh attestation verify oci://$IMG --owner cloudscale-ch \ | |
| --predicate-type https://slsa.dev/provenance/v1 | |
| # The provenance claim's actual content: which commit/workflow run produced | |
| # this image (buildDefinition, runDetails.builder.id): | |
| gh attestation verify oci://$IMG --owner cloudscale-ch \ | |
| --predicate-type https://slsa.dev/provenance/v1 \ | |
| --format json \ | |
| --jq '.[].verificationResult.statement.predicate' | |
| # SBOM (predicate type spdx.dev/Document/v2.3, must be given explicitly, | |
| # otherwise gh defaults to provenance and the SBOM is never checked) | |
| gh attestation verify oci://$IMG --owner cloudscale-ch \ | |
| --predicate-type https://spdx.dev/Document/v2.3 | |
| # A verified copy of the SBOM's actual content, extracted from the checked | |
| # attestation rather than trusted from the release asset on its own: | |
| gh attestation verify oci://$IMG --owner cloudscale-ch \ | |
| --predicate-type https://spdx.dev/Document/v2.3 \ | |
| --format json \ | |
| --jq '.[].verificationResult.statement.predicate' | |
| # Everything attached at once: | |
| cosign tree "$IMG" | |
| ``` | |
| `sbom.spdx.json` is also attached to the GitHub release directly, but that copy isn't | |
| independently verifiable as a standalone file: its attestation's subject is the image, | |
| not the file. The SBOM extraction command above prints the verified content instead. | |
| ### Release Manifests | |
| Unlike the image, these are plain files with no registry to push into, so | |
| `cosign sign-blob` produces `checksums.txt.bundle` as a standalone file instead. Both | |
| checks below matter: `shasum` proves your files match `checksums.txt`; the signature | |
| proves `checksums.txt` itself is genuine. Either alone isn't enough. | |
| ```bash | |
| cd "$(mktemp -d)" | |
| gh release download "$TAG" \ | |
| -R cloudscale-ch/cluster-api-provider-cloudscale \ | |
| -p 'checksums.txt' \ | |
| -p 'checksums.txt.bundle' \ | |
| -p '*.yaml' | |
| # Files match their checksums. `shasum` ships by default on macOS and Linux; | |
| # GNU coreutils users can use `sha256sum -c` instead (BSD sha256sum's -c compares | |
| # against a single digest string, not a checksum file). | |
| shasum -a 256 -c checksums.txt | |
| # checksums.txt itself is signed (predicate type sigstore.dev/cosign/sign/v1). The | |
| # bundle is a self-describing Sigstore bundle: signature + certificate + | |
| # transparency-log proof. | |
| cosign verify-blob \ | |
| --bundle checksums.txt.bundle \ | |
| --certificate-identity-regexp "$ID_REGEXP" \ | |
| --certificate-oidc-issuer "$ISSUER" \ | |
| checksums.txt | |
| # Each manifest also carries its own build provenance (predicate type | |
| # slsa.dev/provenance/v1, gh's default, named explicitly here anyway): | |
| for f in *.yaml; do | |
| gh attestation verify "$f" --owner cloudscale-ch \ | |
| --predicate-type https://slsa.dev/provenance/v1 | |
| done | |
| ``` | |
| No SBOM check here: an SBOM describes a built artifact's dependencies, and plain YAML | |
| manifests have none. | |
| dist/*.yaml \ | ||
| dist/checksums.txt \ | ||
| dist/checksums.txt.bundle \ | ||
| sbom.spdx.json |
There was a problem hiding this comment.
The Sigstore Quickstart suggest to run verification in the CI: https://docs.sigstore.dev/quickstart/quickstart-ci/#verifying-a-blob
Might be smart thing todo.
closes #52 - had to update because govulncheck reported some things. I'm not yet sure if I like this, might be that we'd want to get rid of govulncheck again if it's too noisy.