This demo shows how to enforce cryptographic provenance verification for container images in Kubernetes using the Sigstore Policy Controller and Chainguard Images.
Provenance is cryptographic proof of where a container image came from and how it was built. Unlike a simple signature that says "this is authentic," provenance tells you:
- Who built it - The identity of the build system (verified via OIDC)
- Where it was built - The specific CI/CD pipeline and repository
- How it was built - Build parameters, dependencies, and toolchain
- What's inside - Software Bill of Materials (SBOM)
All of this is cryptographically signed and recorded in a tamper-evident transparency log.
Without provenance, you're trusting that:
- The image tag hasn't been overwritten with malicious content
- The image actually came from the claimed source
- The build process wasn't compromised
With provenance verification, you have cryptographic proof of the entire supply chain.
Every Chainguard image includes:
| Attestation | Purpose |
|---|---|
| Signature | Proves the image is authentic and unmodified |
| SLSA Provenance | Proves where and how the image was built |
| SBOM | Proves what software packages are inside |
All attestations are signed using Sigstore's keyless signing and recorded in the Rekor transparency log.
- Kubernetes cluster with admin access
kubectlconfigured to access your clusterhelmCLI installedcosignCLI (for manual verification)jq(optional, for readable output)
Before setting up cluster policies, let's manually verify a Chainguard image's provenance.
cosign tree cgr.dev/chainguard/nginx:latestOutput shows the image's signatures and attestations:
π¦ Supply Chain Security Related artifacts for an image: cgr.dev/chainguard/nginx:latest
βββ πΎ Attestations for an image tag: cgr.dev/chainguard/nginx:sha256-...
βββ π sha256:... (SLSA Provenance)
βββ π sha256:... (SBOM)
βββ π sha256:... (Build Config)
βββ π Signatures for an image tag: cgr.dev/chainguard/nginx:sha256-...
βββ π sha256:...
cosign verify \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity=https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/heads/main \
cgr.dev/chainguard/nginx:latestThis verifies:
- The signature is valid
- The signing certificate was issued by the trusted OIDC provider (GitHub Actions)
- The signer identity matches Chainguard's release workflow
- The signature is recorded in the Rekor transparency log
cosign verify-attestation \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity=https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/heads/main \
--type=https://slsa.dev/provenance/v1 \
cgr.dev/chainguard/nginx:latestThis proves the image has a valid SLSA provenance attestation showing exactly how it was built.
cosign verify-attestation \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity=https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/heads/main \
--type=https://spdx.dev/Document \
cgr.dev/chainguard/nginx:latestThis proves the image has a verified Software Bill of Materials.
Try the same commands on an upstream Docker Hub image:
cosign tree docker.io/library/nginx:latestMost upstream images have no signatures or attestations - you're trusting blindly.
Now let's enforce provenance verification automatically for all pods.
# Create namespace
kubectl create namespace cosign-system
# Add Helm repo
helm repo add sigstore https://sigstore.github.io/helm-charts
helm repo update
# Install
helm install policy-controller -n cosign-system sigstore/policy-controller --devel
# Wait for ready
kubectl -n cosign-system wait --for=condition=Available deployment/policy-controller-webhook --timeout=120s# Create namespace
kubectl create namespace chainguard-test
# Enable policy enforcement
kubectl label namespace chainguard-test policy.sigstore.dev/include=truekubectl apply -f policies/chainguard-provenance-required.yamlThis policy requires:
- Images must match
cgr.dev/chainguard/** - Images must have a valid keyless signature
- Images must have a verified SLSA provenance attestation
View the policy:
kubectl get clusterimagepolicy chainguard-provenance-required -o yamlTest 1: Chainguard Image with Provenance (ALLOWED)
kubectl run nginx-chainguard --image=cgr.dev/chainguard/nginx:latest -n chainguard-testExpected: pod/nginx-chainguard created
The pod is admitted because:
- β Image matches policy glob
cgr.dev/chainguard/** - β Signature verified against Chainguard's OIDC identity
- β SLSA provenance attestation verified
Test 2: Upstream Image without Provenance (DENIED)
kubectl run nginx-upstream --image=docker.io/library/nginx:latest -n chainguard-testExpected:
Error from server (BadRequest): admission webhook "policy.sigstore.dev" denied the request:
validation failed: no matching policies: spec.containers[0].image
index.docker.io/library/nginx:latest@sha256:...
The pod is denied because it has no matching policy (and no provenance to verify).
Test 3: Other Images without Provenance (DENIED)
kubectl run redis --image=redis:latest -n chainguard-test
kubectl run alpine --image=alpine:latest -n chainguard-testAll denied - only images with verified Chainguard provenance are allowed.
kubectl delete pod nginx-chainguard -n chainguard-test| File | Description |
|---|---|
policies/chainguard-provenance-required.yaml |
Requires SLSA provenance - verifies signature AND build provenance |
policies/chainguard-signed-images.yaml |
Signature only - verifies keyless signature (no attestation check) |
policies/chainguard-static-allow.yaml |
Allow only - no verification (not recommended for production) |
spec:
images:
- glob: "cgr.dev/chainguard/**"
authorities:
- name: chainguard-provenance
keyless:
url: https://fulcio.sigstore.dev
identities:
- issuer: https://token.actions.githubusercontent.com
subject: https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/heads/main
ctlog:
url: https://rekor.sigstore.dev
attestations:
- name: must-have-slsa-provenance
predicateType: https://slsa.dev/provenance/v1 # <-- Requires provenance!Key components:
- keyless - Uses Sigstore's keyless signing (no keys to manage)
- identities - Verifies the signer is Chainguard's GitHub Actions workflow
- ctlog - Checks the Rekor transparency log
- attestations - Requires SLSA provenance attestation
- Provenance β Signature - A signature proves authenticity; provenance proves the entire build chain
- Keyless signing - No keys to manage, rotate, or secure
- Transparency logs - All signatures are publicly auditable
- Policy enforcement - Kubernetes automatically rejects images without valid provenance
- Supply chain security - Cryptographic proof from source code to running container
kubectl get pods -n cosign-system
kubectl logs -n cosign-system -l app.kubernetes.io/name=policy-controllerkubectl get ns -l policy.sigstore.dev/include=truekubectl get clusterimagepolicy
kubectl describe clusterimagepolicy chainguard-provenance-required- SLSA Framework - Supply chain Levels for Software Artifacts
- Sigstore - Keyless signing for software artifacts
- Chainguard Images - Secure container images
- Policy Controller Docs
- Verifying Chainguard Images
MIT