Every object that reaches the Kubernetes API server — a Pod, a Deployment, a ConfigMap — passes through authentication, then authorization, then a stage almost nobody outside platform teams thinks about: admission control. This is the last checkpoint before the object is written to etcd and becomes real, and it's the only stage in the pipeline that can actually look inside the object and say no based on its contents. RBAC can tell you whether a service account is allowed to create a Pod in the production namespace. It cannot tell you that the pod it's creating runs as UID 0, mounts / from the host, or pulls an unsigned image from a registry nobody approved. That gap is exactly what admission controllers close. Kubernetes formalized the mechanism with MutatingAdmissionWebhook and ValidatingAdmissionWebhook, configured via the stable admissionregistration.k8s.io/v1 API, and it's the same mechanism the in-tree PodSecurityPolicy controller once used before it was deprecated in Kubernetes 1.21 and removed outright in 1.25 — replaced by the built-in Pod Security Admission controller. This post walks through what admission control actually does, where RBAC's authority ends, and the policies real clusters enforce with it today.
What does an admission controller actually do?
An admission controller is a piece of code — either compiled into the API server or running as an external webhook — that intercepts a request after it has been authenticated and authorized, and before the object is persisted. The pipeline runs in two phases. Mutating webhooks run first and can rewrite the incoming object: injecting a sidecar container, adding default resource limits, or setting a label. Validating webhooks run second and can only allow or deny — they see the (possibly mutated) final object and return a pass/fail verdict, optionally with a human-readable reason surfaced back to kubectl apply. Both are registered cluster-wide via MutatingWebhookConfiguration and ValidatingWebhookConfiguration objects, which specify which resources and API groups the webhook should be called for, and what happens if the webhook endpoint is unreachable (failurePolicy: Fail or Ignore). If no admission controller is configured at all, the API server admits anything that passes RBAC — which is precisely the gap that catches teams off guard.
Why isn't RBAC enough on its own?
RBAC answers exactly one question: is this identity permitted to perform this verb on this resource type in this namespace? It's a coarse, structural check — it has no schema awareness of what's inside the request body. A Role that grants create on pods cannot be narrowed to "except pods requesting hostNetwork: true," because RBAC's grammar doesn't reach that deep. That means any developer or CI pipeline with permission to deploy at all can, by default, deploy a container running as root, mount the host filesystem, disable seccomp, or pull nginx:latest from an unverified public registry — and RBAC will authorize every one of those requests without complaint. This isn't a misconfiguration of RBAC; it's outside RBAC's design scope entirely. Admission control exists to answer the question RBAC structurally cannot: not "who can deploy," but "what may a deployment actually contain." Clusters that rely on RBAC alone are enforcing identity policy while leaving workload policy completely unenforced.
What replaced PodSecurityPolicy, and why?
PodSecurityPolicy was Kubernetes' original built-in admission controller for restricting pod-level security settings, but it was notoriously difficult to reason about — policies were matched to pods indirectly through RBAC bindings, which made debugging "why was my pod rejected" a genuine chore. It was deprecated in Kubernetes 1.21 and removed in 1.25. Its replacement, Pod Security Admission (PSA), is a built-in, non-webhook admission controller that enforces three predefined Pod Security Standards — Privileged, Baseline, and Restricted — applied per namespace via a simple label like pod-security.kubernetes.io/enforce=restricted. PSA is deliberately narrower in scope than what third-party engines offer; it only covers pod-level security fields, not arbitrary custom policy like "images must come from registry.company.com" or "every deployment needs a cost-center label." For anything beyond the built-in standards, clusters still need a general-purpose policy engine running as a webhook.
What do OPA Gatekeeper and Kyverno actually enforce in practice?
OPA Gatekeeper and Kyverno are the two dominant open-source projects implementing admission control as configurable validating and mutating webhooks; the underlying Open Policy Agent and Kyverno are both graduated projects under the Cloud Native Computing Foundation. Gatekeeper wraps the Open Policy Agent's Rego language in Kubernetes-native ConstraintTemplate and Constraint CRDs; Kyverno uses plain YAML policies that Kubernetes engineers can read without learning a new language. Their published policy libraries converge on the same well-worn set of guardrails: deny any pod with privileged: true or allowPrivilegeEscalation: true, deny hostPath volume mounts and hostNetwork/hostPID, require every container to declare resources.requests and resources.limits so a single misbehaving pod can't starve a node, restrict images to an explicit registry allow-list, and reject :latest tags in favor of pinned, immutable digests. None of these are exotic — they're the baseline most platform teams converge on within their first few months of running Kubernetes in production, precisely because the default admission-free posture allows all of them.
How does image signature verification fit into admission control?
Image provenance checking is one of the newer, fastest-growing uses of admission webhooks, driven by the rise of supply-chain attacks that compromise a build pipeline rather than the running cluster. Sigstore's policy-controller, built on the cosign toolchain, runs as a validating admission webhook that checks whether an image reference presented in a pod spec carries a valid cryptographic signature — and can also require a matching in-toto attestation, such as an SBOM or SLSA provenance statement, before admitting the pod. The logic mirrors a supply-chain version of TLS certificate validation: the cluster refuses to run code whose origin it cannot cryptographically verify, regardless of who is deploying it or what their RBAC role allows. This closes a specific real gap — a compromised CI token or a typosquatted base image can produce an image that's technically valid YAML and technically permitted by RBAC, but has no business running in a production cluster. Signature verification at admission is the only enforcement point positioned to catch that before the container ever starts.
How does Safeguard apply admission control to policy enforcement?
Safeguard's guardrails framework treats the Kubernetes admission controller as one of six enforcement points across the software lifecycle — alongside IDE, commit, CI, registry, and runtime — and ships it as a Helm chart (safeguard-admission) built on the same Kyverno and Sigstore policy-controller foundations described above. On every incoming pod spec, the controller resolves the image digest, fetches its SBOM and signature from the registry or the Safeguard Portal, evaluates the applicable policy-as-code rules — like blocking any image containing a KEV-listed CVE or one missing SLSA level 3 provenance — and admits, warns, or denies accordingly, with a policy.mode=audit setting available for teams who want to observe violations before flipping to enforce mode. Every decision is logged to a signed audit record, so a policy that started as a manual RBAC review becomes a queryable, replayable enforcement trail instead of a rule nobody can verify was actually followed.