DevSecOps

How to Set Up Sigstore in Your Build Pipeline

Wire Sigstore into GitHub Actions end-to-end: OIDC identity, Cosign signing, Rekor transparency, and policy-controller enforcement — with working snippets.

Shadab Khan
Security Engineer
4 min read

Sigstore gives you a zero-secrets, publicly auditable chain of custody for every artifact you ship. This tutorial wires all four Sigstore components — Cosign, Fulcio, Rekor, and policy-controller — into a single GitHub Actions pipeline that signs container images, attests SBOMs, publishes to the public transparency log, and enforces admission policy in Kubernetes. We will use Cosign v2.2.4, the sigstore/cosign-installer@v3.5.0 action, and Sigstore policy-controller 0.9. Prerequisites: a GitHub repo with id-token: write permission, push access to GHCR, and a dev Kubernetes cluster (kind or minikube is fine). Plan for 60 minutes end-to-end.

Which Sigstore components do I need?

You need Cosign as the CLI, Fulcio as the certificate authority, Rekor as the transparency log, and policy-controller as the Kubernetes admission webhook. The public Sigstore instance is free and production-grade — you do not need to self-host anything to get started.

For air-gapped or high-sovereignty environments, the same components can be deployed privately via sigstore/helm-charts, but that is a separate tutorial. Start with the public instance to learn the mental model.

How do I bootstrap identity in CI?

Grant id-token: write to the workflow and let cosign-installer handle the rest. GitHub Actions issues an OIDC token scoped to the workflow, Fulcio validates it, and Cosign gets a 10-minute code-signing certificate.

permissions:
  contents: read
  id-token: write
  packages: write
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
      - uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9af48c2b6d2a2a1e0
        with:
          cosign-release: 'v2.2.4'

If you forget id-token: write, Cosign prints Error: getting signer: getting key from Fulcio: retrieving cert: interactive mode not supported. That is the canonical "you forgot to grant OIDC" signal.

How do I sign both the image and an SBOM?

Run cosign sign for the image and cosign attest --predicate for the SBOM. Each produces a separate signature that consumers can verify independently.

export IMAGE=ghcr.io/acme/api
docker buildx build --push --tag $IMAGE:${GITHUB_SHA} --metadata-file meta.json .
DIGEST=$(jq -r '."containerimage.digest"' meta.json)

cosign sign --yes $IMAGE@$DIGEST
syft $IMAGE@$DIGEST -o cyclonedx-json > sbom.cdx.json
cosign attest --yes --predicate sbom.cdx.json \
  --type cyclonedx $IMAGE@$DIGEST

The --type cyclonedx flag sets the in-toto predicate type to https://cyclonedx.org/bom, which is the MIME type consumers filter on. Mismatched predicate types are a common reason verifiers skip an attestation silently.

How do I inspect the Rekor entry?

Every signature and attestation you publish is recorded in Rekor, the public transparency log. Use cosign tree to list entries for an image, and rekor-cli to fetch full details.

cosign tree ghcr.io/acme/api@$DIGEST
# Signatures for an image tag:
#   ghcr.io/acme/api:sha256-....sig
# Attestations for an image tag:
#   ghcr.io/acme/api:sha256-....att

rekor-cli get --log-index 89532104 --format json | jq '.Attestation'

Rekor is append-only, so leaked secrets or mis-signed artifacts are discoverable forever. Treat cosign attest --predicate inputs as public — do not embed credentials in a predicate.

How do I verify in a downstream consumer?

Call cosign verify with the expected certificate identity and OIDC issuer. Consumers should hard-code the builder workflow path so a compromised fork cannot produce acceptable signatures.

cosign verify \
  --certificate-identity "https://github.com/acme/api/.github/workflows/release.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/acme/api@$DIGEST | jq '.[0].optional.Bundle.Payload.logIndex'

Use --certificate-identity (exact match) not --certificate-identity-regexp when you can. A regex that accepts .* is equivalent to no verification at all and is the most common misconfiguration in the wild.

How do I enforce policy at admission?

Install Sigstore policy-controller and apply a ClusterImagePolicy that requires a keyless signature from your release workflow. The webhook rejects unsigned or mis-attested pods before they schedule.

helm repo add sigstore https://sigstore.github.io/helm-charts
helm install policy-controller sigstore/policy-controller \
  --namespace cosign-system --create-namespace --version 0.9.0
kubectl label namespace production policy.sigstore.dev/include=true
kubectl apply -f - <<'EOF'
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata: { name: acme-release }
spec:
  images:
    - glob: ghcr.io/acme/**
  authorities:
    - keyless:
        identities:
          - issuer: https://token.actions.githubusercontent.com
            subject: https://github.com/acme/api/.github/workflows/release.yml@refs/heads/main
  mode: enforce
EOF

Start in mode: warn for the first week to surface false positives before flipping to enforce. A misconfigured policy that denies every pod in production is an outage, not a security win.

How Safeguard Helps

Safeguard treats Sigstore attestations as first-class supply chain evidence. When you push a signed image and attested SBOM, Safeguard pulls the Rekor entries, correlates them with your component inventory, and highlights which signed components are actually reachable in your deployed code — powered by Griffin AI's reachability analysis. The platform auto-ingests CycloneDX SBOMs carried as Sigstore attestations, so you do not need a separate SBOM upload pipeline. Policy gates can require a valid Cosign signature with a pinned identity before promotion, and failures open Jira tickets with the offending workflow and digest. Sigstore gives you the cryptographic facts; Safeguard turns them into enforceable deployment policy.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.