How to Verify Signed Container Images with Cosign and Policy Controller
A fail-safe Sigstore workflow for signing immutable image digests, constraining identities, enforcing admission policy, and preserving rollback access.
A valid container signature proves only that some accepted identity or key signed a particular artifact. Production trust requires more: immutable digest references, a policy that constrains which identity may sign which repository, transparency evidence, controlled admission rollout, and an emergency path that cannot quietly become the default.
Sign the digest produced by the build
Authenticate Cosign through the CI platform’s supported OpenID Connect flow and sign an immutable image reference. Resolve and record the digest before policy enforcement:
IMAGE='registry.example.com/payments/api@sha256:0123456789abcdef...'
cosign sign "$IMAGE"
Keyless signing asks Fulcio for a short-lived certificate tied to an OIDC identity and records evidence through Sigstore’s transparency infrastructure. It removes a long-lived repository signing key, but it does not mean “trust any keyless signature.” Verification must constrain certificate issuer and subject or workflow identity.
cosign verify \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
--certificate-identity='https://github.com/example/payments/.github/workflows/release.yml@refs/heads/main' \
"$IMAGE"
Use the exact identity form emitted by the current CI integration and inspect a real verified signature before encoding policy. Branch and tag workflows can produce different subjects. Pin the Cosign release and review changes to certificate identity semantics.
Separate artifact selection from verification
Signing a mutable tag does not freeze what a deployment later pulls. Record the digest in the deployment manifest or have trusted automation replace a reviewed tag with a digest. Admission should verify the digest that the runtime will use, not merely check that some historical digest once shared the same tag.
Limit registry write access so an attacker who compromises a deployer cannot replace both the artifact selection and policy. Builder, signer, manifest updater, and admission administrator are separate privileges even when one organization operates all of them.
Install Policy Controller in audit scope first
Sigstore Policy Controller evaluates ClusterImagePolicy resources during Kubernetes admission. Install the version supported by the target Kubernetes release and confirm its webhook certificates, replicas, namespace selectors, and failure policy before adding enforcement.
A policy can match image repositories and require keyless signatures from a constrained issuer and subject. The exact CRD schema evolves, so start from the documentation shipped with the installed controller rather than copying an old manifest. Keep the intended logic conceptually narrow:
images: registry.example.com/payments/**
issuer: https://token.actions.githubusercontent.com
identity: the reviewed release workflow on the protected main branch
Apply it first to a test namespace or audit/warn path. Exercise a valid signed digest, unsigned image, signature from the wrong repository, valid certificate from the wrong workflow, changed tag, registry outage, transparency-service outage, controller restart, and rollback image.
Decide availability semantics deliberately
Admission verification is on the API write path. Fail-closed prevents unverified deployment during dependency failure but can block emergency recovery. Fail-open preserves deployment availability while creating a trust gap. Match the webhook failurePolicy, cached verification behavior, and external network dependencies to the threat model, then monitor every bypass or error.
Mirror required trust material and document offline verification where supported. An emergency exception should identify workload, digest, reason, approver, and expiry. A namespace permanently excluded from policy is not a break-glass procedure; it is an unmonitored second deployment path.
Verify more than the signature
Cosign can also attach attestations, but accepting any attestation is insufficient. Check predicate type, subject digest, builder identity, source repository/ref, and the policy version that evaluates it. SLSA provenance, SBOMs, vulnerability results, and signatures answer different questions and should not be collapsed into one “secure” badge.
Record signature bundle, certificate chain, Rekor evidence or bundle, policy decision, image digest, deployment identity, and admission-controller version for incident response. Preserve enough data to verify old releases after certificates, workflows, or online services change.
The rollout is complete when a legitimate release and rollback succeed, each negative case is rejected for the expected reason, and an operator can explain exactly which identity was trusted—not merely report that “Cosign passed.”
Related:
- How to Order Argo CD Deployments with Sync Phases, Waves, and Hooks
- How to Back Up and Restore Kubernetes etcd Without Creating a False Recovery Plan
Sources: