Kubernetes CEL Policy Expression Management
Kubernetes CEL Policy Expression Management enables fine-grained control over cluster policies using structured, reusable expressions for enforcement and validation.
Kubernetes CEL Policy Expression Management is the practice of authoring, testing, and maintaining Common Expression Language expressions used across Kubernetes' policy surfaces — ValidatingAdmissionPolicy, the structured AuthenticationConfiguration claim validation, and custom resource validation rules — where CEL provides a constrained, safe way to express conditions and transformations directly within cluster configuration rather than delegating to external code.
CEL Fundamentals in a Kubernetes Context
The Evaluation Object Model
CEL expressions in admission policy evaluate against a well-defined set of variables — typically object (the incoming resource), oldObject (the previous version, for updates), request (metadata about the operation), and params (bound parameter resource data) — and expressions are written as single, side-effect-free boolean or value expressions rather than imperative code.
validations:
- expression: "object.spec.replicas <= 10"
message: "replica count must not exceed 10"
Safety by Design
CEL deliberately excludes unbounded loops, arbitrary function definitions, and external I/O, which is what makes it safe to execute directly within the API server's request path without the sandboxing concerns that would apply to an arbitrary scripting language; its expressiveness is intentionally bounded to keep evaluation fast and predictable.
Common Expression Patterns
Iterating Over Collections
CEL provides macros like all, exists, and exists_one for reasoning over list fields, commonly used to validate that every container in a pod spec, rather than just the first, satisfies a given condition.
expression: >
object.spec.containers.all(c,
has(c.securityContext) && c.securityContext.runAsNonRoot == true)
Comparing Old and New State on Updates
Expressions can reference both object and oldObject to express immutability rules or to restrict what may change during an update, which is only meaningful when the underlying request is an UPDATE operation.
expression: >
oldObject == null ||
object.spec.serviceAccountName == oldObject.spec.serviceAccountName
message: "serviceAccountName is immutable after creation"
Working With Optional Fields
has() checks whether an optional field is present before attempting to access it, avoiding evaluation errors when a field may or may not exist on a given object; expressions that access nested optional fields without first checking has() are a common source of unexpected evaluation failures.
expression: >
!has(object.spec.hostNetwork) || object.spec.hostNetwork == false
Testing and Validating Expressions
Dry-Running Against Representative Objects
Because a CEL expression error surfaces only when a matching request is actually evaluated, testing new or modified expressions against a representative sample of real object manifests in a staging environment — including edge cases like missing optional fields — catches evaluation errors before they affect production traffic.
CEL Expression Validation at Policy Creation Time
The API server performs static type-checking on CEL expressions when a ValidatingAdmissionPolicy is created or updated, rejecting syntactically invalid or type-mismatched expressions immediately rather than allowing them to be applied and fail later at evaluation time — treating this creation-time rejection as an early warning rather than something to work around.
kubectl apply -f policy.yaml --dry-run=server
Maintaining Expressions Over Time
Keeping Expressions Readable
Because CEL expressions are embedded as strings within YAML, complex logic can become difficult to read; breaking a policy into several smaller validations entries, each with a focused expression and a clear, specific message, keeps individual failures diagnosable rather than producing one large, opaque boolean condition whose exact failure reason is unclear.
validations:
- expression: "object.spec.containers.all(c, has(c.resources.limits))"
message: "all containers must specify resource limits"
- expression: "object.spec.containers.all(c, has(c.resources.requests))"
message: "all containers must specify resource requests"
Reviewing Expressions Alongside API Schema Changes
As the schema of a resource evolves across Kubernetes versions (new optional fields, deprecated fields), CEL expressions referencing those fields should be reviewed for continued correctness — an expression checking a field that becomes deprecated or restructured in a newer API version may silently stop evaluating as intended without producing an obvious error.
Version-Controlling Policy Expressions
Storing ValidatingAdmissionPolicy manifests, including their CEL expressions, in the same version-controlled repository as other cluster configuration provides the same review, diff, and rollback capability for policy logic changes as for any other infrastructure change, which is particularly valuable given how easy it is to subtly alter a CEL expression's meaning with a small syntactic change.