Est.
FeaturesLong read

What Attestation Verification Looks Like Inside a Kubernetes Admission Controller

A five-step walkthrough of how admission controllers verify attestations cryptographically.

· 10 min read
Cover illustration for “What Attestation Verification Looks Like Inside a Kubernetes Admission Controller”
Features · October 13, 2026 · 10 min read · 2,236 words

This article is about what actually happens inside a Kubernetes admission controller when it verifies an attestation, step by step, from the moment a pod creation request lands to the moment it is allowed or rejected. The angle is mechanical: intercepting the request, resolving the image to a digest, fetching the attestation bundle, verifying it cryptographically, and checking its contents against policy are five distinct operations, each with its own failure mode, and understanding them individually is what makes the enforcement trustworthy.

Why the admission controller enforces attestation

A workload that has already started has already touched node resources, already mounted secrets, and already reached the network. The admission controller sits between authentication and authorization on one side and persistence to etcd on the other, inside the API server's own request pipeline. Every pod creation, whether it comes from a Deployment rollout, a Helm chart install, or someone running kubectl run by hand, passes through this gate before a single container executes.

What makes this gate useful for attestation specifically is the kind of question it can ask. One is about supply-chain provenance: how was this image built, and by what? The other is about hardware: is the workload about to run on genuine, unmodified silicon? Both get enforced at admission, but as later sections show, the mechanics barely overlap.

Routing an admission request to the webhook

Kubernetes defines two kinds of dynamic admission webhooks. Mutating webhooks run first and can change the object being submitted, adding defaults or injecting fields. Validating webhooks run after every mutation has finished, and they can only accept or reject what they see, not alter it. Attestation enforcement is built on the validating type, for a simple reason: it needs to evaluate the final, settled version of the object, not a draft that might still change underneath it.

The routing itself is configured through a ValidatingWebhookConfiguration, a cluster resource that tells the API server which requests to forward and where. When an incoming request matches the rules defined there, the API server packages it as an AdmissionReview object and sends it to the webhook's clientConfig endpoint, an HTTPS callback the control plane has to be able to reach. The rules stanza is specific: it names API groups, versions, the operations that trigger it (CREATE, for instance), and the resource kinds in question. The reasoning is practical: if enforcement only happened at the Pod level, a user could watch their Deployment get accepted, only to find the pods underneath it stuck and failing because policy is blocking them downstream, with no clear link back to the cause.

Whatever the webhook decides, it has to answer in the same AdmissionReview format it received, marking the request allowed or denied. The default timeout for this round trip is 10 seconds, a number that becomes important once attestation verification adds real network and cryptographic work to that window, a tension this piece returns to later.

Resolving the image tag to a digest before verification can begin

A tag like myapp:latest can point to a different image tomorrow, or an hour from now, because tags are mutable by design. That mutability makes a tag useless as the basis for a security check: nothing stops the tag from being repointed between the moment a workload is admitted and the moment the kubelet actually pulls the image. A policy that checks a tag is checking something that may no longer refer to the artifact that runs.

Attestations attach to an image's digest instead of its tag, a fixed hash of its actual content that cannot be silently repointed. Sigstore's policy-controller handles this directly, resolving tags to digests specifically to make sure the image that ends up running is the same one that was reviewed at admission time. It's called out as a distinct job the controller performs, not a side effect of some other function.

Once that digest is in hand, everything downstream becomes deterministic. The digest is what gets used to look up OCI referrers in the registry, what gets matched against policy patterns, and what gets cached for the rest of the admission request so the same image isn't re-resolved multiple times in one pipeline run. Nothing past this point works without it.

Fetching the attestation bundle from the registry

With a stable digest established, the next job is pulling the attestation material itself, which lives in the registry as an OCI artifact attached to that specific digest. When policy-controller is installed, it intercepts image pull requests and checks for an attestation stored as an OCI attached artifact containing a Sigstore Bundle. That bundle carries two things together: the attestation claim itself, and the cryptographic material needed to check it, including certificates, signatures, transparency log entries, and timestamps.

How this retrieval actually happens depends on the registry. OCI 1.1 introduced a referrers API as the standard way to attach arbitrary artifacts to any manifest by digest, and this is the preferred path when a registry supports it. Either way, what comes back is the same bundle: the in-toto Statement and the cryptographic material needed to verify it, retrieved together as one unit, ready for the next stage to open up.

Cryptographic verification of the attestation bundle

Diagram: Five Stages of Admission Controller Attestation Verification. Visualizes: Illustrate the five sequential pipeline stages an admission controller executes when verifying an attestation, from the moment a pod creation request arrives to its…

This is where the actual cryptography happens, and it happens in a specific order, each step gating the next. The attestation inside the bundle is wrapped in a DSSE envelope (Dead Simple Signing Envelope), and the first check is whether that envelope's signature matches the signing certificate bundled alongside it.

Next comes the certificate itself: is it one the cluster actually trusts? Policy-controller manages this through a TrustRoot custom resource, which defines the keys and certificates accepted for verification. A TrustRoot can also point to a TUF repository root, which lets the cluster keep pulling updated trusted key material over time rather than relying on a static, aging set of keys.

The third check is a transparency log inclusion proof, provided by Rekor. Only once all three checks clear, the signature is valid, the certificate chain traces to a trusted root, and the inclusion proof confirms the timestamp, does the decoded Statement payload move on to the policy layer. None of these are optional extras layered on for extra assurance. They're sequential gates, and failing any one of them stops the process regardless of what the payload underneath claims to say.

Evaluating the attestation payload against policy

Once the bundle is verified, what's left is a structured in-toto Statement, a JSON object carrying a predicateType and a predicate body describing how and where the artifact was built. The fields a policy chooses to check determine how much that policy is actually worth.

The two fields that matter most for SLSA-style provenance enforcement are builder identity and source repository. Consider what happens if a policy checks only one of them. The build process looks legitimate; the content isn't. Checking both fields together is what closes that gap.

Different engines express these checks differently. OPA Gatekeeper's integration with GitHub Artifact Attestations, in public preview since June 24, 2025, lets teams write Rego policies requiring build provenance, an SBOM, or any other custom attestation type, blocking images that don't satisfy them before they ever deploy.

The admission logic inside policy-controller isn't a simple pass/fail. Every ClusterImagePolicy that matches a given image digest must be satisfied, an AND condition across all matched policies. An image that doesn't match any policy at all gets rejected by default, unless an operator has explicitly configured no-match-policy to warn or allow instead. Silence from policy, in this system, means rejection, not permission.

How the three main policy engines structure this pipeline

Three engines implement this same basic pipeline today, Sigstore Policy Controller, OPA Gatekeeper paired with a provider like Ratify, and Kyverno, and each one splits up the work of fetching, verifying, and evaluating in its own way.

Policy Controller is a dedicated admission controller built around cosign-based verification from the start.

Gatekeeper takes a different shape. As of June 2025, GitHub released a public-preview Gatekeeper provider for GitHub Artifact Attestations that does exactly this: it fetches and verifies the attestation, hands back structured metadata, and Gatekeeper then evaluates that metadata against a Rego policy. Splitting verification from policy evaluation this way means teams already running Gatekeeper for other purposes can add attestation enforcement without swapping out their policy engine.

Kyverno integrates with Cosign directly through its verifyImages rule, supporting both keyless and key-based verification, and handles the SLSA provenance flow without needing an external tool in the loop. Policy here is written in Kyverno's own rule syntax.

What separates these approaches isn't which is better so much as where the seams sit. Policy Controller and Kyverno bundle verification and policy evaluation into one component each. Gatekeeper splits them, keeping the core policy engine general-purpose at the cost of an extra network hop and an additional moving part to run and monitor. That architectural choice, bundled versus split, is also where failures differ, which is the subject of the next section.

Where each pipeline stage fails

Every stage described above has its own distinct way of failing, and the webhook's failurePolicy setting is what decides whether that failure blocks the cluster or quietly lets work through unchecked. That single configuration choice is arguably the sharpest decision in the entire setup.

Tag resolution can fail outright if the webhook can't reach the registry at all, in which case whether the request proceeds depends entirely on failurePolicy. Attestation fetching can fail in a quieter way: no OCI referrer turns up for the resolved digest, and the image simply has no attestation attached. Policy evaluation can fail even when the bundle verifies cleanly: the predicate fields simply don't match what policy requires, wrong builder ID, wrong source repository, or a missing SBOM predicate. This is specifically the failure mode that catches a build coming from an untrusted pipeline or an unauthorized fork, the exact scenario described in the previous section.

Then there's the webhook itself going unreachable, through a network misconfiguration, a missing TLS trust relationship, or a registry that's simply slow to respond. For namespaces where supply-chain enforcement actually matters, Fail is the correct posture, paired with a tested break-glass procedure for genuine emergencies. Ignore has a place, but only for controls that are optional or still experimental, not for anything meant to gate what runs in production.

Hardware TEE attestation at admission: a different pipeline for confidential workloads

Everything described so far verifies how an image was built. Hardware TEE attestation asks a different question entirely: is the workload about to run on genuine, unmodified hardware? The evidence chain supporting that question doesn't resemble the supply-chain pipeline at all, and the enforcement point isn't pod admission alone but key release, the moment a workload asks for a secret it needs to operate.

The conceptual shape follows the IETF RATS model. An Attester, the workload running inside the TEE, produces Evidence, a hardware-signed report describing its own state. On AMD SEV-SNP hardware, this plays out concretely: the Secure Processor takes a launch measurement, a SHA-384 digest covering the initial guest memory and configuration, and bundles it together with VM metadata and a nonce supplied by the verifier (which blocks replay attacks) into an ATTESTATION_REPORT signed by the VCEK.

Confidential Containers, a CNCF project, is what makes this pattern native to Kubernetes. Inside that confidential guest run two components: the Attestation Agent, which gathers the hardware Evidence and speaks the attestation protocol, and the Confidential Data Hub, which handles retrieving secrets and decrypting images.

Key release is where enforcement actually bites. A workload that can't prove it's running the right code on genuine hardware simply never gets the key, no matter how far it has otherwise progressed through admission.

Kubernetes introduces one specific wrinkle here. One response to this decouples static platform measurements, frozen at boot time, from dynamic pod identities, embedding them instead in the report_data field of a TDX Quote, allowing attestation to operate at the pod level. None of this is free. TEE attestation adds real overhead: first-touch memory operations cost noticeably more than on a conventional VM, and boot time increases depending on memory size and kernel configuration, overhead that operators sizing admission timeouts need to account for directly.

Attestation enforcement for data-privacy workloads

Attestation verification at admission is the mechanism that lets a cluster make a specific, cryptographically backed claim: that only workloads with verified provenance, running on hardware that's proven itself genuine, are ever allowed to execute. For AI workloads handling sensitive data, that claim is the foundation any privacy guarantee actually rests on.

Supply-chain attestation makes sure that only images built by an authorized pipeline, from an authorized source, ever reach a running state, catching anything built outside controlled infrastructure before it executes. Hardware TEE attestation takes that further: a workload doesn't just need to prove where it came from, it has to prove it's executing inside a genuine confidential environment on unmodified hardware before it ever receives the keys needed to touch encrypted data. Put together, provenance checks at admission and hardware checks gating key release are what it looks like to build privacy into the architecture itself, rather than bolting a policy on top of infrastructure that has no way to verify what it's actually running. Infrastructure that cannot verify what is running on it cannot make a real privacy guarantee about what it processes, no matter what its documentation claims.

Sources

  1. Kubernetes Policy Controller - Sigstore
  2. Dynamic Admission Control
  3. Enforce admission policies with artifact attestations in Kubernetes using OPA Gatekeeper - GitHub Changelog

More in Features