Kubernetes native policy management — Kyverno

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.
$ kubectl create -f https://raw.githubusercontent.com/kyverno/kyverno/master/definitions/release/install.yaml
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
$
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 4d3hNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kyverno-svc ClusterIP 10.105.10.50 <none> 443/TCP 4d3hNAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/kyverno 1/1 1 1 4d3hNAME 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 policiesrequire-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.