
Table of Contents
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.
cosign sign --yes <image>. This attaches an immutable signature to the registry artifact using short-lived certificates from Fulcio, eliminating long-lived private key management while providing verifiable proof of origin and integrity for supply chain security.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.
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.
| Feature | Cosign (Sigstore) | Notary v2 / ORAS | Docker Content Trust |
|---|---|---|---|
| Key Management | Keyless (OIDC) default | X.509 / KMS / Keyless | Docker-managed keys only |
| Transparency Log | Rekor (built-in) | Optional / Pluggable | None |
| Registry Compatibility | Universal (OCI referrers) | OCI-native (ORAS) | Docker Hub / Limited |
| Kubernetes Enforcement | Kyverno / Policy Controller | Ratify / OPA | Docker daemon only |
| Attestations (SBOM, SLSA) | Native cosign attest | Via ORAS artifacts | Not supported |
| Community Momentum (2026) | De facto standard | Growing (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.
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.