Variables
Variables make policies smarter and reusable by enabling references to data in the policy definition, the admission review request, and external data sources like ConfigMaps and the Kubernetes API Server.
Variables are stored as JSON and Kyverno supports using JMESPath(pronounced “James path”) to select and transform JSON data. With JMESPath, values from data sources are referenced in the format of {{key1.key2.key3}}
. For example, to reference the name of an new/incoming resource during a kubectl apply
action such as a Namespace, you would write this as a variable reference: {{request.object.metadata.name}}
. The policy engine will substitute any values with the format {{ <JMESPath> }}
with the variable value before processing the rule.
Note
Variables are not currently allowed inmatch
or exclude
statements or patchesJson6902.path
.
Pre-defined Variables
Kyverno automatically creates a few useful variables and makes them available within rules:
-
serviceAccountName
: the “userName” which is the last part of a service account (i.e. without the prefixsystem:serviceaccount:<namespace>:
). For example, when processing a request fromsystem:serviceaccount:nirmata:user1
Kyverno will store the valueuser1
in the variableserviceAccountName
. -
serviceAccountNamespace
: the “namespace” part of the serviceAccount. For example, when processing a request fromsystem:serviceaccount:nirmata:user1
Kyverno will storenirmata
in the variableserviceAccountNamespace
. -
request.roles
: a list of roles stored in an array the given account may have. For example,["foo:dave"]
. -
request.clusterRoles
: a list of cluster roles stored in an array. For example,["dave-admin","system:basic-user","system:discovery","system:public-info-viewer"]
-
images
: a map of container image information, if available. See Variables from container images for more information.
Note
One of eitherrequest.roles
or request.clusterRoles
will be substituted as variables but not both.
Variables from policy definitions
Kyverno policy definitions can refer to other fields in the policy definition as a form of “shortcut”. This can be a useful way to analyze and compare values without having to explicitly define them.
In order for Kyverno to refer to these existing values in a manifest, it uses the notation $(./../key_1/key_2)
. This may look familiar as it is essentially the same way Linux/Unix systems refer to relative paths. For example, consider the policy manifest snippet below.
1validationFailureAction: enforce
2rules:
3- name: check-tcpSocket
4 match:
5 resources:
6 kinds:
7 - Pod
8 validate:
9 message: "Port number for the livenessProbe must be less than that of the readinessProbe."
10 pattern:
11 spec:
12 ^(containers):
13 - livenessProbe:
14 tcpSocket:
15 port: "$(./../../../readinessProbe/tcpSocket/port)"
16 readinessProbe:
17 tcpSocket:
18 port: "3000"
In this above example, for any containers found in a Pod spec, the field readinessProbe.tcpSocket.port
must be 3000
and the field livenessProbe.tcpSocket.port
must be the same value. The lookup expression can be thought of as a cd
back three levels and down into the readinessProbe
object.
Operators also work on manifest lookup variables as well so the previous snippet could be modified as such.
1- livenessProbe:
2 tcpSocket:
3 port: "$(<./../../../readinessProbe/tcpSocket/port)"
4 readinessProbe:
5 tcpSocket:
6 port: "3000"
In this case, the field livenessProbe.tcpSocket.port
must now be less than the value specified in readinessProbe.tcpSocket.port
.
For more information on operators see the Operators section.
Escaping Variables
In some cases, you wish to write a rule containing a variable for action on by another program or process flow and not for Kyverno’s use. For example, with the variables in $()
notation, as of Kyverno 1.5.0 these can be escaped with a leading backslash (\
) and Kyverno will not attempt to substitute values. Variables written in JMESPath notation can also be escaped using the same syntax, for example \{{ request.object.metadata.name }}
.
In the below policy, the value of OTEL_RESOURCE_ATTRIBUTES
contains references to other environment variables which will be quoted literally as, for example, $(POD_NAMESPACE)
.
1apiVersion: kyverno.io/v1
2kind: Policy
3metadata:
4 name: add-otel-resource-env
5spec:
6 background: false
7 rules:
8 - name: imbue-pod-spec
9 match:
10 resources:
11 kinds:
12 - v1/Pod
13 mutate:
14 patchStrategicMerge:
15 spec:
16 containers:
17 - (name): "?*"
18 env:
19 - name: NODE_NAME
20 value: "mutated_name"
21 - name: POD_IP_ADDRESS
22 valueFrom:
23 fieldRef:
24 fieldPath: status.podIP
25 - name: POD_NAME
26 valueFrom:
27 fieldRef:
28 fieldPath: metadata.name
29 - name: POD_NAMESPACE
30 valueFrom:
31 fieldRef:
32 fieldPath: metadata.namespace
33 - name: POD_SERVICE_ACCOUNT
34 valueFrom:
35 fieldRef:
36 fieldPath: spec.serviceAccountName
37 - name: OTEL_RESOURCE_ATTRIBUTES
38 value: >-
39 k8s.namespace.name=\$(POD_NAMESPACE),
40 k8s.node.name=\$(NODE_NAME),
41 k8s.pod.name=\$(POD_NAME),
42 k8s.pod.primary_ip_address=\$(POD_IP_ADDRESS),
43 k8s.pod.service_account.name=\$(POD_SERVICE_ACCOUNT),
44 rule_applied=$(./../../../../../../../../name)
Using a Pod definition as below, this can be tested.
1apiVersion: v1
2kind: Pod
3metadata:
4 name: test-env-vars
5spec:
6 containers:
7 - name: test-container
8 image: busybox
9 command: ["sh", "-c"]
10 args:
11 - while true; do
12 echo -en '\n';
13 printenv OTEL_RESOURCE_ATTRIBUTES;
14 sleep 10;
15 done;
16 env:
17 - name: NODE_NAME
18 value: "node_name"
19 - name: POD_NAME
20 valueFrom:
21 fieldRef:
22 fieldPath: metadata.name
23 - name: POD_NAMESPACE
24 valueFrom:
25 fieldRef:
26 fieldPath: metadata.namespace
27 - name: POD_IP_ADDRESS
28 valueFrom:
29 fieldRef:
30 fieldPath: status.podIP
31 restartPolicy: Never
The result of the mutation of this Pod with respect to the OTEL_RESOURCE_ATTRIBUTES
environment variable will be as follows.
1- name: OTEL_RESOURCE_ATTRIBUTES
2 value: k8s.namespace.name=$(POD_NAMESPACE), k8s.node.name=$(NODE_NAME), k8s.pod.name=$(POD_NAME),
3 k8s.pod.primary_ip_address=$(POD_IP_ADDRESS), k8s.pod.service_account.name=$(POD_SERVICE_ACCOUNT),
4 rule_applied=imbue-pod-spec
Variables from admission review requests
Kyverno operates as a webhook inside Kubernetes. Whenever a new request is made to the Kubernetes API server, for example to create a Pod, the API server sends this information to the webhooks registered to listen to the creation of Pod resources. This incoming data to a webhook is passed as a AdmissionReview
object. There are four commonly used data properties available in any AdmissionReview request:
{{request.operation}}
: the type of API action being performed (CREATE
,UPDATE
,DELETE
, orCONNECT
).{{request.object}}
: the object being created or modified. It is null forDELETE
requests.{{request.oldObject}}
: the object being modified. It is null forCREATE
andCONNECT
requests.{{request.userInfo}}
: contains information on who/what submitted the request which includes thegroups
andusername
keys.{{request.namespace}}
: the Namespace of the object subject to the operation.
Here are some examples of looking up this data:
- Reference a resource name (type string)
{{request.object.metadata.name}}
- Reference the metadata (type object)
{{request.object.metadata}}
- Reference the name of a new Namespace resource being created
{{request.object.name}}
- Reference the name of a user who submitted a request
{{request.userInfo.username}}
Variables from the AdmissionReview
can also be combined with user-defined strings to create values for messages and other fields.
- Build a name from multiple variables (type string)
"ns-owner-{{request.namespace}}-{{request.userInfo.username}}-binding"
Let’s look at an example of how this AdmissionReview data can be used in Kyverno policies.
In the below ClusterPolicy
, we wish to know which account created a given Pod resource. We can use information from the AdmissionReview
contents, specifically the username
key, to write this information out in the form of a label. Apply the following sample.
1apiVersion: kyverno.io/v1
2kind: ClusterPolicy
3metadata:
4 name: who-created-this
5spec:
6 background: false
7 rules:
8 - name: who-created-this
9 match:
10 resources:
11 kinds:
12 - Pod
13 mutate:
14 patchStrategicMerge:
15 metadata:
16 labels:
17 created-by: "{{request.userInfo.username}}"
This sample will mutate all incoming Pod creation requests with a label named created-by
and the value of the authenticated user based on their kubeconfig
.
Create a simple Pod resource.
1kubectl run busybox --image busybox:1.28
Now get
the newly-created busybox
Pod.
1kubectl get po busybox --show-labels
2
3NAME READY STATUS RESTARTS AGE LABELS
4busybox 0/1 Pending 0 25m created-by=kubernetes-admin,run=busybox
In the output, we can clearly see the value of our created-by
label is kubernetes-admin
which, in this case, is the user who created the Pod.
Variables from container images
Kyverno extracts image data from the AdmissionReview request and makes this available as a variable named images
of type map in the rule context. Here is an example:
1{
2 "containers": {
3 "tomcat": {
4 "registry": "https://ghcr.io",
5 "name": "tomcat",
6 "tag": "9"
7 }
8 },
9 "initContainers": {
10 "vault": {
11 "registry": "https://ghcr.io",
12 "name": "vault",
13 "tag": "v3"
14 }
15 }
16}
Whenever an AdmissionReview request has containers
or initContainers
defined, the images
variable can be referenced as shown in the examples below:
Reference the image properties of container tomcat
:
- Reference the registry URL
{{images.containers.tomcat.registry}}
- Reference the image name
{{images.containers.tomcat.name}}
- Reference the image tag
{{images.containers.tomcat.tag}}
- Reference the digest
{{images.containers.tomcat.digest}}
Reference the image properties of initContainer vault
:
- Reference the registry URL
{{images.initContainers.vault.registry}}
- Reference the image name
{{images.initContainers.vault.name}}
- Reference the image tag
{{images.initContainers.vault.tag}}
- Reference the digest
{{images.initContainers.vault.digest}}
This same pattern and image variable arrangement also works for ephemeral containers.
Kyverno by default sets an empty registry to docker.io
and an empty tag to latest
.
Note
Note that certain characters must be escaped for JMESPath processing (ex.-
in the case of container’s name), escaping can be done by using double quotes with double escape character \
, for example, {{images.containers.\"my-container\".tag}}
.
You can also fetch image properties of all containers for further processing. For example, {{ images.containers.*.name }}
creates a string list of all image names.
Variables from external data sources
Some policy decisions require access to cluster resources and data managed by other Kubernetes controllers or external applications. For these types of policies, Kyverno allows HTTP calls to the Kubernetes API server and the use of ConfigMaps.
Data fetched from external sources is stored in a per-rule processing context that is used to evaluate variables by the policy engine. Once the data from external sources is stored in the context, it can be referenced like any other variable data.
Learn more about ConfigMap lookups and API Server calls in the External Data Sources section.
Nested Lookups
It is also possible to nest JMESPath expressions inside one another when mixing data sourced from a ConfigMap and AdmissionReview, for example. By including one JMESPath expression inside the other, Kyverno will first substitute the inner expression before building the outer one as seen in the below example.
Note
Nesting JMESPath expressions is supported in Kyverno version 1.3.0 and higher. 1apiVersion: kyverno.io/v1
2kind: ClusterPolicy
3metadata:
4 name: resource-annotater
5spec:
6 background: false
7 rules:
8 - name: add-resource-annotations
9 context:
10 - name: LabelsCM
11 configMap:
12 name: resource-annotater-reference
13 namespace: default
14 match:
15 resources:
16 kinds:
17 - Pod
18 mutate:
19 overlay:
20 metadata:
21 annotations:
22 foo: "{{LabelsCM.data.{{ request.object.metadata.labels.app }}}}"
In this example, AdmissionReview data is first collected in the inner expression in the form of {{request.object.metadata.labels.app}}
while the outer expression is built from a ConfigMap context named LabelsCM
.
Evaluation Order
Kyverno policies can contain variables in:
- Rule context
- Rule preconditions
- Rule definitions:
- Validation patterns
- Validation deny rules
- Mutate strategic merge patches (
patchesStrategicMerge
) - Generate resource data definitions
Variables are not supported in the match
and exclude
elements, so that rules can be matched quickly without having to load and process data. Variables are also not supported in the patchesJson6902.path
key.
Since variables can be nested, it is important to understand the order in which the variables are evaluated. During admission control, here is how the engine processes rules:
- The set of matching rules is determined by creating a hash from the request information to retrieve all matching rules based on the rule and resource types.
- Each matched rule is further processed to fully evaluate the match and retrieve conditions.
- The rule context is then evaluated and variables are loaded from data sources.
- The preconditions are then checked.
- The rule body is processed.
This ordering makes it possible to use request data when defining the context, and context variables in preconditions. Within the context itself, each variable is evaluated in the order of definition. Hence, if required, a variable can reference a prior variable but attempts to use a subsequent definition will result in errors.
JMESPath custom functions
In addition to the list of built-in functions JMESPath offers, Kyverno augments these by adding several others which makes it even easier to craft Kyverno policies.
General
1base64_decode(string) string
2base64_encode(string) string
3compare(string, string) bool
4equal_fold(string, string) bool
5label_match(object, object) bool (object arguments must be enclosed in backticks; ex. `{{request.object.spec.template.metadata.labels}}`)
6parse_json(string) any (decodes a valid JSON encoded string to the appropriate type. Opposite of `to_string` function)
7parse_yaml(string) any
8path_canonicalize(string) string
9pattern_match(pattern string, string|number) bool ('*' matches zero or more alphanumeric characters, '?' matches a single alphanumeric character)
10regex_match(string, string|number) bool
11regex_replace_all(regex string, src string|number, replace string|number) string (converts all parameters to string)
12regex_replace_all_literal(regex string, src string|number, replace string|number) string (converts all parameters to string)
13replace(str string, old string, new string, n float64) string
14replace_all(str string, old string, new string) string
15semver_compare(string, string) bool (Use operators [>, <, etc] with string inputs for comparison logic)
16split(str string, sep string) []string
17time_since(<layout>, <time1>, <time2>) string (all inputs as string)
18to_upper(string) string
19to_lower(string) string
20trim(str string, cutset string) string
21truncate(str string, length float64) string (length argument must be enclosed in backticks; ex. "{{request.object.metadata.name | truncate(@, `9`)}}")
Arithmetic
1add(number, number) number
2add(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity)
3add(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration)
4subtract(number, number) number
5subtract(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity)
6subtract(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration)
7multiply(number, number) number
8multiply(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity)
9multiply(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration)
10divide(quantity|number, quantity|number) quantity|number (returns a quantity if exactly one of the parameters is a quantity, else a number; the divisor must be non-zero)
11divide(duration|number, duration|number) duration|number (returns a duration if exactly one of the parameters is a duration, else a number; the divisor must be non-zero)
12modulo(number, number) number
13modulo(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity; the divisor must be non-zero)
14modulo(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration; the divisor must be non-zero)
Note
The JMESPath arithmetic functions work for scalars (ex., 10), resource quantities (ex., 10Mi), and durations (ex., 10h). If the input is a scalar, it must be enclosed in backticks so the parameter is treated as a number. Resource quantities and durations are enclosed in single quotes to be treated as strings.The special variable {{@}}
may be used to refer to the current value in a given field, useful for source values.
To find examples of some of these functions in action, see the Kyverno policies library.