Sigstore
Sigstore is a free, open-source suite of tools for software supply chain security. It provides cryptographic signing and verification for containers, artifacts, and Git commits — with a public transparency log (Rekor) that makes all signatures auditable and tamper-evident.
Installation
Install cosign
# macOS
brew install cosign
# Linux — binary
LATEST=$(curl -s https://api.github.com/repos/sigstore/cosign/releases/latest | jq -r .tag_name)
curl -L "https://github.com/sigstore/cosign/releases/download/${LATEST}/cosign-linux-amd64" \
-o cosign
chmod +x cosign
sudo mv cosign /usr/local/bin/
# Linux — package (Debian/Ubuntu)
curl -sLO https://github.com/sigstore/cosign/releases/latest/download/cosign_amd64.deb
dpkg -i cosign_amd64.deb
# Verify
cosign version
Install gitsign
# macOS
brew install gitsign
# Linux
LATEST=$(curl -s https://api.github.com/repos/sigstore/gitsign/releases/latest | jq -r .tag_name)
curl -L "https://github.com/sigstore/gitsign/releases/download/${LATEST}/gitsign_linux_amd64" \
-o gitsign
chmod +x gitsign
sudo mv gitsign /usr/local/bin/
# Configure Git to use gitsign
git config --global commit.gpgsign true
git config --global gpg.x509.program gitsign
git config --global gpg.format x509
Install rekor-cli
# macOS
brew install rekor-cli
# Linux
LATEST=$(curl -s https://api.github.com/repos/sigstore/rekor/releases/latest | jq -r .tag_name)
curl -L "https://github.com/sigstore/rekor/releases/download/${LATEST}/rekor-cli-linux-amd64" \
-o rekor-cli
chmod +x rekor-cli
sudo mv rekor-cli /usr/local/bin/
rekor-cli version
Install policy-controller (Kubernetes)
helm repo add sigstore https://sigstore.github.io/helm-charts
helm repo update
helm install policy-controller sigstore/policy-controller \
--namespace cosign-system \
--create-namespace \
--set commonNodeSelector."kubernetes\.io/os"=linux
# Verify
kubectl get pods -n cosign-system
kubectl get crds | grep policy.sigstore.dev
Configuration
Keyless Signing Setup (OIDC)
# Keyless signing uses your OIDC identity (GitHub Actions, Google, etc.)
# No key management required — Fulcio issues ephemeral certificates
# Set environment variables for keyless mode
export COSIGN_EXPERIMENTAL=1 # Deprecated in newer versions — keyless is default now
# In GitHub Actions, set OIDC token permissions:
# permissions:
# id-token: write
# contents: read
Key-Based Signing Setup
# Generate a signing key pair
cosign generate-key-pair
# Output: cosign.key (private), cosign.pub (public)
# Protect cosign.key — store in KMS or Vault, not in source control
# Generate with KMS backend (AWS KMS)
cosign generate-key-pair --kms awskms:///arn:aws:kms:us-east-1:123456789:key/abc123
# Generate with KMS backend (GCP KMS)
cosign generate-key-pair --kms gcpkms://projects/my-project/locations/us/keyRings/my-ring/cryptoKeys/my-key
Core Commands
| Command | Description |
|---|---|
cosign sign <image> | Sign a container image (keyless) |
cosign sign --key cosign.key <image> | Sign with a private key |
cosign verify <image> | Verify image signature (keyless) |
cosign verify --key cosign.pub <image> | Verify with a public key |
cosign attach sbom --sbom sbom.json <image> | Attach SBOM to image |
cosign download sbom <image> | Download attached SBOM |
cosign attest --predicate sbom.json <image> | Attach attestation |
cosign verify-attestation <image> | Verify attestation |
cosign triangulate <image> | Show OCI tag for signature/attestation |
cosign copy <src> <dst> | Copy image with all signatures |
cosign tree <image> | Show all supply chain metadata |
rekor-cli upload --artifact <file> | Upload artifact to Rekor log |
rekor-cli search --sha <hash> | Search Rekor by artifact hash |
rekor-cli get --uuid <uuid> | Retrieve a Rekor log entry |
gitsign verify --certificate-identity <email> | Verify a Git commit |
gitsign log | Show signed commit log |
cosign generate-key-pair | Generate signing key pair |
cosign public-key --key cosign.key | Extract public key |
cosign import-key-pair --key private.pem | Import existing key |
Advanced Usage
Keyless Container Signing (GitHub Actions)
# .github/workflows/sign.yml
name: Build and Sign
on:
push:
branches: [main]
permissions:
id-token: write # Required for keyless signing
contents: read
packages: write
jobs:
build-and-sign:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: sigstore/cosign-installer@v3
- name: Build and push image
id: build
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/my-org/my-app:${{ github.sha }}
- name: Sign image (keyless)
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
cosign sign --yes \
ghcr.io/my-org/my-app@${DIGEST}
- name: Verify signature
run: |
cosign verify \
--certificate-identity-regexp "https://github.com/my-org/my-app/.github/workflows/sign.yml.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/my-org/my-app@${{ steps.build.outputs.digest }}
SBOM Attestation
# Generate SBOM with syft
syft ghcr.io/my-org/my-app:latest -o cyclonedx-json > sbom.json
# Attach SBOM as an attestation
cosign attest \
--predicate sbom.json \
--type cyclonedx \
ghcr.io/my-org/my-app:latest
# Verify and download SBOM attestation
cosign verify-attestation \
--certificate-identity-regexp ".*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--type cyclonedx \
ghcr.io/my-org/my-app:latest | jq '.payload | @base64d | fromjson'
Custom Provenance Attestation
# Create a SLSA provenance predicate
cat > provenance.json <<EOF
{
"buildType": "https://github.com/actions/runner/github-hosted",
"builder": {
"id": "https://github.com/actions/runner"
},
"invocation": {
"configSource": {
"uri": "git+https://github.com/my-org/my-app@refs/heads/main",
"digest": {"sha1": "$GITHUB_SHA"},
"entryPoint": ".github/workflows/build.yml"
}
}
}
EOF
# Attach
cosign attest \
--predicate provenance.json \
--type slsaprovenance \
ghcr.io/my-org/my-app:latest
Policy Controller — ClusterImagePolicy
# Require all images in production to be signed by our CI pipeline
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-signed-images
spec:
images:
- glob: "ghcr.io/my-org/**"
authorities:
- keyless:
url: https://fulcio.sigstore.dev
identities:
- issuer: https://token.actions.githubusercontent.com
subjectRegExp: "https://github.com/my-org/.*/\\.github/workflows/.*@refs/heads/main"
ctlog:
url: https://rekor.sigstore.dev
Key-Based ClusterImagePolicy
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-key-signed
spec:
images:
- glob: "docker.io/myrepo/*"
authorities:
- key:
data: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----
Namespace Opt-In for Policy Enforcement
# Label namespaces to enforce policy-controller admission
kubectl label namespace production \
policy.sigstore.dev/include=true
# Verify the label is applied
kubectl get namespace production --show-labels
Rekor — Transparency Log Queries
# Search for all entries for a given artifact hash
sha256=$(shasum -a 256 myartifact.tar.gz | awk '{print $1}')
rekor-cli search --sha "sha256:${sha256}"
# Get a specific entry by UUID
rekor-cli get --uuid <uuid> --format json | jq .
# Verify an artifact is in the log
rekor-cli verify --artifact myartifact.tar.gz --signature myartifact.sig --public-key cosign.pub
# Search for entries by email identity (keyless)
rekor-cli search --email ci-bot@myorg.com
gitsign Commit Verification
# Sign commits (automatically, after git config above)
git commit -S -m "feat: add feature"
# Verify a commit's signature
git verify-commit HEAD
# Use gitsign's verbose verify
gitsign verify \
--certificate-identity ci@myorg.com \
--certificate-oidc-issuer https://accounts.google.com \
HEAD
# Show signed commit log
gitsign log --signer-identity
# Check verification in git log
git log --show-signature
Common Workflows
Verify an Image Before Deployment
# Verify image is signed by the expected CI pipeline before deploying
cosign verify \
--certificate-identity-regexp "https://github.com/my-org/my-app/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/my-org/my-app:v1.2.3
# If verification passes: exit code 0
# If verification fails: non-zero exit, safe to block the deploy
Inspect Image Supply Chain Metadata
# Show all supply chain artifacts (signatures, SBOMs, attestations)
cosign tree ghcr.io/my-org/my-app:latest
# Download and inspect the SBOM
cosign download sbom ghcr.io/my-org/my-app:latest | jq .
# Verify and print attestation payload
cosign verify-attestation \
--certificate-identity-regexp ".*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--type slsaprovenance \
ghcr.io/my-org/my-app:latest | jq -r '.payload | @base64d | fromjson'
Integrate Verification in Kubernetes Admission
# 1. Install policy-controller (see Installation section)
# 2. Create ClusterImagePolicy (see Advanced Usage section)
# 3. Label namespaces to enforce
kubectl label namespace production policy.sigstore.dev/include=true
# 4. Test: try to deploy an unsigned image
kubectl run test --image=nginx:latest -n production
# Expected: admission webhook denies the request
# 5. Monitor violations
kubectl get events -n production --field-selector reason=FailedCreate
kubectl logs -n cosign-system deploy/policy-controller-webhook
Tips and Best Practices
- Sign images by digest, not tag — tags are mutable; always sign and verify using the immutable
@sha256:...digest. - Use keyless signing in CI — OIDC-based keyless signing eliminates key management entirely; the CI system identity (GitHub Actions, Google SA) is the authority.
- Store key-based signing keys in KMS — if you must use keys, use
awskms://,gcpkms://, orazurekms://URIs; never commitcosign.keyto Git. - Attest SBOMs alongside signatures — a signature proves who built the image; an SBOM attestation proves what’s in it — both are needed for supply chain security.
- Enable policy-controller in
warnmode first — set--policy-controller-warn-mode=trueto log violations without blocking, then flip to enforce. - Use
--certificate-identity-regexpcarefully — overly broad patterns allow any CI pipeline to be an authority; lock down to your org’s exact workflow paths. - Monitor the Rekor log for your org — use
rekor-cli search --emailor watch for unexpected entries to detect unauthorized signing. - Pin
cosign-installerAction version — use a commit SHA instead of a tag to prevent supply chain attacks on the tool itself. - Include
cosign verifyin your CD pipeline — verify images at deploy time, not just at build time, to catch images pushed outside the CI pipeline. - Use
cosign treeregularly — it gives an immediate view of all supply chain metadata for an image, making it easy to spot missing SBOMs or attestations.