Kyverno Policy Enforcement
Enforce best practices, validate resources, and mutate configurations automatically using Kyverno admission controller.
Time: ~15 minutes Difficulty: Intermediate
What You Will Learn
Section titled “What You Will Learn”- Kyverno ClusterPolicy resources: validating, mutating, and generating policies
- Validating admission: block resources that violate policies
- Mutating admission: automatically fix or add configuration
- Policy enforcement vs audit mode
- Testing policies with compliant and non-compliant resources
Prerequisites
Section titled “Prerequisites”Kyverno must be installed before running this demo:
helm repo add kyverno https://kyverno.github.io/kyverno/helm repo updatehelm install kyverno kyverno/kyverno -n kyverno --create-namespaceWait for Kyverno to be ready:
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=kyverno -n kyverno --timeout=120sDeploy
Section titled “Deploy”Navigate to the demo directory:
cd demos/kyvernoCreate the namespace and apply the policies:
kubectl apply -f manifests/namespace.yamlkubectl apply -f manifests/policy-require-labels.yamlkubectl apply -f manifests/policy-add-default-resources.yamlkubectl apply -f manifests/policy-disallow-latest.yamlWait for policies to be ready:
kubectl get clusterpoliciesAll policies should show READY: true.
Verify
Section titled “Verify”Test 1: Good Pod (Passes All Policies)
Section titled “Test 1: Good Pod (Passes All Policies)”kubectl apply -f manifests/test-good-pod.yamlThis pod should be created successfully because it:
- Has a
teamlabel - Uses a specific image tag (nginx:1.27, not :latest)
- Has resource requests and limits
Verify the pod is running:
kubectl get pod good-pod -n kyverno-demoTest 2: Bad Pod (Missing Required Label)
Section titled “Test 2: Bad Pod (Missing Required Label)”kubectl apply -f manifests/test-bad-pod-no-label.yamlThis pod is blocked with an error message:
Error from server: error when creating "manifests/test-bad-pod-no-label.yaml":admission webhook "validate.kyverno.svc-fail" denied the request:
policy Pod/kyverno-demo/bad-pod-no-label for resource violation:
require-team-label: check-team-label: Pod is missing required label 'team'. Add metadata.labels.team to your pod spec.Test 3: Bad Pod (Using :latest Tag)
Section titled “Test 3: Bad Pod (Using :latest Tag)”kubectl apply -f manifests/test-bad-pod-latest.yamlThis pod is blocked because it uses the :latest tag:
Error from server: error when creating "manifests/test-bad-pod-latest.yaml":admission webhook "validate.kyverno.svc-fail" denied the request:
policy Pod/kyverno-demo/bad-pod-latest-tag for resource violation:
disallow-latest-tag: require-image-tag: Container images must have a specific tag. Do not use :latest or omit the tag.Test 4: Mutation in Action
Section titled “Test 4: Mutation in Action”Create a pod without resource limits to see the mutating policy add them:
cat <<EOF | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: mutated-pod namespace: kyverno-demo labels: team: platformspec: containers: - name: app image: nginx:1.27EOFCheck that Kyverno added default resource limits:
kubectl get pod mutated-pod -n kyverno-demo -o jsonpath='{.spec.containers[0].resources}' | jqOutput shows the mutation added default values:
{ "limits": { "cpu": "200m", "memory": "128Mi" }, "requests": { "cpu": "100m", "memory": "64Mi" }}What is Happening
Section titled “What is Happening”manifests/ namespace.yaml # kyverno-demo namespace policy-require-labels.yaml # Validates all Pods have a 'team' label policy-add-default-resources.yaml # Mutates Pods to add default resource limits policy-disallow-latest.yaml # Blocks containers using :latest tag test-good-pod.yaml # Compliant pod (passes all policies) test-bad-pod-no-label.yaml # Non-compliant (missing team label, blocked) test-bad-pod-latest.yaml # Non-compliant (uses :latest tag, blocked)Kyverno policies work at admission time. When you try to create a resource, Kyverno webhooks intercept the request and apply policies before the resource is stored in etcd.
Three policy types:
| Type | What It Does |
|---|---|
| Validate | Checks if a resource meets criteria. Blocks it if not (Enforce mode) or logs a warning (Audit mode). |
| Mutate | Modifies the resource automatically (adds labels, sets defaults, injects sidecars). |
| Generate | Creates additional resources (like NetworkPolicies or ConfigMaps) when a resource is created. |
validationFailureAction:
Enforce: Blocks non-compliant resources (used inpolicy-require-labels.yamlandpolicy-disallow-latest.yaml)Audit: Logs violations but allows the resource to be created
Policy patterns:
"?*"means “any value, but must be present”"!*:latest"means “any value except :latest”"*:*"means “must contain a colon” (enforces explicit tag)+(field)in mutate patches means “add this field only if it does not exist”
Experiment
Section titled “Experiment”-
List all Kyverno policy violations across the cluster:
Terminal window kubectl get policyreport -A -
Change a policy to audit mode instead of enforce:
Terminal window kubectl edit clusterpolicy require-team-label# Change validationFailureAction to AuditNow the bad-pod-no-label will be created with a warning instead of being blocked.
-
Write a custom policy to require all Services to have a
contactannotation:apiVersion: kyverno.io/v1kind: ClusterPolicymetadata:name: require-contact-annotationspec:validationFailureAction: Enforcerules:- name: check-contactmatch:any:- resources:kinds:- Servicenamespaces:- kyverno-demovalidate:message: "All services must have a 'contact' annotation"pattern:metadata:annotations:contact: "?*" -
Create a generate policy that automatically adds a default NetworkPolicy when a namespace is created:
apiVersion: kyverno.io/v1kind: ClusterPolicymetadata:name: add-networkpolicyspec:rules:- name: default-deny-ingressmatch:any:- resources:kinds:- Namespacegenerate:apiVersion: networking.k8s.io/v1kind: NetworkPolicyname: default-deny-ingressnamespace: "{{request.object.metadata.name}}"synchronize: truedata:spec:podSelector: {}policyTypes:- Ingress -
Use policy exceptions to exclude specific resources from a policy:
apiVersion: kyverno.io/v2kind: PolicyExceptionmetadata:name: allow-latest-for-devnamespace: kyverno-demospec:exceptions:- policyName: disallow-latest-tagruleNames:- require-image-tagmatch:any:- resources:kinds:- Podnamespaces:- kyverno-demonames:- dev-*
Cleanup
Section titled “Cleanup”Delete the namespace (removes all test pods) and the ClusterPolicies:
kubectl delete namespace kyverno-demokubectl delete clusterpolicy require-team-label add-default-resources disallow-latest-tagTo remove Kyverno itself:
helm uninstall kyverno -n kyvernokubectl delete namespace kyvernoFurther Reading
Section titled “Further Reading”See docs/deep-dive.md for a detailed explanation of Kyverno architecture, policy reports, background scanning, webhook configuration, and advanced patterns like variable substitution and external data sources.
Next Step
Section titled “Next Step”Move on to Velero Backup and Restore to learn how to back up and restore cluster resources and persistent volumes.