Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXIX · Artifact SigningKeyless

Keyless signing with Fulcio — OIDC-issued ephemeral keys

Advanced⏱ ~26 mingitcosign

What you'll learn

  • Trace the keyless flow from OIDC token to short-lived cert to signature
  • Sign an image keylessly with cosign from a CI environment that provides OIDC tokens
  • Verify a keyless signature against a pinned OIDC identity and the expected issuer
  • Identify the production failure modes of the keyless flow (issuer URL, subject pinning, Rekor inclusion)

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 keyless flow removes the long-lived private key from the supply chain. The signing material is the OIDC token that the CI provider (GitHub Actions, GitLab CI, CircleCI) issues for a specific workflow run; the certificate that binds the run to the signing key is short-lived and issued by Fulcio; the signature is recorded in Rekor. The private key never exists outside the CI runtime.

The keyless flow end-to-end

The keyless flow has six steps. Each maps to a Sigstore component or a CI environment variable:

  1. CI runner requests an OIDC token. The CI provider authenticates the workflow and issues a token that names the workflow (e.g., https://github.com/org/repo/.github/workflows/build.yml@refs/heads/main).
  2. cosign exchanges the token with Fulcio. cosign proves possession of the OIDC token; Fulcio issues a short-lived X.509 certificate naming the OIDC subject.
  3. cosign generates an ephemeral keypair. The private key is held in memory for the duration of the sign operation.
  4. cosign signs the digest with the private key. The signature is bundled with the Fulcio certificate and the Rekor entry.
  5. Rekor records the signature. The transparency log entry is the tamper-evident record.
  6. Registry stores the result. The signature, certificate, and Rekor entry are OCI artifacts that refer to the signed digest.
flowchart LR
    A["CI workflow"] --> B["OIDC token"]
    B --> C["Fulcio"]
    C --> D["Short-lived cert"]
    B --> E["cosign sign --keyless"]
    D --> E
    E --> F["Ephemeral key"]
    F --> G["Signature"]
    G --> H["Rekor"]
    G --> I["Registry"]
    H --> J["Verifier"]
    I --> J
    J --> K["Admit"]
    J --> L["Deny"]

The verifier inverts the process. The verifier fetches the signature, fetches the certificate, checks the certificate against the pinned OIDC subject and the expected issuer, checks the Rekor entry, and either accepts or rejects.

The signing command

The signing command is the same shape as the keypair flow minus the --key argument:

cosign sign --keyless image:$COMMIT_SHA

The command requires the OIDC token environment variables that the CI provider sets. GitHub Actions sets ACTIONS_ID_TOKEN_REQUEST_TOKEN and ACTIONS_ID_TOKEN_REQUEST_URL; the Sigstore Fulcio client uses these to obtain the token that proves the workflow identity to Fulcio.

The output is the same OCI artifact shape as the keypair flow: the signature, the certificate (which Fulcio returned), and the Rekor entry are all stored alongside the image in the registry.

The verification command

The verification command pins the OIDC identity and the issuer. The pins are the policy that turns the signature into a verifiable claim:

cosign verify --keyless --certificate-identity $CERT_IDENTITY \
    --certificate-oidc-issuer https://token.actions.githubusercontent.com \
    image:$DIGEST

The two pins are the policy:

  • --certificate-identity is the expected OIDC subject. The canonical value is the workflow URL, e.g. https://github.com/org/repo/.github/workflows/build.yml@refs/heads/main.
  • --certificate-oidc-issuer is the expected OIDC issuer. The canonical value for GitHub Actions is https://token.actions.githubusercontent.com.

A wildcard subject is weaker than pinning the workflow path. A wildcard issuer is a bug. The verifier rejects any signature whose certificate does not match both pins.

Production failure modes

The keyless flow has three failure modes the production team must own:

  • Issuer mismatch. The CI provider’s OIDC issuer URL changes (or the team migrates to a different CI provider). The verifier rejects every signature because the issuer does not match. Failures cascade; the audit shows no signature matched.
  • Subject drift. The team renames the workflow or moves the file. The pinned subject is stale; the verifier rejects every new signature. The fix is to update the pin and redeploy the policy.
  • Rekor inclusion failure. The CI job succeeds but the Rekor entry is missing. The signature is signed but not recorded; the tamper-evidence property is lost. The CI job should fail if the Rekor inclusion proof is missing.

Production discipline

  1. Sign keyless from CI, not from a developer laptop. The OIDC token is the OIDC token that names the workflow; a locally-issued token is a token that has no chain.
  2. Pin the OIDC subject to the exact workflow path. A wildcard is a verification policy that can be satisfied by any workflow in the org.
  3. Pin the OIDC issuer to the CI provider’s issuer URL. A wildcard issuer is a bug.
  4. Fail the CI job if Rekor inclusion fails. Tamper- evidence is the property; the property is not optional.
  5. Document the pin in the policy object. The pin is the policy; the policy object is the artifact.

Cross-course references

  • Git, CI/CD & GitOps — Part LIII-06 (Image Signing in CI) is the previous lesson on cosign; LXIX-03 deepens the keyless flow.
  • Git, CI/CD & GitOps — Part LXIX-02 (Cosign and the Sigstore Stack) maps the components; LXIX-03 is the deep-dive on keyless.
  • Container Security for Production Sysadmins — Part VII (Admission Control) is the verifier side.

Quiz

Knowledge check · 4 questions

  1. Q1. In a keyless cosign flow, what is the role of the OIDC token issued by the CI provider?

  2. Q2. The `--certificate-identity-regexp 'https://github.com/org/.*'` wildcard is the production-grade verifier pin for the OIDC subject.

  3. Q3. Name the two flags that pin the OIDC identity in `cosign verify --keyless` and what each one pins.

  4. Q4. Diagnose why every keyless signature is being rejected by the verifier after a CI migration.

    Team T migrates its CI from GitHub Actions to GitLab CI. The cosign sign command is unchanged; the verify command pins --certificate-oidc-issuer https://token.actions.githubusercontent.com. After the migration, every signature is rejected because the issuer does not match. The team is unable to deploy.

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