Static Pod Manifests: hostPath's Admission Bypass
A DaemonSet with a writable hostPath onto /etc/kubernetes/manifests on a control-plane node hands any compromised container a path to root-level pods that kubelet starts directly, skipping RBAC, PodSecurity admission and every webhook the API server would normally enforce.
Operational summary
At a glance
- Symptom
- A compromised container writes a static pod manifest into the mounted directory, and kubelet starts it as root without any API server admission check.
- Likely cause
- A DaemonSet mounts /etc/kubernetes/manifests as a writable hostPath volume and tolerates the control-plane node taint.
- Impact
- Attackers gain a root pod on the control-plane node, read the cluster CA key from /etc/kubernetes/pki, and mint arbitrary cluster-admin credentials.
- Verification signal
- Re-run the detection test and a controlled negative-path test.
- Safe correction
- Deny hostPath mounts under /etc/kubernetes and /var/lib/kubelet via Kyverno or Gatekeeper, and remove control-plane tolerations from cluster-wide DaemonSets.
- Rollback or recovery
- Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
The Trap
hostPath volumes pointing at the kubelet’s static pod manifest directory, typically /etc/kubernetes/manifests, mounted read-write into a container scheduled onto a control-plane node. Kubelet watches this directory directly on disk and starts anything placed there as a pod, independent of the kube-apiserver’s admission chain.
The Default State
kubeadm sets staticPodPath to /etc/kubernetes/manifests in the KubeletConfiguration by default, and this is where etcd, kube-apiserver, kube-scheduler and kube-controller-manager themselves run from. Node-level agents, backup tooling, and log shippers frequently ship Helm charts that mount /etc/kubernetes as a hostPath volume for config discovery or certificate rotation, without setting readOnly: true. These DaemonSets commonly add a toleration for node-role.kubernetes.io/control-plane:NoSchedule to guarantee full-fleet coverage, so the mount lands on control-plane nodes as well as workers, with no PodSecurity restriction on hostPath usage in namespaces that predate baseline enforcement.
The Blast Radius
A container compromised through a dependency CVE writes a YAML file into the mounted manifests directory describing a pod with hostPID: true, hostNetwork: true, and a hostPath mount of the root filesystem. Kubelet’s fileCheckFrequency, 20 seconds by default, picks it up and starts it as root on the host. No ServiceAccount token, RBAC binding, PodSecurity admission label, or Gatekeeper/Kyverno policy ever sees this pod, because static pods bypass the API server entirely; only a read-only mirror pod appears afterwards in the API for observability. From inside that pod the attacker reads /etc/kubernetes/pki, extracts the cluster CA key and kubelet client certificates, and issues themselves valid cluster-admin credentials, converting one DaemonSet compromise into full control-plane takeover.
The Lead Mechanic Fix
Write a Kyverno ClusterPolicy or OPA Gatekeeper constraint denying any hostPath volume whose path matches /etc/kubernetes* or /var/lib/kubelet/plugins_registry* across all namespaces, with no exemption list. Remove control-plane tolerations from cluster-wide DaemonSets unless a specific workload requires scheduling there; use a dedicated nodeSelector instead. Confirm the running staticPodPath with kubectl get –raw “/api/v1/nodes//proxy/configz” and lock the directory at the OS level with chmod 700 root:root plus chattr +i outside kubelet’s own write window. Add a Falco or auditd rule watching for create and write syscalls against the manifest path, alerting immediately rather than relying on Kubernetes audit logs, which never see this activity.
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Deny hostPath mounts under /etc/kubernetes and /var/lib/kubelet via Kyverno or Gatekeeper, and remove control-plane tolerations from cluster-wide DaemonSets.
Validate the vendor-specific syntax in official documentation before applying it.
Verify, roll back or escalate
Verify
Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.
Rollback
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.