Chapter 18 — Admission control and policy
In one sentence
Admission controllers inspect (and can mutate or reject) every object before it persists, and a policy engine there is how you enforce "no privileged pods", "all images signed", "every namespace has a NetworkPolicy", and hundreds of other rules that RBAC can't express.
Where we are
RBAC (Ch 17) decides who can create a Deployment. It can't say what a Deployment is allowed to contain. Admission policy does — it's the enforcement point for Pod Security, image provenance (Ch 13), and your org's rules.
How it works
The admission chain (after authn + authz):
1. MUTATING admission webhooks may change the object (inject
sidecars, set defaults, add labels). Order matters; can re-invoke.
2. Object SCHEMA validation the API server's own checks.
3. VALIDATING admission webhooks accept or reject; cannot change.
4. VALIDATING ADMISSION POLICIES in-tree, CEL expressions, NO webhook
(no extra pod, no availability risk) -- GA in 1.30. Prefer these for
simple allow/deny.
5. persist to etcd.
(Built-in controllers like NamespaceLifecycle, ResourceQuota,
PodSecurity, and the LimitRanger also run in this pipeline.)
Pod Security Admission (PSA) — the built-in, replaces PodSecurityPolicy:
Three LEVELS, applied per-namespace via labels:
privileged -- no restrictions.
baseline -- blocks known privilege escalations: no privileged, no
hostPath (mostly), no hostNetwork/PID/IPC, no dangerous
caps added, no host ports (mostly), AppArmor not
unconfined, etc. "Don't do the obviously bad thing."
restricted -- hardened: runAsNonRoot, allowPrivilegeEscalation:false,
drop ALL caps (NET_BIND_SERVICE allowed),
seccompProfile RuntimeDefault|Localhost, no hostPath,
volumes limited to safe types, etc.
Three MODES per level:
enforce -- reject violating pods
audit -- allow but record in the audit log
warn -- allow but return a warning to the client
Labels on the namespace:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.30
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted
Rollout pattern: set `warn` + `audit` to `restricted` cluster-wide,
watch the audit log / warnings for breakage, fix workloads, THEN flip
`enforce`. Exempt only specific system namespaces, by name, with a
comment.
Policy engines (for everything PSA can't express):
OPA GATEKEEPER ConstraintTemplate (Rego) + Constraint CRDs. Rich, a
large policy library, `gator` for unit-testing policies, audit mode
that reports existing violations. Rego has a learning curve.
KYVERNO YAML policies, no Rego. validate / mutate / generate /
verifyImages / cleanup. `Audit` vs `Enforce`. Policy Reports. Great
DX; CLI `kyverno apply` / `kyverno test` for CI.
VALIDATING ADMISSION POLICY (VAP) in-tree, CEL, no webhook. Use for
simple deny rules -> zero operational risk (a broken webhook can wedge
the whole cluster; VAP can't).
Kubewarden (WASM policies), jsPolicy (JS/TS) -- alternatives.
Policies worth enforcing (beyond PSA restricted):
[ ] images: only from approved registries, BY DIGEST, SIGNED with the
expected identity + required attestations (Ch 13).
[ ] every namespace has a default-deny NetworkPolicy (generate one if
missing -- Kyverno `generate`).
[ ] no `serviceAccountName` outside an allowlist for the creator
(blocks the Ch 17 path).
[ ] resource requests/limits set (DoS / noisy-neighbour).
[ ] `automountServiceAccountToken: false` unless annotated as needing
the API.
[ ] no `:latest`, no unpinned tags.
[ ] required labels: owner, team, data-classification.
[ ] no LoadBalancer/NodePort Services except in an allowlist.
[ ] no `hostPath` except exact paths for named system DaemonSets.
[ ] deny creating `ClusterRoleBinding` to cluster-admin except
break-glass; deny `bind`/`escalate`/`impersonate` grants in new
Roles.
[ ] PDBs / topology spread for critical workloads (availability is a
security property -- Ch 28).
Webhook availability = cluster availability. A validating webhook with
failurePolicy: Fail that becomes unavailable blocks all matching API
writes (potentially the whole cluster). Mitigations: failurePolicy: Ignore
for non-critical rules (accepting a fail-open gap), tight namespaceSelector /
objectSelector to scope what the webhook sees, exclude kube-system, run the
webhook HA, and prefer VAP (no webhook) for critical deny rules.
Worked example
Rolling PSA restricted without an outage, then adding a signed-image gate.
1. Label every non-system namespace:
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted
(enforce still `privileged` / unset)
2. Deploy something; watch `kubectl` warnings and grep the audit log
for `pod-security.kubernetes.io/audit-violations`. Typical hits:
- container runs as root -> set runAsNonRoot + a non-root uid in
the image
- missing seccompProfile -> add `RuntimeDefault`
- caps not dropped -> `drop: ["ALL"]`
- writes to `/` -> `readOnlyRootFilesystem: true` + emptyDir /tmp
3. Fix the workloads (usually a shared base manifest / Helm values).
4. Flip `enforce: restricted` per namespace once its audit log is
clean. Exempt `kube-system` and the CNI/CSI namespaces BY NAME with
a comment; work to shrink that list.
5. Add Kyverno `verifyImages` (Ch 13) in `Audit`, watch the Policy
Report for images that wouldn't pass, migrate them through the
paved-road pipeline, then flip to `Enforce`.
6. Add a VAP (CEL) for the simple stuff so it can't be a webhook
outage:
validations:
- expression: "object.spec.template.spec.containers.all(c,
!c.image.contains(':latest') && c.image.contains('@sha256:'))"
message: "images must be pinned by digest"
Practice (75 min) — kind cluster
- Apply PSA
warn+audit: restrictedcluster-wide. Deploy a few real charts. Collect every violation. Fix a workload to passrestricted. Flipenforceon its namespace. - Install Kyverno. Write and test (with
kyverno test) policies for: approved-registries + digest-pinned; default-deny NetworkPolicy generation;serviceAccountNameallowlist; requiredownerlabel. Run inAudit, read the Policy Report, thenEnforce. - Do the same registry/digest rule as a ValidatingAdmissionPolicy (CEL). Compare: no pod, no availability risk.
- Break a webhook on purpose: scale Kyverno to 0 with
failurePolicy: Failon a broad rule — watch API writes start failing. Set a tightnamespaceSelectorandfailurePolicy: Ignorefor non-critical rules; observe the trade-off. - Run
gator verifyon a Gatekeeper policy library, or Polaris / kubescape for a policy gap report.
Common mistakes
- PSA
enforceflipped cluster-wide with noaudit/warnsoak. Instant outage for every non-conformant workload. - Exempting whole namespaces "temporarily". The exemption list becomes permanent. Exempt by exact name, with a comment and a ticket to remove.
- Policy engine stuck in
Auditforever. Attackers don't read Policy Reports. Get toEnforce. - One giant
Fail-policy webhook selecting everything. A single point of failure for the whole control plane. Scope it; use VAP for critical deny. - Mutating webhooks that add privileges (a sidecar with broad RBAC, a privileged init container) — now the webhook is an escalation vector.
- Not covering all pod-spec paths (initContainers, ephemeralContainers, Deployment/DaemonSet/CronJob templates) — attackers use the uncovered field.
- Rego/CEL bugs that fail open. Unit-test policies (
kyverno test,gator) in CI like any code.
Check yourself
- Give the order of the admission chain and where mutation vs rejection happens.
- Name the three PSA levels and the three modes, and the safe rollout order.
- When would you choose a ValidatingAdmissionPolicy over a webhook-based policy?
- Why is
failurePolicy: Failon a broad webhook an availability risk, and name two mitigations. - List five admission policies worth enforcing beyond PSA
restricted. - Why must a policy cover initContainers and workload templates, not just
Pod?
Further reading
- Docs: Kubernetes "Pod Security Admission", "Admission Controllers Reference", "Validating Admission Policy", "Dynamic Admission Control".
- Engines: OPA Gatekeeper (+ the
gatekeeper-library), Kyverno (+ the Kyverno policy library andkyverno-json), Kubewarden. - Guidance: NSA/CISA Kubernetes Hardening; CIS Benchmark policy sections; "Pod Security Admission migration from PSP" (Kubernetes blog).
- Talks: "The Path to Pod Security" and "Policy as Code with Kyverno/OPA" from KubeCon.