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+)
kubectlwith 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.


