Sign and verify images with Cosign and Kyverno
Keyless image signing with cosign in CI and signature verification at the admission controller, so unsigned or tampered images simply stop scheduling.
Scanning tells you an image was clean when it left CI. It says nothing about whether the image running in your cluster is the one CI actually built. Signing closes that gap: Cosign signs the digest in the pipeline, Kyverno verifies the signature at admission, and an unsigned or tampered image simply never schedules. That is the difference between trusting a tag and trusting a cryptographic chain from build to kubelet.
Modern Cosign is keyless — there is no private key to store or rotate. It exchanges your CI's OIDC token for a short-lived certificate from Fulcio and records the signature in the Rekor transparency log. This walkthrough signs by digest in GitHub Actions, verifies with Kyverno, and shows what happens when someone deploys an unsigned image. For the full supply-chain sequence, see Software supply chain security.
Unsigned images should never become Pods. Digest, not tag.
Sign the digest, never a tag. Tags are mutable; a signature on `:latest` guarantees nothing.
cosign sign --yes registry.io/myapp@sha256:9f2a1c...Generating ephemeral keys ...Retrieving signed certificate from Fulcio ...Requesting OIDC token (github-actions) ...tlog entry created: rekor.sigstore.dev index 74839201 cosign verify --certificate-oidc-issuer=github registry.io/myapp@sha256:9f2a1c...Verified OK — cert identity ci@myorg, issuer GitHub ActionsSign in the pipeline
Sign the digest, never a tag. Cosign reads the CI's OIDC identity automatically when COSIGN_EXPERIMENTAL is enabled, so there are no secrets in this step — the trust anchor is your identity provider, not a key file in the repo. Grant the workflow id-token: write so GitHub can mint the token Fulcio expects.
permissions:id-token: writecontents: read- name: Sign image (keyless)env:COSIGN_EXPERIMENTAL: "1"run: |DIGEST=$(crane digest "$REGISTRY/$IMAGE:$TAG")cosign sign --yes "$REGISTRY/$IMAGE@$DIGEST"
Verify at admission with Kyverno
A ClusterPolicy with verifyImages intercepts every pod, checks the signature against the expected OIDC identity, and rejects anything that does not match. Set validationFailureAction: Enforce — in Audit it only logs, and logging is not a control when an attacker can still schedule. Pin the issuer URL and subject pattern to your org's CI identity.
apiVersion: kyverno.io/v1kind: ClusterPolicymetadata:name: verify-imagesspec:validationFailureAction: Enforcerules:- name: require-signaturematch:any:- resources:kinds: ["Pod"]verifyImages:- imageReferences: ["registry.io/*"]attestors:- entries:- keyless:issuer: "https://token.actions.githubusercontent.com"subject: "https://github.com/myorg/*"
Deploy an image CI never signed and admission stops it cold — the pod object is never created. That failure mode is what you want: tampering at the registry or a developer pulling an unvetted image from Docker Hub should never reach a running pod. Test the deny path in staging before you flip Enforce in production, and keep Audit logs for a week so you know what would have broken.
Roll out gradually: start with validationFailureAction: Audit on one namespace, confirm only expected unsigned images appear in policy reports, then switch to Enforce cluster-wide. Pair verifyImages with image digest pinning in Deployments so :latest cannot bypass the check by mutating behind your back.
kubectl run demo --image=registry.io/unsigned:latestkyverno admission webhook: verify-images Error: admission webhook denied the request:verify-images: failed to verify image registry.io/unsigned:latest: no matching signatures found images signed by CI schedule normallyWhere this goes next
The same keyless flow signs attestations — attach an SBOM or a SLSA provenance statement and verify those at admission too. That is the jump from 'this image is ours' to 'this image was built from this commit by this pipeline'. The Software supply chain security course covers signing, SBOMs, provenance, and SLSA from commit to cluster.
Go deeper in a courseSoftware supply chain securitySigning, SBOMs, provenance and SLSA — commit to cluster.View course