L15. Image Signing and Verification with Cosign and Sigstore
Video generating
Check back soon for the video lesson on Image Signing and Verification with Cosign and Sigstore
Image scanning tells you what is wrong with an image. Image signing tells you whether an image is trustworthy. Learn how to sign images with Cosign and verify signatures at admission time.
Why Sign Images?
Scanning detects known vulnerabilities. Signing proves provenance: this image was built by your pipeline, from your source code, and has not been tampered with. Without signing, an attacker who compromises your registry can replace images with malicious versions.
The Sigstore Ecosystem
Sigstore is an open-source project that provides free code signing infrastructure:
| Component | Purpose |
|---|---|
| Cosign | Signs and verifies container images |
| Fulcio | Issues short-lived signing certificates (no key management) |
| Rekor | Transparency log that records all signing events |
Signing with Cosign
Key-based signing (self-managed keys):# Generate a keypair
cosign generate-key-pair# Sign an image
cosign sign --key cosign.key myregistry.io/myapp:v1.0
# Verify a signature
cosign verify --key cosign.pub myregistry.io/myapp:v1.0
Keyless signing (recommended for CI/CD):
cosign sign myregistry.io/myapp:v1.0In keyless mode, Cosign uses Fulcio to issue a short-lived certificate based on your OIDC identity (GitHub Actions, GitLab CI, Google). The signing event is recorded in Rekor's transparency log. No keys to manage, rotate, or protect.
CI/CD Integration
Sign images in your build pipeline after scanning passes:
# GitHub Actions
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-slate-300">name: Sign image</li>
</ul>
env:
COSIGN_EXPERIMENTAL: "1"
run: |
cosign sign myregistry.io/myapp:${{ github.sha }} \
-a "repo=${{ github.repository }}" \
-a "workflow=${{ github.workflow }}" \
-a "commit=${{ github.sha }}"Annotations (-a) attach metadata like the source repo, workflow name, and commit SHA to the signature for audit purposes.
Verification at Admission
Use the Sigstore Policy Controller or Kyverno to verify signatures before pods are created:
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-signing
spec:
images:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-slate-300">glob: "myregistry.io/**"</li>
</ul>
authorities:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-slate-300">keyless:</li>
</ul>
url: https://fulcio.sigstore.dev
identities:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-slate-300">issuer: https://token.actions.githubusercontent.com</li>
</ul>
subject: "https://github.com/myorg/myapp/.github/workflows/build.yml@refs/heads/main"This policy requires that all images from myregistry.io are signed by a specific GitHub Actions workflow running on the main branch. Images signed from a fork or a different branch are rejected.
Software Bill of Materials (SBOM)
Cosign can also attach SBOMs to images:
# Generate SBOM
trivy image --format spdx-json myapp:v1.0 > sbom.json# Attach to image
cosign attach sbom --sbom sbom.json myregistry.io/myapp:v1.0
SBOMs provide a complete inventory of software components in the image, enabling vulnerability tracking over the image's lifetime.
- ✓Scanning detects known vulnerabilities; signing proves provenance and integrity of the image
- ✓Keyless signing with Fulcio uses OIDC identity (GitHub Actions, GitLab CI) and eliminates key management
- ✓Rekor transparency logs provide a tamper-proof record of all signing events
- ✓Sigstore Policy Controller and Kyverno can verify image signatures at admission time before pods are created
- ✓SBOMs attached to images with Cosign provide a complete software inventory for lifecycle vulnerability tracking
1. What problem does image signing solve that image scanning does not?
2. What is the advantage of keyless signing with Cosign and Fulcio?
3. How can you ensure that only images built from your main branch are deployed?