Git, CI/CD & GitOpsLXIX · Artifact SigningVerification
Verify on deploy — the cluster admission controller, policy-controller
What you'll learn
- Configure a Kubernetes admission controller to verify Sigstore signatures on pod creation
- Write a ClusterImagePolicy that pins the OIDC subject and the issuer
- Distinguish the Sigstore policy-controller from kyverno and Connaisseur for verification
- Recognise the production failure modes of admission-time verification (policy object, failurePolicy, namespacing)
Prerequisites
Practice
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A signature on a container image is metadata. The metadata becomes load-bearing only when something checks it before the pod is admitted. The cluster admission controller is the production pattern that makes the signature reject an unsigned or wrongly-signed image at deploy time. The signature is the data structure; the admission controller is the policy that reads the data structure.
The verifier at the deployment boundary
The verification side of the Sigstore stack has three production-grade options:
- Sigstore policy-controller. A Kubernetes admission
controller shipped as a
ValidatingWebhookthat verifies Sigstore signatures againstClusterImagePolicyresources. The native Sigstore option. - Kyverno. A general-purpose policy engine with a
verifyImagesrule type that callscosign verifyagainst a pinned identity. The broader policy engine. - Connaisseur. A Kubernetes admission controller that validates signatures against a configurable policy. The pre-policy-controller option.
flowchart LR
A["Pod create"] --> B["Admission webhook"]
B --> C["policy-controller"]
C --> D["ClusterImagePolicy"]
D --> E["cosign verify"]
E --> F["Accept"]
E --> G["Reject"]
C --> H["kyverno"]
H --> E
C --> I["Connaisseur"]
I --> E
The three options differ in syntax and in the policy objects they consume, but the underlying check is the same: fetch the signature, verify against the pinned identity, admit or reject.
The ClusterImagePolicy object
The Sigstore policy-controller consumes ClusterImagePolicy
resources. The resource is the cluster’s policy; the resource
is the artifact the team operates:
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: cosigned-prod
spec:
images:
- glob: "ghcr.io/org/*"
authorities:
- keyless:
identities:
- issuer: https://token.actions.githubusercontent.com
subjectRegExp: "^https://github.com/org/repo/.github/workflows/build.yml@refs/heads/main$"
ctlog:
url: https://rekor.sigstore.dev
The policy pins:
- The image glob. The pattern the policy applies to.
ghcr.io/org/*matches every image in the org’s registry. - The identities. The OIDC issuer and the OIDC subject pattern. The wildcard on the subject is a wildcard the team must own; a tighter pattern is stronger.
- The transparency log. The Rekor URL the policy checks against. The public-good instance is the default.
The policy is cluster-scoped; the policy applies to every pod that matches the glob. The team operates the policy object as a Kubernetes resource.
The verification flow
The admission controller runs on every pod creation:
- Pod creation request. The kubelet submits the pod spec to the API server.
- Admission webhook fires. The webhook intercepts the request.
- Controller resolves the image. The controller extracts the image reference and resolves to a digest.
- Fetch the signature. The controller fetches the signature from the registry.
- Verify against the policy. The controller checks the signature against the pinned identities and the Rekor entry.
- Accept or reject. The controller returns the decision to the API server; the pod is admitted or rejected.
The verification is synchronous. The pod is not created until the verification completes. The deploy is blocked if the verification fails.
Production failure modes
The admission-time verification has three failure modes the production team must own:
- Policy object missing. The ClusterImagePolicy is absent or has been deleted. The controller has no policy; the verification passes for every image. The fix is to store the policy in Git and reconcile via GitOps.
- FailurePolicy misconfigured. The webhook’s
failurePolicyisIgnore. A broken connection admits every pod. The fix is to setfailurePolicy: Fail. - Namespacing. The policy is bound to a namespace; the production pods are in a different namespace. The fix is to scope the policy correctly (cluster-wide for cluster-wide images, per-namespace for per-namespace policy).
Production discipline
- Install the policy-controller (or kyverno) and ship the ClusterImagePolicy in the same GitOps repo as the manifests. The policy object is a Kubernetes resource; the resource is a GitOps artifact.
- Set
failurePolicy: Failon the admission webhook. A permissive failure mode is a verification policy that fails open. - Pin the OIDC subject to the specific workflow path. A wildcard is a verification policy that can be satisfied by any workflow in the org.
- Periodically test the policy with a deliberately non-signed image. The policy is only as good as the test that exercises its failure mode.
- Monitor the admission-time verification result. A spike in rejections is the operational signal that something is wrong with the build pipeline.
Cross-course references
- Git, CI/CD & GitOps — Part LXI-04 (Partial Deployment and Resumability) is the broader CI/CD failure pattern; the admission controller is the deployment-side correlate.
- Container Security for Production Sysadmins — Part VII (Admission Control) is the broader policy side; LXIX-06 is the Sigstore-specific deep dive.
- Git, CI/CD & GitOps — Part LXIX-03 (Keyless Signing with Fulcio) is the signing side; LXIX-06 is the verification side.
Quiz
Knowledge check · 4 questions
Q1. What is the production-default `failurePolicy` for a validating admission webhook that verifies Sigstore signatures?
Q2. Post-deploy signature verification (deploy first, then check) is the production default for admission-time verification.
Q3. Name the three production-grade options for admission-time verification of Sigstore signatures on Kubernetes.
Q4. Diagnose why the cluster is admitting unsigned images despite a signing policy in place.
Team T ships a ClusterImagePolicy that pins the OIDC subject to the build workflow. The policy-controller is installed. Six months later, audit finds that 12% of pods in production are running unsigned images. The team confirms the policy object is present and the controller is running; the unsigned pods are in a namespace that was added after the policy was installed.
Passing score: 75%. Answers are checked in this browser.