An admission controller is code inside kube-apiserver that intercepts create/update/delete requests after authentication and authorization and before the object is stored in etcd. Read requests (get, list) skip admission.
flowchart LR
R["Allowed request"] --> M["Mutating admission<br/>(may change the object)"]
M --> S["Schema validation"]
S --> V["Validating admission<br/>(accept / reject)"]
V --> E[("etcd")]
- Mutating runs before validating: for example filling in the default StorageClass or ServiceAccount, injecting a sidecar.
- Validating can only say yes or no: for example LimitRanger, ResourceQuota, Pod Security.
- The two special plugins MutatingAdmissionWebhook and ValidatingAdmissionWebhook call external webhooks configured with
MutatingWebhookConfiguration/ValidatingWebhookConfigurationobjects (how tools such as Kyverno, OPA Gatekeeper and cert-manager plug in).
Plugins are enabled with a kube-apiserver flag (comma-separated list; many plugins are on by default):
kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...
# kubeadm: edit /etc/kubernetes/manifests/kube-apiserver.yaml, the kubelet restarts the pod
| Plugin | What it does |
|---|---|
NamespaceLifecycle |
Blocks new objects in a namespace that is terminating or does not exist |
LimitRanger |
Applies CPU/memory defaults and limits from LimitRanges |
DefaultStorageClass |
Fills in the default StorageClass for PVCs |
| Command | Meaning |
|---|---|
kubectl get validatingwebhookconfigurations |
Registered validating webhooks |
kubectl get mutatingwebhookconfigurations |
Registered mutating webhooks |
Pod Security Admission
The built-in PodSecurity plugin enforces three standard levels, chosen per namespace with labels:
| Level | Allows |
|---|---|
privileged |
Everything, for system namespaces |
baseline |
Blocks known privilege escalations: privileged containers, host namespaces, hostPath |
restricted |
Also requires non-root, dropped capabilities, seccomp, no privilege escalation |
Each namespace can set three modes: enforce (reject), audit (record in audit logs), and warn (show a warning to the user). Start with warn and audit on restricted, fix workloads, then switch to enforce.
Policy engines
| Tool | Policies written in | Strengths |
|---|---|---|
| Kyverno | Kubernetes-style YAML | Validate, mutate, and generate resources; easy to read |
| OPA Gatekeeper | Rego language | Very flexible, shared with other OPA uses |
| ValidatingAdmissionPolicy | CEL expressions, built into Kubernetes | No webhook to run; good for simple rules |
Typical rules: images must come from registry.example.com, every pod must have resource limits, the latest tag is forbidden, and every namespace needs an owner label.
A built-in CEL policy
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata: { name: require-replicas-limit }
spec:
matchConstraints:
resourceRules:
- { apiGroups: ["apps"], apiVersions: ["v1"], operations: ["CREATE","UPDATE"], resources: ["deployments"] }
validations:
- expression: "object.spec.replicas <= 10"
message: "replicas must be 10 or fewer"
A ValidatingAdmissionPolicyBinding then applies it to chosen namespaces.
Webhook risks
A webhook with failurePolicy: Fail blocks every matching request when the webhook service is down, which can stop deployments cluster-wide. Run webhooks with several replicas, exclude kube-system with a namespace selector, and keep timeouts short. kubectl apply --dry-run=server runs admission without saving, a safe way to test whether a manifest will be accepted.