Pod Security Standards define three security profiles (privileged, baseline, restricted) enforced at namespace level. They close the gap between RBAC permission to create pods and restrictions on what kinds of pods can be created.
Know the three PSS levels (privileged, baseline, restricted) and what each blocks. Understand how to label namespaces with pod-security.kubernetes.io/enforce.
Migrate clusters from PodSecurityPolicy (deprecated) to PodSecurity Admission. Use warn mode to identify violations before enforcing. Fix Helm charts and manifests that use dangerous fields.
Design namespace security tiers: restricted for application workloads, baseline for platform components, privileged only for explicitly approved system namespaces (kube-system). Enforce via Gatekeeper ConstraintTemplates for finer control beyond PSS three levels.
Pod Security Standards define three security profiles (privileged, baseline, restricted) enforced at namespace level. They close the gap between RBAC permission to create pods and restrictions on what kinds of pods can be created.
Helm chart with privileged sidecar deployed by admin to production namespace
RCE exploited in main app container
Attacker uses privileged sidecar + Docker socket to escape to host
Cryptominer installed on host; SSH key added for persistence
Anomalous CPU spike detected; incident response begins; pod security baseline enforced
The question this raises
How does a privileged container escape to the host -- and what namespace-level control prevents privileged pods from being deployed regardless of who applies the Helm chart?
A namespace is labeled pod-security.kubernetes.io/enforce: baseline. A pod spec has privileged: false, hostPID: false, but requests the SYS_ADMIN capability. What happens?
Lesson outline
The Privileged Pod Escape Path
A container with privileged: true or SYS_ADMIN capability can escape to the host -- mounting the host filesystem, seeing all host processes, loading kernel modules. Pod Security Standards enforce namespace-level restrictions on dangerous pod spec fields, preventing escape-enabling configurations from being deployed regardless of who applies them.
Privileged level
Use for: No restrictions. Required only for system components that genuinely need host access: CNI plugins (must configure host network), storage drivers (must access host block devices), node agents (must read host /proc). Use only in kube-system and dedicated system namespaces.
Baseline level
Use for: Blocks the most dangerous settings. All application namespaces should enforce at minimum baseline. Prevents: hostPID, hostNetwork, privileged containers, dangerous capabilities, hostPath mounts. Does not require non-root.
Restricted level
Use for: Full hardening: non-root required, no privilege escalation, seccomp profile required, drop all capabilities. Highest security. Breaks legacy charts. Use warn mode to identify violations first, then fix charts, then enforce.
Container Escape Techniques -> PSS Field that Enables -> Level that Blocks privileged: true -> full host access (sysfs, /proc, all devs) -> baseline blocks: privileged: true hostPID: true -> see/signal all host processes; nsenter host namespaces -> baseline blocks: hostPID: true hostPath: /var/run/docker.sock -> Docker socket = create new containers with any config -> baseline blocks: hostPath volumes (most) SYS_ADMIN capability -> mount filesystems, load kernel modules -> baseline blocks: SYS_ADMIN, SYS_PTRACE, NET_RAW, etc. runAsUser: 0 (root in container) -> file UID 0; easier exploit of kernel vulns -> restricted blocks: runAsUser: 0, runAsNonRoot: false allowPrivilegeEscalation: true (default!) -> child process can gain more privileges than parent -> restricted blocks: allowPrivilegeEscalation not explicitly false
Each dangerous field maps to a specific escape technique; PSS levels close these paths systematically
PSS Migration Strategy
Cluster without pod security; privileged pods in production
“Any Helm chart can add privileged: true; developers may not know the risk; no enforcement at namespace level”
“Label namespaces with enforce: baseline; use warn: restricted to identify what needs fixing; fix charts; promote to restricted over 2 sprints”
Legacy app running as root (uid 0)
“Restricted PSS blocks it; rolling deployment fails; engineering blocked”
“Fix Dockerfile: USER 1000; add securityContext.runAsNonRoot: true to pod spec; test; enforce restricted on namespace”
Applying PSS to a namespace
01
1. Choose enforcement level: start with baseline for all prod namespaces
02
2. First apply warn mode: kubectl label ns production pod-security.kubernetes.io/warn=restricted
03
3. Deploy workloads normally; watch for PSS warning messages in kubectl output
04
4. Fix all violations (runAsRoot, missing seccomp, dangerous capabilities)
05
5. Switch to enforce: kubectl label ns production pod-security.kubernetes.io/enforce=baseline
06
6. For new namespaces, enforce at creation time via namespace template in GitOps
1. Choose enforcement level: start with baseline for all prod namespaces
2. First apply warn mode: kubectl label ns production pod-security.kubernetes.io/warn=restricted
3. Deploy workloads normally; watch for PSS warning messages in kubectl output
4. Fix all violations (runAsRoot, missing seccomp, dangerous capabilities)
5. Switch to enforce: kubectl label ns production pod-security.kubernetes.io/enforce=baseline
6. For new namespaces, enforce at creation time via namespace template in GitOps
1spec:2securityContext:Pod-level securityContext: applies to all containers in the pod3runAsNonRoot: true4runAsUser: 10005seccompProfile:6type: RuntimeDefault7containers:8- name: app9image: myapp:v210securityContext:allowPrivilegeEscalation: false -- child processes cannot gain more privileges11allowPrivilegeEscalation: false12readOnlyRootFilesystem: true13capabilities:drop: ALL -- removes all Linux capabilities; add back only what is needed14drop: ["ALL"] # drop all capabilities15# No hostPID, hostNetwork, hostPath16# No privileged: true
PSS enforcement failure modes
Pod running as root without privilege restrictions -- passes baseline, blocked by restricted
spec:
containers:
- name: app
image: my-legacy-app:v1
# runs as root (uid 0) inside container
# no seccomp profile
# no capabilities drop
# allowPrivilegeEscalation defaults to true
# passes baseline PSS but fails restrictedspec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: my-legacy-app:v1-nonroot # Dockerfile: USER 1000
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]Restricted PSS requires all four: runAsNonRoot, explicit allowPrivilegeEscalation: false, seccompProfile, and dropping all capabilities. The image itself must support non-root (USER in Dockerfile). Cannot patch the image default user from pod spec alone.
| Level | What it blocks | What still allowed | Migration effort | Recommended for |
|---|---|---|---|---|
| Privileged | Nothing | Everything | None | kube-system, CNI, storage drivers |
| Baseline | Container escape fields | Root containers, no seccomp | Low | All application namespaces minimum |
| Restricted | Root + escape + capabilities | Very little beyond defaults | Medium-high (fix charts) | New greenfield application namespaces |
| OPA/Gatekeeper custom | Whatever you specify | Whatever you allow | High (write Rego) | Finer-grained control beyond PSS three levels |
PSS three levels
📖 What the exam expects
Privileged: no restrictions (for system components like CNI, storage drivers). Baseline: blocks most dangerous settings (hostPID, hostNetwork, privileged, dangerous capabilities). Restricted: full hardening (no root, drop all capabilities, seccomp required).
Toggle between what certifications teach and what production actually requires
Security hardening design questions and container escape scenario discussions.
Common questions:
Strong answer: Mentions using warn mode for gradual rollout, fixing Helm charts to pass restricted PSS, and Seccomp profiles as defense-in-depth beyond PSS restricted.
Red flags: Not knowing that privileged: true bypasses container isolation, or using PSP (removed in K8s 1.25).
Related concepts
Explore topics that connect to this one.
Ready to see how this works in the cloud?
Switch to Career Paths for structured paths (e.g. Developer, DevOps) and provider-specific lessons.
View role-based pathsSign in to track your progress and mark lessons complete.
Questions? Discuss in the community or start a thread below.
Join DiscordSign in to start or join a thread.