Security and Supply Chain
Topic 64

Securing Releases

Security

A signed commit proves who wrote the source. Release security proves something different and downstream: that the artifact you ship was actually built from that source, by your pipeline, and not swapped or tampered with somewhere between the green build and the published binary. Those are two separate guarantees, and signing commits alone gives you only the first.

Build provenance, the SLSA framework, and signed attestations close the second gap. Together they create a verifiable chain from a specific commit to the exact bytes a consumer downloads, so a tampered artifact can be detected rather than trusted on faith.

The Gap

A passing CI run and a binary in your releases page are not cryptographically linked. Nothing in the artifact, by default, says which commit produced it or which workflow built it — so a binary substituted after the build, or built from different source, is indistinguishable from the real one. Provenance is what turns an implicit assumption into a checkable claim.

Build Provenance Attestations

The actions/attest-build-provenance action produces a signed statement describing what was built, from which commit, by which workflow run, backed by Sigstore. That attestation is the link the bare artifact lacks: it ties the output bytes to their build inputs in a form a consumer can later verify against your repository and workflow.

SLSA Levels

SLSA is the supply-chain framework that grades how trustworthy that provenance is. The key property is non-falsifiability — provenance is only meaningful if the build environment could not have lied about it. Provenance generated by a hosted, isolated builder like GitHub Actions maps toward SLSA Build L2/L3, whereas a build on mutable, attacker-reachable infrastructure earns far less trust.

Signed Releases

Beyond provenance, sign the release artifacts themselves so consumers can verify origin independently: Sigstore/cosign for container images, GPG or cosign for files. The strongest form is keyless signing, where the signature is tied to the workflow's OIDC identity rather than a stored signing key — there is no long-lived private key to leak.

Verification

None of this matters unless the consuming side checks it. A consumer verifies the attestation or signature before trusting the artifact — gh attestation verify for build provenance, cosign verify for signed images. Provenance that nobody verifies on the way in is theater; the verification step is what makes the whole chain real.

Common Mistakes
  • Publishing release binaries with no provenance, so a consumer cannot distinguish your artifact from a tampered substitute.
  • Building the release on a self-hosted runner with mutable state, undermining the non-falsifiability that provenance depends on.
  • Signing the source commit but shipping an unsigned binary, leaving the build-to-artifact step unverifiable.
  • Treating SLSA as a checkbox and generating provenance that nobody verifies on the consuming side.
  • Storing the signing key as a long-lived secret instead of using keyless Sigstore signing tied to the workflow's OIDC identity.
Best Practices
  • Generate provenance with actions/attest-build-provenance on every release build.
  • Sign container images and artifacts with Sigstore/cosign using keyless OIDC-based signing.
  • Verify attestations at deploy time with gh attestation verify or cosign verify before promoting an artifact.
  • Build releases on hosted runners to keep provenance non-falsifiable.
  • Target an explicit SLSA build level and document exactly how consumers verify your artifacts.
Comparable toolsGitLab SLSA provenance and artifact attestationsCircleCI SLSA provenance via orbsSigstore/cosign CI-agnostic keyless signing and verificationin-toto framework for supply-chain attestations across systems

Knowledge Check

What does build provenance prove that a green CI run does not?

  • That a specific artifact was built from a specific commit by a specific workflow, in a form a consumer can verify
  • That every unit and integration test passed in CI before the release tag was cut
  • That the underlying source commit was cryptographically signed by its original author
  • That none of the declared dependencies in the manifest had any known published vulnerabilities at the time of the build

Why does building on a self-hosted runner with mutable state weaken provenance?

  • Provenance depends on non-falsifiability, and a mutable, attacker-reachable build environment can lie about what it produced
  • Self-hosted runners are technically unable to run the attest-build-provenance action at all
  • Generating provenance at all requires a paid GitHub-hosted runner tier that self-hosted runners are simply not entitled to use
  • Carrying mutable state between builds makes each run slower, though no less trustworthy

Why is keyless Sigstore signing preferred over a stored signing key?

  • The signature is tied to the workflow's OIDC identity, so there is no long-lived private key that can leak
  • Keyless signing produces noticeably smaller signature payloads to store and distribute
  • A stored signing key is fundamentally unable to sign OCI container images, whereas keyless signing can do so
  • Keyless signing lets consumers skip the verification step entirely when pulling the artifact

What makes an attestation actually meaningful?

  • The consumer verifies it before trusting the artifact; unverified provenance changes nothing
  • It is published to the public releases page right alongside the downloadable binary, so anyone fetching the artifact can find the matching provenance file
  • It is generated automatically on every commit, not only on tagged releases
  • It is stored centrally in the repository's Security tab for auditors to browse during a compliance review

You got correct