Skip to content

feat(supply-chain): cosign-sign release artifacts (#2)#28

Merged
awbx merged 1 commit into
mainfrom
feat/supply-chain-cosign
May 19, 2026
Merged

feat(supply-chain): cosign-sign release artifacts (#2)#28
awbx merged 1 commit into
mainfrom
feat/supply-chain-cosign

Conversation

@awbx
Copy link
Copy Markdown
Owner

@awbx awbx commented May 19, 2026

Closes #2.

Summary

Wires cosign keyless signing into the release pipeline. Every release artifact gets a signature bound to this repository's GitHub Actions OIDC identity — no public key to track, no secret to compromise. Container images get in-registry signatures via `docker_signs:`. The checksum file is the transitive root: once `checksums.txt` is signed, every binary whose hash appears in it is attested by the same identity.

What changes

File Change
`.goreleaser.yaml` `signs:` block (sign `checksums.txt`), `docker_signs:` block (sign per-arch images), `release.footer` template surfaces verify commands in every release's notes
`.github/workflows/release.yml` `id-token: write` permission for OIDC, cosign installer step
`.github/workflows/ci.yml` `goreleaser check` step in the snapshot job — config bugs fail PRs instead of the next tagged release
`README.md` New §Verify a release with the cosign verify-blob and verify commands

Out of scope

Test plan

  • CI: `goreleaser snapshot` job passes (validates the full config including `signs:` / `docker_signs:`)
  • CI: all other required checks green
  • Local: `goreleaser check` exits 0 on this branch (verified locally — 1 file validated, no new warnings beyond the pre-existing `dockers_v2` / `homebrew_casks` deprecation notices)
  • Sanity: `id-token: write` permission is the only new permission added — no `actions: write` or other escalations
  • Sanity: cosign installer is pinned to `@v3` (major version pin matches the rest of the workflow's action versions)
  • Post-merge dry-run: a snapshot build with `--snapshot` does NOT attempt to sign (signs run only on real releases, gated by the `--snapshot` flag in GoReleaser). The first real test is the next `v*` tag.

Verification (after the next tagged release)

```sh
cosign verify-blob
--certificate-identity-regexp 'https://github.com/awbx/cronix/.*'
--certificate-oidc-issuer https://token.actions.githubusercontent.com
--certificate checksums.txt.pem
--signature checksums.txt.sig
checksums.txt
```

The identity-regex matches the workflow URL embedded in the cosign cert by Sigstore's Fulcio CA. Anything not matching `https://github.com/awbx/cronix/.*\` did not come from this repo.

Wire cosign keyless signing into the release pipeline so every
artifact published from this repository can be verified against the
GitHub Actions OIDC identity that produced it, with no public key to
manage and no secret to compromise.

What ships in this change:

- .goreleaser.yaml: signs: block runs cosign sign-blob against
  checksums.txt, producing checksums.txt.sig + checksums.txt.pem
  alongside it on every release. Once the checksum file is signed,
  every binary whose hash appears in it is transitively attested by
  the same OIDC identity.
- .goreleaser.yaml: docker_signs: block signs every per-arch
  container image. Signatures are attached in-registry; verification
  does not require pulling the image, just calling cosign verify
  against the image reference.
- .goreleaser.yaml: release.footer template surfaces the verify
  commands inside every release's notes, so users hit them at the
  point of download instead of having to dig through docs.
- .github/workflows/release.yml: id-token: write permission added
  to the job so the OIDC token is available for Fulcio to bind to
  the signing cert, plus a sigstore/cosign-installer step to make
  the cosign binary available to GoReleaser.
- .github/workflows/ci.yml: goreleaser check step added to the
  snapshot job. The existing build --snapshot --clean only exercises
  the build pipeline; check parses every section including
  signs/docker_signs so a malformed cosign config fails the PR
  rather than the next tagged release.
- README.md: Verify a release section documents the cosign
  verify-blob and cosign verify commands, the certificate-identity
  regex to expect, and what "wrong identity" means operationally
  (the artifact is not from this project; do not use it).

Out of scope:

- SLSA build provenance (#3), syft SBOM (#4), arm64 end-to-end
  verification (#5). These share the GoReleaser hook surface but
  warrant their own PRs to keep review tractable.
- cronix version --verify subcommand. Useful but a CLI-level change
  better handled in a follow-up so this PR stays pipeline-only.

Signed-off-by: Abdelhadi Sabani <asabani.work@gmail.com>
@awbx awbx merged commit 5dcfc31 into main May 19, 2026
8 checks passed
@awbx awbx deleted the feat/supply-chain-cosign branch May 19, 2026 11:24
awbx added a commit that referenced this pull request May 19, 2026
Wire cosign keyless signing into the release pipeline so every
artifact published from this repository can be verified against the
GitHub Actions OIDC identity that produced it, with no public key to
manage and no secret to compromise.

What ships in this change:

- .goreleaser.yaml: signs: block runs cosign sign-blob against
  checksums.txt, producing checksums.txt.sig + checksums.txt.pem
  alongside it on every release. Once the checksum file is signed,
  every binary whose hash appears in it is transitively attested by
  the same OIDC identity.
- .goreleaser.yaml: docker_signs: block signs every per-arch
  container image. Signatures are attached in-registry; verification
  does not require pulling the image, just calling cosign verify
  against the image reference.
- .goreleaser.yaml: release.footer template surfaces the verify
  commands inside every release's notes, so users hit them at the
  point of download instead of having to dig through docs.
- .github/workflows/release.yml: id-token: write permission added
  to the job so the OIDC token is available for Fulcio to bind to
  the signing cert, plus a sigstore/cosign-installer step to make
  the cosign binary available to GoReleaser.
- .github/workflows/ci.yml: goreleaser check step added to the
  snapshot job. The existing build --snapshot --clean only exercises
  the build pipeline; check parses every section including
  signs/docker_signs so a malformed cosign config fails the PR
  rather than the next tagged release.
- README.md: Verify a release section documents the cosign
  verify-blob and cosign verify commands, the certificate-identity
  regex to expect, and what "wrong identity" means operationally
  (the artifact is not from this project; do not use it).

Out of scope:

- SLSA build provenance (#3), syft SBOM (#4), arm64 end-to-end
  verification (#5). These share the GoReleaser hook surface but
  warrant their own PRs to keep review tractable.
- cronix version --verify subcommand. Useful but a CLI-level change
  better handled in a follow-up so this PR stays pipeline-only.

Signed-off-by: Abdelhadi Sabani <asabani.work@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

supply-chain: cosign-sign release artifacts via GoReleaser

1 participant