Kyverno

Kyverno is a security solution for Kubernetes that can validate, modify, and generate specifications using admission controllers. Kyverno uses rule definitions based on CRDs.

logo

Prerequisites

We need a Kubernetes cluster, which can be created following these instructions. We also need the kubectl binary configured with the cluster’s kubeconfig, and the helm binary.

Installation

We install Kyverno using Helm.

# Add the repository
helm repo add kyverno https://kyverno.github.io/kyverno/

# Install *kyverno*
helm install kyverno kyverno/kyverno -n kyverno --create-namespace

We verify that the Kyverno Pod is running correctly.

$ kubectl get po -n kyverno
NAME                       READY   STATUS    RESTARTS   AGE
kyverno-6ffff9dc94-rlwj4   1/1     Running   0          2m15s

The Kyverno installation created MutatingWebhookConfiguration and ValidatingWebhookConfiguration CRDs.

$ kubectl get mutatingwebhookconfigurations
NAME                                    WEBHOOKS   AGE
kyverno-policy-mutating-webhook-cfg     1          2m53s
kyverno-resource-mutating-webhook-cfg   2          2m52s
kyverno-verify-mutating-webhook-cfg     1          2m53s

$ kubectl get validatingwebhookconfigurations
NAME                                      WEBHOOKS   AGE
kyverno-policy-validating-webhook-cfg     1          66s
kyverno-resource-validating-webhook-cfg   2          66s

Usage

  • Defining a Cluster-wide Policy

The following specification defines a ClusterPolicy at the cluster level, which ensures that no Pod is created if it doesn’t have a label named app.kubernetes.io/name.

cluster-pod-label-policy.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-labels
spec:
  validationFailureAction: enforce
  rules:
  - name: check-for-labels
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "label 'app.kubernetes.io/name' is required"
      pattern:
        metadata:
          labels:
            app.kubernetes.io/name: "?*"

We create the resource.

kubectl apply -f cluster-pod-label-policy.yaml

We test this ClusterPolicy by creating the following Deployment that doesn’t contain the label:

kubectl create deployment nginx --image=nginx

We should get an error message similar to the following.

error: failed to create deployment: admission webhook "validate.kyverno.svc-fail" denied the request:

resource Deployment/default/nginx was blocked due to the following policies

require-labels:
  autogen-check-for-labels: 'validation error: label ''app.kubernetes.io/name'' is
    required. Rule autogen-check-for-labels failed at path /spec/template/metadata/labels/app.kubernetes.io/name/'

We delete this ClusterPolicy.

kubectl delete -f cluster-pod-label-policy.yaml
  • Defining a Namespace-scoped Policy

The following specification defines a Policy that automatically adds the app.kubernetes.io/name label property to Pods that don’t have it.

pod-add-label-policy.yaml
apiVersion: kyverno.io/v1
kind: Policy
metadata:
  name: require-labels
spec:
  validationFailureAction: enforce
  rules:
  - name: check-for-labels
    match:
      any:
      - resources:
          kinds:
          - Pod
    mutate:
      patchStrategicMerge:
        metadata:
          labels:
            +(app.kubernetes.io/name): "{{request.object.metadata.name}}"

We create the resource in a new test namespace:

kubectl create ns test
kubectl apply -f pod-add-label-policy.yaml -n test

Next, we create a simple Pod.

kubectl run mongo --image=mongo:5.0

Then, we verify the label app.kubernetes.io/name: mongo has been added to the Pod.

$ kubectl get po mongo -o jsonpath='{.metadata.labels}'
{"app.kubernetes.io/name":"mongo","run":"mongo"}

We delete the Policy:

kubectl delete -f pod-add-label-policy.yaml -n test
  • Automatic Resource Generation

The following specification defines a ClusterPolicy that automatically creates a NetworkPolicy that prevents all traffic between Pods when a new namespace is created.

create-network-policy.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: default
spec:
  rules:
  - name: deny-all-traffic
    match:
      any:
      - resources:
          kinds:
          - Namespace
    exclude:
      any:
      - resources:
          namespaces:
          - kube-system
          - default
          - kube-public
          - kyverno
    generate:
      kind: NetworkPolicy
      name: deny-all-traffic
      namespace: "{{request.object.metadata.name}}"
      data:  
        spec:
          # select all pods in the namespace
          podSelector: {}
          policyTypes:
          - Ingress
          - Egress

We create the resource:

kubectl apply -f create-network-policy.yaml

Let’s now create a new namespace and making sure a NetworkPolicy has been created inside it.

kubectl create ns test
kubectl -n test get netpol

We should get a result similar to the following.

NAME               POD-SELECTOR   AGE
deny-all-traffic   <none>         4s

Summary

Kyverno is a solution that positions itself as an alternative to PodSecurityPolicy, which has been removed in Kubernetes 1.25. One of Kyverno’s strengths is using CRDs to define rules to be applied at the cluster level (ClusterPolicy) or namespace level (Policy).

Many examples of Policy and ClusterPolicy are available at https://kyverno.io/policies/