Skip to content

feat(release): sign release artifacts and add build provenance#395

Draft
joshua-temple wants to merge 1 commit into
mainfrom
feat/ossf-signed-releases
Draft

feat(release): sign release artifacts and add build provenance#395
joshua-temple wants to merge 1 commit into
mainfrom
feat/ossf-signed-releases

Conversation

@joshua-temple

@joshua-temple joshua-temple commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

Problem

cascade publishes GitHub Releases via GoReleaser but attaches no signatures, no SBOM, and no build provenance, and the build is not reproducible (release ldflags embed a wall-clock timestamp). This blocks the OpenSSF Scorecard Signed-Releases check and the OpenSSF Best Practices signed_releases and build_reproducible criteria.

Fix

  • Reproducible build: add -trimpath, set mod_timestamp to the commit timestamp, and switch the embedded date ldflag from build time to the commit date.
  • cosign keyless signing (OIDC + Sigstore) of checksums.txt, emitting .sig and .pem as release assets.
  • GPG detached signing of checksums.txt (.asc) using a passphraseless release key imported from the CASCADE_RELEASE_GPG_KEY repo secret. The public key is published at docs/cascade-release-public-key.asc.
  • SBOM generation (syft, SPDX JSON) per archive.
  • SLSA build provenance via actions/attest-build-provenance, verifiable with gh attestation verify.
  • Least-privilege job permissions (id-token: write, attestations: write only on the release job).
  • docs/release-verification.md: how to verify cosign, GPG, and provenance, and how to reproduce the build.

All new actions are SHA-pinned and verified to resolve.

Verification

  • go build ./..., go test ./..., golangci-lint run ./... all pass.
  • goreleaser check valid; actionlint clean on the workflow.
  • Signing and SBOM only execute on a real release, so they will be exercised by the next rc.

Maintainer setup (done)

The signing key is provisioned: a passphraseless RSA-4096 key is generated, the CASCADE_RELEASE_GPG_KEY and CASCADE_RELEASE_GPG_FINGERPRINT repo secrets are set, and the public key is committed at docs/cascade-release-public-key.asc. The key has no passphrase by design (a passphrase would live as a repo secret in the same trust boundary), and is disposable: regenerate and republish the public key if needed.

Note on Scorecard score

cosign .sig/.pem and GPG .asc ship as release assets, satisfying Signed-Releases (signature credit). Build provenance is stored in GitHub's attestation API rather than as a .intoto.jsonl release asset; reaching the full provenance credit would require a tag-pinned generator that conflicts with SHA-pinning, so it is intentionally not pursued.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant