Skip to content

CEL Libraries

Kyverno enhances Kubernetes’ CEL environment with libraries enabling complex policy logic and advanced features. These libraries are available in both ValidatingPolicy and MutatingPolicy.

The Resource library provides functions like resource.Get() and resource.List() to retrieve Kubernetes resources from the cluster, either individually or as a list. These are useful for writing policies that depend on the state of other resources, such as checking existing ConfigMaps, Services, or Deployments before validating or mutating a new object.

CEL ExpressionPurpose
resource.Get("v1", "configmaps", "default", "clusterregistries").data["registries"]Fetch a ConfigMap value from a specific namespace
resource.List("apps/v1", "deployments", "").items.size() > 0Check if there are any Deployments across all namespaces
resource.Post("authorization.k8s.io/v1", "subjectaccessreviews", {…})Perform a live SubjectAccessReview (authz check) against the Kubernetes API
resource.List("apps/v1", "deployments", object.metadata.namespace).items.exists(d, d.spec.replicas > 3)Ensure at least one Deployment in the same namespace has more than 3 replicas
resource.List("apps/v1", "deployments", object.metadata.namespace, { "env": "pod" }).items.exists(d, d.spec.replicas > 3)Ensure at least one Deployment in the same namespace with an label pair env:prod has more than 3 replicas
resource.List("v1", "services", "default").items.map(s, s.metadata.name).isSorted()Verify that Service names in the default namespace are sorted alphabetically
resource.List("v1", "services", object.metadata.namespace).items.map(s, s.metadata.name).isSorted()Use object.metadata.namespace to dynamically target the current resource’s namespace

In the sample policy below, resource.Get() retrieves a ConfigMap which is then used in the policy evaluation logic:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: restrict-image-registries
spec:
validationActions:
- Deny
evaluation:
background:
enabled: false
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['pods']
variables:
- name: allContainers
expression: >-
object.spec.containers
+ object.spec.?initContainers.orValue([])
+ object.spec.?ephemeralContainers.orValue([])
- name: cm
expression: >-
resource.Get("v1", "configmaps", "kube-system", "allowed-registry")
- name: allowedRegistry
expression: "variables.cm.data[?'registry'].orValue('')"
validations:
- expression: 'variables.allContainers.all(c, c.image.startsWith(variables.allowedRegistry))'
messageExpression: '"image must be from registry: " + string(variables.allowedRegistry)'

This sample policy demonstrates how to use resource.Post() to perform a live access check using Kubernetes’ SubjectAccessReview API:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-subjectaccessreview
spec:
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: [v1]
operations: [CREATE, UPDATE]
resources: [configmaps]
variables:
- name: res
expression: >-
{
"kind": dyn("SubjectAccessReview"),
"apiVersion": dyn("authorization.k8s.io/v1"),
"spec": dyn({
"resourceAttributes": dyn({
"resource": "namespaces",
"namespace": string(object.metadata.namespace),
"verb": "delete",
"group": ""
}),
"user": dyn(request.userInfo.username)
})
}
- name: subjectaccessreview
expression: >-
resource.Post("authorization.k8s.io/v1", "subjectaccessreviews", variables.res)
validations:
- expression: >-
has(variables.subjectaccessreview.status) && variables.subjectaccessreview.status.allowed == true
message: >-
User is not authorized.

This sample policy uses resource.List() to retrieve all existing Ingress resources and ensures that the current Ingress does not introduce duplicate HTTP paths across the cluster:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: unique-ingress-path
spec:
validationActions: [Deny]
evaluation:
background:
enabled: false
matchConstraints:
resourceRules:
- apiGroups: ['networking.k8s.io']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['ingresses']
variables:
- name: allpaths
expression: >-
resource.List("networking.k8s.io/v1", "ingresses", "" ).items
- name: nspath
expression: >-
resource.List("networking.k8s.io/v1", "ingresses", object.metadata.namespace ).items
validations:
- expression: >-
!object.spec.rules.orValue([]).exists(rule,
rule.http.paths.orValue([]).exists(path,
(
variables.allpaths.orValue([]).exists(existing_ingress,
existing_ingress.spec.rules.orValue([]).exists(existing_rule,
existing_rule.http.paths.orValue([]).exists(existing_path,
existing_path.path == path.path
)
)
)
&&
! variables.nspath.orValue([]).exists(existing_ingress,
existing_ingress.metadata.namespace != object.metadata.namespace &&
existing_ingress.spec.rules.orValue([]).exists(existing_rule,
existing_rule.http.paths.orValue([]).exists(existing_path,
existing_path.path == path.path
)
)
)
)
)
)
message: >-
The root path already exists in the cluster but not in the namespace.

The HTTP library allows interaction with external HTTP/S endpoints using http.Get() and http.Post() within policies. These functions enable real-time validation against third-party systems, remote config APIs, or internal services, supporting secure communication via CA bundles for HTTPS endpoints.

CEL ExpressionPurpose
http.Get("https://internal.api/health").status == "ok" Validate external service health before proceeding
http.Get("https://service/data").metadata.team == object.metadata.labels.team Enforce label matching from remote service metadata
http.Post("https://audit.api/log", {"kind": object.kind}, {"Content-Type": "application/json"}).logged == trueConfirm logging of the resource to an external system
http.Get("https://certs.api/rootCA").cert == object.spec.certValidate a certificate field in the object against external data

The following policy fetches data from an external or internal HTTP(S) endpoint and makes it accessible within the policy:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: vpol-http-get
spec:
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: [v1]
operations: [CREATE, UPDATE]
resources: [pods]
variables:
- name: externalData
expression: >-
http.Get("http://test-api-service.default.svc.cluster.local:80")
validations:
- expression: >-
variables.externalData.metadata.labels.app == object.metadata.labels.app
messageExpression: "'only create pod with labels, variables.get.metadata.labels.app: ' + string(variables.get.metadata.labels.app)"

The following sample sends a POST request with a payload to an external service:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: vpol-http-post
spec:
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
resources: ['pods']
operations: ['CREATE']
variables:
- name: response
expression: >-
http.Post(
"http://test-api-service.default.svc.cluster.local/",
{"labels": object.metadata.labels.app},{"Content-Type": "application/json"})
validations:
- expression: variables.response.received == "test"
messageExpression: >-
'External POST call did not return the expected response ' + string(variables.response.received)

When communicating over HTTPS, Kyverno uses the provided CA bundle to validate the server’s certificate.

The User library includes functions like parseServiceAccount() to extract metadata from the user or service account that triggered the admission request. These expressions help enforce policies based on user identity, namespace association, or naming conventions of service accounts.

CEL ExpressionPurpose
parseServiceAccount(request.userInfo.username).Name == "my-sa"Validate that the request is made by a specific ServiceAccount
parseServiceAccount(request.userInfo.username).Namespace == "system"Ensure the ServiceAccount belongs to the system namespace
parseServiceAccount(request.userInfo.username).Name.startsWith("team-")Enforce naming convention for ServiceAccounts
parseServiceAccount(request.userInfo.username).Namespace in ["dev", "prod"]Restrict access to specific namespaces only

This sample policy ensures that only service accounts in the kube-system namespace with names like replicaset-controller, deployment-controller, or daemonset-controller are allowed to create pods:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: restrict-pod-creation
spec:
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
resources: ['pods']
operations: ['CREATE']
variables:
- name: sa
expression: parseServiceAccount(request.userInfo.username)
validations:
- expression: variables.sa.Namespace == "kube-system"
message: Only kube-system service accounts can create pods
- expression: variables.sa.Name in ["replicaset-controller", "deployment-controller", "daemonset-controller"]
message: Only trusted system controllers can create pods
apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: add-service-account-labels
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
resources: ['pods']
operations: ['CREATE', 'UPDATE']
variables:
- name: sa
expression: parseServiceAccount(request.userInfo.username)
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: |
Object{
metadata: Object.metadata{
labels: {
"service-account": string(variables.sa.Name),
"namespace": string(variables.sa.Namespace)
}
}
}

The Image library offers functions to parse and analyze image references. It allows policy authors to inspect registries, tags, and digests, ensuring image standards, such as requiring images from a specific registry or prohibiting tags, are enforced.

CEL ExpressionPurpose
image("nginx:latest")Convert an image string into an image object (must be used before calling any image methods)
isImage("nginx:latest")Check if the string is a valid image
image("nginx:latest").registry()Get the image registry (e.g., docker.io)
image("nginx:latest").repository()Get the image repository path (e.g., library/nginx)
image("nginx:latest").identifier()Get the image identifier (e.g., tag or digest part)
image("nginx:latest").tag()Get the tag portion of the image (e.g., latest)
image("nginx@sha256:abcd...").digest()Get the digest portion of the image
image("nginx:sha256:abcd...").containsDigest()Check if the image string includes a digest
object.spec.containers.map(c, image(c.image)).map(i, i.registry()).all(r, r == "ghcr.io")Ensure all container images come from the ghcr.io registry
object.spec.containers.map(c, image(c.image)).all(i, i.containsDigest())Ensure all images include a digest

The following sample ensures that all images use a digest:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-images
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: [v1]
operations: [CREATE, UPDATE]
resources: [pods]
variables:
- name: images
expression: >-
object.spec.containers.map(e, parseImageReference(e.image))
+ object.spec.?initContainers.orValue([]).map(e, parseImageReference(e.image))
+ object.spec.?ephemeralContainers.orValue([]).map(e, parseImageReference(e.image))
validations:
- expression: >-
variables.images.map(i, i.containsDigest()).all(e, e)
message: >-
images must be specified using a digest

The ImageData library extends image inspection with OCI registry metadata like architecture, OS, digests, tags, and layers. Using image.GetMetadata(), it fetches details about container images from OCI registries, enabling precise validation of image content and compatibility.

CEL ExpressionPurpose
image.GetMetadata("nginx:1.21").config.architecture == "amd64"Ensure the image architecture is amd64
image.GetMetadata("nginx:1.21").config.os == "linux"Verify the image is built for Linux
image.GetMetadata("nginx:1.21").config.author == "docker"Check the image author
image.GetMetadata("nginx:1.21").config.variant == "v7"Validate architecture variant
image.GetMetadata("nginx:1.21").config.created != ""Ensure image has a creation timestamp
image.GetMetadata("nginx:1.21").config.docker_version.startsWith("20.")Check Docker version used to build the image
image.GetMetadata("nginx:1.21").config.container == "nginx"Validate container name
image.GetMetadata("nginx:1.21").config.os_features.exists(f, f == "sse4")Check if specific OS feature exists
image.GetMetadata("nginx:1.21").digest.startsWith("sha256:")Validate that image has a proper SHA256 digest
image.GetMetadata("nginx:1.21").manifest.schemaVersion == 2Check if the image manifest uses schema version 2
image.GetMetadata("nginx:1.21").manifest.mediaType == "application/vnd.docker.distribution.manifest.v2+json"Validate the media type of the image manifest
image.GetMetadata("nginx:1.21").manifest.layers.size() > 0Ensure the manifest lists image layers
image.GetMetadata("nginx:1.21").manifest.annotations.exists(a, a.key == "org.opencontainers.image.title")Check if a specific annotation is present
image.GetMetadata("nginx:1.21").manifest.subject != nullCheck if the image has a subject (e.g., SBOM reference)
image.GetMetadata("nginx:1.21").manifest.config.mediaType.contains("json")Validate that the config descriptor has a JSON media type
image.GetMetadata("nginx:1.21").manifest.layers.all(l, l.mediaType.startsWith("application/vnd.docker"))Ensure all layers have Docker-compatible media types

The image.GetMetadata() function extracts key metadata from OCI images, allowing validation based on various attributes.

This sample policy ensures pod images have metadata, are amd64, and use manifest schema version 2:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-image-details
spec:
validationActions: [Deny]
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: [v1]
operations: [CREATE, UPDATE]
resources: [pods]
variables:
- name: imageRef
expression: object.spec.containers[0].image
- name: imageKey
expression: variables.imageRef
- name: image
expression: image.GetMetadata(variables.imageKey)
validations:
- expression: variables.image != null
message: >-
Failed to retrieve image metadata
- expression: variables.image.config.architecture == "amd64"
messageExpression: >-
string(variables.image.config.architecture) + ' image architecture is not supported'
- expression: variables.image.manifest.schemaVersion == 2
message: >-
Only schemaVersion 2 image manifests are supported

The GlobalContext library introduces shared variables across policies through globalContext.Get(). These variables are populated from external API calls via GlobalContextEntry resources, making it possible to validate requests against cluster-wide configurations or aggregated data with improved efficiency.

CEL ExpressionPurpose
globalContext.Get("gctxentry-apicall-correct", "") != 0Ensure a specific deployment exists before allowing resource creation
globalContext.Get("team-cluster-values", "").someValue == "enabled"Validate shared cluster-wide configuration using global context data
globalContext.Get("global-pod-labels", "").contains(object.metadata.labels)Check that pod labels match predefined global labels

To use this feature, first a GlobalContextEntry must be defined:

apiVersion: kyverno.io/v2alpha1
kind: GlobalContextEntry
metadata:
name: gctxentry-apicall-correct
spec:
apiCall:
urlPath: '/apis/apps/v1/namespaces/test-globalcontext-apicall-correct/deployments'
refreshInterval: 1h

The following policy ensures that a specific deployment exists before allowing Pod creation.

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: cpol-apicall-correct
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: [v1]
operations: ['CREATE', 'UPDATE']
resources: ['pods']
variables:
- name: dcount
expression: >-
globalContext.Get("gctxentry-apicall-correct", "")
validations:
- expression: >-
variables.dcount != 0
message: >-
main-deployment should exist

By leveraging Global Context, Kyverno eliminates redundant queries and enables efficient, cross-policy data sharing, enhancing validation accuracy and performance.

The Hash library introduces the ability to run hash functions on arbitrary values. This can be used for verification as well as mutating objects with hash information.

CEL ExpressionPurpose
md5("somestring")Obtain the md5 hash of an arbitrary string value
`sha1(“somestring”)Obtain the sha1 hash of an arbitrary string value
sha256("somestring")Obtain the sha256 hash of an arbitrary string value

The following policy ensures that the hash of the image name in a certain deployment matches the required value which represents nginx:latest:

apiVersion: policies.kyverno.io/v1beta1
kind: ValidatingPolicy
metadata:
name: check-hash
spec:
matchConstraints:
resourceRules:
- apiGroups: [apps]
apiVersions: [v1]
operations: [CREATE, UPDATE]
resources: [deployments]
variables:
- name: expectedHash
expression: >-
"403554a5dfea78d1ca4a8ff5830ac2ae"
- name: md5sum
expression: >-
md5(object.spec.template.spec.containers[0].image)
- name: accept
expression: >-
variables.md5sum == variables.expectedHash
validations:
- expression: >-
variables.accept
messageExpression: >-
'Expected MD5 hash ' + variables.expectedHash + ', got: ' + variables.md5sum

The Math library provides mathematical operations for numeric computations in policies. It includes functions for rounding numbers to specific precision levels, supporting both positive and negative precision values.

CEL ExpressionPurpose
math.round(10.125, 2)Round to 2 decimal places (returns 10.13)
math.round(10.125, 0)Round to nearest integer (returns 10.0)
math.round(12345.6789, -2)Round to nearest hundred (returns 12300.0)

This sample policy validates that CPU requests are rounded to reasonable precision and don’t exceed 8 cores:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: validate-cpu-precision
spec:
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['pods']
variables:
- name: containers
expression: >-
object.spec.containers + object.spec.?initContainers.orValue([])
- name: cpuRequests
expression: >-
variables.containers.map(c,
has(c.resources.requests) && has(c.resources.requests.cpu) ?
double(c.resources.requests.cpu.replace('m', '')) / 1000.0 : 0.0
)
validations:
- expression: >-
variables.cpuRequests.all(cpu,
cpu == 0.0 || math.round(cpu, 2) == cpu
)
message: >-
CPU requests must be rounded to at most 2 decimal places
- expression: >-
variables.cpuRequests.all(cpu, cpu <= 8.0)
messageExpression: >-
'CPU request ' + string(variables.cpuRequests.filter(c, c > 8.0)[0]) +
' exceeds maximum of 8.0 cores'

The Random library generates random strings based on regex patterns, useful for creating unique identifiers, tokens, or default values in mutating policies. It supports custom patterns or uses a default pattern when no argument is provided.

CEL ExpressionPurpose
random()Generate random 8-character alphanumeric string [0-9a-z]{8}
random("[A-Z0-9]{8}")Generate 8-character uppercase alphanumeric string
random("[0-9]{4}-[0-9]{4}")Generate pattern like 1234-5678
random("[a-f0-9]{32}")Generate 32-character hex string
random("[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}")Generate UUID-like pattern

This sample mutating policy adds a unique tracking ID to deployments:

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: add-tracking-id
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['apps/v1']
operations: ['CREATE']
resources: ['deployments']
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: |
Object{
metadata: Object.metadata{
labels: {
"tracking-id": random("[a-z0-9]{8}-[a-z0-9]{4}"),
}
}
}

The Transform library provides data transformation utilities for converting between different data structures. The listObjToMap() function merges two lists of objects into a map by extracting specified key and value fields.

CEL ExpressionPurpose
listObjToMap([{"name": "app1"}], [{"version": "v1"}], "name", "version")Create map {"app1": "v1"} from two lists
listObjToMap([{"id": "x"}, {"id": "y"}], [{"val": 1}, {"val": 2}], "id", "val")Create map {"x": 1, "y": 2}
listObjToMap(object.spec.containers.map(c, {"name": c.name}), object.spec.containers.map(c, {"image": c.image}), "name", "image")Map container names to images

This sample policy validates that container environment variables match expected values:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: require-key
spec:
failurePolicy: Fail
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['pods']
variables:
- name: envMap
expression: >-
listObjToMap(
object.spec.containers[0].env,
object.spec.containers[0].env,
"name",
"value"
)
validations:
- expression: >-
variables.envMap["KEY"] == "123-456-789"
message: >-
KEY must be 123-456-789.

In practice:

Terminal window
/ # cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: object-from-list-demo
labels:
foo: bar
spec:
containers:
- name: containername01
image: containerimage:01
env:
- name: KEY
value: "89383938"
- name: endpoint
value: "licensing.corp.org"
EOF
Error from server: error when creating "STDIN": admission webhook "vpol.validate.kyverno.svc-fail" denied the request: Policy require-key failed: KEY must be 123-456-789.

The JSON library provides JSON parsing capabilities through the json.unmarshal() function, which converts JSON strings into structured data that can be used in policy expressions.

CEL ExpressionPurpose
json.unmarshal('{"key": "value"}')Parse JSON string into map
json.unmarshal('{"count": 5}').countParse and access nested field
json.unmarshal('[1, 2, 3]')Parse JSON array
json.unmarshal(object.metadata.annotations['config'])Parse JSON from annotation

This sample policy validates configuration stored as JSON in annotations:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: require-security-approval
spec:
failurePolicy: Fail
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: ['apps']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['deployments']
variables:
- name: approval
expression: >-
json.unmarshal(object.metadata.annotations["security.approval"])
validations:
- expression: >-
variables.approval.approved == true
message: >-
Deployment is not approved by security policy.

When applied:

Terminal window
/ # cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-prod
labels:
app: nginx
annotations:
security.approval: |
{
"approved": false,
"reviewedBy": "platform-security",
"ticket": "SEC-4821",
"reviewedAt": "2026-01-12T09:41:00Z",
"notes": "Pending image vulnerability scan"
}
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
resources:
requests:
cpu: "1"
EOF
Error from server: error when creating "STDIN": admission webhook "vpol.validate.kyverno.svc-fail" denied the request: Policy require-security-approval failed: Deployment is not approved by security policy.

The YAML library enables parsing of YAML-formatted strings into structured data using the yaml.parse() function. This is particularly useful for validating YAML configurations stored in ConfigMaps or annotations.

CEL ExpressionPurpose
yaml.parse('key: value')Parse YAML string into map
yaml.parse('items:\n- a\n- b\n- c')Parse YAML with lists
yaml.parse(object.data['config.yaml'])Parse YAML from ConfigMap data
yaml.parse('count: 42').countParse and access nested field

This sample policy validates YAML value in a deployment annotation:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: check-goodboi
spec:
failurePolicy: Fail
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: [apps]
apiVersions: [v1]
operations: [CREATE, UPDATE]
resources: [deployments]
variables:
- name: pets
expression: >-
yaml.parse(object.metadata.annotations.pets)
validations:
- expression: >-
variables.pets.species.isGoodBoi == true
message: >-
"Only good bois allowed."

Seeing it in action:

Terminal window
/ # cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: some-deploy
labels:
app: nginx
annotations:
pets: |-
species:
dog: lab
name: dory
color: black
height: 15
isGoodBoi: false
snacks:
- chimken
- fries
- pizza
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
resources:
requests:
cpu: 1
EOF
Error from server: error when creating "STDIN": admission webhook "vpol.validate.kyverno.svc-fail" denied the request: Policy check-goodboi failed: "Only good bois allowed."

The kyverno CEL execution environment comes with a few functions related to time. Which are getting the current time, truncate timestamps, and convert timestamps to cron format.

CEL ExpressionPurpose
time.now()Get current timestamp
time.now() - duration("24h")Get timestamp from 24 hours ago
time.truncate(timestamp("2025-01-02T03:45:27Z"), duration("1h"))Truncate to nearest hour (returns 2025-01-02T03:00:00Z)
time.toCron(timestamp("2025-01-02T15:30:00Z"))Convert timestamp to cron format (returns "30 15 2 1 4")
time.now() + duration("7d")Get timestamp 7 days in the future

This deleting policy deletes policy exceptions that were created more than 24 hours ago and is evaluated every 10 minutes:

apiVersion: policies.kyverno.io/v1
kind: DeletingPolicy
metadata:
name: delete-exceptions
spec:
conditions:
- expression: time.now() - timestamp(object.metadata.creationTimestamp) > duration("24h")
name: check-creation-time
matchConstraints:
resourceRules:
- apiGroups:
- 'policies.kyverno.io'
apiVersions:
- v1
resources:
- policyexceptions
schedule: '*/10 * * * *'

This deleting policy deletes policy exceptions whose creation timestamp when truncated to the nearest hour is longer than 24 hours:

apiVersion: policies.kyverno.io/v1
kind: DeletingPolicy
metadata:
name: delete-exceptions-trunacted
spec:
conditions:
- expression: time.now() - time.truncate(timestamp(object.metadata.creationTimestamp), duration("1h")) > duration("24h")
name: check-creation-time
matchConstraints:
resourceRules:
- apiGroups:
- 'policies.kyverno.io'
apiVersions:
- v1
resources:
- policyexceptions
schedule: '*/10 * * * *'

The X509 library enables decoding and validation of X.509 certificates and certificate signing requests (CSRs) in PEM format. It extracts certificate metadata including issuer, subject, validity periods, key usage, extensions, and public key information.

CEL ExpressionPurpose
x509.decode(cert).IsCACheck if certificate is a Certificate Authority
x509.decode(cert).NotBeforeGet certificate validity start date
x509.decode(cert).NotAfterGet certificate expiration date
x509.decode(cert).Issuer.CommonNameExtract issuer common name
x509.decode(cert).DNSNamesGet list of DNS names from Subject Alternative Names
x509.decode(cert).KeyUsageGet certificate key usage flags
x509.decode(cert).BasicConstraintsValidCheck if basic constraints extension is valid
x509.decode(cert).PublicKey.EGet RSA public key exponent

This sample policy validates the expiry date of a webhook certificate being more than one week away:

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: test-x509-decode
spec:
failurePolicy: Fail
validationActions:
- Deny
matchConstraints:
resourceRules:
- apiGroups: ['admissionregistration.k8s.io']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources:
- validatingwebhookconfigurations
- mutatingwebhookconfigurations
validations:
- expression: >-
timestamp(x509.decode(string(base64.decode(object.webhooks[0].clientConfig.caBundle))).NotAfter) - time.now() > duration("168h")
message: >-
Certificate in the webhook will expire in less than a week.

The full certificate JSON object has the following structure:

{
"AuthorityKeyId": null,
"BasicConstraintsValid": true,
"CRLDistributionPoints": null,
"DNSNames": null,
"EmailAddresses": null,
"ExcludedDNSDomains": null,
"ExcludedEmailAddresses": null,
"ExcludedIPRanges": null,
"ExcludedURIDomains": null,
"ExtKeyUsage": null,
"Extensions": [
{
"Critical": true,
"Id": [2, 5, 29, 15],
"Value": "AwICpA=="
},
{
"Critical": true,
"Id": [2, 5, 29, 19],
"Value": "MAMBAf8="
},
{
"Critical": false,
"Id": [2, 5, 29, 14],
"Value": "BBSWivt1n53+61ZGAczAi0mleejTKg=="
}
],
"ExtraExtensions": null,
"IPAddresses": null,
"IsCA": true,
"Issuer": {
"CommonName": "*.kyverno.svc",
"Country": null,
"ExtraNames": null,
"Locality": null,
"Names": [
{
"Type": [2, 5, 4, 3],
"Value": "*.kyverno.svc"
}
],
"Organization": null,
"OrganizationalUnit": null,
"PostalCode": null,
"Province": null,
"SerialNumber": "",
"StreetAddress": null
},
"IssuingCertificateURL": null,
"KeyUsage": 37,
"MaxPathLen": -1,
"MaxPathLenZero": false,
"NotAfter": "2023-10-10T12:46:32Z",
"NotBefore": "2022-10-10T11:46:32Z",
"OCSPServer": null,
"PermittedDNSDomains": null,
"PermittedDNSDomainsCritical": false,
"PermittedEmailAddresses": null,
"PermittedIPRanges": null,
"PermittedURIDomains": null,
"PolicyIdentifiers": null,
"PublicKey": {
"E": 65537,
"N": "28595925905962223424520947352207105451744616797088171943239289907331901888529856098458304611629660120574607501039902142361333982065793213267074854658525100799280158707840279479550961169213763526857247298653141711003931642606662052674943191476488665842309583311097351331994267413776792462637192775240062778036062353517979538994974045127175206597906751521558536719043095219698535279694800624795673809356898452438518041024126624051887044932164506019573725987204208750674129677584956156611454245004918943771571492757639432459688931855526941886354880727024912384140238027697348634609952850513122734230521040730560514233467"
},
"PublicKeyAlgorithm": 1,
"Raw": "MIIC7TCCAdWgAwIBAgIBADANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA0qLmt5dmVybm8uc3ZjMB4XDTIyMTAxMDExNDYzMloXDTIzMTAxMDEyNDYzMlowGDEWMBQGA1UEAwwNKi5reXZlcm5vLnN2YzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOKF+2P0Ufp855hpdsGD4lYkd6oU7HZAOWm1XskAMwrdsqWwTNNAinyHRoPQIbNbGDQ+r6Cggc2mlxHJ90PnC2weHj5otaD17Z+ARZpJZ4HMWkEfFt8sxwo9vuQJRWihqNwFheowjswoSB1DHnPufrZHfztkMoRx278ZfHaIMdlSTg50ektkNDoHA3OJsxxw54X3HR1iq6SZwN8xNT0TI6B6BbfAYWMNmKCiZ2iV6kW//XnTEqGd2WcmhuP0SjwO4tCJbj9oV6+Bj/uhFr7J4foErMaodYDBtQs/ul2tcAwSBHfnC2KcLbiZTZsC0Rs0WPJ4YwF/cOsD7Z/RmLs4FHsCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJaK+3Wfnf7rVkYBzMCLSaV56NMqMA0GCSqGSIb3DQEBCwUAA4IBAQDY7F6b+t9BX7098JyGk6zeT39MoLdSv+8IaKXn+m8GyOKn3CZkruko57ycvPd4taC0gggtmUYynFhwPMQr+boNrrK9rat8Jw3yPPsBq/8D/s6tvwxSNXBfPUI5OvNIB/hA5XpJpdHQaCkYm+FWkcJsolkkbSOfVjUjImW26JHBnnPPtR4Y7dx0SVoPS19IC0T5RmdvgqlXj4XbhTnX3QOujVHn8u+wQ8po7EngHDQs+onfkp8ipe0QpEJL1ZdW2LhyDXGKrZ2y8UPZ9wYNzxHWaj1Thu4B9YFdsPUwWqSxn9e+FygpoktlD8YgT7jwgiVKX7Koz++zyvMIdhvRrtgS",
"RawIssuer": "MBgxFjAUBgNVBAMMDSoua3l2ZXJuby5zdmM=",
"RawSubject": "MBgxFjAUBgNVBAMMDSoua3l2ZXJuby5zdmM=",
"RawSubjectPublicKeyInfo": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4oX7Y/RR+nznmGl2wYPiViR3qhTsdkA5abVeyQAzCt2ypbBM00CKfIdGg9Ahs1sYND6voKCBzaaXEcn3Q+cLbB4ePmi1oPXtn4BFmklngcxaQR8W3yzHCj2+5AlFaKGo3AWF6jCOzChIHUMec+5+tkd/O2QyhHHbvxl8dogx2VJODnR6S2Q0OgcDc4mzHHDnhfcdHWKrpJnA3zE1PRMjoHoFt8BhYw2YoKJnaJXqRb/9edMSoZ3ZZyaG4/RKPA7i0IluP2hXr4GP+6EWvsnh+gSsxqh1gMG1Cz+6Xa1wDBIEd+cLYpwtuJlNmwLRGzRY8nhjAX9w6wPtn9GYuzgUewIDAQAB",
"RawTBSCertificate": "MIIB1aADAgECAgEAMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNVBAMMDSoua3l2ZXJuby5zdmMwHhcNMjIxMDEwMTE0NjMyWhcNMjMxMDEwMTI0NjMyWjAYMRYwFAYDVQQDDA0qLmt5dmVybm8uc3ZjMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4oX7Y/RR+nznmGl2wYPiViR3qhTsdkA5abVeyQAzCt2ypbBM00CKfIdGg9Ahs1sYND6voKCBzaaXEcn3Q+cLbB4ePmi1oPXtn4BFmklngcxaQR8W3yzHCj2+5AlFaKGo3AWF6jCOzChIHUMec+5+tkd/O2QyhHHbvxl8dogx2VJODnR6S2Q0OgcDc4mzHHDnhfcdHWKrpJnA3zE1PRMjoHoFt8BhYw2YoKJnaJXqRb/9edMSoZ3ZZyaG4/RKPA7i0IluP2hXr4GP+6EWvsnh+gSsxqh1gMG1Cz+6Xa1wDBIEd+cLYpwtuJlNmwLRGzRY8nhjAX9w6wPtn9GYuzgUewIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUlor7dZ+d/utWRgHMwItJpXno0yo=",
"SerialNumber": 0,
"Signature": "2Oxem/rfQV+9PfCchpOs3k9/TKC3Ur/vCGil5/pvBsjip9wmZK7pKOe8nLz3eLWgtIIILZlGMpxYcDzEK/m6Da6yva2rfCcN8jz7Aav/A/7Orb8MUjVwXz1COTrzSAf4QOV6SaXR0GgpGJvhVpHCbKJZJG0jn1Y1IyJltuiRwZ5zz7UeGO3cdElaD0tfSAtE+UZnb4KpV4+F24U5190Dro1R5/LvsEPKaOxJ4Bw0LPqJ35KfIqXtEKRCS9WXVti4cg1xiq2dsvFD2fcGDc8R1mo9U4buAfWBXbD1MFqksZ/XvhcoKaJLZQ/GIE+48IIlSl+yqM/vs8rzCHYb0a7YEg==",
"SignatureAlgorithm": 4,
"Subject": {
"CommonName": "*.kyverno.svc",
"Country": null,
"ExtraNames": null,
"Locality": null,
"Names": [
{
"Type": [2, 5, 4, 3],
"Value": "*.kyverno.svc"
}
],
"Organization": null,
"OrganizationalUnit": null,
"PostalCode": null,
"Province": null,
"SerialNumber": "",
"StreetAddress": null
},
"SubjectKeyId": "lor7dZ+d/utWRgHMwItJpXno0yo=",
"URIs": null,
"UnhandledCriticalExtensions": null,
"UnknownExtKeyUsage": null,
"Version": 3
}