Kubernetes ValidatingAdmissionPolicy and CEL Explained

Updated on
9 min read

Kubernetes ValidatingAdmissionPolicy gives platform teams a way to check API requests with Common Expression Language (CEL) rules that run inside the Kubernetes API server. It is useful when cluster administrators need consistent guardrails without deploying and operating a custom admission webhook. This explainer covers how policies and bindings work, what the approach can and cannot validate, and how to test a rule before enforcing it.

What Is Kubernetes ValidatingAdmissionPolicy?

ValidatingAdmissionPolicy is a Kubernetes API resource for defining validations that run during admission. A policy describes which API requests to match and contains CEL expressions that evaluate the request object. A separate ValidatingAdmissionPolicyBinding connects that policy to requests in scope and specifies what happens when a validation fails.

The Kubernetes documentation for ValidatingAdmissionPolicy describes the API objects and their configuration. The stable API is admissionregistration.k8s.io/v1, available in Kubernetes 1.30 and later. The policy feature grew from the CEL admission control Kubernetes Enhancement Proposal, which outlines the design goal of expressing common validations without a separate webhook service.

As part of the Kubernetes project, ValidatingAdmissionPolicy is maintained as a native admission API rather than an add-on policy service.

CEL is a small expression language designed to evaluate safely in constrained environments. Its language definition describes its expression syntax and evaluation model, and the CEL project provides an overview of the language. In Kubernetes, CEL expressions inspect structured API data, rather than running arbitrary scripts with access to the cluster or a network.

The Problem It Solves

Kubernetes validates objects against the API schema, but schema validation cannot express every organizational rule. A cluster may need to limit replicas, require approved labels, prevent certain storage classes, or enforce relationships between fields. Without a policy at the API boundary, teams often duplicate checks in CI pipelines, rely on application conventions, or introduce a policy engine.

Admission webhooks can implement sophisticated rules, but they require a service that the API server can reach. Operators then need to deploy and secure that service, manage certificates, keep it available, and understand its effect on API request latency. A webhook outage or a misconfigured failure policy can affect the ability to create or update resources.

ValidatingAdmissionPolicy addresses the subset of checks that can be expressed as CEL over the API request and supported parameters. It keeps those evaluations in the API server and gives platform teams native warning, audit, and denial outcomes. It does not replace webhooks for checks that need external services, custom libraries, or arbitrary code.

How It Works

When a client submits a matching create, update, delete, or connect request, the API server processes it through authentication, authorization, and admission. Mutating admission controllers run before validation. ValidatingAdmissionPolicy evaluates against the resulting object and the request context; a failed rule can then be denied or surfaced as a warning or audit annotation according to the binding.

The separation between policy and binding is central to the design:

  1. The policy defines the rule. Its matchConstraints select API groups, versions, resources, and operations. Its validations list contains CEL expressions and failure messages.
  2. The binding selects where it applies. A binding names a policy and may narrow its scope, for example to namespaces that match a selector. It also sets the validation actions.
  3. The API server evaluates the request. CEL expressions can inspect the object, the old object on updates, and request information exposed by the API. A binding can also supply parameter resources when a policy is designed to use parameters.
  4. The action determines the outcome. Deny rejects the request; Warn lets it proceed while returning a warning to the client; and Audit records a policy result in the audit event.

The policy is cluster-scoped but has no enforcement effect merely because it exists. Without a binding, the policy is not applied. This makes the two-resource model useful for reusing a rule while changing its scope or enforcement mode independently.

Dimension ValidatingAdmissionPolicy Validating admission webhook
Rule format CEL expressions in Kubernetes API resources Application code served by an external endpoint
Evaluation location Inside the API server API server sends a request to the webhook
Operational dependencies Kubernetes API server and policy resources Webhook service, network path, certificates, and API server configuration
Supported checks CEL over request data and configured parameters Arbitrary logic available to the webhook implementation
Effect on objects Validates; it does not mutate A validating webhook validates; mutation requires a mutating webhook
Failure feedback Binding actions can deny, warn, or audit Webhook response allows or rejects; warnings require supported response behavior
Best fit Rules expressible as bounded CEL evaluations Rules requiring external data, custom code, or other integrations

Components and Key Concepts

Match constraints declare the API resources and operations a policy considers. Match narrowly: a rule intended for Deployments should not accidentally evaluate unrelated objects. Namespace and object selectors can further scope matching where appropriate.

CEL validations are boolean expressions. A validation succeeds when its expression evaluates to true; a false result can include a message that explains the rejected request. Expressions should be small and bounded because API admission is on the request path. Kubernetes also limits CEL evaluation cost to protect API server responsiveness.

Bindings and validation actions control enforcement. A binding can start with Warn while teams observe requests, then change to Deny once the rule has been exercised. Audit adds policy results to audit records, which are useful only when the cluster retains and makes those records available. The API response and configured audit pipeline are separate feedback channels.

Parameters make a rule reusable when a policy has a paramKind. A binding can identify the parameter object through paramRef, allowing teams to maintain policy logic separately from values such as an approved list or a numeric limit. Parameterization adds resource access and lifecycle considerations, so a fixed CEL expression is simpler when only one rule is needed.

Failure policy controls how evaluation errors are handled. Fail fails closed for errors such as an expression that cannot be evaluated; Ignore lets the request continue when evaluation errors occur. This is different from a CEL expression returning false: the validation action handles a failed validation, while failurePolicy handles evaluation problems. Choose deliberately and test both expected and edge-case objects.

Real-World Use Cases

  • Workload guardrails: Limit Deployment replicas, require resource labels, or prohibit a configuration that exceeds an organization’s operational boundaries.
  • Namespace-specific rules: Use bindings and selectors to apply stricter requirements to production namespaces while allowing a different development policy.
  • Migration from advisory to preventive checks: Return warnings while application teams find and fix noncompliant manifests, then use denial once the rule is understood.
  • Policy without a custom service: Enforce straightforward resource invariants in clusters where operating another admission service would add unnecessary complexity.

These rules complement other controls rather than replace them. Kubernetes Pod Security Admission applies the built-in Pod Security Standards, while a ValidatingAdmissionPolicy can add custom checks expressed in CEL. The container security best practices guide covers controls outside API-object validation, including image and runtime security.

Getting Started: Limit Deployment Replicas

Use a test cluster first. Check that its API server serves the stable policy resources:

kubectl version -o json
kubectl api-resources --api-group=admissionregistration.k8s.io

Save the following as replica-limit-policy.yaml. It defines a rule for apps/v1 Deployments and a binding that denies matching requests when they specify more than five replicas:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: limit-deployment-replicas
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: ["apps"]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["deployments"]
  validations:
    - expression: "object.spec.replicas <= 5"
      message: "Deployments may have no more than five replicas"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: limit-deployment-replicas
spec:
  policyName: limit-deployment-replicas
  validationActions:
    - Deny

Apply the resources and inspect them:

kubectl apply -f replica-limit-policy.yaml
kubectl get validatingadmissionpolicy limit-deployment-replicas
kubectl get validatingadmissionpolicybinding limit-deployment-replicas

Create a test Deployment manifest with spec.replicas: 6, a matching selector and Pod-template label, and a valid container image. Submit it as a server-side dry run:

kubectl apply --dry-run=server -f deployment-with-six-replicas.yaml

The API server should reject the request with the policy message. Change the replica count to five and repeat; the request should pass admission without creating a Deployment. Server-side dry runs exercise admission against the cluster, so they are more useful here than a local YAML parser.

For a rollout on existing namespaces, replace Deny with Warn first, review warnings from representative deployment workflows, and fix violations in the source manifests. Then switch the binding to Deny. If a policy is unexpectedly silent, inspect both resources and confirm the binding references the correct policy; check the API version and resource rules as well. If evaluation fails, review the server response, expression, and failurePolicy. The Kubernetes architecture guide explains where admission fits in the API server’s role, and the Kubernetes CI/CD and Argo CD guide covers deployment workflows that can surface warnings before enforcement.

Common Misconceptions

“Creating a policy immediately enforces it.” A binding is also required. The policy defines what to check; the binding chooses its scope and result.

“CEL policies can replace every webhook.” CEL is suitable for expressions over request data and configured parameters. Rules that need external lookups, specialized libraries, or more complex processing still need an appropriate service or policy system.

“A warning means the request was blocked.” Warn allows the request and returns a warning. Use Deny when a failed validation must prevent the API object from being stored.

Changelog

  • Initial publication.
TBO Editorial

About the Author

TBO Editorial writes about the latest updates about products and services related to Technology, Business, Finance & Lifestyle. Do get in touch if you want to share any useful article with our community.