Cosign Image Signing & Verification
Enforce container image signing and verification using Cosign/Sigstore with Kyverno admission policies to prevent deployment of unsigned images.
Overview
This template enforces container image signing and verification using Cosign (part of the Sigstore project) combined with Kyverno admission control. Every container image deployed to your cluster must be cryptographically signed, preventing attackers from injecting malicious images by compromising a registry or mutating tags.
Security threat addressed: Without image signature verification, an attacker who gains access to your container registry can replace legitimate images with malicious ones. Tag mutability means myapp:v1.0 can be silently overwritten with malware. Cosign signatures guarantee the image was built by your trusted CI/CD pipeline.
When to use: Implement this in production clusters after establishing a signing workflow in your CI/CD pipeline. Start by signing images with cosign sign, then deploy these policies to enforce verification.
Threat Model
- Registry compromise defense: Even if an attacker gains write access to your registry, unsigned or incorrectly signed images are rejected at admission.
- Tag mutability protection: Signatures are tied to the image digest (SHA256), not the tag. Overwriting a tag breaks the signature.
- Supply chain integrity: Guarantees that only images built by your authorized CI/CD pipelines are deployed.
MITRE ATT&CK:
- T1195.002 — Supply Chain Compromise: Prevents deployment of compromised images from the supply chain.
- T1525 — Implant Internal Image: Blocks deployment of attacker-crafted images that weren’t built by trusted pipelines.
YAML Source
# Kyverno ClusterPolicy — Verify image signatures (key-based)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
labels:
app.kubernetes.io/name: k8s-security
app.kubernetes.io/part-of: k8s-security-pro
app.kubernetes.io/managed-by: k8s-security-pro
annotations:
policies.kyverno.io/title: Verify Image Signatures
policies.kyverno.io/category: Supply Chain Security
policies.kyverno.io/severity: high
policies.kyverno.io/description: >-
Verifies that all container images are signed with Cosign using the
configured public key. Unsigned or incorrectly signed images are blocked.
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "registry.example.com/*" # ACTION REQUIRED: Your registry
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
ACTION REQUIRED: Paste your Cosign public key here
Generated with: cosign generate-key-pair
-----END PUBLIC KEY-----
---
# Kyverno ClusterPolicy — Verify keyless signatures (Sigstore)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-keyless-signature
labels:
app.kubernetes.io/name: k8s-security
app.kubernetes.io/part-of: k8s-security-pro
app.kubernetes.io/managed-by: k8s-security-pro
annotations:
policies.kyverno.io/title: Verify Keyless Signatures (Sigstore)
policies.kyverno.io/category: Supply Chain Security
policies.kyverno.io/severity: high
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-keyless
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/your-org/*" # ACTION REQUIRED: Your GHCR org
attestors:
- count: 1
entries:
- keyless:
subject: "https://github.com/your-org/*" # ACTION REQUIRED
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
---
# Kyverno ClusterPolicy — Require image digest (no tags)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-image-digest
labels:
app.kubernetes.io/name: k8s-security
app.kubernetes.io/part-of: k8s-security-pro
app.kubernetes.io/managed-by: k8s-security-pro
spec:
validationFailureAction: Enforce
rules:
- name: require-digest
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Images must use a digest (sha256) instead of a tag."
pattern:
spec:
containers:
- image: "*@sha256:*"
Deployment
- Generate Cosign keys:
cosign generate-key-pair - Sign your images in CI/CD:
cosign sign --key cosign.key registry.example.com/myapp@sha256:abc123 - Update the public key in the ClusterPolicy
- Apply policies:
kubectl apply -f 25_cosign_image_signing.yaml - Test with an unsigned image (should be rejected)
Verification
# Check policies are active
kubectl get clusterpolicy
# Test with signed image (should pass)
kubectl run test --image=registry.example.com/signed-image@sha256:abc123
# Test with unsigned image (should fail)
kubectl run test-unsigned --image=registry.example.com/unsigned-image:latest
# Expected: admission webhook denied the request
# Verify signature manually
cosign verify --key cosign.pub registry.example.com/myapp@sha256:abc123
Further Reading
- Kubernetes Supply Chain Security — Complete supply chain hardening guide
- Kubernetes Security Tools Compared — Cosign vs other signing tools
- Template 11: Kyverno Disallow Latest Tag — Enforce tag policies
- Template 19: Kyverno Policy Bundle — Additional Kyverno security policies
- Template 22: Trivy Vulnerability Scanning — Scan images for vulnerabilities
Get Full Access to This Template
This template is included in the Enterprise tier and above.
View Pricing Plans