Skip to main content
cd ../config-traps
risk/register/podsecurity-admissions-silent-privileged-default.html
Kubernetes Pod Securityhigh severityKubernetes

PodSecurity Admission's Silent Privileged Default

Overview

When PodSecurityPolicy is removed but namespaces aren't labelled for Pod Security Admission, Kubernetes falls back to the privileged profile cluster-wide. New namespaces created by CI pipelines or Helm inherit no restriction at all, and admission logs show clean passes because privileged pods are permitted by design.

At a glance

Unsafe setting
Namespaces left without pod-security.kubernetes.io labels after PodSecurityPolicy removal.
Failure trigger
A namespace created without an enforce label admits a pod requesting privileged mode, hostPID, or a hostPath mount.
Blast radius
Unlabelled namespaces default to the privileged profile, permitting container escape and lateral movement with no admission denial logged.
Recommended control
Set a cluster-wide restricted default via AdmissionConfiguration and enforce label presence on namespace creation with a validating policy.

Fix commands and configuration

--admission-control-config-file
PodSecurityConfiguration
defaults.enforce: restricted

The Trap

Decommissioning PodSecurityPolicy at the 1.25 removal deadline and adopting the built-in PodSecurity admission (PSA) controller without labelling every namespace explicitly. Teams treat PSA migration as complete once a handful of audited namespaces carry the pod-security.kubernetes.io/enforce label, leaving everything else to whatever the cluster falls back to.

The Default State

Any namespace without pod-security.kubernetes.io/enforce, audit, or warn labels is evaluated against the compiled-in default, which is privileged for all three modes unless overridden via an AdmissionConfiguration file. PSP enforced a deny-by-default posture; PSA enforces nothing unless a label says otherwise. Migration scripts typically label production namespaces flagged during a PSP audit and stop there. CI-generated ephemeral namespaces, new team namespaces, and anything created after the migration window inherit no restriction whatsoever.

The Blast Radius

A branch-deploy pipeline creates a namespace via Helm without the enforce label. A pod manifest requests privileged: true, hostPID: true, and a hostPath mount of /var/lib/kubelet/pods. PSA admits it silently — the privileged profile permits all of this, so no audit annotation, no warning event, nothing for SIEM rules keyed on admission denials to catch. From that pod the attacker reads service account tokens mounted for every other pod scheduled on the node, escapes via the host mount namespace, and pivots to any node sharing the same worker pool image, since NetworkPolicy objects were never reinstated either. Security teams report PSA as “deployed” based on label coverage in the handful of namespaces they checked, while the actual enforcement surface across the cluster is unchanged from having no policy engine at all.

The Lead Mechanic Fix

Set a cluster-wide default via the kube-apiserver --admission-control-config-file pointing to a PodSecurityConfiguration with defaults.enforce: restricted and explicit exemptions.namespaces for kube-system, kube-node-lease, and kube-public only. Backfill existing namespaces with kubectl label ns --all pod-security.kubernetes.io/enforce=baseline pod-security.kubernetes.io/enforce-version=v1.29 --overwrite, pinning the version to stop silent policy drift on cluster upgrades. Then block the gap permanently with a Kyverno ClusterPolicy or ValidatingAdmissionPolicy that rejects namespace creation lacking the enforce label, so no namespace can exist in the unlabelled privileged state again.