Skip to main content
KBY Technologies Logo
cd ../config-traps
risk/register/kube-apiserver-flag-drift-disables-podsecurity-silently.html
Kubernetes Pod Securityhigh severityKubernetes

kube-apiserver Flag Drift Disables PodSecurity Silently

Overview

A leftover -disable-admission-plugins=PodSecurity flag on a kubeadm-managed kube-apiserver removes enforcement entirely, so namespace labels such as enforce=restricted stop meaning anything. Privileged, hostPID and hostNetwork pods land in production namespaces with no denial event, no audit warning, and no Gatekeeper trigger to catch the gap.

At a glance

Unsafe setting
--disable-admission-plugins=PodSecurity left active in kube-apiserver flags after an incident fix.
Failure trigger
A pod declaring privileged: true, hostPID: true, or hostNetwork: true is scheduled into a namespace labelled pod-security.kubernetes.io/enforce=restricted.
Blast radius
Privileged pods bypass all namespace-level PodSecurity enforcement with no denial event or audit trail generated.
Recommended control
Pin PodSecurity explicitly via --admission-control-config-file and alert on missing apiserver_admission_controller_admission_duration_seconds_count metrics for that plugin.

Fix commands and configuration

ps -ef | grep kube-apiserver
--disable-admission-plugins
--admission-control-config-file

The Trap

Admission plugin drift on self-managed kube-apiserver instances: the PodSecurity admission controller has been removed from the API server’s active plugin chain via --disable-admission-plugins=PodSecurity. Unlike a validating webhook set to failurePolicy: Ignore, a disabled built-in plugin generates no admission review whatsoever, not even a dry-run warning annotation. Namespace labels remain visible in kubectl get ns --show-labels, but nothing on the API server is reading them.

The Default State

During an incident, an operator appends --disable-admission-plugins=PodSecurity to /etc/kubernetes/manifests/kube-apiserver.yaml on a kubeadm static pod, to unblock a CI pipeline throwing “forbidden: violates PodSecurity” against a namespace mislabelled during a migration. The label issue gets fixed; the flag does not get reverted. Because the kubeadm-config ConfigMap is not automatically reconciled against live control-plane manifests, the flag survives node joins from the same kubeadm configuration and persists across kubeadm upgrade apply, which preserves existing extraArgs by design.

The Blast Radius

With PodSecurity absent from the admission chain, labels like pod-security.kubernetes.io/enforce=restricted become decorative metadata. Pods declaring privileged: true, hostPID: true, or hostNetwork: true are admitted without a single denial event. Kyverno or Gatekeeper policies that assume baseline coverage from PodSecurity and only add supplementary mutation rules won’t catch it either. A compromised sidecar pulled into a “restricted” production namespace gets full host device access, and forensic review of audit logs shows nothing unusual, because there was never a denial to log. The absence of a control produces no signal of its own absence.

The Lead Mechanic Fix

Run ps -ef | grep kube-apiserver on every control-plane node, or inspect the live static pod spec, and grep the command line for --disable-admission-plugins. Strip PodSecurity from that list and let the kubelet reconcile the static pod. Then pin enforcement explicitly via --admission-control-config-file, configuring PodSecurity with defaults: enforce: restricted and an exemptions.namespaces list limited strictly to kube-system. Add a Prometheus alert on the disappearance of the apiserver_admission_controller_admission_duration_seconds_count{name="PodSecurity"} series, and gate every kubeadm upgrade apply behind a diff of the version-controlled ClusterConfiguration against live manifest flags.