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.
Resource library
Section titled “Resource library”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 Expression | Purpose |
|---|---|
resource.Get("v1", "configmaps", "default", "clusterregistries").data["registries"] | Fetch a ConfigMap value from a specific namespace |
resource.List("apps/v1", "deployments", "").items.size() > 0 | Check 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/v1kind: ValidatingPolicymetadata: name: restrict-image-registriesspec: 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/v1kind: ValidatingPolicymetadata: name: check-subjectaccessreviewspec: 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/v1kind: ValidatingPolicymetadata: name: unique-ingress-pathspec: 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.HTTP library
Section titled “HTTP library”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 Expression | Purpose |
|---|---|
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 == true | Confirm logging of the resource to an external system |
http.Get("https://certs.api/rootCA").cert == object.spec.cert | Validate 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/v1kind: ValidatingPolicymetadata: name: vpol-http-getspec: 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/v1kind: ValidatingPolicymetadata: name: vpol-http-postspec: 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.
User library
Section titled “User library”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 Expression | Purpose |
|---|---|
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/v1kind: ValidatingPolicymetadata: name: restrict-pod-creationspec: 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 podsapiVersion: policies.kyverno.io/v1kind: MutatingPolicymetadata: name: add-service-account-labelsspec: 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) } } }Image library
Section titled “Image library”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 Expression | Purpose |
|---|---|
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/v1kind: ValidatingPolicymetadata: name: check-imagesspec: 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 digestImageData library
Section titled “ImageData library”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 Expression | Purpose |
|---|---|
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 == 2 | Check 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() > 0 | Ensure 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 != null | Check 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/v1kind: ValidatingPolicymetadata: name: check-image-detailsspec: 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 supportedGlobalContext library
Section titled “GlobalContext library”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 Expression | Purpose |
|---|---|
globalContext.Get("gctxentry-apicall-correct", "") != 0 | Ensure 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/v2alpha1kind: GlobalContextEntrymetadata: name: gctxentry-apicall-correctspec: apiCall: urlPath: '/apis/apps/v1/namespaces/test-globalcontext-apicall-correct/deployments' refreshInterval: 1hThe following policy ensures that a specific deployment exists before allowing Pod creation.
apiVersion: policies.kyverno.io/v1kind: ValidatingPolicymetadata: name: cpol-apicall-correctspec: 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 existBy leveraging Global Context, Kyverno eliminates redundant queries and enables efficient, cross-policy data sharing, enhancing validation accuracy and performance.
Hash library
Section titled “Hash library”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 Expression | Purpose |
|---|---|
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/v1beta1kind: ValidatingPolicymetadata: name: check-hashspec: 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.md5sumMath library
Section titled “Math library”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 Expression | Purpose |
|---|---|
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/v1kind: ValidatingPolicymetadata: name: validate-cpu-precisionspec: 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'Random library
Section titled “Random library”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 Expression | Purpose |
|---|---|
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/v1kind: MutatingPolicymetadata: name: add-tracking-idspec: 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}"), } } }Transform library
Section titled “Transform library”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 Expression | Purpose |
|---|---|
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/v1kind: ValidatingPolicymetadata: name: require-keyspec: 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:
/ # cat <<EOF | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: object-from-list-demo labels: foo: barspec: containers: - name: containername01 image: containerimage:01 env: - name: KEY value: "89383938" - name: endpoint value: "licensing.corp.org"EOFError 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.JSON library
Section titled “JSON library”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 Expression | Purpose |
|---|---|
json.unmarshal('{"key": "value"}') | Parse JSON string into map |
json.unmarshal('{"count": 5}').count | Parse 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/v1kind: ValidatingPolicymetadata: name: require-security-approvalspec: 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:
/ # cat <<EOF | kubectl apply -f -apiVersion: apps/v1kind: Deploymentmetadata: 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"EOFError 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.YAML library
Section titled “YAML library”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 Expression | Purpose |
|---|---|
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').count | Parse and access nested field |
This sample policy validates YAML value in a deployment annotation:
apiVersion: policies.kyverno.io/v1kind: ValidatingPolicymetadata: name: check-goodboispec: 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:
/ # cat <<EOF | kubectl apply -f -apiVersion: apps/v1kind: Deploymentmetadata: name: some-deploy labels: app: nginx annotations: pets: |- species: dog: lab name: dory color: black height: 15 isGoodBoi: false snacks: - chimken - fries - pizzaspec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest resources: requests: cpu: 1EOFError from server: error when creating "STDIN": admission webhook "vpol.validate.kyverno.svc-fail" denied the request: Policy check-goodboi failed: "Only good bois allowed."Time functions
Section titled “Time functions”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 Expression | Purpose |
|---|---|
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/v1kind: DeletingPolicymetadata: name: delete-exceptionsspec: 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/v1kind: DeletingPolicymetadata: name: delete-exceptions-trunactedspec: 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 * * * *'X509 library
Section titled “X509 library”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 Expression | Purpose |
|---|---|
x509.decode(cert).IsCA | Check if certificate is a Certificate Authority |
x509.decode(cert).NotBefore | Get certificate validity start date |
x509.decode(cert).NotAfter | Get certificate expiration date |
x509.decode(cert).Issuer.CommonName | Extract issuer common name |
x509.decode(cert).DNSNames | Get list of DNS names from Subject Alternative Names |
x509.decode(cert).KeyUsage | Get certificate key usage flags |
x509.decode(cert).BasicConstraintsValid | Check if basic constraints extension is valid |
x509.decode(cert).PublicKey.E | Get 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/v1kind: ValidatingPolicymetadata: name: test-x509-decodespec: 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}