A scan proves what is in an artifact; a signature proves who built it and that it has not been tampered with. Sign container images keylessly with Sigstore cosign and verify them before anything runs.
Scanning answers "is this artifact safe?"; signing answers "is this the exact artifact my pipeline built, unmodified?" Without a signature, an attacker who compromises your registry can swap your image for theirs and every downstream deploy trusts it. A verified signature binds the artifact to its builder and guarantees integrity — the foundation for "only run what we made."
Scanning and signing answer different questions:
SCAN -> "what's inside, and is any of it known-vulnerable?"
SIGN -> "was this exact bytes-for-bytes artifact produced by us,
and has anyone changed it since?"
Attack signing defeats: registry compromise / image swap / MITM.
attacker replaces registry/app:1.5.0 with a backdoored image
-> unsigned: cluster pulls and runs it, none the wiser
-> signed + verified: signature doesn't match -> admission REJECTS itTraditional signing means managing long-lived private keys — themselves a secret to leak. Sigstore's keyless flow instead ties a signature to a short-lived identity (your CI's OIDC token), records it in a public transparency log (Rekor), and needs no key for you to store. In CI, the pipeline signs as itself; there is no signing key to steal.
# in CI (GitHub Actions), sign the image keylessly as the workflow identity
cosign sign --yes \
registry.example.com/app@sha256:abc123...
# cosign opens a browser/OIDC flow (or uses the CI OIDC token),
# gets a short-lived cert from Fulcio tied to your identity,
# and records the signature in the Rekor transparency log.
# No private key on disk, nothing to rotate or leak.
# always sign by DIGEST (@sha256:...), never a mutable tag like :latest.A signature is only worth anything if something checks it. Verification asserts both that the signature is valid and that it came from the identity you expect — your CI's workflow, not just "someone." Pin the expected identity and issuer so a valid signature from the wrong source is still rejected.
# verify the image was signed by OUR pipeline, not just anyone
cosign verify \
--certificate-identity-regexp 'https://github.com/my-org/.+' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
registry.example.com/app@sha256:abc123...
# a valid signature from the WRONG identity still fails — that's the point.
# Enforce this at the cluster with an admission policy (next: SLSA + policy),
# so unverified images can never be scheduled.Cosign can sign and attach any artifact to an image as an attestation — most usefully the SBOM and, next lesson, the build provenance. A signed, attached SBOM means the inventory itself is tamper-evident and travels with the image, so a verifier can trust "this is the real bill of materials for this exact image."
# attach the SBOM to the image as a signed attestation
cosign attest --yes \
--predicate sbom.cdx.json \
--type cyclonedx \
registry.example.com/app@sha256:abc123...
# later, anyone can pull + verify the SBOM is authentic for this image:
cosign verify-attestation --type cyclonedx \
--certificate-identity-regexp 'https://github.com/my-org/.+' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
registry.example.com/app@sha256:abc123...