ValidatingPolicy

Validate Kubernetes Resources or JSON payloads

The Kyverno ValidatingPolicy type extends the Kubernetes ValidatingAdmissionPolicy type for complex policy evaluations and other features required for Policy-as-Code. A ValidatingPolicy is a superset of a ValidatingAdmissionPolicy and contains additional fields for Kyverno specific features.

Comparison with ValidatingAdmissionPolicy

The table below compares major features across the Kubernetes ValidatingAdmissionPolicy and Kyverno ValidatingPolicy types.

FeatureValidatingAdmissionPolicyValidatingPolicy
EnforcementAdmissionAdmission, Background, Pipelines, …
PayloadsKubernetesKubernetes, Any JSON or YAML
DistributionKubernetesHelm, CLI, Web Service, API, SDK
CEL LibraryBasicExtended
BindingsManualAutomatic
Auto-generation-Pod Controllers, ValidatingAdmissionPolicy
External Data_Kubernetes resources or API calls
Caching_Global Context, image verification results
Background scans-Periodic, On policy creation or change
Exceptions-Fine-grained exceptions
Reporting_Policy WG Reports API
Testing_Kyverno CLI (unit), Chainsaw (e2e)

Additional Fields

The ValidatingPolicy extends the Kubernetes ValidatingAdmissionPolicy with the following additional fields for Kyverno features. A complete reference is provided in the API specification.

evaluation

The spec.evaluation field defines how the policy is applied and how the payload is processed. It can be used to enable, or disable, admission request processing and background processing for a policy. It is also used to manage whether the payload is processed as JSON or a Kubernetes resource.

 1apiVersion: policies.kyverno.io/v1alpha1
 2kind: ValidatingPolicy
 3metadata:
 4  name: sample
 5spec:
 6  evaluation:
 7    admission:
 8      enabled: false
 9    background:
10      enabled: true
11    mode : Kubernetes
12  ...

The mode can be set to JSON for non-Kubernetes payloads.

Refer to the API Reference for details.

webhookConfiguration

The spec.webhookConfiguration field defines properties used to manage the Kyverno admission controller webhook settings.

1apiVersion: policies.kyverno.io/v1alpha1
2kind: ValidatingPolicy
3metadata:
4  name: check-deployment-labels
5spec:
6  webhookConfiguration:
7   timeoutSeconds: 15
8  ...

In the policy above, webhookConfiguration.timeoutSeconds is set to 15, which defines how long the admission request waits for policy evaluation. The default is 10s, and the allowed range is 1–30s. After this timeout, the request may fail or ignore the result based on the failure policy. Kyverno reflects this setting in the generated ValidatingWebhookConfiguration.

Refer to the API Reference for details.

autogen

The spec.autogen field defines policy auto-generation behaviors, to automatically generate policies for pod controllers and generate ValidatingAdmissionPolicy types for Kubernetes API server execution.

Here is an example of generating policies for deployments, jobs, cronjobs, and statefulsets and also generating a ValidatingAdmissionPolicy from the ValidatingPolicy declaration:

 1 apiVersion: policies.kyverno.io/v1alpha1
 2 kind: ValidatingPolicy
 3 metadata:
 4   name: disallow-capabilities
 5 spec:
 6   autogen:
 7    validatingAdmissionPolicy:
 8     enabled: true
 9    podControllers:
10      controllers:
11       - deployments
12       - jobs
13       - cronjobs
14       - statefulsets

Generating a ValidatingAdmissionPolicy from a ValidatingPolicy provides the benefits of faster and more resilient execution during admission controls while leveraging all features of Kyverno.

Refer to the API Reference for details.

Kyverno CEL Libraries

Kyverno enhances Kubernetes’ CEL environment with libraries enabling complex policy logic and advanced features. For comprehensive documentation of all available CEL libraries, see the CEL Libraries documentation.

Resource library

Resource Library Examples

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 Examples

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 Examples

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

Image Library Examples

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 Examples

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 Examples

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.

Policies are applied cluster-wide to help secure your cluster. However, there may be times when teams or users need to test specific tools or resources. In such cases, users can use PolicyException to bypass policies without modifying or tweaking the policies themselves. This ensures that your policies remain secure while providing the flexibility to bypass them when needed.


Last modified July 23, 2025 at 11:13 AM PST: Add docs for MutatingPolicy (#1601) (d1aa986)