Skip to content

Enforce Pod Security Admission

Severity: highApplies to: Kubernetes 1.25+Applies to: Kubernetes 1.34
The fix
Terminal window
# Enforce the restricted standard on a namespace with three labels
kubectl label namespace orders \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted

PodSecurityPolicy is gone — don’t look for it

Section titled “PodSecurityPolicy is gone — don’t look for it”

If a hardening guide tells you to write a PodSecurityPolicy, stop reading it. PSP was deprecated in 1.21 and removed entirely in 1.25. The API doesn’t exist. A cluster on any current version has no PSP, and manifests referencing it are inert.

Its replacement, Pod Security Admission (PSA), is built into the API server and works completely differently — and much more simply. There’s no policy object, no RBAC binding, no controller to install. You apply one of three built-in Pod Security Standards to a namespace with labels, and the API server enforces it on every pod created there.

This matters because the two are not similar. Time spent trying to make PSP work, or copying a PSP-based guide, is time spent on an API that Kubernetes removed. PSA is the answer, and it’s less work.

Standard What it allows
privileged Everything — no restrictions. The unsafe default.
baseline Blocks known privilege escalations: no privileged, no host namespaces, no hostPath, limited capabilities.
restricted Baseline plus hardening: must run as non-root, drop all capabilities, seccomp RuntimeDefault, read-only where possible.

restricted is the target for application namespaces — it enforces most of the workload security context automatically, so a pod that tries to run privileged or as root is rejected at admission. baseline is the pragmatic floor when restricted breaks too much to adopt at once.

The unlabelled default is effectively privileged — no enforcement — which is why a fresh namespace lets a pod request anything.

Each standard can be applied in three modes, and using them together is how you adopt safely:

  • enforce — reject pods that violate the standard. The teeth.
  • warn — allow, but return a warning to the user who created it.
  • audit — allow, but record a violation in the audit log.

Set warn and audit to restricted before you set enforce, so you see what would be rejected without breaking anything. That’s the safe rollout, and it’s the whole reason the three modes exist.

It’s per-namespace, and kube-system is exempt

Section titled “It’s per-namespace, and kube-system is exempt”

PSA is applied per namespace, so hardening the cluster means labelling every application namespace — a new namespace with no label has no enforcement. kube-system and other control-plane namespaces legitimately run privileged pods and should not be set to restricted; scope your enforcement to workload namespaces.

For cluster-wide defaults, the API server’s AdmissionConfiguration can set a baseline that applies everywhere unless a namespace overrides it — better than relying on remembering to label each one.