Safeguard
Container Security

What is Admission Control (Kubernetes)

Admission control is the last checkpoint in Kubernetes before an object is written to etcd — here's how webhooks, PSA, and policy engines enforce it.

Vikram Iyer
Cloud Security Engineer
7 min read

Admission control is the stage in the Kubernetes API request pipeline where objects get inspected, mutated, or rejected after authentication and authorization but before they're persisted to etcd. Every kubectl apply, every Deployment rollout, every Pod created by a controller passes through a chain of admission controllers compiled into kube-apiserver and, optionally, through external webhooks you register yourself. This is the last checkpoint where a cluster can say no.

It matters because RBAC only answers "is this user allowed to create a Pod?" — it says nothing about whether that Pod runs as root, mounts the host filesystem, pulls an unsigned image, or skips a resource limit. In 2018, a misconfigured Kubernetes dashboard at Tesla — reachable with no authentication at all — let attackers deploy cryptomining pods directly into the cluster, an incident an admission-time policy blocking unauthenticated or unscanned workloads would have stopped cold. Admission control is where that kind of policy actually gets enforced, request by request.

How does a Kubernetes admission controller actually work?

An admission controller is a piece of code — compiled-in or an external HTTP webhook — that kube-apiserver calls synchronously for every write request (create, update, delete, connect) after the request has already passed authentication and authorization. The request flows through two ordered phases: mutating admission controllers first, which can modify the object (for example, injecting a sidecar container or a default resource limit), followed by validating admission controllers, which can only accept or reject — they cannot change the object body. If any controller in either phase rejects the request, the API server returns an error and nothing is written to etcd. You enable the built-in controllers via the --enable-admission-plugins flag on kube-apiserver; as of Kubernetes 1.29, the documented recommended default set includes NamespaceLifecycle, LimitRanger, ServiceAccount, PodSecurity, DefaultStorageClass, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, and ResourceQuota.

What's the difference between validating and mutating admission webhooks?

A mutating webhook can rewrite the incoming object before it's stored; a validating webhook can only allow or deny it. Both are registered as MutatingWebhookConfiguration or ValidatingWebhookConfiguration resources pointing at an HTTPS endpoint you run — often a small service inside the cluster itself, fronted by a Service and a TLS-terminating certificate issued via cert-manager or the cluster's built-in CA. Mutating webhooks run first and are commonly used to inject Envoy sidecars (Istio's istio-sidecar-injector is a canonical example), set default seccompProfile values, or add labels required by downstream tooling. Validating webhooks run second, after all mutations settle, and are where policy engines like OPA Gatekeeper (CNCF Sandbox project since 2019, graduated 2023) and Kyverno (CNCF Incubating, joined 2022) enforce rules such as "no container may run with privileged: true" or "every image reference must include a digest, not just a tag." Each webhook call has a timeoutSeconds field capped at 30 seconds by the API server, and a failurePolicy of Fail or Ignore that determines what happens if your webhook doesn't answer in time.

Why did Kubernetes deprecate PodSecurityPolicy in favor of admission control?

PodSecurityPolicy (PSP) was deprecated in Kubernetes 1.21 (April 2021) and removed entirely in 1.25 (August 2022) because its authorization model was widely regarded as confusing and error-prone — a Pod's permissions depended on which PSPs the creating service account and user were both bound to via RBAC, which produced inconsistent, hard-to-audit results across clusters. It was replaced by Pod Security Admission (PSA), a built-in admission controller (alpha in 1.22, beta and on-by-default in 1.23, GA in 1.25) that applies one of three predefined profiles — privileged, baseline, or restricted — directly via a namespace label such as pod-security.kubernetes.io/enforce: restricted. The restricted profile, for instance, requires runAsNonRoot: true, drops all Linux capabilities except NET_BIND_SERVICE, and forbids host namespaces and privilege escalation. Teams that relied on custom PSP logic beyond these three tiers migrated to OPA Gatekeeper or Kyverno validating webhooks, which is why "admission control" today usually means the combination of PSA plus a policy-engine webhook rather than any single mechanism.

How do you enforce image and SBOM policy at admission time?

You enforce it with a validating webhook that queries an image's metadata — signature, SBOM, and scan results — before the Pod is allowed to run, rejecting anything that doesn't meet policy. In practice this means the webhook checks for a valid Sigstore/Cosign signature, confirms an SBOM attestation exists in the registry (via the in-toto/SLSA provenance format or a CycloneDX/SPDX attachment), and calls out to a vulnerability database to block images carrying unpatched Critical or High CVEs above a defined CVSS threshold — commonly 7.0 or higher, or any CVE with a known exploit in CISA's KEV catalog. Kyverno's verifyImages rule and Sigstore's policy-controller are the two most common open-source implementations of this pattern. The tradeoff is latency and availability: every Pod creation now depends on a network call to a scanner or registry, so most production configurations cache verification results and set failurePolicy: Fail only after confirming the webhook's own SLA, since an unreachable webhook with Fail set can block all deployments cluster-wide.

What happens when an admission webhook fails or times out?

The outcome is governed entirely by the webhook's failurePolicy setting — Fail blocks the request (and everything else routed through that webhook) if the endpoint is unreachable or times out, while Ignore lets the request through as if the webhook didn't exist. The default timeoutSeconds for a webhook call is 10 seconds, and the API server treats a timeout exactly like an error response, applying the configured failurePolicy. This is a well-known operational failure mode: a single misbehaving or under-scaled admission webhook Deployment with failurePolicy: Fail and no namespaceSelector exclusion has, in real incidents, stalled all cluster deployments — including the webhook's own Pod restarts — because the webhook can't approve its own upgrade. The standard mitigations are excluding kube-system and the webhook's own namespace via namespaceSelector, running the webhook with at least two replicas behind a PodDisruptionBudget, and setting Ignore for non-security-critical checks while reserving Fail for a small, high-availability policy set.

Which built-in admission controllers should be enabled by default?

The Kubernetes project itself recommends a specific baseline, documented in the "Using Admission Controllers" reference, that most managed offerings (EKS, GKE, AKS) already enable and lock. That baseline includes NamespaceLifecycle (prevents object creation in terminating namespaces), LimitRanger (enforces default and max resource requests), ServiceAccount (auto-mounts service account tokens), PodSecurity (replaces PSP as described above), ResourceQuota, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, DefaultStorageClass, CertificateApproval/CertificateSigning (governs the CSR API), and DefaultIngressClass. GKE Autopilot and EKS on Fargate additionally run a managed set of Gatekeeper-style constraints that block hostNetwork, hostPID, and unbounded resource requests before they ever reach a node. Clusters that disable PodSecurity or run MutatingAdmissionWebhook/ValidatingAdmissionWebhook without it are effectively running with no enforced pod security standard at all — any RBAC-authorized user can create a privileged, host-mounting Pod.

How Safeguard Helps

Admission control is only as good as the policy behind it, and static image scanning alone tells you a CVE exists — not whether the vulnerable function is ever reachable from an entry point your admission webhook should actually block. Safeguard's reachability analysis traces call paths from each dependency's vulnerable function up to your application's exposed handlers, so your admission-time gate can enforce "block Critical CVEs that are reachable" instead of "block every CVE," cutting false-positive blocks that otherwise train teams to set failurePolicy: Ignore. Griffin, Safeguard's AI security agent, reviews the Dockerfile and dependency changes behind a failing admission check and opens an auto-fix pull request with the patched version or a pinned digest, so a blocked deployment turns into a merged fix in minutes rather than a Slack thread. Safeguard also generates and ingests SBOMs (CycloneDX and SPDX) at build time, giving your validating webhook a verifiable attestation to check against before a Pod is ever scheduled, closing the gap between what you scanned and what actually runs in the cluster.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.