Enforce Pod Security Standards
The platform sets no default Pod Security level. Every namespace runs at privileged (no restrictions) until you label it, so roll out baseline or restricted audit-first, without breaking running workloads.
Prerequisites#
kubectlaccess to your workload cluster- A namespace with workloads deployed that you want to harden
Levels and modes#
Pod Security Standards (PSS) is a built-in Kubernetes feature that restricts what pods can do inside a namespace.
Levels go from least to most strict:
privileged: no restrictions. This is the default.baseline: blocks well-known dangerous settings such as privileged containers and host namespace access.restricted: hardened. Pods must run as non-root, containers must drop all Linux capabilities (fine-grained kernel privileges) with onlyNET_BIND_SERVICEallowed back, and must use a seccomp (secure computing mode) profile set toRuntimeDefault.
Both baseline and restricted forbid appArmorProfile: Unconfined, so once a namespace is at baseline or above, a pod cannot opt out of the default AppArmor profile .
Modes control what happens when a pod violates the level:
enforce: reject the pod.audit: allow the pod and record the violation in the API audit log.warn: allow the pod and return a warning to whoever submitted it.
You can set enforce, audit, and warn at once on the same namespace. Aim for restricted on your application namespaces. Use baseline when a workload genuinely needs a setting that restricted forbids.
Why baseline is the minimum#
The node hardening does not reach privileged pods. The runtime's default AppArmor profile confines non-privileged containers only; a privileged container has full host access on its node, so there is nothing left for a profile to protect. And every namespace starts at privileged, so in an unlabeled namespace anyone who can create a pod can set privileged: true and bypass that hardening. Labeling the namespace at baseline or above rejects privileged containers and host namespace access at admission. That label is what makes the node hardening bind to every workload in the namespace.
A privileged pod is still limited. It gets host-level access on the node it runs on, not root on every node in the cluster. On that node, the sealed OS blocks changes to the base system and tamper detection flags changes to the writable area, which limits what an attacker can quietly persist.
Roll out a stricter level#
Audit first, enforce later #
Label the namespace to warn and audit at your target level, but do not enforce yet. This shows you what would be rejected without blocking anything currently running:
$ kubectl label namespace team-a \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
Redeploy your workloads, or trigger pod restarts, and read the warnings. Fix the workloads that violate the level. The most common fix is adding a securityContext block to the pod spec:
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities: {drop: ["ALL"]}
seccompProfile: {type: RuntimeDefault}
See Set security contexts for what each field does and how to apply it.
Enforce #
Once warnings are clean, add enforce:
$ kubectl label namespace team-a \
pod-security.kubernetes.io/enforce=restricted \
--overwrite
From this point, a pod that violates restricted is rejected at admission before it starts.
Verify #
# Show the labels on the namespace:
$ kubectl get namespace team-a -o jsonpath='{.metadata.labels}' | jq .
# A privileged pod should now be rejected:
$ kubectl -n team-a run bad --image=busybox --privileged --restart=Never -- sleep 1
# Expected: Error from server (Forbidden): ...
What to know#
Platform namespaces must stay at privileged. Platform components need host access to work, so those namespaces are the residual you accept. What bounds it is RBAC : who can create pods in a platform namespace. Apply PSS only to your own namespaces.
PSS is coarse and namespace-scoped. For rules that must apply across namespaces (for example, "images must come from this registry" or "every pod needs a cost label"), write a native ValidatingAdmissionPolicy: a Kubernetes policy that runs inside the API server and can reject a resource outright. See Control admission for how it differs from a webhook.
Combine with other controls. Pair a PSS level with a default-deny NetworkPolicy (see Segment with network policies ) and a ResourceQuota or LimitRange. Together they address the main risks in a shared namespace.
Related#
Use AppArmor
Every non-privileged container on Syself Linux runs under an enforcing AppArmor profile by default. What the profile blocks, how to change it per pod, where SELinux stands, and how it fits the full workload hardening stack.
Control admission
How the platform's admission protection prevents a broken webhook from locking your cluster, what it restricts tenant webhooks and Cilium network policies from doing, and how to write hard-block rules safely.