Container Image Signing with Sigstore Cosign
Container image signing Cosign has become the industry standard for verifying that container images running in production are exactly what your CI/CD pipeline built. Sigstore’s Cosign tool provides cryptographic signatures that prove image provenance, detect tampering, and enforce trust policies at deployment time. With keyless signing through OIDC identity, there are no private keys to manage or rotate.
This guide covers the complete signing workflow: from signing images in CI pipelines to verifying signatures with Kubernetes admission controllers. You will learn how to implement a zero-trust container supply chain that prevents unauthorized or modified images from ever reaching your clusters.
Understanding Sigstore and Cosign
Sigstore is an open-source project that provides free code signing infrastructure. Cosign, its container signing tool, uses three components: a transparency log (Rekor) that records all signing events, a certificate authority (Fulcio) that issues short-lived certificates, and OIDC identity providers for keyless signing.
Keyless Signing Flow:
1. Developer/CI triggers cosign sign
2. Cosign requests OIDC token (GitHub, Google, etc.)
3. Fulcio CA verifies OIDC token, issues short-lived cert
4. Cosign signs the image digest with the cert
5. Signature + cert stored in OCI registry alongside image
6. Signing event recorded in Rekor transparency log
Verification Flow:
1. Kubernetes admission controller intercepts pod creation
2. Policy engine fetches signature from OCI registry
3. Verifies signature against Fulcio root CA
4. Checks Rekor log for signing event
5. Validates identity (who signed) and claims
6. Admits or rejects the podSigning Images in CI/CD Pipelines
The most common approach is keyless signing in GitHub Actions, where the OIDC token from the workflow identity automatically authenticates with Fulcio. No secrets or keys are needed.
# .github/workflows/build-sign.yml
name: Build, Push, and Sign Container Image
on:
push:
branches: [main]
permissions:
contents: read
packages: write
id-token: write # Required for keyless signing
jobs:
build-sign:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push
id: build
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/myorg/myapp:latest,ghcr.io/myorg/myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Sign Image (Keyless)
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
cosign sign --yes \
ghcr.io/myorg/myapp@${DIGEST}
- name: Attach SBOM
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
# Generate SBOM with syft
syft ghcr.io/myorg/myapp@${DIGEST} -o spdx-json > sbom.spdx.json
# Attach SBOM as signed attestation
cosign attest --yes \
--predicate sbom.spdx.json \
--type spdxjson \
ghcr.io/myorg/myapp@${DIGEST}
- name: Verify Signature
run: |
cosign verify \
--certificate-identity-regexp "https://github.com/myorg/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/myorg/myapp@${DIGEST} | jq .Kubernetes Admission Control with Kyverno
Moreover, signing images is only half the story. You need admission controllers that reject unsigned or incorrectly signed images. Kyverno and Sigstore Policy Controller both integrate with Cosign verification.
# Kyverno policy to enforce Cosign signatures
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-cosign-signature
spec:
validationFailureAction: Enforce
background: false
webhookTimeoutSeconds: 30
rules:
- name: verify-image-signature
match:
any:
- resources:
kinds: ["Pod"]
namespaces: ["production", "staging"]
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
attestations:
- type: https://spdx.dev/Document
conditions:
- all:
- key: "{{ creationInfo.created }}"
operator: NotEquals
value: ""
- name: block-unsigned-images
match:
any:
- resources:
kinds: ["Pod"]
namespaces: ["production"]
validate:
message: "All images must be signed with Cosign"
deny:
conditions:
any:
- key: "{{ request.object.spec.containers[].image }}"
operator: AnyNotIn
value: "ghcr.io/myorg/*"Key-Based Signing for Air-Gapped Environments
# Generate a key pair for environments without internet
cosign generate-key-pair
# Sign with private key
cosign sign --key cosign.key \
--annotations "build-id=abc123" \
--annotations "git-sha=def456" \
registry.internal.com/myapp:v1.0.0
# Verify with public key
cosign verify --key cosign.pub \
registry.internal.com/myapp:v1.0.0
# Distribute public key as Kubernetes secret
kubectl create secret generic cosign-pub \
--from-file=cosign.pub=cosign.pub \
-n kyvernoAdditionally, for air-gapped environments, you can run a private Rekor instance and Fulcio CA internally. This gives you the same transparency guarantees without external network dependencies.
When NOT to Use Container Image Signing
Image signing adds complexity to your CI/CD pipeline and deployment workflow. For development environments, personal projects, or internal tooling with low security requirements, the overhead may not be justified. Furthermore, if your images are built and consumed within a single trusted environment (same CI system and cluster), network-level controls may provide sufficient security.
Consequently, implement signing for production workloads, regulated environments, and any pipeline where images cross trust boundaries. As a result, start with keyless signing in your main CI pipeline and expand to admission control once you have validated the signing workflow is reliable.
Key Takeaways
Container image signing Cosign with Sigstore provides a practical, free, and industry-standard approach to container supply chain security. Keyless signing eliminates key management overhead, transparency logs provide auditability, and Kubernetes admission controllers enforce trust policies. Start with keyless signing in GitHub Actions, add Kyverno policies for production namespaces, and attach SBOMs for complete supply chain visibility.
Key Takeaways
- Start with a solid foundation and build incrementally based on your requirements
- Test thoroughly in staging before deploying to production environments
- Monitor performance metrics and iterate based on real-world data
- Follow security best practices and keep dependencies up to date
- Document architectural decisions for future team members
For more security topics, explore our guide on Kubernetes security best practices and DevSecOps pipeline security. The Sigstore documentation and Cosign GitHub repository are the authoritative references.
In conclusion, Container Image Signing Sigstore is an essential topic for modern software development. By applying the patterns and practices covered in this guide, you can build more robust, scalable, and maintainable systems. Start with the fundamentals, iterate on your implementation, and continuously measure results to ensure you are getting the most value from these approaches.