Preconditions
Preconditions allow controlling policy rule execution by building expressions 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 (\
). For more detailed information on writing JMESPath expressions for use in preconditions
, see the JMESPath page here.
1{{request.object.spec.template.metadata.annotations.\"foo.k8s.corp.net/bar\"}}
You may specify multiple statements in the preconditions
field and controlling how they operate by using any
and all
statements.
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.
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*"
Note
Thesepreconditions
use a JMESPath syntax for non-existence checks, see the JMESPath page here for details.
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 any:
12 - resources:
13 kinds:
14 - Deployment
15 preconditions:
16 any:
17 - key: "{{ request.object.metadata.labels.color || '' }}"
18 operator: Equals
19 value: blue
20 - key: "{{ request.object.metadata.labels.app || '' }}"
21 operator: Equals
22 value: busybox
23 all:
24 - key: "{{ request.object.metadata.labels.animal || '' }}"
25 operator: Equals
26 value: cow
27 - key: "{{ request.object.metadata.labels.env || '' }}"
28 operator: Equals
29 value: qa
30 validate:
31 message: "Foxes must be used based on this label combination."
32 pattern:
33 spec:
34 template:
35 spec:
36 containers:
37 - name: "*foxes*"
Operators
The following operators are currently supported for precondition evaluation:
- Equals
- NotEquals
- In (deprecated)
- AnyIn
- AllIn
- NotIn (deprecated)
- AnyNotIn
- AllNotIn
- GreaterThan
- GreaterThanOrEquals
- LessThan
- LessThanOrEquals
- DurationGreaterThan
- DurationGreaterThanOrEquals
- DurationLessThan
- DurationLessThanOrEquals
The set operators, In
, AnyIn
, AllIn
, NotIn
, AnyNotIn
and AllNotIn
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”] AllIn [“str1”, “str2”, “str3”]). In this case AllIn
checks if all of the strings part of the key are in the value set, AnyIn
checks if any of the strings part of the key are in the value set, AllNotIn
checks if all of the strings part of the key is not in the value set and AnyNotIn
checks if any of the strings part of the key is not in the value set. Sets of other types are currently not supported. Old operators In
and NotIn
work like AllIn
and AnyNotIn
, these are now deprecated and will be removed in a future version.
The duration operators can be used for things such as validating an annotation that is a duration unit. Duration operators expect numeric key or value as seconds or as a string that is a valid Go time duration, eg: “1h”. The string units supported are s
(second), m
(minute) and h
(hour). Full details on supported duration strings are covered by time.ParseDuration.
The GreaterThan
, GreaterThanOrEquals
, LessThan
and LessThanOrEquals
operators can also be used with Kubernetes resource quantities. Any value handled by resource.ParseQuantity can be used, this includes comparing values that have different scales.
Example:
1apiVersion: kyverno.io/v1
2kind: ClusterPolicy
3metadata:
4 name: resource-quantities
5spec:
6 validationFailureAction: enforce
7 background: false
8 rules:
9 - name: memory-limit
10 match:
11 any:
12 - resources:
13 kinds:
14 - Pod
15 preconditions:
16 any:
17 - key: "{{request.object.spec.containers[*].resources.requests.memory}}"
18 operator: LessThan
19 value: 1Gi
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 any:
4 - resources:
5 kinds:
6 - Pods
7 preconditions:
8 any:
9 - key: "{{request.object.spec.template.spec.containers.image}}"
10 operator: Equals
11 value: "bash:*"
Matching requests without a service account
In this example, the rule is only applied to requests from ServiceAccounts (i.e. when the {{serviceAccountName}}
is not empty).
1 - name: generate-owner-role
2 match:
3 any:
4 - resources:
5 kinds:
6 - Namespace
7 preconditions:
8 any:
9 - key: "{{serviceAccountName}}"
10 operator: NotEquals
11 value: ""
Matching requests from specific service accounts
In this example, the rule is only applied to requests from a ServiceAccount with name build-default
and build-base
.
1 - name: generate-default-build-role
2 match:
3 any:
4 - resources:
5 kinds:
6 - Namespace
7 preconditions:
8 any:
9 - key: "{{serviceAccountName}}"
10 operator: AnyIn
11 value: ["build-default", "build-base"]