KubernetesLXII · Pod Security StandardsPod Security Standards
Restricted profile — minimum-allow for production
What you'll learn
- Identify every field required by `restricted`
- Write a manifest that passes `restricted`
- Use the namespace label to enforce `restricted` cluster-wide
- Recognise the failure modes (over-restriction, system workloads)
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
The restricted profile is the production target for
Kubernetes workloads. It is the strictest profile and
forbids every common privilege escalation path.
Workloads that pass restricted are hardened to the
operating-system level: they run as non-root, drop
every Linux capability, cannot escalate privilege, and
carry a constrained seccomp profile.
What restricted requires
| Field | Required | Rationale |
|---|---|---|
securityContext.runAsNonRoot: true | Required | Prevent root execution |
securityContext.runAsUser (non-zero) | Required if runAsNonRoot is not set | Same |
securityContext.allowPrivilegeEscalation: false | Required | Prevent setuid binaries from escalating |
securityContext.capabilities.drop: ["ALL"] | Required | Default-deny capabilities |
securityContext.capabilities.add | Limited (only NET_BIND_SERVICE) | Allow network binding for non-root |
securityContext.seccompProfile.type | RuntimeDefault or Localhost | Constrain syscalls |
| Volume types | Only configMap, csi, downwardAPI, emptyDir, ephemeral, persistentVolumeClaim, projected, secret | Keep the Pod off host storage |
hostNetwork, hostPID, hostIPC | Forbidden | Prevent host namespace sharing |
hostPath volumes | Forbidden | Prevent host filesystem access |
securityContext.privileged | Forbidden | Prevent privilege escalation |
A workload that meets every requirement is hardened to the OS level: the kernel’s capability set is constrained, the syscall surface is constrained, and the Pod cannot reach the host.
A restricted manifest
apiVersion: v1
kind: Pod
metadata:
name: api
namespace: prod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: myapp:v1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/myapp
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 2
periodSeconds: 5
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
Every field meets the restricted profile:
runAsNonRoot: true,runAsUser: 1000— runs as a non-root user.allowPrivilegeEscalation: false— setuid binaries cannot escalate.capabilities.drop: ["ALL"]— no capabilities; the workload can do nothing privileged.seccompProfile.type: RuntimeDefault— constrained to the runtime’s default seccomp profile.- Only
emptyDirvolumes — one of the eight typesrestrictedpermits. - No host namespaces, no hostPath, no privileged.
The manifest also sets readOnlyRootFilesystem: true
and mounts emptyDir at /tmp and /var/cache/myapp
so the application can still write. That is hardening
the team chose, not something restricted demanded;
the Pod would pass the profile without it.
flowchart LR
A[restricted Pod] --> B[runAsNonRoot, runAsUser not 0]
A --> C[Allowed volume types only]
A --> D[drop ALL capabilities]
A --> E[seccomp RuntimeDefault]
A --> F[allowPrivilegeEscalation: false]
A --> G[No hostNetwork/PID/IPC]
A --> H[No hostPath]
A --> I[No privileged]
The namespace label
apiVersion: v1
kind: Namespace
metadata:
name: prod
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
A Pod in prod that violates any field is rejected.
The audit log captures the violation; the warning
event surfaces it in kubectl get events.
Over-restriction and exceptions
Some workloads cannot meet restricted:
- System workloads.
kube-proxy, CNI agents, CSI drivers need host access. They run inkube-systemwithenforce: privileged. - Log shippers. Some log shippers need to read
/var/logon the host. They run in dedicated namespaces withenforce: baselineorprivileged. - Debug tools.
kubectl debug node/<node>needs host access. The debug Pod is indefault(or a dedicated debug namespace withenforce: privileged).
The discipline is to default to restricted and
document every exception.
Production patterns
restrictedcluster-wide for production namespaces. Every production namespace hasenforce: restricted.restrictedfor new namespaces. New namespaces are created withenforce: restrictedfrom the start.- Migration to
restricted. Existing namespaces usewarnorauditfirst, then switch toenforce. - Document every exception. A list of namespaces
that use
baselineorprivileged, with the reason for each, is reviewed quarterly.
Production failure modes
- A workload cannot meet
restrictedand the team changes the namespace label. The label is a control surface; the workload must be fixed, not the namespace. - The namespace is created without a PSS label.
The namespace is implicitly
privileged. The fix is to addenforce: restrictedat creation. runAsNonRoot: trueis set but the image requires root. The Pod fails to start withPermission denied. The fix is to fix the image (USER directive) or to mount the necessary directories as writable.readOnlyRootFilesystem: trueis set but the workload writes to the root fs. The Pod fails to start withRead-only file system. The fix is to mount anemptyDirat the write path.
Cross-course references
- The Linux course covers the kernel features
(capabilities, seccomp, namespaces) that
restrictedenforces. - The Observability course covers the audit log
entries for
restrictedviolations.
Quiz
Knowledge check · 4 questions
Q1. Which of the following is *required* by the `restricted` profile?
Q2. The `restricted` Pod Security Standard requires `readOnlyRootFilesystem: true`.
Q3. Your `prod` namespace has `enforce: restricted`. A workload sets `readOnlyRootFilesystem: true` but the application writes to `/var/log/myapp.log` and to `/tmp/myapp.sock`. The Pod fails to start with `Read-only file system`. How do you fix it?
The workload is a hardened Java application. It writes logs to `/var/log/myapp.log` and uses a Unix domain socket at `/tmp/myapp.sock`. Both paths are on the read-only root filesystem. The fix is to mount `emptyDir` at both paths.
Q4. Name four fields required by `restricted` and explain what each one prevents.
Passing score: 75%. Answers are checked in this browser.
Production discipline
The restricted profile is the production target.
Every production namespace enforces restricted;
exceptions (privileged, baseline) are explicit and
documented. A cluster whose namespaces are mostly
restricted has a workload security programme that
is auditable. The audit log records every violation;
the SIEM alerts on the violations. A workload that
cannot meet restricted is a finding — the team
either fixes the workload or documents the exception
with a deadline.