Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXX · ProvenanceVerify

Consuming and verifying attestations — the verifier side and the policy

Advanced⏱ ~24 mingitghcosign

What you'll learn

  • Verify a GitHub-generated attestation with gh attestation verify
  • Pin the OIDC subject, the OIDC issuer, and the source repository in the verifier policy
  • Author a Sigstore policy file that enumerates the accepted signers and sources
  • Diagnose why a verification fails when the OIDC subject drift after a workflow rename

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

The attestation is load-bearing only when the verifier checks it. An attestation the team generates but never verifies is an attestation the team could have not generated; the artifact is admitted without the policy being enforced. The verifier side is where the SLSA level is earned. The verifier fetches the attestation, checks the signature, checks the OIDC subject, checks the source, and admits or denies.

The verifier command

The gh attestation verify command is the platform-native verifier for GitHub-generated attestations:

gh attestation verify ./dist/app-binary \
    --owner org \
    --repo app \
    --signer-workflow 'org/app/.github/workflows/build.yml@refs/heads/main'

The command fetches the attestation from the GitHub attestations API for the artifact digest, verifies the signature against the Fulcio certificate in the Sigstore bundle, checks the OIDC subject against the pinned workflow path, and reports the attestation summary. The same command can verify a container image instead of a file. The --signer-workflow flag is the policy; the wildcard org/.* is too permissive; the specific path is the policy.

The Sigstore policy file

The verifier policy can be authored as a Sigstore policy file and stored in the repository. The policy file enumerates the accepted signers, the expected sources, and the artifact shapes:

apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
  name: app-provenance
spec:
  images:
    - glob: "ghcr.io/org/app:*"
  authorities:
    - name: github-actions
      keyless:
        url: https://fulcio.sigstore.dev
        identities:
          - issuer: https://token.actions.githubusercontent.com
            subjectRegExp: "^https://github.com/org/app/.github/workflows/build.yml@refs/heads/main$"

The policy is the contract. The verifier fetches the attestation, checks the OIDC issuer against issuer, checks the OIDC subject against the subjectRegExp, and admits or denies. The policy file is version-controlled, reviewed, and deployed through the same change-management process as the application code.

flowchart LR
    A["Artifact"] --> B["Verifier"]
    B --> C["Fetch attestation"]
    C --> D["Check signature"]
    D --> E["Check OIDC issuer"]
    E --> F["Check OIDC subject"]
    F --> G["Check source repo"]
    G --> H["Admit"]
    G --> I["Deny"]

The chain is signature, issuer, subject, source. Each step is a check; each check is a line in the policy file.

The producer and the verifier share a pin

The producer (the workflow) declares its identity by existing; the verifier declares the accepted identities by pinning. The two declared identities must match. The mismatch modes are a workflow path change, a source branch change, and a CI provider migration (the OIDC issuer URL changes). The fix is the same in every case: update the verifier pin to match the new identity, deploy the policy, and re-verify. The pin is the policy; the policy is the artifact.

The policy is the audit deliverable

The audit team consumes the policy file, not the binary. The policy file documents which workflow can sign (the subjectRegExp), which source repository is trusted (the images glob), which OIDC issuer is trusted (the issuer field), and which signing key was used (the keyless block). The four lines are the policy; the audit team asks for the policy file; the team hands over the policy file; the audit is complete.

Production discipline

  1. Pin the workflow path, the OIDC issuer, and the source repository in the policy. Three pins; three lines.
  2. Wire the verifier into the admission controller. The policy is not enforced until the verifier runs.
  3. Version the policy file in the repository. The policy is a code artifact; the policy is reviewed.
  4. Update the policy when the producer changes. A mismatch is a verified reject; the policy must follow the producer.

Cross-course references

  • Git, CI/CD & GitOps — Part LXX-05 (GitHub Actions Artifact Attestations) is the producer side; LXX-06 is the verifier side.
  • Git, CI/CD & GitOps — Part LXIX-06 (Verify on Deploy) is the cosign verification pattern; LXX-06 extends it to attestations.
  • Container Security for Production Sysadmins — Part VII (Admission Control) is the enforcement side that the verifier feeds.

Quiz

Knowledge check · 4 questions

  1. Q1. Which flag on `gh attestation verify` scopes the artifact search to a specific GitHub owner?

  2. Q2. Authoring a Sigstore policy file that pins the workflow path is not sufficient to satisfy the SLSA L2 verifier requirement.

  3. Q3. Name the three pins the Sigstore policy file declares for a keyless GitHub Actions attestation, and what each one pins.

  4. Q4. Diagnose why every verification is failing after the team renamed the workflow file, and recommend the fix.

    Team T renames the workflow from `build.yml` to `build-push.yml` and updates the path in the repository. The build workflow runs successfully; the attestations are generated. The admission controller rejects every pod with the message 'OIDC subject does not match pinned workflow'. The team cannot deploy.

Passing score: 75%. Answers are checked in this browser.