Skip to main content

Enforce Pod Security Standards

Inspect 1.36

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#

  • kubectl access 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 only NET_BIND_SERVICE allowed back, and must use a seccomp (secure computing mode) profile set to RuntimeDefault.

Both baseline and restricted forbid appArmorProfile: Unconfined, so once a namespace is at baseline or above, a pod cannot opt out of the default .

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 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 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:

yaml
		securityContext:
  runAsNonRoot: true
  allowPrivilegeEscalation: false
  capabilities: {drop: ["ALL"]}
  seccompProfile: {type: RuntimeDefault}
	

See 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 : 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 for how it differs from a webhook.

Combine with other controls. Pair a PSS level with a default-deny NetworkPolicy (see ) and a ResourceQuota or LimitRange. Together they address the main risks in a shared namespace.