CKS — Pod Security Standards (PSS)
Pod Security Standards (PSS)
Pod Security Standards define three progressively restrictive security levels for Pods. They replaced PodSecurityPolicy (PSP), which was removed in Kubernetes 1.25, and are now the built-in mechanism for controlling what Pods are allowed to do.
Every CKS candidate must know all three levels, what each restricts, and when to apply them.
1. The Three Levels
Security Spectrum:
Privileged ←————————————————————————→ Restricted
(No restrictions) (Maximum lockdown)
┌────────────┐ ┌────────────┐ ┌─────────────┐
│ Privileged │ │ Baseline │ │ Restricted │
│ │ │ │ │ │
│ Unrestricted │ Prevents │ │ Hardened │
│ access for │ │ known │ │ workloads │
│ system │ │ privilege │ │ with full │
│ workloads │ │ escalation │ │ lockdown │
└────────────┘ └────────────┘ └─────────────┘
Privileged
- No restrictions whatsoever
- Allows hostNetwork, hostPID, hostIPC, privileged containers, all capabilities
- Intended for system-level workloads: CNI plugins, storage drivers, logging agents
- Should only be used in
kube-systemor dedicated infrastructure namespaces
Baseline
- Prevents known privilege escalation vectors
- Blocks hostNetwork, hostPID, hostIPC, privileged containers
- Blocks host path volumes, host ports, and certain dangerous capabilities
- Still allows running as root, allows some capabilities
- Suitable for most general workloads during migration
Restricted
- Maximum security for untrusted or multi-tenant workloads
- Must run as non-root (
runAsNonRoot: true) - Must drop ALL capabilities (can only add back
NET_BIND_SERVICE) - Must set
allowPrivilegeEscalation: false - Must have a Seccomp profile (
RuntimeDefaultorLocalhost) - Volume types restricted to safe set (configMap, secret, emptyDir, PVC, etc.)
- This is the target level for production workloads on the CKS exam
2. Complete Comparison Table
This table shows every field controlled by Pod Security Standards:
| Control | Privileged | Baseline | Restricted |
|---|---|---|---|
| hostNetwork | Allowed | Blocked | Blocked |
| hostPID | Allowed | Blocked | Blocked |
| hostIPC | Allowed | Blocked | Blocked |
| hostPorts | Allowed | Blocked | Blocked |
| privileged containers | Allowed | Blocked | Blocked |
| Capabilities — add | Any | Only: AUDIT_WRITE, CHOWN, DAC_OVERRIDE, FOWNER, FSETID, KILL, MKNOD, NET_BIND_SERVICE, SETFCAP, SETGID, SETPCAP, SETUID, SYS_CHROOT |
Only: NET_BIND_SERVICE |
| Capabilities — drop ALL | Not required | Not required | Required |
| hostPath volumes | Allowed | Blocked | Blocked |
| Allowed volume types | All | All except hostPath | configMap, csi, downwardAPI, emptyDir, ephemeral, persistentVolumeClaim, projected, secret |
| procMount | Any | Default only | Default only |
| seccompProfile | No restriction | No restriction | Must be RuntimeDefault or Localhost |
| runAsNonRoot | No restriction | No restriction | Must be true |
| runAsUser | Any | Any | Must not be 0 (root) |
| allowPrivilegeEscalation | Allowed | Allowed | Must be false |
| sysctls | Any | Only safe sysctls | Only safe sysctls |
| appArmorProfile | Any | Not unconfined (if set) |
Not unconfined (if set) |
| seLinuxOptions type | Any | Only allowed types | Only allowed types |
| windowsOptions — hostProcess | Allowed | Blocked | Blocked |
Exam Tip: Memorize what Restricted requires:
runAsNonRoot: true,drop: ["ALL"],allowPrivilegeEscalation: false, andseccompProfile.type: RuntimeDefault. You’ll need to write compliant Pods under time pressure.
3. Pod Examples for Each Level
Privileged Pod (system workload)
apiVersion: v1
kind: Pod
metadata:
name: cni-installer
namespace: kube-system
spec:
hostNetwork: true
hostPID: true
containers:
- name: install-cni
image: calico/cni:v3.27
securityContext:
privileged: true
volumeMounts:
- name: cni-bin
mountPath: /host/opt/cni/bin
- name: cni-conf
mountPath: /host/etc/cni/net.d
volumes:
- name: cni-bin
hostPath:
path: /opt/cni/bin
- name: cni-conf
hostPath:
path: /etc/cni/net.d
This Pod uses hostNetwork, hostPID, privileged, and hostPath — it only passes the Privileged level.
Baseline Pod (general workload)
apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
containers:
- name: app
image: nginx:1.27
ports:
- containerPort: 80
# Runs as root (UID 0) — allowed under Baseline
# No Seccomp — allowed under Baseline
# No explicit capabilities drop — allowed under Baseline
This Pod has no host-level access but runs as root without dropping capabilities. It passes Baseline but fails Restricted.
Restricted Pod (hardened workload)
apiVersion: v1
kind: Pod
metadata:
name: secure-web
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.27-alpine
ports:
- containerPort: 8080
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: run
emptyDir: {}
This Pod passes the Restricted level — non-root, all capabilities dropped, privilege escalation disabled, Seccomp enabled, read-only root filesystem with writable directories via emptyDir.
4. Migration from PodSecurityPolicy (PSP) to PSS
PodSecurityPolicy was deprecated in Kubernetes 1.21 and removed in 1.25. Pod Security Standards (enforced via Pod Security Admission) replaced it.
Migration Timeline
Kubernetes 1.21: PSP deprecated, PSA introduced (alpha)
Kubernetes 1.22: PSA beta
Kubernetes 1.23: PSA stable
Kubernetes 1.25: PSP REMOVED — PSA is the only built-in option
PSP → PSS Mapping
| PSP Field | PSS Equivalent |
|---|---|
spec.privileged: false |
Baseline level (blocks privileged) |
spec.hostNetwork: false |
Baseline level (blocks hostNetwork) |
spec.hostPID: false |
Baseline level (blocks hostPID) |
spec.runAsUser: MustRunAsNonRoot |
Restricted level (runAsNonRoot: true) |
spec.requiredDropCapabilities: [ALL] |
Restricted level (drop: ["ALL"]) |
spec.allowPrivilegeEscalation: false |
Restricted level |
spec.volumes: [configMap, secret, ...] |
Restricted level (limited volume types) |
spec.allowedCapabilities |
Use PSA exemptions or OPA/Gatekeeper for fine-grained control |
Key Differences
| Feature | PSP | PSS/PSA |
|---|---|---|
| Enforcement | Admission controller (cluster-wide) | Namespace labels |
| Granularity | Per-policy, RBAC-bound | Three fixed levels + exemptions |
| Custom rules | Extensive per-field control | Fixed levels — use OPA/Gatekeeper for custom |
| Status | Removed in 1.25 | Built-in, stable |
| Mutation | Could modify Pods (inject defaults) | No mutation — only validation |
Warning: PSA does not mutate Pods. Unlike PSP, it won’t inject
runAsNonRoot: trueinto your Pod spec. Your manifests must already be compliant.
5. When to Use Each Level
| Level | Use Case | Example Namespaces |
|---|---|---|
| Privileged | System infrastructure that needs host access | kube-system, kube-node-lease, monitoring (for node exporters) |
| Baseline | General workloads during migration or development | staging, dev, namespaces being migrated from PSP |
| Restricted | Production workloads, multi-tenant clusters, CKS exam | production, tenant-a, tenant-b, any security-sensitive namespace |
Decision Flowchart
Does the workload need host access (hostNetwork, hostPID, privileged)?
├── YES → Privileged (only in infrastructure namespaces)
└── NO
├── Can it run as non-root with all caps dropped?
│ ├── YES → Restricted ✓ (target state)
│ └── NO → Baseline (temporary — plan migration to Restricted)
└── Is it a third-party image you can't modify?
├── YES → Baseline + plan to rebuild image
└── NO → Restricted ✓
6. Identifying Non-Compliant Pods
Check Which Level a Running Pod Violates
# Dry-run check: will this Pod pass the Restricted level?
kubectl label --dry-run=server --overwrite ns default \
pod-security.kubernetes.io/enforce=restricted
# Check all Pods in a namespace against Restricted
kubectl get pods -n my-namespace -o json | \
jq -r '.items[] | select(
.spec.containers[].securityContext.allowPrivilegeEscalation != false or
.spec.securityContext.runAsNonRoot != true
) | .metadata.name'
Common Violations and Fixes
| Violation | Root Cause | Fix |
|---|---|---|
pods "x" is forbidden: violates ... runAsNonRoot |
Container runs as root | Add runAsNonRoot: true + runAsUser: 1000 |
violates ... capabilities |
Capabilities not dropped | Add capabilities: drop: ["ALL"] |
violates ... allowPrivilegeEscalation |
Default is true |
Set allowPrivilegeEscalation: false |
violates ... seccompProfile |
No Seccomp profile | Add seccompProfile: type: RuntimeDefault |
violates ... hostPath |
Using hostPath volume | Switch to PVC or emptyDir |
7. CKS Exam Patterns
Pattern 1: Identify the PSS Level
Task: Given a Pod spec, determine which PSS level it satisfies.
Strategy:
1. Check for hostNetwork, hostPID, privileged → if present, only Privileged
2. Check for runAsNonRoot, drop ALL, allowPrivilegeEscalation: false,
Seccomp profile → if all present, Restricted
3. Otherwise → Baseline
Pattern 2: Fix a Pod to Meet Restricted
Task: A Pod is being rejected. Make it compliant with the Restricted level.
Checklist:
□ spec.securityContext.runAsNonRoot: true
□ spec.securityContext.seccompProfile.type: RuntimeDefault
□ containers[*].securityContext.allowPrivilegeEscalation: false
□ containers[*].securityContext.capabilities.drop: ["ALL"]
□ No hostPath volumes
□ No hostNetwork/hostPID/hostIPC
□ No privileged: true
Summary
| Concept | Detail |
|---|---|
| What is PSS | Three built-in security levels for Pods: Privileged, Baseline, Restricted |
| Privileged | No restrictions — for system workloads only |
| Baseline | Blocks known escalation vectors (hostNetwork, privileged, hostPID) |
| Restricted | Full lockdown — non-root, drop ALL, no privilege escalation, Seccomp required |
| Replaces | PodSecurityPolicy (removed in 1.25) |
| Enforcement | Via Pod Security Admission (PSA) — namespace labels |
| Mutation | PSA does NOT mutate Pods — manifests must be compliant up front |
| CKS target | Restricted level for production namespaces |
Practice Questions
Q1: A Pod has hostNetwork: true and privileged: true. Which PSS levels will accept this Pod?
Only the Privileged level.
- Baseline rejects
hostNetwork: trueandprivileged: true - Restricted also rejects them (Restricted is a superset of Baseline restrictions)
- Only Privileged has no restrictions and will accept this Pod
These settings should only appear in infrastructure namespaces like kube-system.
Q2: What are the four mandatory security context settings required by the Restricted PSS level?
The Restricted level requires:
runAsNonRoot: true— Pod or container levelallowPrivilegeEscalation: false— each containercapabilities.drop: ["ALL"]— each container (onlyNET_BIND_SERVICEcan be added back)seccompProfile.type: RuntimeDefault(orLocalhost) — Pod or container level
Example:
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:1.0
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
Missing any one of these four causes the Pod to be rejected under Restricted enforcement.
Q3: Your team is migrating from PodSecurityPolicy to Pod Security Standards. The existing PSP allows running as root but blocks hostNetwork and privileged containers. Which PSS level matches this policy?
Baseline.
- Baseline blocks
hostNetwork,hostPID,hostIPC, andprivilegedcontainers — matching the existing PSP - Baseline still allows running as root, which the PSP also allowed
- Restricted would be too strict because it requires
runAsNonRoot: true, which the current workloads don’t satisfy
Migration path:
- Apply Baseline enforcement to match current PSP behavior
- Apply Restricted in
warnmode to identify non-compliant Pods - Fix all workloads to be Restricted-compliant
- Switch enforcement to Restricted
kubectl label ns my-namespace \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/warn=restricted
Q4: A colleague says "We set PSA to Restricted, so our Pods automatically run as non-root now." Is this correct?
No, this is incorrect.
Pod Security Admission (PSA) performs validation only — it does not mutate Pods. This is a key difference from the old PodSecurityPolicy.
- PSA rejects Pods that don’t meet the Restricted level
- PSA does not inject
runAsNonRoot: trueor any other field into your Pod spec - If your manifest doesn’t already have
runAsNonRoot: true, the Pod will be rejected, not fixed
This means all manifests, Helm charts, and CI/CD pipelines must be updated to include the required security context fields before enabling Restricted enforcement.
The workflow is:
- Start with
warnmode to identify non-compliant manifests - Fix all manifests to include required security context settings
- Then enable
enforcemode
Q5: Which volume types are allowed under the Restricted PSS level? Why is hostPath blocked?
Allowed volume types under Restricted:
| Volume Type | Why Allowed |
|---|---|
configMap |
Read-only configuration data, controlled by RBAC |
csi |
CSI-managed volumes with access control |
downwardAPI |
Pod metadata — no host access |
emptyDir |
Ephemeral, Pod-scoped — no host access |
ephemeral |
Generic ephemeral volumes with PVC backend |
persistentVolumeClaim |
PVC-managed with access modes, no direct host access |
projected |
Combines configMap, secret, downwardAPI, serviceAccountToken |
secret |
Sensitive data, controlled by RBAC |
Why hostPath is blocked:
hostPath mounts a directory from the host node’s filesystem directly into the Pod. This is dangerous because:
- Container escape — A Pod can mount
/from the host and gain full read/write access to the node - Cross-Pod data access — Pods on the same node can share data through host directories, bypassing network policies
- Credential theft — Mounting
/etc/kubernetes/pki/exposes cluster CA keys and certificates - Persistence — Attackers can write backdoors to the host filesystem that survive Pod deletion
# DANGEROUS — blocked by Baseline and Restricted
volumes:
- name: host-root
hostPath:
path: / # Full host filesystem access!
| Previous | Up | Next |
|---|---|---|
| Pod Security | Pod Security | Pod Security Admission |