Cosign — Container Image Signing & Verification
Sign, verify, and attach metadata to container images with Sigstore — the standard for software supply chain security without key infrastructure.
✅ Pros
- + Keyless signing: sign with OIDC identity (GitHub Actions, Google, Microsoft) — no key management
- + Transparent log (Rekor): all signatures recorded publicly for auditability
- + OCI registry native: signatures stored alongside images in the same registry
- + Kubernetes policy enforcement via Kyverno or OPA Gatekeeper
- + SBOM attestation: attach SBOMs, SLSA provenance, and custom attestations to images
⚠️ Cons
- − Keyless signing requires OIDC provider — complex in air-gapped environments
- − Verification policies require additional tooling (Kyverno, Connaisseur)
- − Relatively new: ecosystem still maturing, some rough edges
- − Rekor transparency log entries are public — not suitable for private infrastructure details
What is Cosign?
Cosign is a tool for signing and verifying container images. It’s part of the Sigstore project — a set of open-source tools for code signing that provides a transparent, auditable record of who signed what and when.
Supply chain attacks have become a major threat vector (SolarWinds, Log4Shell, XZ Utils). Cosign addresses this by enabling you to cryptographically verify that a container image was built by your CI/CD system, hasn’t been tampered with, and meets your security policy before it runs in production.
Quick Start
# Install Cosign
brew install cosign
# Or via Go
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
# Sign an image with keyless signing (GitHub Actions OIDC)
cosign sign --yes ghcr.io/myorg/myapp:v1.0.0
# Verify a signed image
cosign verify \
--certificate-identity "https://github.com/myorg/myapp/.github/workflows/release.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/myorg/myapp:v1.0.0
# Sign with a key pair (traditional approach)
cosign generate-key-pair # Creates cosign.key and cosign.pub
cosign sign --key cosign.key ghcr.io/myorg/myapp:v1.0.0
cosign verify --key cosign.pub ghcr.io/myorg/myapp:v1.0.0
Keyless Signing with Sigstore
Keyless signing is the recommended approach — no key management required:
# In GitHub Actions CI/CD:
# 1. GitHub generates an OIDC token for the workflow
# 2. Cosign exchanges the OIDC token for a short-lived certificate
# 3. The certificate ties the signature to the workflow identity
# 4. Signature + certificate recorded in Rekor (public transparency log)
# 5. Certificate expires after signing — no long-lived keys to manage
cosign sign --yes \
--rekor-url=https://rekor.sigstore.dev \
ghcr.io/myorg/myapp:${{ github.sha }}
The verification command checks:
- The signature is cryptographically valid
- The signing certificate was issued by the trusted CA
- The signing identity matches the expected CI/CD workflow
- The signature is recorded in the Rekor transparency log
CI/CD Integration
GitHub Actions
name: Build and Sign
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: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: build-push
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Sign the image
run: |
cosign sign --yes \
ghcr.io/${{ github.repository }}@${{ steps.build-push.outputs.digest }}
env:
COSIGN_EXPERIMENTAL: 1
Verification in CI
- name: Verify image before deployment
run: |
cosign verify \
--certificate-identity "https://github.com/${{ github.repository }}/.github/workflows/release.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/myorg/myapp:${{ env.IMAGE_TAG }}
Attestations
Cosign supports attaching attestations — structured claims about the image:
# Attach an SBOM as an attestation
syft ghcr.io/myorg/myapp:latest -o cyclonedx-json > sbom.json
cosign attest --yes \
--predicate sbom.json \
--type cyclonedx \
ghcr.io/myorg/myapp:latest
# Verify the SBOM attestation
cosign verify-attestation \
--type cyclonedx \
--certificate-identity "..." \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/myorg/myapp:latest | jq '.payload | @base64d | fromjson'
# Attach SLSA provenance attestation
cosign attest --yes \
--predicate provenance.json \
--type slsaprovenance \
ghcr.io/myorg/myapp:latest
Kubernetes Policy Enforcement with Kyverno
Ensure only signed images run in your cluster:
# Kyverno ClusterPolicy to require Cosign signatures
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce
rules:
- name: check-image-signature
match:
resources:
kinds: [Pod]
namespaces: [production, staging]
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- count: 1
entries:
- keyless:
subject: "https://github.com/myorg/*/.github/workflows/*.yml@*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
Verify Before Pull (Admission Control)
With Kyverno or Connaisseur, unsigned images are rejected at admission:
Error from server: admission webhook "mutate.kyverno.svc" denied the request:
policy Pod/production/myapp for resource violation:
require-signed-images/check-image-signature:
Image ghcr.io/myorg/myapp:latest failed signature verification.
Cosign + Kyverno is the standard approach for software supply chain security in Kubernetes environments in 2026.