Oktober 2026
M D M D F S S
 1234
567891011
12131415161718
19202122232425
262728293031  

GitHub Attestations or Cosign: When the Switch Pays Off

Written in

von

GitHub Attestations and cosign both produce Sigstore signatures. The question is therefore not whether you sign, but who runs the trust infrastructure and who ultimately verifies. Why provenance evidence is needed in the first place is covered in Provenance and SLSA, in Provenance and Attestation, and in enforcing provenance in the cluster with Kyverno. The cosign mechanics are covered in the cosign post from June 24. What Sigstore feels like without key management for commits is shown in gitsign in practice. This post only covers the decision in between. This article is also available in German: GitHub Attestations oder cosign: wann sich der Wechsel lohnt.

What GitHub Attestations handles

The workflow obtains a short-lived certificate via OIDC; the action handles signing and storage. Which Sigstore instance sits behind it depends, according to the GitHub docs, on the repository’s visibility. Public repositories use the Sigstore Public Good Instance. A copy of the bundle is kept by GitHub, and it also lands in a publicly readable transparency log. Private repositories use GitHub’s own Sigstore instance: same codebase, but without a transparency log and federated only for GitHub Actions.

You attest an image like this:

permissions:
id-token: write
contents: read
attestations: write
packages: write
steps:
# tag used here for readability; pin to the commit SHA in real workflows
- uses: actions/attest@v4
with:
subject-name: ghcr.io/ORG/IMAGE
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

actions/attest-build-provenance produces the same result. As of version 4 it is, according to the README, just a wrapper around actions/attest; new implementations should use actions/attest directly. How to pin actions by SHA is covered in securing GitHub Actions.

The prerequisite that comes before the technology

Attestations are available for public repositories on all current plans. If you use GitHub Free, Pro, or Team, that’s the only place you get them. For private and internal repositories, the docs require GitHub Enterprise Cloud. So before comparing approaches, check your plan.

Verifying with gh attestation verify

gh attestation verify oci://ghcr.io/ORG/IMAGE:TAG \
--repo ORG/REPO \
--signer-workflow ORG/REPO/.github/workflows/release.yml

For oci:// you need to be logged in to the registry. According to the CLI manual, the command requires at least --owner or --repo. Verification gets more precise with --signer-workflow. By default it enforces the predicate type https://slsa.dev/provenance/v1. --deny-self-hosted-runners fails attestations from self-hosted runners. With cosign, you pin the expected identity with --certificate-identity and --certificate-oidc-issuer (docs).

Offline verification is documented for GitHub. There’s gh attestation download, gh attestation trusted-root, and --bundle with --custom-trusted-root (docs). An air-gapped network alone is therefore not, by itself, a reason to run your own stack. The trusted-root file has no built-in expiry. Anything signed afterward is only valid until the instance’s next key rotation (a few times a year, according to the docs), and you won’t see revocations offline.

When you stick with GitHub

  • Your build runs in GitHub Actions, and the repositories are public or you have Enterprise Cloud.
  • You ship binaries or container images whose consumers can use gh.
  • You don’t want to run signing infrastructure yourself.

On their own, attestations deliver SLSA v1.0 Build Level 2. For Level 3, GitHub points to a reusable workflow shared across many repositories (docs). In that case the signing identity is that workflow, and --signer-workflow or --signer-repo becomes mandatory. The GitHub docs don’t call for hardened runners for Level 3; what matters is the reusable workflow.

When cosign is the better choice

  1. Private repository without Enterprise Cloud. Attestations aren’t available. cosign keyless uses the public Sigstore instance, which, according to the Sigstore docs, is open to all developers. Note the trade-off: the digest, signature, and certificate end up in a publicly auditable log (docs), and according to the Fulcio docs, the OIDC token carries workflow and source-repository details that get embedded in the certificate. The conclusion is that for private code, the workflow identity becomes publicly visible. That’s an inference from these details, not an explicit statement in the docs. Whether you accept that is something to settle beforehand.
  2. CI outside GitHub Actions. GitHub’s private instance federates only with GitHub Actions. The public Sigstore instance, by contrast, lists GitLab CI/CD among its supported OIDC issuers too (see Fulcio docs).
  3. Full control over Fulcio, Rekor, and the trust root. That’s possible, but it’s operational work. cosign then verifies against your own TUF root or a trusted-root file, with a signing config starting in cosign v3 (docs). As a starting point, the docs point to the scaffolding project.

An honest accounting belongs to that third point. According to the security model, misbehavior by Rekor or Fulcio can go undetected if nobody monitors the logs, and Fulcio does not monitor its own certificate log. The public Rekor instance states an SLO of 99.5% availability (docs). That’s a useful benchmark for your own operation.

What neither one solves

GitHub says so itself: an attestation does not guarantee secure software; it links the artifact to source code and build instructions. It’s also only useful once someone actually verifies it. According to the CLI manual, the only thing the generating workflow cannot manipulate is signature.certificate and verifiedTimestamps. The content of statement.predicate can be forged if an attacker controls the workflow’s execution context. As a countermeasure, the manual points to a trusted builder: build and signing inside a reusable workflow whose execution can’t be influenced by inputs from the calling workflow.

In the cluster, you enforce GitHub attestations with the Sigstore Policy Controller and the trust-policies Helm chart (docs). You can delete attestations for artifacts that consumers should no longer trust. Anyone who has set up verification can no longer use the artifact afterward (docs).

Bottom line

Switching to cosign is worth it if you lack the plan, if you don’t build on GitHub Actions, or if you need to hold your own trust root. In every other case, running your own stack means taking on operations and log monitoring yourself. Start with attestations and switch once one of these three criteria concretely applies.

Questions or comments about this setup: mm@mhm-dl.de.

Using the newsletter sign-up form, you enter your email address, tick the consent box, and then confirm your subscription by email. By signing up, you consent to the newsletter covering topics including training on Docker, Kubernetes, CI/CD, Git workflows, and DevSecOps tooling, as well as the requirements of NIS2, CRA, and DORA and their technical implementation.

Hinterlasse einen Kommentar

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..