April 17, 2024·6 min read·engineeringkubernetesadmissioncel

Kubernetes 1.30 puts declarative admission validation in the API server

Stable CEL admission policies let Kubernetes evaluate validation rules inside the API server. Their policy and binding model separates reusable logic from enforcement and scope.

A rule that compares fields in a Kubernetes object should not always require another service in the admission path. Kubernetes 1.30 makes that architectural choice available through stable CEL admission control. The April 17 release announcement lists CEL for admission control among the capabilities graduating to stable. The change gives administrators an in-process way to validate matching API requests using declarative expressions.

The important distinction is where the rule executes and how its configuration is represented. An external validating webhook requires a separately operated endpoint. A ValidatingAdmissionPolicy places the validation expression in a Kubernetes API object and lets the API server evaluate it. That is useful when the decision can be expressed using the request and the data available to the policy. It also makes the rule part of the same declarative configuration system as the resources it governs.

I regard the API machinery as Kubernetes's most durable contribution. Controllers and custom resources make it possible to extend the system without putting every application concern into the built-in workload types. Stable admission policy adds another useful tool to that model, a cluster can describe some of its own acceptance rules as resources instead of operating a service solely to compare object fields.

Definition and activation are separate

The 1.30 API definitions describe a ValidatingAdmissionPolicy as validation that accepts or rejects an object without changing it. That last qualification matters. A policy can decide whether submitted data satisfies a rule. It does not rewrite the resource to make the rule pass. A deployment that needs mutation still needs a mechanism suited to changing objects.

The policy contains matching constraints and validation expressions. A ValidatingAdmissionPolicyBinding connects it to the configuration that determines where and how it takes effect. Parameter resources can supply values used by a reusable policy. The versioned documentation states that a policy and corresponding binding are both necessary for an effect on admission.

This separation avoids embedding every deployment decision inside the expression. The same logic can use different parameter values in different scopes. A rule limiting replicas, for example, can be defined once while a binding selects the applicable parameter resource. The mechanism gives administrators a way to separate policy authorship from the local values and scope used when applying it.

There is a corresponding debugging consequence. Finding a policy object does not prove that a request is subject to it. The matching rules and binding must also select that request. Understanding an admission decision requires examining the active combination, including parameters when present. The expression alone is only part of the configuration that the API server evaluates.

A concrete replica limit

The following example uses the stable API to reject Deployment requests whose replica count exceeds five. It deliberately avoids parameter resources so the relationship between the policy and its binding remains visible. The field names and resource kinds follow the 1.30 API definitions.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: deployment-replica-limit.example.com
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups: [apps]
      apiVersions: [v1]
      operations: [CREATE, UPDATE]
      resources: [deployments]
  validations:
  - expression: object.spec.replicas <= 5
    message: Deployment replicas must not exceed five
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: deployment-replica-limit
spec:
  policyName: deployment-replica-limit.example.com
  validationActions: [Deny]

The policy's matching constraints select Deployment create and update requests. Its expression examines the incoming object's replica count. The binding chooses denial as the response to a failed validation. Without a selector narrowing the binding's scope, this is a broad rule for the matched requests, so the absence of scoping is a substantive configuration choice.

The rule is also deliberately narrow in what it claims. It checks the replica field on the selected Deployment requests. It does not express a cluster-wide compute budget or coordinate capacity across applications. A small CEL expression is useful because its meaning can remain precise. Expanding its description into a general resource-governance claim would promise behavior that the expression does not implement.

Failed rules and failed evaluation differ

A validation expression returning false and an error evaluating the policy are different events. The API documents failurePolicy for misconfiguration and evaluation failures. It explicitly says that this field does not decide how a false validation result is handled. The binding's validationActions supplies that behavior. Confusing these fields can turn a seemingly correct rule into an incorrectly understood enforcement arrangement.

The supported actions are Deny, Warn, and Audit. Denial rejects a failing request, warning reports the failure to its client, and auditing includes the validation failure in the request's audit event. Warn and Audit can be used together. The API prohibits combining Deny and Warn, because returning both a denial and a warning for the same failure duplicates the response.

This gives a policy a useful observation mode without changing its logic. A binding can report failures while administrators examine what the proposed rule matches. That is still an active policy configuration, and its scope deserves the same attention as enforcement. Observing failures only in a narrow namespace does not establish what a broader binding would do elsewhere.

For evaluation errors, Fail and Ignore describe the policy's error handling. The binding still determines the enforcement action where the API says those failures are subject to enforcement. This design requires more careful reading than treating one flag as a universal fail-open or fail-closed switch. The distinction preserves a useful separation between an object violating a rule and the system failing to evaluate that rule.

I prefer having those decisions represented explicitly. An administrator can inspect the expression, its matching configuration and the enforcement action as ordinary resources. That makes a policy review more concrete, but it does not excuse broad or ambiguous matching rules. A rule must reach the intended requests before its validation logic can protect anything.

Inputs and expression structure

The API exposes the incoming object as object and the existing object as oldObject. The comments specify that oldObject is null on creation and object is null on deletion. A policy that compares old and new values therefore needs matching conditions or expressions appropriate to the operation. A field access valid during an update is not automatically meaningful during every admission operation.

Other available inputs include request attributes and the namespace object. Parameter resources supply params when the policy declares their kind. The availability of these inputs is part of the feature's practical scope. A validation can use the information the API provides, while decisions requiring unrelated external state may still need a different implementation.

Named variables help organize expressions that otherwise repeat work or become hard to read. The API requires their dependency order to be acyclic, with a variable referring only to variables defined earlier. Match conditions cannot use those composed variables because matching happens before the rest of policy evaluation. This ordering is a concrete part of the execution model, not merely a formatting preference.

The policy's status also exposes type-checking warnings. A warning identifies the expression field and the type-checking problem, which helps separate a mistaken field reference from an intended rule violation. That diagnostic support is useful when the policy matches more than one kind of object. Reusable logic still needs to make sense for the resource types it selects.

Bindings also determine how many evaluations a request requires. The API comments describe one evaluation for a policy and binding without parameters. When a binding selects several parameter resources, the policy runs against each selected parameter. Each combination gets an independent CEL cost budget. Adding another binding or parameter does not change whether an existing combination fits its own budget, although it creates additional evaluation work for matching requests.

That detail matters when designing reusable policy configuration. Selecting many parameters is an execution decision as well as a way to organize configuration. A collection of small expressions can still require repeated evaluations because of the bindings around it. The policy and its selected parameter set must be reviewed together to understand the work applied to each request.

Stable validation changes the operating choice

The release announcement also graduates CEL-based admission webhook match conditions. These are related to request selection, but they are distinct from replacing a webhook's validation implementation with a ValidatingAdmissionPolicy. CEL can therefore reduce unnecessary webhook invocation in one configuration and provide the validation itself in another. The architecture depends on which admission mechanism owns the decision.

For rules expressible using the supported inputs, in-process evaluation removes the separately operated validation endpoint from that decision path. The remaining work is policy engineering inside the control plane, including scope and parameter ownership. Kubernetes 1.30 makes that a stable configuration choice. A policy's exact matching and enforcement semantics now deserve the same review as the application fields the policy constrains.