A Kubernetes admission controller is a check and rewrite step that every request to the Kubernetes API passes through before an object is actually stored in the cluster. It can reject a manifest that breaks a rule, or adjust it beforehand, for example by adding a missing label. Without this step, every broken or unsafe manifest would land in the cluster unchecked.
Once you understand this flow, it also becomes clear how tools such as Gatekeeper or Kyverno enforce their own policies without touching a line of Kubernetes source code.
I run Kubernetes clusters in production and wrote the Kubernetes Practical Guide published by Rheinwerk Computing, with its own chapter on governance and security. You can find all my Kubernetes articles collected on the Kubernetes page.
What a Kubernetes admission controller checks
Every request to the Kubernetes API server passes through several stages before an object actually lands in the cluster. First, the API server figures out who is sending the request, usually via the certificate in your kubeconfig. Then it checks whether that user is even allowed to perform the action, which is handled by RBAC. How the permission model behind it is built is covered in Kubernetes RBAC: Roles and Permissions.
Only after that do admission controllers come into play, in two passes. In the first pass, mutating admission controllers are still allowed to change the manifest, for example adding a default label or setting a CPU limit. Kubernetes then checks whether the changed manifest is formally valid. In the second pass, validating admission controllers may only accept or reject the result, they cannot change anything anymore. Only once this step passes does the object land in etcd and become active in the cluster.
Kubernetes already ships with a number of built-in admission controllers, for example for default storage classes or for resource limits per namespace. Which of them are active is decided by the API server configuration, which is up to your cluster admins. A full overview is available in the Kubernetes documentation on admission controllers.
Kubernetes admission controller webhook: hooking in your own rules
Built-in admission controllers cover many standard cases, but custom rules, such as which image registries are allowed, need more flexibility. That is what two special admission controllers are for, and neither contains any logic of its own: they forward the request to an external service instead. These are the MutatingAdmissionWebhook and the ValidatingAdmissionWebhook.
A Kubernetes admission controller webhook is technically nothing more than an HTTPS endpoint, one you can run yourself or one shipped by a ready-made tool. On every matching request, Kubernetes sends the manifest to that endpoint and waits for an answer: allowed, rejected, or, in the case of the mutating webhook, a changed manifest. This exact mechanism is what the two best known policy tools in the Kubernetes world, Gatekeeper and Kyverno, build on. For simple checks there have also been Validating Admission Policies for a few releases now: rules written in the Common Expression Language that the API server evaluates itself, with no webhook service of your own.
Enforcing kubernetes policies: Gatekeeper or Kyverno
Both tools solve the same task in different ways. Gatekeeper builds on the Open Policy Agent and writes rules in the Rego language, split into a reusable ConstraintTemplate and a concrete Constraint that fills that template with values. Kyverno skips a dedicated language entirely and describes rules as plain Kubernetes YAML that compares a pattern against the manifest directly.
|
Gatekeeper |
Kyverno |
| Rule language |
Rego (Open Policy Agent) |
YAML pattern |
| Objects |
ConstraintTemplate, Constraint |
ClusterPolicy, Policy |
| Getting started |
steeper learning curve, powerful for complex logic |
quick start for Kubernetes-native teams |
| Typical use |
reusable rule sets across several clusters |
registry, label and resource rules |
For getting started, I would recommend Kyverno if your team already works with YAML and does not want to learn a new language. For more complex rule sets with many exceptions, the Rego investment behind Gatekeeper pays off. In my own production cluster I run Gatekeeper, because I wanted rules I could reuse across several clusters.
An example: allow images only from a trusted registry
The following Kyverno policy requires that containers only pull images from a registry you own. It first runs in Audit mode, so you can see which manifests would violate it before you actually start blocking them.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: trusted-registry-only
spec:
validationFailureAction: Audit
rules:
- name: check-registry
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Only images from registry.example.internal are allowed."
pattern:
spec:
containers:
- image: "registry.example.internal/*"
match.any.resources.kinds limits the rule to Pods. validate.pattern describes the expected pattern for spec.containers[].image. Only once validationFailureAction is set to Enforce does Kubernetes actually reject a matching manifest, while in Audit mode the violation only shows up in the policy report.
Resource quotas and limit ranges: policy without a webhook
Not every rule needs a webhook. Two built-in objects solve a similar task without installing a policy engine: ResourceQuota and LimitRange. A ResourceQuota limits how many Pods, or how much CPU and memory, an entire namespace may request in total, which protects the cluster from one team claiming every resource. A LimitRange instead sets a minimum, maximum and default for a single Pod or container, independent of the namespace's quota.
Both objects complement Gatekeeper and Kyverno, they do not replace them. A ResourceQuota does not stop someone from pulling an image from the wrong registry, and a policy engine does not set automatic defaults for requests. In practice I combine both: LimitRange and ResourceQuota for resource limits per namespace, Gatekeeper for everything beyond that.
Audit first, enforce later: rolling out policies safely
The biggest mistake when starting out with Kubernetes policies is turning a new rule straight to Enforce mode on a live cluster. I did exactly that too early once myself and ended up blocking a deployment that had been running quietly for months. Since then I start every new rule in Audit mode, or the equivalent reporting mode in Gatekeeper, and watch for a while which existing objects would violate it.
Only once the report stops showing unexpected hits do I switch to Enforce, and even then only for new objects at first, not retroactively for existing ones. This order costs a few extra days, but it prevents exactly the kind of outage a well meant rule can otherwise cause.
Frequently asked questions
What is the difference between a mutating and a validating admission webhook?
A mutating admission webhook can still change a manifest, for example by adding a label. A validating admission webhook can only accept or reject a manifest. Kubernetes always calls all mutating webhooks first and only then the validating webhooks, so validation sees the final, already changed manifest.
Do I need Gatekeeper or Kyverno if I already use RBAC?
Yes, if you want to control more than access rights. RBAC decides who may perform an action, such as creating a Pod. A policy engine like Gatekeeper or Kyverno decides what that Pod is allowed to look like, such as which registry or which security context is permitted. The two layers complement each other, neither replaces the other.
Do resource quotas replace a policy engine?
No. ResourceQuota and LimitRange set limits for resources such as CPU and memory per namespace or Pod, and nothing beyond that. For rules about images, labels or security settings, you still need Gatekeeper or Kyverno.
How do I test a new Kyverno policy without blocking the cluster?
Set validationFailureAction to Audit first and review the policy report before switching to Enforce. That way you see which existing objects would violate the new rule without a single deployment actually being rejected.
Where to go next
Admission controllers and policies are the third building block, alongside RBAC and Pod Security Standards, for securing a Kubernetes cluster against unsafe manifests. How RBAC works in detail is covered in Kubernetes RBAC: Roles and Permissions, and the baseline settings for Pods in Kubernetes Pod Security Standards. If you want to build your own objects instead of relying on ready-made policy engines, the starting point is Kubernetes Operator and CRD Explained.
My suggestion for getting started: install Kyverno or Gatekeeper in a test cluster, write a single rule in Audit mode, and watch the report for a week before you even think about Enforce.
In full detail with all examples, this is covered in chapter 7 of my Kubernetes Practical Guide (Rheinwerk Computing).