Admission Control
Pod Security Admission
The built in controller that enforces the Pod Security Standards on namespaces.
Pod Security Admission (PSA) is the built-in admission controller that enforces the Pod Security Standards. It needs no installation and is configured entirely with namespace labels.
The Three Standards
The Pod Security Standards define three levels:
| Level | Meaning |
|---|---|
privileged | No restrictions. For system components that genuinely need host access. |
baseline | Blocks known privilege escalations: privileged containers, host namespaces, hostPath volumes. |
restricted | Current hardening best practice: non-root, all capabilities dropped, seccomp enabled. |
The Three Modes
Each level can be applied in three modes, and a namespace can combine them:
- enforce: violating pods are rejected.
- audit: violations are recorded in the audit log, pods still run.
- warn: violations return a warning to the client, pods still run.
Audit and warn exist so you can measure the blast radius of a policy before enforcing it.
Label A Namespace
Create a namespace and enforce the restricted standard on it:
kubectl create namespace psa-lab
kubectl label namespace psa-lab \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted
Try to run a plain pod:
kubectl run bare --image=busybox:1.36 --restart=Never -n psa-lab -- sleep 3600
Expected output:
Error from server (Forbidden): pods "bare" is forbidden: violates PodSecurity
"restricted:latest": allowPrivilegeEscalation != false, unrestricted capabilities,
runAsNonRoot != true, seccompProfile
The error lists exactly which fields are missing. Fix them:
apiVersion: v1
kind: Pod
metadata:
name: hardened
namespace: psa-lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sleep", "3600"]
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
kubectl apply -f hardened-pod.yaml
kubectl get pod hardened -n psa-lab
Expected output:
NAME READY STATUS RESTARTS AGE
hardened 1/1 Running 0 5s
Pin The Standard Version
The definition of each level can gain checks as Kubernetes evolves. Pin the standard to a Kubernetes version so an upgrade cannot change policy underneath you:
kubectl label namespace psa-lab \
pod-security.kubernetes.io/enforce-version=v1.31
Roll Out Safely
For an existing namespace with running workloads, start with warn and audit only:
kubectl label namespace lab \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
Every apply now prints warnings for violating pods without breaking anything. Once the workloads are clean, add the enforce label.
Enforcement only happens when pods are created. Already-running pods are not evicted when you add labels; they are re-checked the next time they are recreated.
Clean Up
kubectl delete namespace psa-lab
kubectl label namespace lab \
pod-security.kubernetes.io/warn- \
pod-security.kubernetes.io/audit-
Practical Guidance
- Enforce
restrictedon every application namespace; treat exceptions as findings to fix. - Use
baselineas the floor for namespaces that cannot yet meetrestricted. - Always set
warnandauditto the level you plan to enforce next, so drift is visible early. - Pin
enforce-versionso cluster upgrades do not change enforcement silently. - PSA cannot express custom rules such as allowed registries or required labels. For those, add a policy engine, covered next.