Skip to content

Commit dec02a4

Browse files
add a blog for generating VAPs (#1151)
* add a blog for generating VAPs Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> * fix Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> * fix: update the blog date Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> --------- Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
1 parent fc04b87 commit dec02a4

File tree

1 file changed

+354
-0
lines changed
  • content/en/blog/general/generate-vaps

1 file changed

+354
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
---
2+
date: 2024-02-26
3+
title: "Generating Kubernetes ValidatingAdmissionPolicies from Kyverno Policies"
4+
linkTitle: "Generating Kubernetes ValidatingAdmissionPolicies from Kyverno Policies"
5+
author: Mariam Fahmy
6+
description: "Generating Kubernetes ValidatingAdmissionPolicies from Kyverno Policies"
7+
---
8+
In the [previous blog post](/blog/2023/11/13/using-cel-expressions-in-kyverno-policies/), we discussed writing [Common Expression Language (CEL)](https://github.com/google/cel-spec) expressions in Kyverno policies for resource validation. CEL was first introduced to Kubernetes for the Validation rules for CustomResourceDefinitions, and then it was used by Kubernetes ValidatingAdmissionPolicies in 1.26.
9+
10+
ValidatingAdmissionPolicies offer a declarative, in-process alternative to validating admission webhooks.
11+
12+
ValidatingAdmissionPolicies use the Common Expression Language (CEL) to declare the validation rules of a policy. Validation admission policies are highly configurable, enabling policy authors to define policies that can be parameterized and scoped to resources as needed by cluster administrators.
13+
14+
This post will show you how to generate Kubernetes ValidatingAdmissionPolicies and their bindings from Kyverno policies.
15+
16+
## Prerequisite
17+
Generating Kubernetes ValidatingAdmissionPolicies require the following:
18+
1. A cluster with Kubernetes 1.26 or higher.
19+
2. Enable the `ValidatingAdmissionPolicy` [feature gate](https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/).
20+
3. Enable the `admissionregistration.k8s.io/v1beta1` API for v1.28 and v1.29.
21+
OR
22+
Enable the `admissionregistration.k8s.io/v1alpha1` API for v1.26 and v1.27.
23+
4. Set the `--generateValidatingAdmissionPolicy` flag in the Kyverno admission controller.
24+
5. Grant the admission controller service account the required permissions to generate ValidatingAdmissionPolicies and their bindings.
25+
26+
In this post, we will use the beta version of Kubernetes 1.29.
27+
28+
## Installation & Setup
29+
1. Create a local cluster
30+
31+
```bash
32+
kind create cluster --image "kindest/node:v1.28.0" --config - <<EOF
33+
kind: Cluster
34+
apiVersion: kind.x-k8s.io/v1alpha4
35+
featureGates:
36+
ValidatingAdmissionPolicy: true
37+
runtimeConfig:
38+
admissionregistration.k8s.io/v1beta1: true
39+
admissionregistration.k8s.io/v1alpha1: true
40+
nodes:
41+
- role: control-plane
42+
- role: worker
43+
EOF
44+
```
45+
46+
2. Add the Kyverno Helm repository.
47+
48+
```bash
49+
helm repo add kyverno https://kyverno.github.io/kyverno/
50+
helm repo update
51+
```
52+
53+
3. Create a new file that overrides the values in the chart.
54+
55+
```bash
56+
cat << EOF > new-values.yaml
57+
features:
58+
generateValidatingAdmissionPolicy:
59+
enabled: true
60+
61+
admissionController:
62+
rbac:
63+
clusterRole:
64+
extraResources:
65+
- apiGroups:
66+
- admissionregistration.k8s.io
67+
resources:
68+
- validatingadmissionpolicies
69+
- validatingadmissionpolicybindings
70+
verbs:
71+
- create
72+
- update
73+
- delete
74+
- list
75+
EOF
76+
```
77+
78+
4. Deploy Kyverno
79+
80+
```bash
81+
helm install kyverno kyverno/kyverno -n kyverno --create-namespace --version v3.1.4 --values new-values.yaml
82+
```
83+
84+
We are now ready to generate Kubernetes ValidatingAdmissionPolicies from Kyverno policies.
85+
86+
## Generating Kubernetes ValidatingAdmissionPolicies
87+
In this section, we will create a Kyverno policy that ensures no hostPath volumes are in use for Deployments, and then we will have a look at the generated ValidatingAdmissionPolicy and its binding. Finally, we will create a Deployment that violates the policy.
88+
89+
Let’s start with creating the Kyverno policy.
90+
91+
```bash
92+
kubectl apply -f - <<EOF
93+
apiVersion: kyverno.io/v1
94+
kind: ClusterPolicy
95+
metadata:
96+
name: disallow-host-path
97+
spec:
98+
validationFailureAction: Enforce
99+
background: false
100+
rules:
101+
- name: host-path
102+
match:
103+
any:
104+
- resources:
105+
kinds:
106+
- Deployment
107+
validate:
108+
cel:
109+
expressions:
110+
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume, !has(volume.hostPath))"
111+
message: "HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset."
112+
EOF
113+
```
114+
115+
You can check whether a ValidatingAdmissionPolicy is generated or not from the Kyverno policy status.
116+
117+
```bash
118+
$ kubectl get cpol disallow-host-path -o jsonpath='{.status}'
119+
120+
{
121+
"autogen":{
122+
123+
},
124+
"conditions":[
125+
{
126+
"lastTransitionTime":"2023-09-12T11:42:13Z",
127+
"message":"Ready",
128+
"reason":"Succeeded",
129+
"status":"True",
130+
"type":"Ready"
131+
}
132+
],
133+
"ready":true,
134+
"rulecount":{
135+
"generate":0,
136+
"mutate":0,
137+
"validate":1,
138+
"verifyimages":0
139+
},
140+
"validatingadmissionpolicy":{
141+
"generated":true,
142+
"message":""
143+
}
144+
}
145+
```
146+
147+
Let’s try getting the ValidatingAdmissionPolicy and its binding.
148+
149+
```bash
150+
$ kubectl get validatingadmissionpolicy
151+
NAME VALIDATIONS PARAMKIND AGE
152+
disallow-host-path 1 <unset> 8m12s
153+
154+
$ kubectl get validatingadmissionpolicybindings
155+
NAME POLICYNAME PARAMREF AGE
156+
disallow-host-path-binding disallow-host-path <unset> 8m30s
157+
```
158+
159+
You may notice that the ValidatingAdmissionPolicy and the ValidatingAdmissionPolicyBinding share the same name as the Kyverno policy they originate from, with the binding having a "-binding" suffix.
160+
161+
Let’s have a look at the ValidatingAdmissionPolicy and its binding in detail.
162+
163+
```bash
164+
$ kubectl get validatingadmissionpolicy disallow-host-path -o yaml
165+
apiVersion: admissionregistration.k8s.io/v1beta1
166+
kind: ValidatingAdmissionPolicy
167+
metadata:
168+
creationTimestamp: "2023-09-12T11:42:13Z"
169+
generation: 1
170+
labels:
171+
app.kubernetes.io/managed-by: kyverno
172+
name: disallow-host-path
173+
ownerReferences:
174+
- apiVersion: kyverno.io/v1
175+
kind: ClusterPolicy
176+
name: disallow-host-path
177+
uid: e540d96b-c683-4380-a84f-13411384241a
178+
resourceVersion: "11294"
179+
uid: 9f3e0161-d010-4a6f-bd28-bf9c87151795
180+
spec:
181+
failurePolicy: Fail
182+
matchConstraints:
183+
matchPolicy: Equivalent
184+
namespaceSelector: {}
185+
objectSelector: {}
186+
resourceRules:
187+
- apiGroups:
188+
- apps
189+
apiVersions:
190+
- v1
191+
operations:
192+
- CREATE
193+
- UPDATE
194+
resources:
195+
- deployments
196+
scope: '*'
197+
validations:
198+
- expression: '!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume,
199+
!has(volume.hostPath))'
200+
message: HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath
201+
must be unset.
202+
variables: null
203+
status:
204+
observedGeneration: 1
205+
typeChecking: {}
206+
```
207+
208+
```bash
209+
$ kubectl get validatingadmissionpolicybindings disallow-host-path-binding -o yaml
210+
apiVersion: admissionregistration.k8s.io/v1beta1
211+
kind: ValidatingAdmissionPolicyBinding
212+
metadata:
213+
creationTimestamp: "2023-09-12T11:42:13Z"
214+
generation: 1
215+
labels:
216+
app.kubernetes.io/managed-by: kyverno
217+
name: disallow-host-path-binding
218+
ownerReferences:
219+
- apiVersion: kyverno.io/v1
220+
kind: ClusterPolicy
221+
name: disallow-host-path
222+
uid: e540d96b-c683-4380-a84f-13411384241a
223+
resourceVersion: "11292"
224+
uid: 2fec35c3-8a8c-42a7-8a02-a75e8882a01e
225+
spec:
226+
policyName: disallow-host-path
227+
validationActions:
228+
- Deny
229+
```
230+
231+
Now, let’s try deploying an app that uses a hostPath:
232+
233+
```bash
234+
kubectl apply -f - <<EOF
235+
apiVersion: apps/v1
236+
kind: Deployment
237+
metadata:
238+
name: nginx
239+
spec:
240+
replicas: 2
241+
selector:
242+
matchLabels:
243+
app: nginx
244+
template:
245+
metadata:
246+
labels:
247+
app: nginx
248+
spec:
249+
containers:
250+
- name: nginx-server
251+
image: nginx
252+
volumeMounts:
253+
- name: udev
254+
mountPath: /data
255+
volumes:
256+
- name: udev
257+
hostPath:
258+
path: /etc/udev
259+
EOF
260+
```
261+
262+
As expected, the deployment creation is rejected by the API server and not by the Kyverno admission controller.
263+
264+
```bash
265+
The deployments "nginx" is invalid: ValidatingAdmissionPolicy 'disallow-host-path' with binding 'disallow-host-path-binding' denied request: HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset.
266+
```
267+
268+
If either the ValidatingAdmissionPolicy or the binding is deleted/updated for some reason, the controller is responsible for reverting it.
269+
270+
Let’s try deleting the ValidatingAdmissionPolicy.
271+
272+
```bash
273+
$ kubectl delete validatingadmissionpolicy disallow-host-path
274+
validatingadmissionpolicy.admissionregistration.k8s.io "disallow-host-path" deleted
275+
276+
$ kubectl get validatingadmissionpolicy
277+
NAME VALIDATIONS PARAMKIND AGE
278+
disallow-host-path 1 <unset> 11s
279+
```
280+
281+
In addition, you can update the Kyverno policy, and the controller will re-generate the ValidatingAdmissionPolicy accordingly. For example, you can change the Kyverno policy to match statefulsets too.
282+
283+
patch.yaml:
284+
```yaml
285+
spec:
286+
rules:
287+
- name: host-path
288+
match:
289+
any:
290+
- resources:
291+
kinds:
292+
- Deployment
293+
- StatefulSet
294+
validate:
295+
cel:
296+
expressions:
297+
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume, !has(volume.hostPath))"
298+
message: "HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset."
299+
```
300+
301+
```bash
302+
kubectl patch cpol disallow-host-path --type merge --patch-file patch.yaml
303+
```
304+
305+
The ValidatingAdmissionPolicy will be updated to match StatefulSets too.
306+
307+
```yaml
308+
apiVersion: admissionregistration.k8s.io/v1beta1
309+
kind: ValidatingAdmissionPolicy
310+
metadata:
311+
creationTimestamp: "2023-09-12T12:54:48Z"
312+
generation: 2
313+
labels:
314+
app.kubernetes.io/managed-by: kyverno
315+
name: disallow-host-path
316+
ownerReferences:
317+
- apiVersion: kyverno.io/v1
318+
kind: ClusterPolicy
319+
name: disallow-host-path
320+
uid: e540d96b-c683-4380-a84f-13411384241a
321+
resourceVersion: "29208"
322+
uid: 9325e2b7-9131-4ff4-9e56-244129cb625e
323+
spec:
324+
failurePolicy: Fail
325+
matchConstraints:
326+
matchPolicy: Equivalent
327+
namespaceSelector: {}
328+
objectSelector: {}
329+
resourceRules:
330+
- apiGroups:
331+
- apps
332+
apiVersions:
333+
- v1
334+
operations:
335+
- CREATE
336+
- UPDATE
337+
resources:
338+
- deployments
339+
- statefulsets
340+
scope: '*'
341+
validations:
342+
- expression: '!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume,
343+
!has(volume.hostPath))'
344+
message: HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath
345+
must be unset.
346+
variables: null
347+
status:
348+
observedGeneration: 2
349+
typeChecking: {}
350+
```
351+
352+
## Conclusion
353+
In this blog, we discussed how to generate Kubernetes ValidatingAdmissionPolicies from Kyverno policies. You can use CEL expressions in Kyverno policies to validate resources through either the Kyverno engine or the API server. In the next blog, we will discuss how to generate BackgroundScan reports for ValidatingAdmissionPolicies.
354+

0 commit comments

Comments
 (0)