Kubernetes native policy management — Kyverno
By: Paul Gutierrez
Tags: Kubernetes, Containers, Kyverno, Clusterpolicy

A key principle of Kubernetes is declarative configuration management. Developers and operators specify the desired state and Kubernetes controllers will try and reconcile the current state with the desired state.

While this nature makes Kubernetes an awesome platform as what it is today, managing configuration can become quite complex as workload increase. Another challenge is determining whose responsibility is it to configure the right settings, for security, best practices, and standardization.

The solution to this challenge is to leverage on policies to validate configurations for best practices and compliance. This is where Kyverno comes in.

Kyverno provides an easy way to validate settings, generate defaults and mutate resources, using policies. Kyverno policies are Kubernetes resources that can be written in YAML or JSON. The policies match other resource based on Kubernetes label selectors with support for wildcards.

How does Kyverno works

Kyverno installs itself as an admission controller, which receives webhook events for all API object changes. Kubernetes supports two special dynamic admission controllers which are designed to allow extensibility via tools like Kyverno, the MutatingAdmissionWebhook and the ValidatingAdmissionWebhook. As the names imply, controllers of these types can change and validate API objects.

Deploy Kyverno

Deploying Kyverno to a Kubernetes Cluster is pretty straight-forward. You can just apply the manifest available on Github.



namespace/kyverno created

customresourcedefinition.apiextensions.k8s.io/clusterpolicies.kyverno.io created

customresourcedefinition.apiextensions.k8s.io/clusterpolicyreports.wgpolicyk8s.io created

customresourcedefinition.apiextensions.k8s.io/clusterreportchangerequests.kyverno.io created

customresourcedefinition.apiextensions.k8s.io/generaterequests.kyverno.io created

customresourcedefinition.apiextensions.k8s.io/policies.kyverno.io created

customresourcedefinition.apiextensions.k8s.io/policyreports.wgpolicyk8s.io created

customresourcedefinition.apiextensions.k8s.io/reportchangerequests.kyverno.io created

serviceaccount/kyverno-service-account created

clusterrole.rbac.authorization.k8s.io/kyverno:admin-policies created

clusterrole.rbac.authorization.k8s.io/kyverno:admin-policyreport created

clusterrole.rbac.authorization.k8s.io/kyverno:admin-reportchangerequest created

clusterrole.rbac.authorization.k8s.io/kyverno:customresources created

clusterrole.rbac.authorization.k8s.io/kyverno:generatecontroller created

clusterrole.rbac.authorization.k8s.io/kyverno:policycontroller created

clusterrole.rbac.authorization.k8s.io/kyverno:userinfo created

clusterrole.rbac.authorization.k8s.io/kyverno:webhook created

clusterrolebinding.rbac.authorization.k8s.io/kyverno:customresources created

clusterrolebinding.rbac.authorization.k8s.io/kyverno:generatecontroller created

clusterrolebinding.rbac.authorization.k8s.io/kyverno:policycontroller created

clusterrolebinding.rbac.authorization.k8s.io/kyverno:userinfo created

clusterrolebinding.rbac.authorization.k8s.io/kyverno:webhook created

configmap/init-config created

service/kyverno-svc created

deployment.apps/kyverno created

$ $ kubectl create -f https://raw.githubusercontent.com/kyverno/kyverno/master/definitions/release/install.yaml namespace/kyverno createdcustomresourcedefinition.apiextensions.k8s.io/clusterpolicies.kyverno.io createdcustomresourcedefinition.apiextensions.k8s.io/clusterpolicyreports.wgpolicyk8s.io createdcustomresourcedefinition.apiextensions.k8s.io/clusterreportchangerequests.kyverno.io createdcustomresourcedefinition.apiextensions.k8s.io/generaterequests.kyverno.io createdcustomresourcedefinition.apiextensions.k8s.io/policies.kyverno.io createdcustomresourcedefinition.apiextensions.k8s.io/policyreports.wgpolicyk8s.io createdcustomresourcedefinition.apiextensions.k8s.io/reportchangerequests.kyverno.io createdserviceaccount/kyverno-service-account createdclusterrole.rbac.authorization.k8s.io/kyverno:admin-policies createdclusterrole.rbac.authorization.k8s.io/kyverno:admin-policyreport createdclusterrole.rbac.authorization.k8s.io/kyverno:admin-reportchangerequest createdclusterrole.rbac.authorization.k8s.io/kyverno:customresources createdclusterrole.rbac.authorization.k8s.io/kyverno:generatecontroller createdclusterrole.rbac.authorization.k8s.io/kyverno:policycontroller createdclusterrole.rbac.authorization.k8s.io/kyverno:userinfo createdclusterrole.rbac.authorization.k8s.io/kyverno:webhook createdclusterrolebinding.rbac.authorization.k8s.io/kyverno:customresources createdclusterrolebinding.rbac.authorization.k8s.io/kyverno:generatecontroller createdclusterrolebinding.rbac.authorization.k8s.io/kyverno:policycontroller createdclusterrolebinding.rbac.authorization.k8s.io/kyverno:userinfo createdclusterrolebinding.rbac.authorization.k8s.io/kyverno:webhook createdconfigmap/init-config createdservice/kyverno-svc createddeployment.apps/kyverno created

Let’s validate if Kyverno was deployed correctly

$ kubectl get all -n kyverno

NAME READY STATUS RESTARTS AGE

pod/kyverno-698fd6dc8f-4sswh 1/1 Running 0 4d3h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

service/kyverno-svc ClusterIP 10.105.10.50 <none> 443/TCP 4d3h NAME READY UP-TO-DATE AVAILABLE AGE

deployment.apps/kyverno 1/1 1 1 4d3h NAME DESIRED CURRENT READY AGE

replicaset.apps/kyverno-698fd6dc8f 1 1 1 4d3h

$

Time to test Kyverno

Now that Kyverno is installed let’s create a simple validation policy to test it out. The policy below ensures that all pods have an app label. Pretty straight-forward, the validationFailureAction specifies whether to enforce ( as used below: enforce) this requirement or only audit and report (by setting to audit) the violation.

apiVersion: kyverno.io/v1

kind: ClusterPolicy

metadata:

name: require-app-label

spec:

validationFailureAction: enforce

rules:

- name: require-app-label

match:

resources:

kinds:

- Pod

validate:

message: "Label `app` is required"

pattern:

metadata:

labels:

app: "?*"

Let’s test this policy by creating a pod

$ kubectl create namespace fusion

namespace/fusion created

$

$ kubectl run nginx — image=nginx -n fusion

Error from server: admission webhook “validate.kyverno.svc” denied the request:

resource Pod/fusion/nginx was blocked due to the following policies

require-app-label:

require-app-label: ‘Validation error: Label `app` is required; Validation rule require-app-label failed at path /metadata/labels/app/’

$

As you see from the above, the resource creation failed as we didn’t provide a label for our pod.

Now let’s create another pod, this time providing a label.

$ kubectl run nginx --image=nginx -n fusion --labels="blah=blah"

Error from server: admission webhook "validate.kyverno.svc" denied the request: resource Pod/fusion/nginx was blocked due to the following policies require-app-label:

require-app-label: 'Validation error: Label `app` is required; Validation rule require-app-label failed at path /metadata/labels/app/'

$

As expected, it failed to create the pod again since the label we provided is “blah”. Let’s create a pod with the correct label this time.

$ kubectl run nginx --image=nginx -n fusion --labels="app=nginx"

pod/nginx created

$

Since we satisfied the ClusterPolicy requirements, providing app as our label, our cluster successfully created the pod this round.

Aside from Validation, Kyverno also supports Mutation and Generation. With Mutation, it changes the resource if it matches specific conditions. A great example is to set the imagePullPolicy of a Pod resource to Always. Let’s try it out.

apiVersion: kyverno.io/v1

kind: ClusterPolicy

metadata:

name: image-pull-policy-always

spec:

rules:

- name: image-pull-policy-latest

match:

resources:

kinds:

- Pod

mutate:

overlay:

spec:

containers:

- (image): "*:latest"

imagePullPolicy: "Always"

Let’s see first how our cluster behaves without this policy.

$ kubectl run nginx --image=nginx:latest --image-pull-policy=Never -n fusion --labels="app=nginx"

pod/nginx created $ kubectl get pod nginx -o json -n fusion | jq '.spec.containers[].imagePullPolicy'

"Never"

$

As shown above, imagePullPolicy is set to Never for our pod. Let’s now deploy the new policy.

$ kubectl apply -f image-pull-policy-always.yaml

clusterpolicy.kyverno.io/image-pull-policy-always created

$

Let’s create a new pod and see the effect of the new policy

$ kubectl run nginx2 --image=nginx:latest --image-pull-policy=Never -n fusion --labels="app=nginx"

pod/nginx2 created

$ kubectl get pod nginx2 -o json -n fusion | jq '.spec.containers[].imagePullPolicy'

"Always"

$

We now see that the imagePullPolicy is set to Always!

Conclusion

Kyverno is a useful tool for everyone who runs a Kubernetes Cluster and is looking for a way to enforce and ensure consistent configurations. Writing Policies is as easy as how you define and manage Kubernetes native resources.

Go give Kyverno a try and let me know your thoughts. Thanks for reading and I hope you learned something new.