Preconditions
Preconditions allow controlling policy rule execution based on variable values.
While match
and exclude
allow filtering requests based on resource and user information, preconditions
can be used to define custom filters for more granular control of when a rule should be applied.
The primary use case for preconditions
is in mutate
or generate
rules when needing to check and ensure a variable, typically from AdmissionReview data, is not empty. In addition to AdmissionReview variables, written as JMESPath expressions, preconditions
can also be used to check against variables from ConfigMap resources. mutate
rules which use patchJson6902
should use preconditions
as a way to filter out results.
For validate
rules, the use of patterns
is often preferable since conditionals can be used.
When specifying a JMESPath expression in a preconditions
statement which contains a special character (ex. /
in the case of Kubernetes annotations), double quote the annotation as a literal string. Escape the double quotes with a backslash character (\
).
1{{request.object.spec.template.metadata.annotations.\"foo.k8s.corp.net/bar\"}}
You may specify multiple statements in the preconditions
field. The use of multiple preconditions
statements function as a logical AND statement.
Any and All Statements
You may further control how preconditions
are evaluated by nesting the expressions under any
and/or all
statements. This gives you further power in building more precise logic for how the rule is triggered. Either or both may be used simultaneously in the same rule with multiple any
statements also being possible (multiple all
statements would be redundant). For each any
/all
statement, each block must overall evaluate to TRUE for the precondition to be processed. If any of the any
/ all
statement blocks does not evaluate to TRUE, preconditions
will not be satisfied and thus the rule will not be applicable.
Note
any
and all
statements are available starting in Kyverno v1.3.4 but are optional. Users can continue to write multiple statements inside the preconditions
field and have them evaluated as a logical AND statement.
For example, consider a Deployment manifest which features many different labels as follows.
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: busybox
5 labels:
6 app: busybox
7 color: red
8 animal: cow
9 food: pizza
10 car: jeep
11 env: qa
12spec:
13 replicas: 1
14 selector:
15 matchLabels:
16 app: busybox
17 template:
18 metadata:
19 labels:
20 app: busybox
21 spec:
22 containers:
23 - image: busybox:1.28
24 name: busybox
25 command: ["sleep", "9999"]
By using any
and all
blocks in the preconditions
statement, it is possible to gain more granular control over when rules are evaluated. In the below sample policy, using an any
block will allow the preconditions
to work as a logical OR operation. This policy will only perform the validation if labels color=blue
OR app=busybox
are found. Because the Deployment manifest above specified color=red
, using the any
statement still allows the validation to occur.
1apiVersion: kyverno.io/v1
2kind: ClusterPolicy
3metadata:
4 name: any-all-preconditions
5spec:
6 validationFailureAction: enforce
7 background: false
8 rules:
9 - name: any-all-rule
10 match:
11 resources:
12 kinds:
13 - Deployment
14 preconditions:
15 any:
16 - key: "{{request.object.metadata.labels.color}}"
17 operator: Equals
18 value: blue
19 - key: "{{request.object.metadata.labels.app}}"
20 operator: Equals
21 value: busybox
22 validate:
23 message: "Busybox must be used based on this label combination."
24 pattern:
25 spec:
26 template:
27 spec:
28 containers:
29 - name: "*busybox*"
Adding an all
block means that all of the statements within that block must evaluate to TRUE for the whole block to be considered TRUE. In this policy, in addition to the previous any
conditions, it checks that all of animal=cow
and env=prod
but changes the validation to look for a container with name having the string foxes
in it. Because the any
block and all
block evaluate to TRUE, the validation is performed, however the Deployment will fail to create because the name is still busybox
. If one of the statements in the all
block is changed so the value of the checked label is not among those in the Deployment, the rule will not be processed and the Deployment will be created.
1apiVersion: kyverno.io/v1
2kind: ClusterPolicy
3metadata:
4 name: any-all-preconditions
5spec:
6 validationFailureAction: enforce
7 background: false
8 rules:
9 - name: any-all-rule
10 match:
11 resources:
12 kinds:
13 - Deployment
14 preconditions:
15 any:
16 - key: "{{request.object.metadata.labels.color}}"
17 operator: Equals
18 value: blue
19 - key: "{{request.object.metadata.labels.app}}"
20 operator: Equals
21 value: busybox
22 all:
23 - key: "{{request.object.metadata.labels.animal}}"
24 operator: Equals
25 value: cow
26 - key: "{{request.object.metadata.labels.env}}"
27 operator: Equals
28 value: qa
29 validate:
30 message: "Foxes must be used based on this label combination."
31 pattern:
32 spec:
33 template:
34 spec:
35 containers:
36 - name: "*foxes*"
Operators
The following operators are currently supported for precondition evaluation:
- Equals
- NotEquals
- In
- NotIn
- GreaterThan
- GreaterThanOrEquals
- LessThan
- LessThanOrEquals
The set operators, In
and NotIn
support a set of strings as the value (e.g. In [“str1”, “str2”]). They also allow you to specify a set of strings as the key (e.g. [“str1”, “str2”] In [“str1”, “str2”, “str3”]). In this case In
checks if all the strings part of the key are in the value set (i.e. key is a subset of value) and NotIn
checks if any of the strings part of the key is not in the value set (i.e. key is not a subset of value). Sets of other types are currently not supported.
Wildcard Matches
String values support the use of wildcards to allow for partial matches. The following example matches on pods that have a container using a bash
image.
1 - name: match-on-image
2 match:
3 resources:
4 kinds:
5 - Pods
6 preconditions:
7 - key: "{{request.object.spec.template.spec.containers.image}}"
8 operator: Equals
9 value: "bash:*"
Matching requests without a service account
In this example, the rule is only applied to requests from service accounts (i.e. when the {{serviceAccountName}}
is not empty).
1 - name: generate-owner-role
2 match:
3 resources:
4 kinds:
5 - Namespace
6 preconditions:
7 - key: "{{serviceAccountName}}"
8 operator: NotEquals
9 value: ""
Matching requests from specific service accounts
In this example, the rule is only applied to requests from a service account with name build-default
and build-base
.
1 - name: generate-default-build-role
2 match:
3 resources:
4 kinds:
5 - Namespace
6 preconditions:
7 - key: "{{serviceAccountName}}"
8 operator: In
9 value: ["build-default", "build-base"]