CEL Libraries

Extended CEL functions for complex policy logic and advanced features

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

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("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:

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: restrict-image-registries
 5spec:
 6  validationActions:
 7    - Deny
 8  evaluation:
 9    background:
10      enabled: false
11  matchConstraints:
12    resourceRules:
13      - apiGroups: [""]
14        apiVersions: ["v1"]
15        operations: ["CREATE", "UPDATE"]
16        resources: ["pods"]
17  variables:
18    - name: allContainers
19      expression: >-
20        object.spec.containers 
21        + object.spec.?initContainers.orValue([]) 
22        + object.spec.?ephemeralContainers.orValue([])
23    - name: cm
24      expression: >-
25        resource.Get("v1", "configmaps", "kube-system", "allowed-registry")
26    - name: allowedRegistry
27      expression: "variables.cm.data[?'registry'].orValue('')"
28  validations:
29    - expression: "variables.allContainers.all(c, c.image.startsWith(variables.allowedRegistry))"
30      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:

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: check-subjectaccessreview
 5spec:
 6  validationActions:
 7    - Deny
 8  matchConstraints:
 9    resourceRules:
10    - apiGroups:   ['']
11      apiVersions: [v1]
12      operations:  [CREATE, UPDATE]
13      resources:   [configmaps]
14  variables:
15    - name: res
16      expression: >-
17        {
18          "kind": dyn("SubjectAccessReview"),
19          "apiVersion": dyn("authorization.k8s.io/v1"),
20          "spec": dyn({
21            "resourceAttributes": dyn({
22              "resource": "namespaces",
23              "namespace": string(object.metadata.namespace),
24              "verb": "delete",
25              "group": ""
26            }),
27            "user": dyn(request.userInfo.username)
28          })
29        }
30    - name: subjectaccessreview
31      expression: >-
32        resource.Post("authorization.k8s.io/v1", "subjectaccessreviews", variables.res)
33  validations:
34    - expression: >-
35        has(variables.subjectaccessreview.status) && variables.subjectaccessreview.status.allowed == true
36      message: >-
37        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:

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: unique-ingress-path
 5spec:
 6  validationActions: [Deny]
 7  evaluation:
 8   background: 
 9    enabled: false
10  matchConstraints:
11    resourceRules:
12      - apiGroups: ["networking.k8s.io"]
13        apiVersions: ["v1"]
14        operations: ["CREATE", "UPDATE"]
15        resources: ["ingresses"]
16  variables:
17        - name: allpaths
18          expression: >-
19            resource.List("networking.k8s.io/v1", "ingresses", "" ).items
20        - name: nspath
21          expression: >-
22            resource.List("networking.k8s.io/v1", "ingresses", object.metadata.namespace ).items    
23  validations:
24    - expression: >-
25            !object.spec.rules.orValue([]).exists(rule, 
26                rule.http.paths.orValue([]).exists(path, 
27                  (
28                    variables.allpaths.orValue([]).exists(existing_ingress, 
29                      existing_ingress.spec.rules.orValue([]).exists(existing_rule, 
30                        existing_rule.http.paths.orValue([]).exists(existing_path, 
31                          existing_path.path == path.path 
32                        )
33                      )
34                    )
35                    &&
36                   ! variables.nspath.orValue([]).exists(existing_ingress, 
37                            existing_ingress.metadata.namespace != object.metadata.namespace &&
38
39                      existing_ingress.spec.rules.orValue([]).exists(existing_rule, 
40                        existing_rule.http.paths.orValue([]).exists(existing_path, 
41                       existing_path.path == path.path
42                        )
43                      )
44                    )
45                  )
46                )
47              )
48
49      message: >-
50        The root path already exists in the cluster but not in the namespace.

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 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.teamEnforce 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:

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: vpol-http-get
 5spec:
 6  validationActions:
 7    - Deny
 8  matchConstraints:
 9    resourceRules:
10      - apiGroups: [""]
11        apiVersions: [v1]
12        operations: [CREATE, UPDATE]
13        resources: [pods]
14  variables:
15    - name: externalData
16      expression: >-
17        http.Get("http://test-api-service.default.svc.cluster.local:80")
18  validations:
19    - expression: >-
20        variables.externalData.metadata.labels.app == object.metadata.labels.app
21      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:

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: vpol-http-post
 5spec:
 6  validationActions:
 7    - Deny
 8  matchConstraints:
 9    resourceRules:
10      - apiGroups: [""]
11        apiVersions: ["v1"]
12        resources: ["pods"]
13        operations: ["CREATE"]
14  variables:
15    - name: response
16      expression: >-
17       http.Post(
18        "http://test-api-service.default.svc.cluster.local/",
19        {"labels": object.metadata.labels.app},{"Content-Type": "application/json"})
20  validations:
21    - expression: variables.response.received == "test"
22      messageExpression: >-
23       '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

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:

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: restrict-pod-creation
 5spec:
 6  validationActions:
 7    - Deny
 8  matchConstraints:
 9    resourceRules:
10      - apiGroups: [""]
11        apiVersions: ["v1"]
12        resources: ["pods"]
13        operations: ["CREATE"]
14  variables:
15    - name: sa
16      expression: parseServiceAccount(request.userInfo.username)
17  validations:
18    - expression: variables.sa.Namespace == "kube-system"
19      message: Only kube-system service accounts can create pods
20    - expression: variables.sa.Name in ["replicaset-controller", "deployment-controller", "daemonset-controller"]
21      message: Only trusted system controllers can create pods
 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: MutatingPolicy
 3metadata:
 4  name: add-service-account-labels
 5spec:
 6  matchConstraints:
 7    resourceRules:
 8      - apiGroups: [""]
 9        apiVersions: ["v1"]
10        resources: ["pods"]
11        operations: ["CREATE", "UPDATE"]
12  variables:
13    - name: sa
14      expression: parseServiceAccount(request.userInfo.username)
15  mutations:
16    - patchType: ApplyConfiguration
17      applyConfiguration:
18        expression: |
19          Object{
20            metadata: Object.metadata{
21              labels: {
22                "service-account": string(variables.sa.Name),
23                "namespace": string(variables.sa.Namespace)
24              }
25            }
26          }

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 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:

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: check-images
 5spec:
 6  matchConstraints:
 7    resourceRules:
 8      - apiGroups: [""]
 9        apiVersions: [v1]
10        operations: [CREATE, UPDATE]
11        resources: [pods]
12  variables:
13    - name: images
14      expression: >-
15        object.spec.containers.map(e, parseImageReference(e.image))
16        + object.spec.?initContainers.orValue([]).map(e, parseImageReference(e.image))
17        + object.spec.?ephemeralContainers.orValue([]).map(e, parseImageReference(e.image))
18  validations:
19    - expression: >-
20        variables.images.map(i, i.containsDigest()).all(e, e)
21      message: >-
22        images must be specified using a digest

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 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:

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: check-image-details
 5spec:
 6  validationActions: [Deny]
 7  matchConstraints:
 8    resourceRules:
 9      - apiGroups:   [""]
10        apiVersions: [v1]
11        operations:  [CREATE, UPDATE]
12        resources:   [pods]
13  variables:
14    - name: imageRef
15      expression: object.spec.containers[0].image
16    - name: imageKey
17      expression: variables.imageRef
18    - name: image
19      expression: image.GetMetadata(variables.imageKey)
20
21  validations:
22    - expression: variables.image != null
23      message: >-
24       Failed to retrieve image metadata
25
26    - expression: variables.image.config.architecture == "amd64"
27      messageExpression: >-
28        string(variables.image.config.architecture) + ' image architecture is not supported'
29
30    - expression: variables.image.manifest.schemaVersion == 2
31      message: >-
32       Only schemaVersion 2 image manifests are supported

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 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:

1apiVersion: kyverno.io/v2alpha1
2kind: GlobalContextEntry
3metadata:
4  name: gctxentry-apicall-correct
5spec:
6  apiCall:
7    urlPath: "/apis/apps/v1/namespaces/test-globalcontext-apicall-correct/deployments"
8    refreshInterval: 1h

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

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: cpol-apicall-correct
 5spec:
 6  matchConstraints:
 7    resourceRules:
 8      - apiGroups: [""]
 9        apiVersions: [v1]
10        operations: ["CREATE", "UPDATE"]
11        resources: ["pods"]
12  variables:
13    - name: dcount
14      expression: >-
15        globalContext.Get("gctxentry-apicall-correct", "")
16  validations:
17    - expression: >-
18        variables.dcount != 0
19      message: >-
20        main-deployment should exist

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


Last modified July 24, 2025 at 2:11 AM PST: update policy types and home page (#1613) (4d01bd5)