Hardening · Workload policy
Admission Control
RBAC decides whether a tenant can create a pod. Admission control decides what that pod is allowed to look like, and it's where most of the "just ask for privileged" breakouts get stopped.
If a tenant can write their own pod specs, then without admission control they can
ask for privileged: true, a hostPath mount of the node’s filesystem, or the
host’s PID and network namespaces, and Kubernetes will happily give it to them.
That’s the increased pod rights problem, and admission control is
the main fix for it.
Pod Security Admission
The built-in starting point is Pod Security Admission (PSA), which has been GA
since Kubernetes 1.25. It applies one of the three
Pod Security Standards
(privileged, baseline or restricted) to each namespace, based on a label
like pod-security.kubernetes.io/enforce: restricted.
For tenant namespaces, restricted is the one to aim for, with baseline as a
fallback for workloads that can’t run under it. PSA runs inside the API server,
so there’s no webhook to go down, and it has warn and audit modes that make
it a lot easier to roll out gradually.
It’s deliberately simple though. You get three profiles, applied per namespace, with no custom rules and no way of looking at anything other than pods.
Policy engines
For everything else there are policy engines like Kyverno and OPA Gatekeeper, or the built-in ValidatingAdmissionPolicy (GA in 1.30), which lets you write rules in CEL without running a webhook at all.
In a multi-tenant cluster there are a couple of rules that come up again and again:
- Protect the namespace labels. Stop tenants changing the
pod-security.kubernetes.io/*labels on their own namespace, otherwise they can switch PSA off for themselves. - Block ExternalIP services, either with the
DenyServiceExternalIPsadmission controller or a policy, to close off CVE-2020-8554. - Enforce node placement, so tenants can’t add tolerations or node selectors that land them on another tenant’s node pool.
- Enforce the runtime, if you’re using sandboxed runtimes, so
tenants can’t just leave out the
runtimeClassName. - Restrict registries and set
imagePullPolicy: Alwaysfor tenant workloads. - Constrain shared components, e.g. forbidding the Istio
meshgateway or limiting the hosts a tenant’s routes can use, and restricting URL fields in operator custom resources to approved destinations.
Making it stick
Admission control has a fair few ways of quietly not applying, so it’s worth checking:
- Tenants can’t change the policies. Anyone who can edit webhook configurations or cluster-wide policies can switch them off or set them to fail open.
- Webhooks fail closed for the policies that matter. If a webhook is set to
Ignoreand goes down, everything gets through. - Policies cover every container field. Rules that only check
containerscan missinitContainersandephemeralContainers. - Namespace selectors use identity. Scope policies with the
kubernetes.io/metadata.namelabel rather than a custom label a tenant could add or remove, and make sure tenants can’t deploy into exempt namespaces likekube-system. - Allowlists over blocklists. A blocklist has to be updated every time Kubernetes adds a new feature, an allowlist doesn’t.
What admission control doesn’t fix
- Kernel exploits. A pod that fully complies with
restrictedis still sharing the node’s kernel. Admission control stops the easy breakouts, not the hard ones. - Anything that doesn’t go through the API server. Static pods are created directly by the kubelet, so someone with node access can skip admission entirely.
- The runtime doing something different. Admission checks the pod that was
requested. The 2026 checkpoint/restore CVEs in containerd, CRI-O and Podman
let a checkpoint image restore with its own root, capabilities and seccomp
settings, so a pod that passed
restrictedcould run with full privileges. - The policy engine itself. Policy engines are cluster-wide operators that evaluate tenant-supplied rules, and Kyverno and Kubewarden both had cross-tenant CVEs in 2026. They’re part of the operator attack surface too.