Safeguard
Container Security

Signing and verifying container images with Sigstore/cosign

How cosign and Sigstore replace long-lived signing keys with short-lived, identity-based certificates and a public transparency log for containers.

Vikram Iyer
Cloud Security Engineer
7 min read

In March 2021, the Codecov breach showed what happens when there's no way to verify that a published artifact matches what a build system actually produced: attackers modified the Bash Uploader script and it went undetected for roughly two months, silently exfiltrating credentials from thousands of CI pipelines. Container images have the same trust gap. A registry pull gives you bytes; it doesn't tell you who built them, whether they match a specific commit, or whether they've been swapped after the fact. Docker Content Trust (Notary v1) tried to solve this a decade ago but required teams to manage and rotate long-lived signing keys, and adoption never took off. Sigstore, launched in 2021 and now a graduated project under the Linux Foundation's OpenSSF, and its CLI tool cosign, fix this with short-lived, identity-based signatures and a public transparency log. Here's how the mechanism actually works, and where it stops being enough.

What is cosign and how is it different from Docker Content Trust?

Cosign is the signing and verification CLI for Sigstore, and it replaces long-lived private keys with identity-based, short-lived certificates. Docker Content Trust, built on Notary v1, required each publisher to generate and safeguard a root signing key indefinitely — lose it, and you lose the ability to prove provenance for every image you ever signed; leak it, and an attacker can forge signatures for your entire history. Cosign, first released by the Sigstore project in 2021 and reaching a stable 2.0 in January 2023, instead signs OCI artifacts (container images, but also Helm charts, SBOMs, and other blobs) and stores the signature as a companion artifact directly in the registry, tagged alongside the image digest. It supports both traditional key pairs (cosign generate-key-pair) and, more importantly, "keyless" signing that never touches a persistent private key at all.

How does keyless signing work if there's no private key to manage?

Keyless signing works by issuing a certificate valid for about 10 minutes, tied to an OIDC identity, instead of a long-lived key. When a developer or CI job runs cosign sign, cosign requests a short-lived code-signing certificate from Fulcio, Sigstore's certificate authority, authenticating with an OIDC token from a provider like GitHub Actions, Google, or a corporate SSO. Fulcio issues a certificate binding that verified identity (e.g., repo:acme/payments-api:ref:refs/heads/main) to a fresh ephemeral key pair generated in memory. Cosign uses that key to sign the image digest, then immediately discards the private key — it's never written to disk. The signature, certificate, and a timestamp are then recorded in Rekor, Sigstore's transparency log built on Trillian (the same Merkle-tree technology behind Certificate Transparency). Because the certificate expires in minutes and every signing event is logged publicly, there's no long-lived secret to steal and no way to backdate a forged signature without it showing up in Rekor's append-only log.

How do you actually sign a container image with cosign?

You sign an image with a single command pointed at its registry reference, and cosign resolves that reference to an immutable digest before signing. In CI, keyless signing looks like this:

cosign sign registry.example.com/payments-api:1.4.2

Cosign opens a browser-based (or device-flow) OIDC login if run interactively, or picks up the ambient OIDC token automatically inside supported CI systems like GitHub Actions. It resolves 1.4.2 to its underlying sha256:... digest so the signature is bound to exact content, not a mutable tag, requests the Fulcio certificate, signs, and pushes the signature to the registry and the entry to Rekor — typically finishing in a few seconds. For teams not ready for keyless workflows, cosign sign --key cosign.key registry.example.com/payments-api:1.4.2 uses a static key pair instead, with the private key protected by a password or stored in a KMS (cosign has native support for AWS KMS, GCP KMS, Azure Key Vault, and HashiCorp Vault).

How do you verify a signed image, and what stops a tampered one from running?

You verify with cosign verify and constrain it to the exact identity that should have signed the image, and any mismatch causes the command to fail with a non-zero exit code. For keyless signatures, verification requires both the expected signer identity and the OIDC issuer:

cosign verify \
  --certificate-identity="https://github.com/acme/payments-api/.github/workflows/release.yml@refs/heads/main" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  registry.example.com/payments-api:1.4.2

Cosign fetches the signature and certificate from the registry, checks the certificate chains to Fulcio's root, confirms the identity fields match exactly what you specified, and cross-checks the Rekor log entry to make sure the signature was actually publicly logged at signing time (blocking a scenario where someone signs offline and never logs it). This step is what makes signing enforceable rather than advisory: on its own, a manual cosign verify run is just a spot check. The real control point is admission — tools like Sigstore's Policy Controller or Kyverno's verifyImages policy plug into the Kubernetes admission chain and reject any pod spec referencing an image that doesn't carry a valid signature matching an allow-listed identity, so an unsigned or wrongly-signed image never reaches a running node.

What does cosign not protect against?

Cosign proves who built an image and that it hasn't changed since, but it says nothing about what's inside that image. A perfectly signed, perfectly provenance-tracked image can still ship a vulnerable dependency — Log4Shell (CVE-2021-44228), disclosed December 10, 2021, was baked into thousands of container images that were, in many cases, still cryptographically "trustworthy" by any signing standard, because the base layers and application code hadn't been tampered with; the flaw was in a legitimate, unmodified library. Signing also doesn't stop a compromised build pipeline from producing and legitimately signing a malicious artifact in the first place — this is why Sigstore signatures are increasingly paired with SLSA provenance attestations (cosign attest --predicate slsa-provenance.json), which record what source commit, builder, and build parameters actually produced the artifact, not just that a trusted identity vouched for it afterward. Signing is an integrity and identity control; it has to sit alongside vulnerability scanning, SBOM analysis, and build-pipeline hardening, not replace them.

Is cosign signing actually required by any regulation or standard?

Not by name, but U.S. federal policy already requires the provenance guarantees cosign is built to provide. Executive Order 14028 (May 12, 2021) directed NIST to define secure software development practices, which became NIST SP 800-218 (the Secure Software Development Framework); federal agencies now require self-attestation or third-party attestation of SSDF compliance from software vendors, and provenance/integrity verification is a core SSDF practice (PW.4, PO.3). SLSA (Supply-chain Levels for Software Artifacts), the framework Google, GitHub, and others formalized through OpenSSF, defines four levels of build integrity, and SLSA Level 2 and above explicitly require signed provenance — exactly the artifact cosign's attest command produces. Registries have followed: GitHub Container Registry, Amazon ECR, Google Artifact Registry, and Docker Hub all support storing and serving cosign signatures natively as OCI artifacts, so there's no proprietary format to work around.

How Safeguard Helps

Cosign and Rekor tell you an image is authentic; they don't tell you whether the vulnerabilities inside it can actually be triggered by your running services. Safeguard layers on top of signed images with reachability analysis, tracing whether a flagged CVE's vulnerable function is actually on an execution path your code calls, so teams stop burning remediation time on findings that a signature check would never catch and a scanner would over-flag. Griffin, Safeguard's AI analysis engine, correlates signature and provenance metadata with SBOM data — whether generated at build time or ingested from an existing pipeline — to flag images where the signer identity, build source, or dependency graph doesn't match expected policy. When a reachable, exploitable issue is confirmed in a signed image, Safeguard can open an auto-fix PR against the affected manifest or lockfile, so the fix ships through the same signed, provenance-tracked pipeline instead of a manual, unverified patch.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.