Kyverno Policy Engine for Kubernetes

Kyverno is a Kubernetes-native policy engine that validates, mutates, and generates resources using YAML-based policies without requiring Rego or a separate policy language. This guide covers installing Kyverno, writing validation and mutation policies, enforcing image verification, and generating audit reports.

Prerequisites

  • Kubernetes cluster (1.25+)
  • kubectl with cluster-admin permissions
  • Helm 3.x
  • Understanding of Kubernetes admission control concepts

Install Kyverno

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

# Install Kyverno (production: 3 replicas for HA)
helm install kyverno kyverno/kyverno \
  --namespace kyverno \
  --create-namespace \
  --set replicaCount=3 \
  --set admissionController.replicas=3 \
  --set backgroundController.replicas=2 \
  --set cleanupController.replicas=1 \
  --set reportsController.replicas=1

# Verify installation
kubectl -n kyverno get pods
kubectl get clusterpolicies

# Install via kubectl (alternative)
kubectl create -f https://github.com/kyverno/kyverno/releases/latest/download/install.yaml

Validation Policies

Validation policies block non-compliant resources at admission time:

Require resource limits on all containers:

# require-resource-limits.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-resource-limits
  annotations:
    policies.kyverno.io/title: Require Resource Limits
    policies.kyverno.io/description: Requires all containers to define CPU and memory limits.
spec:
  validationFailureAction: Enforce   # Enforce=block, Audit=report only
  background: true                    # Also scan existing resources
  rules:
    - name: check-resource-limits
      match:
        any:
        - resources:
            kinds: ["Pod"]
            namespaceSelector:
              matchExpressions:
              - key: kyverno-enforce
                operator: In
                values: ["true"]
      validate:
        message: "CPU and memory limits are required for all containers."
        pattern:
          spec:
            containers:
            - name: "*"
              resources:
                limits:
                  memory: "?*"
                  cpu: "?*"

Require non-root containers:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-non-root
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: check-non-root-user
      match:
        any:
        - resources:
            kinds: ["Pod"]
      exclude:
        any:
        - resources:
            namespaces:
              - kube-system
              - kyverno
      validate:
        message: "Running as root is not allowed. Set runAsNonRoot: true or runAsUser > 0."
        anyPattern:
          - spec:
              securityContext:
                runAsNonRoot: "true"
          - spec:
              containers:
              - securityContext:
                  runAsUser: ">0"

Enforce allowed image registries:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-image-registries
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: validate-registries
      match:
        any:
        - resources:
            kinds: ["Pod"]
      exclude:
        any:
        - resources:
            namespaces: ["kube-system", "kyverno"]
      validate:
        message: "Image must be from an approved registry. Allowed: registry.example.com, gcr.io/myproject"
        pattern:
          spec:
            containers:
            - image: "registry.example.com/* | gcr.io/myproject/*"

Apply and test:

kubectl apply -f require-resource-limits.yaml

# Test: Pod without limits should be rejected
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: no-limits-test
  namespace: default
spec:
  containers:
  - name: app
    image: nginx:alpine
EOF
# Expected: Error - CPU and memory limits are required

Mutation Policies

Mutation policies automatically modify resources to comply with standards:

Auto-add resource limits if missing:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-default-limits
spec:
  rules:
    - name: add-default-resource-limits
      match:
        any:
        - resources:
            kinds: ["Pod"]
      mutate:
        patchStrategicMerge:
          spec:
            containers:
            - (name): "*"
              resources:
                limits:
                  +(memory): "256Mi"   # "+" means: only add if not present
                  +(cpu): "500m"
                requests:
                  +(memory): "64Mi"
                  +(cpu): "100m"

Auto-add security context:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-security-context
spec:
  rules:
    - name: set-security-context
      match:
        any:
        - resources:
            kinds: ["Pod"]
      exclude:
        any:
        - resources:
            namespaces: ["kube-system"]
      mutate:
        patchStrategicMerge:
          spec:
            securityContext:
              +(runAsNonRoot): true
              +(runAsUser): 1000
              +(fsGroup): 1000
            containers:
            - (name): "*"
              securityContext:
                +(allowPrivilegeEscalation): false
                +(readOnlyRootFilesystem): true
                +(runAsNonRoot): true

Auto-add labels:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-labels
spec:
  rules:
    - name: add-managed-by-label
      match:
        any:
        - resources:
            kinds: ["Deployment", "StatefulSet", "DaemonSet"]
      mutate:
        patchStrategicMerge:
          metadata:
            labels:
              +(managed-by): "kyverno"
              +(environment): "{{request.namespace}}"

Generation Policies

Generation policies create new resources automatically when a trigger resource is created:

Auto-create a NetworkPolicy for every new namespace:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: default-deny-networkpolicy
spec:
  rules:
    - name: generate-default-networkpolicy
      match:
        any:
        - resources:
            kinds: ["Namespace"]
      generate:
        apiVersion: networking.k8s.io/v1
        kind: NetworkPolicy
        name: default-deny-all
        namespace: "{{request.object.metadata.name}}"
        synchronize: true   # Update generated resource if policy changes
        data:
          spec:
            podSelector: {}   # Match all pods in namespace
            policyTypes:
              - Ingress
              - Egress

Auto-copy a ConfigMap to new namespaces:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: propagate-config
spec:
  rules:
    - name: copy-ca-configmap
      match:
        any:
        - resources:
            kinds: ["Namespace"]
      exclude:
        any:
        - resources:
            names: ["kube-system", "kyverno"]
      generate:
        apiVersion: v1
        kind: ConfigMap
        name: company-ca-bundle
        namespace: "{{request.object.metadata.name}}"
        synchronize: true
        clone:
          namespace: kube-system
          name: company-ca-bundle    # ConfigMap to clone from

Image Verification with Cosign

Kyverno can verify Cosign signatures as part of admission control:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  background: false   # Image verification must happen at admission time
  webhookTimeoutSeconds: 30
  rules:
    - name: verify-cosign-signature
      match:
        any:
        - resources:
            kinds: ["Pod"]
      exclude:
        any:
        - resources:
            namespaces: ["kube-system", "kyverno"]
      verifyImages:
        - imageReferences:
            - "registry.example.com/*"
          mutateDigest: true       # Replace image tag with digest for immutability
          required: true
          attestors:
            - count: 1
              entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYOsN9LmU6lHfIbMmU3dWwGjJuP
                      ...YOUR_PUBLIC_KEY...
                      -----END PUBLIC KEY-----
                    rekor:
                      url: https://rekor.sigstore.dev   # Transparency log

Audit Reporting

Kyverno generates PolicyReport and ClusterPolicyReport resources for compliance auditing:

# View cluster-wide policy reports (for cluster-scoped resources)
kubectl get clusterpolicyreport -A

# View namespace-scoped policy reports
kubectl get policyreport -A

# See violations in a namespace
kubectl get policyreport -n production -o yaml | \
  grep -A5 "result: fail"

# Summary of all policy results
kubectl get policyreport -A \
  -o jsonpath='{range .items[*]}{.metadata.namespace} {.summary}{"\n"}{end}'

# Generate a detailed report
kubectl get clusterpolicyreport -o json | \
  jq '[.items[].results[] | select(.result=="fail")] | 
      group_by(.policy) | 
      map({policy: .[0].policy, violations: length})'

Best Practice Policy Library

Kyverno maintains a library of community policies:

# Install the Kyverno policy library
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno-policies kyverno/kyverno-policies \
  --namespace kyverno \
  --set podSecurityStandard=restricted

# View installed policies
kubectl get clusterpolicies

# Commonly useful policies from the library:
kubectl get clusterpolicies | grep -E "disallow|require|restrict"

Example of deploying multiple policies at once:

# Apply PSS (Pod Security Standards) baseline policies
kubectl apply -f https://raw.githubusercontent.com/kyverno/policies/main/pod-security/baseline/disallow-privileged-containers/disallow-privileged-containers.yaml

kubectl apply -f https://raw.githubusercontent.com/kyverno/policies/main/pod-security/baseline/disallow-host-namespaces/disallow-host-namespaces.yaml

kubectl apply -f https://raw.githubusercontent.com/kyverno/policies/main/pod-security/restricted/require-run-as-non-root-user/require-run-as-non-root-user.yaml

# Set all installed policies to Audit mode first
kubectl get clusterpolicies -o name | xargs -I{} kubectl patch {} \
  --type=merge \
  -p '{"spec":{"validationFailureAction":"Audit"}}'

Troubleshooting

Policy not enforcing:

# Check policy status
kubectl get clusterpolicy <policy-name> -o yaml | grep -A5 "status:"

# Check Kyverno admission controller logs
kubectl -n kyverno logs -l app.kubernetes.io/component=admission-controller --tail=50

# Test policy against a specific resource
kubectl apply -f test-pod.yaml --dry-run=server -v=5

Mutating policy not applying:

# Verify the patch is valid
kubectl apply -f policy.yaml
kubectl get clusterpolicy <name> -o yaml | grep -A3 "conditions:"

# Check if mutation is applying
kubectl -n kyverno logs -l app.kubernetes.io/component=admission-controller 2>&1 | grep "mutate"

Background scan not running:

# Check the background controller
kubectl -n kyverno logs -l app.kubernetes.io/component=background-controller --tail=30

# Force a background scan
kubectl annotate clusterpolicy <policy-name> \
  policies.kyverno.io/last-scan=$(date -u +%Y-%m-%dT%H:%M:%SZ)

Webhook timeout errors:

# Increase webhook timeout (default 10s)
kubectl patch clusterpolicy <name> \
  --type=merge \
  -p '{"spec":{"webhookTimeoutSeconds": 30}}'

# Check Kyverno pod resource limits
kubectl -n kyverno get pods -o custom-columns="NAME:.metadata.name,CPU:.spec.containers[0].resources.limits.cpu,MEM:.spec.containers[0].resources.limits.memory"

Conclusion

Kyverno provides policy-as-code enforcement for Kubernetes using familiar YAML syntax, making it accessible to teams without Rego experience. The combination of validation (block non-compliant resources), mutation (auto-fix common issues), and generation (create dependent resources automatically) policies enables comprehensive governance from a single tool. Start with policies in Audit mode to understand your compliance baseline before switching to Enforce mode, and use PolicyReport resources to track violations and measure policy adoption over time.