Enforcing Kubernetes Policy With Kyverno Admission Rules
Reviews catch bad manifests inconsistently. Kyverno enforces your rules at admission time, in YAML, with no Rego to learn.
- #kubernetes
- #kyverno
- #policy
- #security
- #admission-control
- #governance
Every team I’ve worked with has a list of rules nobody enforces: don’t run as root, always set resource limits, never pull :latest, label everything with a cost-center. Those rules live in a wiki, get ignored under deadline pressure, and surface in a postmortem. Kyverno moves them out of the wiki and into the cluster’s admission path, where they’re enforced on every apply — and unlike its predecessors, you write them in plain YAML instead of learning a policy language. This is how I roll it out without breaking everyone’s deploys on day one.
What Kyverno does
Kyverno is a Kubernetes admission controller. When any resource is created or updated, the API server calls Kyverno, which checks the resource against your ClusterPolicy objects. It can do three things:
- Validate — block resources that violate a rule (or just warn).
- Mutate — fill in or fix fields automatically (add a default label, inject a securityContext).
- Generate — create companion resources (a default NetworkPolicy for every new namespace).
The headline win over OPA/Gatekeeper is that policies are Kubernetes-native YAML. No Rego. Your team can read and write them without a new language.
Installing
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
kubectl get pods -n kyverno
Start in Audit, not Enforce
This is the rule that keeps you employed: deploy every new policy in Audit mode first. Audit reports violations without blocking anything. You learn what would break before it breaks. Only after the reports are clean do you flip to Enforce.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
spec:
validationFailureAction: Audit # flip to Enforce once clean
rules:
- name: require-limits
match:
any:
- resources:
kinds: [Pod]
validate:
message: "CPU and memory limits are required."
pattern:
spec:
containers:
- resources:
limits:
memory: "?*"
cpu: "?*"
The ?* pattern means “any non-empty value.” Apply this in Audit, watch the policy reports, and you’ll discover exactly how many existing workloads lack limits before you start rejecting them.
Reading the violations
kubectl get policyreport -A
kubectl get clusterpolicyreport
These reports are gold. They tell you the blast radius of a policy across everything already running, not just new applies. I let a policy sit in Audit for a sprint, drive the report to zero by fixing the offenders, then promote to Enforce.
A validate policy with teeth
Once you’re confident, enforce. Disallowing :latest image tags is a classic:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: Enforce
rules:
- name: require-image-tag
match:
any:
- resources:
kinds: [Pod]
validate:
message: "Images must specify an explicit, non-latest tag."
pattern:
spec:
containers:
- image: "!*:latest"
Now any pod trying to use :latest is rejected at admission with a clear message. The rule is enforced uniformly, on every namespace, every time — no reviewer required.
Mutate to fix things automatically
Validation blocks bad input; mutation fixes it. Instead of rejecting pods that lack a securityContext, inject a safe default:
rules:
- name: default-security-context
match:
any:
- resources:
kinds: [Pod]
mutate:
patchStrategicMerge:
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
Mutation is friendlier than validation for org-wide defaults — teams get the secure baseline for free instead of a wall of rejections. I use mutate for defaults and validate for hard prohibitions.
Generate to bootstrap namespaces
A favorite: every new namespace automatically gets a default-deny NetworkPolicy, so no namespace is ever born wide open.
rules:
- name: default-deny
match:
any:
- resources:
kinds: [Namespace]
generate:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
name: default-deny
namespace: "{{request.object.metadata.name}}"
data:
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
This is policy that builds secure defaults rather than just rejecting insecure ones.
Rollout discipline
- Scope your matches. A
Podpolicy with no exclusions also hits kube-system and your CNI pods. Exclude system namespaces, or you’ll wedge cluster components and lock yourself out. - Mind the failurePolicy. If Kyverno’s webhook is down and
failurePolicy: Fail, admissions get blocked cluster-wide. Understand this tradeoff and ensure Kyverno is highly available before enforcing critically. - Exclude controllers thoughtfully. Some operators create pods that legitimately violate your rules. Use
excludeblocks rather than weakening the policy for everyone. - Version-control policies. They’re as load-bearing as your application code. Treat them the same.
Because a single Enforce policy with a too-broad match can block every deploy in the cluster, get these reviewed before promoting them out of Audit. Our AI code review flags policies missing system-namespace exclusions and the kind of overbroad match that takes down kube-system.
Kyverno turns your “rules nobody follows” wiki page into enforced, auditable cluster behavior — written in the YAML your team already knows. For more security and governance guides, see the Kubernetes & Helm category.
Admission policies can block legitimate workloads. Always run new policies in Audit mode and review their scope against your own cluster before enforcing.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.