Sign Container Images with Cosign

Khimananda Oli 8 min read Database
Sign Container Images with Cosign

By Khimananda Oli | Last reviewed: August 2026

Supply chain attacks have shifted from compromising source code to targeting the build artifacts themselves, making it critical to sign container images with Cosign before they ever reach production. Without cryptographic proof of provenance, a compromised registry or rogue insider can swap a legitimate image for a malicious one without triggering any alerts in your deployment pipeline. This guide walks you through implementing keyless signing with Sigstore’s Cosign, integrating it into CI workflows, and enforcing verification at the Kubernetes admission layer.

Developer / CIOIDC IdentityFulcio CAIssues Short-Lived CertRekor LogImmutable Audit TrailOCI RegistryImage + Signature1. Request Cert2. Log Signature3. Attach Sig4. Store Ref
Keyless signing flow: OIDC identity binds the signature to a verified actor via Fulcio and Rekor when you sign container images with Cosign.

How do you sign container images with Cosign in keyless mode?

Keyless signing is the default and recommended workflow in 2026 because it removes the operational burden of managing long-lived private keys. Instead of storing secrets that can be leaked or stolen, Cosign uses your OpenID Connect (OIDC) identity from providers like GitHub Actions, GitLab, Google Cloud, or Azure AD to obtain a short-lived certificate from Fulcio, the public certificate authority for code signing.

Prerequisites and installation

Before you begin, ensure you have access to an OCI-compliant registry (Docker Hub, ECR, GHCR, GCR, etc.) and are authenticated. Install the latest Cosign binary for your platform. On Linux or macOS with Homebrew:

brew install cosign
cosign version

For CI environments, use the official GitHub Action sigstore/cosign-installer which pins a specific version and verifies its own integrity. Never download binaries from unverified sources in automated pipelines.

Signing your first image interactively

When running locally, Cosign will open a browser window to complete the OIDC flow. The --yes flag skips the confirmation prompt regarding experimental features, which is safe now that keyless signing is stable:

# Build and push your image first
docker build -t ghcr.io/myorg/myapp:v1.2.0 .
docker push ghcr.io/myorg/myapp:v1.2.0

# Sign using keyless OIDC flow
cosign sign --yes ghcr.io/myorg/myapp:v1.2.0

Cosign calculates the digest of the pushed image, requests a certificate from Fulcio bound to your email/issuer, signs the digest, records the entry in the Rekor transparency log, and attaches the signature as a separate tag in the registry. You will see output confirming the tlog index and certificate issuer. If this fails, check that your registry supports OCI referrers or fallback tags; most major registries do natively in 2026.

Verifying signatures manually

Verification confirms both integrity (the image hasn't changed) and provenance (who signed it). For keyless signatures, you must specify the expected certificate identity and OIDC issuer:

cosign verify \
  --certificate-identity-regexp=".*@mycompany\.com$" \
  --certificate-oidc-issuer="https://accounts.google.com" \
  ghcr.io/myorg/myapp:v1.2.0

If verification succeeds, Cosign outputs the JSON payload including the subject, issuer, and Rekor UUID. If it fails, you get a clear error indicating whether the signature is missing, invalid, or doesn't match the identity constraints. Always verify against the digest, not just the tag, since tags are mutable.

How do you automate Cosign signing in CI/CD pipelines?

Manual signing works for ad-hoc releases, but production workloads require automated signing within your CI pipeline. This ensures every deployable artifact is signed consistently and ties the signature to the CI system's identity rather than individual developers. For teams adopting DevSecOps practices, embedding signing directly in the build stage prevents unsigned images from ever being promoted.

GitHub Actions with OIDC

GitHub Actions provides built-in OIDC tokens that Fulcio trusts natively. Configure your workflow to request the token and pass it to Cosign:

permissions:
  id-token: write   # Required for OIDC
  packages: write   # Required to push/sign

jobs:
  build-and-sign:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Cosign
        uses: sigstore/cosign-installer@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/${{ github.repository }}:${{ github.sha }}
          
      - name: Sign Image
        run: |
          cosign sign --yes \
            ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}

Note that we sign the digest returned by the build step, not the tag. This eliminates race conditions where a tag could be moved between build and sign steps. The id-token: write permission is mandatory; without it, the OIDC token won't be issued and signing will fail silently or error out.

GitLab CI and other platforms

GitLab CI also supports OIDC via ID tokens. Set the SIGSTORE_ID_TOKEN environment variable from the job's JWT, and Cosign will use it automatically. For platforms without native OIDC support, you can fall back to key-based signing using ephemeral keys generated per-pipeline, though this loses the identity binding benefits. In air-gapped or restricted environments common in Nepal's government and banking sectors, consider running a private Fulcio and Rekor instance via the sigstore/scaffolding Helm chart to maintain keyless workflows without external dependencies.

Build & PushOCI ArtifactScan (Trivy)CVE + SBOMCosign SignOIDC + RekorDeploy GateVerify Before Rundigestpasssigned ref
Secure pipeline flow: scanning precedes signing to avoid attesting vulnerable artifacts, and verification gates deployment.

How does Cosign compare to Notary and Docker Content Trust?

Choosing the right signing tool matters for long-term maintenance and ecosystem compatibility. While Docker Content Trust (DCT) was the original solution, the industry has largely converged on Sigstore/Cosign and CNCF Notary v2. Understanding the trade-offs helps you avoid investing in deprecated technology, especially if you're also evaluating container scanning tools that integrate differently with each.

FeatureCosign (Sigstore)Notary v2 / ORASDocker Content Trust
Key ManagementKeyless (OIDC) defaultX.509 / KMS / KeylessDocker-managed keys only
Transparency LogRekor (built-in)Optional / PluggableNone
Registry CompatibilityUniversal (OCI referrers)OCI-native (ORAS)Docker Hub / Limited
Kubernetes EnforcementKyverno / Policy ControllerRatify / OPADocker daemon only
Attestations (SBOM, SLSA)Native cosign attestVia ORAS artifactsNot supported
Community Momentum (2026)De facto standardGrowing (CNCF)Deprecated / Legacy

In practice, Cosign wins for most teams due to its zero-config keyless experience and deep integration with Kubernetes policy engines. Notary v2 is worth watching if you need strict OCI artifact compliance or are already standardized on ORAS. Avoid starting new projects with Docker Content Trust; it lacks transparency logs, doesn't support modern attestation formats, and receives minimal maintenance.

How do you enforce signature verification in Kubernetes?

Signing alone doesn't protect you; you must prevent unsigned or tampered images from running. In Kubernetes, admission controllers validate signatures before pods are scheduled. This is where your investment in secure secrets handling and RBAC pays off, as misconfigured policies can block legitimate deployments.

Using Kyverno for policy enforcement

Kyverno's verifyImages rule is the most straightforward way to enforce Cosign signatures. Define a ClusterPolicy that checks every container image against your expected identity:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-image-signature
      match:
        any:
          - resources:
              kinds: ["Pod"]
      verifyImages:
        - imageReferences:
            - "ghcr.io/myorg/*"
          attestors:
            - entries:
                - keyless:
                    url: https://fulcio.sigstore.dev
                    identities:
                      - issuer: https://token.actions.githubusercontent.com
                        subject: "https://github.com/myorg/myrepo/.github/workflows/ci.yml@refs/heads/main"

This policy enforces that all pods using images from ghcr.io/myorg/* must have a valid keyless signature issued by GitHub Actions' OIDC provider for the specific workflow and branch. Adjust the subject pattern to match your CI structure precisely; overly broad patterns defeat the purpose of identity binding.

Sigstore Policy Controller alternative

The official Sigstore Policy Controller offers tighter integration with Rekor and supports CUE-language policies for complex validation logic. It's ideal if you need to validate multiple attestations (e.g., signature + SBOM + SLSA provenance) in a single admission decision. Both controllers cache Rekor entries to avoid latency spikes during cluster scaling events.

API ServerAdmission RequestKyverno / Policy CtrlVerify SignatureAllow PodSchedule & RunDeny PodReject + Alertwebhookvalidinvalid
Admission controller validates signatures at pod creation time, blocking unsigned or mismatched images from running.

Start Signing Container Images with Cosign Today

Implementing cryptographic signing is no longer optional for production workloads facing SOC 2, ISO 27001, or SLSA compliance requirements. Start by enabling keyless signing in your primary CI pipeline this week, then add a Kyverno policy in audit mode to measure coverage before enforcing. Track your progress toward full SLSA Level 2+ compliance as you expand attestation coverage beyond basic signatures. If you need help designing a supply chain security strategy tailored to your infrastructure or compliance scope, reach out to discuss your specific requirements.

Frequently Asked Questions

Download the latest binary from the Sigstore GitHub releases page, verify its checksum, and move it to /usr/local/bin. Most package managers like apt or dnf also provide cosign packages for easier updates and dependency management on modern distributions.

Yes, use keyless signing with Fulcio and Rekor. Cosign authenticates via OIDC providers like GitHub Actions or Google Cloud, storing signatures in the public transparency log without requiring local key storage or manual secret rotation.

Cosign uses the Sigstore ecosystem with keyless support and OCI artifact storage. Notary v2 relies on traditional X.509 PKI and separate trust servers. Cosign integrates better with CI pipelines and requires less infrastructure overhead for most teams.

Run cosign verify with the appropriate certificate identity and issuer flags. Kubernetes admission controllers like Kyverno or OPA Gatekeeper can automate this check, blocking unsigned or tampered images from entering your cluster during deployment.

Signatures attach as separate OCI artifacts alongside the original image tag. They share the same repository but use a distinct digest-based tag format, keeping the base image immutable while maintaining cryptographic proof of authenticity and integrity.

Yes, Cosign supports any OCI-compliant registry including AWS ECR, GitLab Container Registry, and Harbor. Configure authentication via docker login or credential helpers before signing or verifying to ensure proper access permissions.

Yes.

Use the official sigstore/cosign-installer action to set up the CLI. Authenticate using GITHUB_TOKEN for keyless signing. Store signatures automatically by pushing to the same GHCR repository within your workflow job steps.

Keyless signing embeds short-lived certificates tied to the build timestamp. Verification checks the Rekor transparency log entry rather than current token validity, so expired tokens do not invalidate previously signed artifacts or break supply chain trust.

Yes.

Generate new key pairs, resign all active images, update admission controller policies to accept both old and new identities temporarily, then revoke the compromised key. Document the incident and audit Rekor logs for unauthorized signing attempts.

Check that your system clock is synchronized and CA certificates are updated. Verify the correct --certificate-identity and --certificate-oidc-issuer flags match the signer. Ensure network access to Fulcio and Rekor endpoints is not blocked by firewalls.

No.

Deploy Kyverno or OPA Gatekeeper with ClusterImageVerification policies specifying trusted issuers and identities. These admission controllers reject pods referencing unsigned images before scheduling, providing runtime enforcement of your software supply chain security requirements.

Yes, Cosign supports appending multiple signatures to a single image digest. Each team signs independently using their own identity. Verification policies can require specific combinations or thresholds of valid signatures before accepting the artifact for production deployment.