A platform team that runs a cluster and the applications on it usually maintains two sets of access rules. One decides what may be admitted into the cluster, which registries images may come from, which workloads may run privileged. The other decides what a user may do once those workloads are serving traffic. They are written in different languages, tested with different tooling, and reviewed by different people.
The wider field has already settled the shape this should take. The OpenID Foundation's AuthZEN Authorization API reached Final Specification in January 2026, standardising how an enforcement point asks a decision point for an answer. It deliberately does not standardise the policy language, because the premise underneath it is one decision point and many thin enforcement points. NIST SP 800-162 decomposes an access control mechanism the same way, a decision point that renders the answer, an enforcement point that applies it, and a context handler that assembles the attributes in between.
Under that model, cluster admission is one more enforcement point rather than a separate discipline. Treating it as a discipline of its own is what produces the second language, and with it the second review process and a second copy of the same organisational facts. Which team owns which namespace. Which workloads may run privileged. Which environments count as production. Copies drift, and eventually an admission rule gates on an environment label the identity provider retired months ago. Adopting externalized authorization for the application half of the estate does not fix that on its own, because the cluster half is still written somewhere else.
Admission control is a webhook, not a special case
Strip away the tooling and Kubernetes admission control is a small HTTP contract. The API server receives a request, authenticates it, checks it against Kubernetes RBAC, then POSTs an AdmissionReview object to each registered webhook and waits. The webhook returns an AdmissionReview response carrying a boolean and, when the answer is no, a status message. Everything else, the constraint templates, the CRDs, the controllers that reconcile them, is what a particular implementation chooses to build on top of that contract.
Cerbos Synapse serves that endpoint directly as a route extension. A route extension registers an HTTP path and a declarative mapping that converts the incoming body into a Cerbos check and the decision back into whatever the caller expects. For admission control the mapping is short. The username in userInfo becomes the principal id and the user's groups become the principal's roles. The operation, CREATE or UPDATE or CONNECT, becomes the action. The kind of the submitted object becomes the resource kind, and the object itself is carried through as a resource attribute alongside the namespace, the name and the API version. The response mapping wraps the decision back into an AdmissionReview the API server accepts.
That mapping is configuration. There is no adapter binary to build, no controller to run, and no custom resource definitions to install and keep in step with the policies.
Why an admission webhook should not hold its own policy language
Germany's federal API authorization infrastructure is the clearest public worked example. Built by Saxony-Anhalt's ministry for infrastructure and digital affairs with FITKO, approved by the country's IT Planning Council in June 2026 and published openly with its architecture decision records, it separates the component that guards the door from the component that decides, and standardises the interface between them on AuthZEN rather than on any particular engine. A standard interface stops every agency welding a different engine into its own stack.
The enforcement point in that split intercepts, asks and applies. It is not where rules live. An admission webhook is close to a textbook instance of the shape, because it has no opinions of its own. It receives an object, forwards it, and returns an answer. A webhook accumulates a policy language not because admission control needs one, but because the tooling on each side of the cluster boundary shipped a different one. That is a history, not a requirement. Read that way, the question stops being which language is better and becomes how many decision points the cluster and the applications on it should share.
CEL is already the cluster's expression language
Kubernetes made its own choice here, and it is the same one. The upstream documentation is explicit that validating admission policies declare their rules in the Common Expression Language, stable since Kubernetes 1.30. A platform engineer who has written a ValidatingAdmissionPolicy has already written CEL.
Cerbos policies are YAML with CEL conditions. The expressions that check a pod's security context in an admission policy are the same shape as the expressions that check a tenant boundary in an application policy, and both are read by the same reviewers. Named variables pull the awkward parts of an object graph out once and reuse them across rules, derived roles turn Kubernetes group membership into policy roles, and policy outputs attach a specific message to each rule so a rejection can explain itself.
This is not a claim that Rego is the wrong tool or that Gatekeeper does its job badly. It is narrower. If an organisation has already committed to a policy language for application authorization, and Kubernetes accepts that same language for its own native admission policies, then a separate language in the middle is a cost with nothing on the other side of the ledger.
What a complete admission ruleset looks like
To find out what this costs in practice, we built and tested a full admission configuration against a disposable cluster. It carries twenty resource policies and one shared derived roles file, registered for CREATE and UPDATE across the workload, networking, RBAC and storage kinds, from pods and deployments through ingresses, role bindings and storage classes, plus CONNECT on the pod exec, attach, port forward and log subresources.
The rules are the ones a platform team recognises. Pods must set a non root user, a read only root filesystem and no privilege escalation, must drop all capabilities, and must pull from an allowed registry, with image digests required in production namespaces. Services may not set external IPs. RBAC objects may not combine wildcard verbs with wildcard resources or bind to anonymous subjects. Storage is restricted to an allowlist of provisioners with no hostPath volumes. Derived roles carve out the exceptions, so the cluster admin group and system components bypass the workload checks rather than needing the same exemption repeated in every policy.
Coverage is checked in two passes. The policies carry a suite of policy tests that run offline against fixtures with no cluster involved, so a rule change is verified in CI in seconds. A second pass replays the same cases through a live webhook to confirm the two agree.
What consolidating actually changes
Three things change, and none of them are about the policy language itself.
The review process collapses into one. A change to which namespaces may run privileged workloads and a change to which users may approve a deployment become the same kind of change, in the same repository, with the same tests and the same reviewers. The organisational fact is written once.
Admission decisions land in the same decision log as application decisions. The embedded PDP can stream its audit trail to Cerbos Hub alongside every other decision the platform makes, so "who tried to create a privileged pod in production" and "who tried to approve their own invoice" are answered from the same place. That matters more than it sounds, because audit trails spread across three systems get reconciled by hand. Governance work is moving towards evidence produced as a by-product of running the system rather than assembled ahead of a review, and a single decision record across the cluster and the applications on it is what makes that evidence coherent.
Admission rules can reference the same attributes application policies use. The AdmissionReview carries the requesting user's groups and any extra claims the authenticator attached, and Synapse can enrich a check with attributes resolved from a directory at decision time rather than from a token. That is the mechanism described in identity attributes at decision time, and the same one that carries department and team context into the Istio and Envoy enforcement points on the data plane.
Failure policy, webhook scope and what admission control does not cover
Admission control is a synchronous hook on the API server's write path, which makes the webhook a cluster availability concern rather than an application one. Every registered write waits on it. Failing closed is the defensible posture, because a webhook that fails open silently admits exactly what it was installed to catch. The benchmark worth borrowing is a little more demanding than that. CoSAI's guidance for agentic systems requires enforcement gateways fronting high capability agents to fail closed with a clearly defined safe degradation path. An admission webhook is not a high capability agent and the requirement does not reach it, but the standard it sets does. A short timeout, a failure policy of Fail, the system namespaces and the webhook's own namespace excluded from validation, and a written procedure for pulling the webhook configuration when it misbehaves, make up such a path. A webhook that can block the pods it needs in order to restart itself is a deadlock waiting for a bad deploy.
That posture is only affordable if the decision itself is cheap and local, which makes the decision point's own dependencies part of the cluster's availability story. Running the engine in process with the webhook, with policy pulled as a versioned bundle and cached, means an admission decision has no runtime dependency on a call to a control plane, so an outage of the control plane does not become an outage of the cluster. If a rule instead needs an attribute from an external directory, that lookup lands on the cluster's write path, and how long a cached copy stays valid becomes a correctness decision rather than a performance one. The deployment topologies available for the PDP on Kubernetes matter here for the same reason.
The layer also only sees what it is registered for. A validating webhook carries an explicit list of API groups, resources and operations, and anything outside that list is never evaluated. This is the caveat for any gateway shaped enforcement point, where coverage is only as complete as the set of paths routing through it. The registration is as much a part of the policy as the rules are, and deserves the same review.
The scope is narrower in one more way. Admission control validates the object being submitted, using the requester's identity as context. It does not decide whether the requester may reach the API server, which resources they may list, or what they may read. That is Kubernetes RBAC, and it runs before the webhook is ever called. An admission policy written as though it were the only gate will be wrong in both directions.
One decision is left in the response mapping. A denial can carry a generic message with the reason going to the decision log, or it can surface the policy's own message to whoever ran the command. The first tells an attacker less, the second saves a support ticket.
Fewer systems have to agree
Consolidating on one policy language does not make the cluster rules simpler. A pod security policy is fiddly in any language because the object it inspects is fiddly. What changes is how many systems have to agree before a rule is true, and how many places a platform engineer has to look before answering a question about who is allowed to do what. The standards work has converged on one decision point and many thin enforcement points, and Kubernetes has already chosen CEL for its own admission policies. If the applications on the cluster are governed by CEL conditions as well, the webhook in between does not need a language of its own.
Try Cerbos to see how this works in practice, or book a call to talk through your architecture with the team.
Go deeper:
- How to adopt externalized authorization (eBook) for the migration patterns teams use when moving rules out of the systems that enforce them
FAQ
Tagged in




