Skip to main content
6 min read·1,078 words

Running Kubernetes Node Components Without Root Privileges

By default, all Kubernetes node components — the kubelet, the container runtime (such as containerd or CRI-O), OCI runtimes (such as runc), CNI plugins, and kube-proxy — run as root on the host. When a container escape vulnerability exists in any of these components, an attacker who exploits it gains full root privileges on the node, with access to every pod's filesystem and every mounted secret on that host.

The KubeletInUserNamespace feature gate addresses this by running all node components inside a Linux user namespace, so the processes that would otherwise be host root are mapped to an unprivileged host user instead. The feature graduated to Beta (enabled by default) in Kubernetes v1.37.


What KubeletInUserNamespace Does

When the KubeletInUserNamespace feature gate is active, all of the node components — kubelet, CRI and OCI runtimes, CNI plugins, and kube-proxy — run as a non-root user on the host, inside a Linux user namespace.

Before (default without rootless mode): A container escape in the container runtime or kubelet grants the attacker root privileges on the host node. The attacker can read secrets mounted on the node, modify host files, and pivot to other pods.

After (rootless mode enabled): A container escape in the same runtime or kubelet lands the attacker as an unprivileged host user. The attacker cannot modify host-level files or escalate privileges to root on the host.

This does not eliminate container escape vulnerabilities — it reduces the blast radius of vulnerabilities in the node components themselves.


Distinguishing KubeletInUserNamespace from Pod User Namespaces

These are two separate features that address different layers of isolation.

FeatureWhat runs in a user namespaceFeature gateGA status
hostUsers: false (User Namespace Pods)The pod's workload processesUserNamespacesSupportStable (v1.36)
KubeletInUserNamespace (Rootless Node)The kubelet, CRI runtime, CNI plugins, kube-proxyKubeletInUserNamespaceBeta (v1.37)

Setting hostUsers: false on a pod puts the pod's containers into a user namespace, but still runs all node components as root on the host. KubeletInUserNamespace does the opposite — it puts the node components into a user namespace while the setting of hostUsers on individual pods remains independent.

The two features are complementary. Using both provides isolation at both the node-component layer and the workload layer.

For details on hostUsers: false, see User Namespaces in Kubernetes.


Requirements

Before enabling rootless node operation, confirm the following prerequisites are met:

  1. Kubernetes v1.22 or later. The KubeletInUserNamespace feature gate was introduced in v1.22 (Alpha) and graduated to Beta (default: true) in v1.37.
  2. Cgroup v2. Cgroup v1 is not supported. Verify with stat -fc %T /sys/fs/cgroup/ — the output must be cgroup2fs.
  3. systemd with user session support. Required for the delegated cgroup tree that allows unprivileged processes to manage their own cgroup hierarchy.
  4. Entries in /etc/subuid and /etc/subgid for the unprivileged user that will run the node components. These files define the UID and GID ranges that the user namespace maps to host IDs.

Managed Kubernetes services (EKS, GKE, AKS) do not expose these node-level configuration options. Rootless node operation is currently applicable to self-managed clusters and selected Kubernetes distributions that explicitly support it (see Deployment Methods below).


Feature Gate and Version History

Kubernetes version rangeStageDefault
v1.22 – v1.36Alphafalse
v1.37+Betatrue

Kubernetes v1.37 and later (Beta, default on): No explicit feature gate configuration is required. The kubelet and API server already have KubeletInUserNamespace enabled. The node still requires the prerequisites above (Cgroup v2, systemd user session, subuid/subgid entries) — enabling the feature gate without meeting those prerequisites will result in the node failing to start.

Kubernetes v1.22 – v1.36 (Alpha, default off): Enable the feature gate explicitly on the kubelet by adding --feature-gates=KubeletInUserNamespace=true to the kubelet's startup flags, or by adding it to the kubelet's configuration file:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
KubeletInUserNamespace: true

Deployment Methods

The KubeletInUserNamespace feature gate works in combination with a user namespace for the host process. There are four established ways to set this up:

1. Inside Rootless Docker, Podman, or nerdctl

Tools like kind and minikube can create cluster nodes as rootless Docker or Podman containers. Node components inside those containers run inside the rootless daemon's user namespace, which is itself unprivileged on the host. The KubeletInUserNamespace gate must be enabled on the in-container kubelet.

2. Inside Kubernetes (User Namespace Pods — Kubernetes-in-Kubernetes)

Usernetes supports a Kubernetes-in-Kubernetes topology where inner cluster nodes run as pods with hostUsers: false. The node components in the inner cluster run inside the pod's user namespace and are therefore unprivileged on the outer host.

3. Inside Unprivileged Containers via Sysbox

Sysbox is a container runtime that enables system containers — containers that run full system software stacks, including Kubernetes nodes, without requiring root. Sysbox does not require Cgroup v2 or the KubeletInUserNamespace feature gate and uses its own isolation mechanism. It is an alternative deployment path for rootless nodes, not an additional layer on top of the feature gate.

4. Directly on Host

K3s includes an experimental rootless mode that runs all K3s components (including its embedded containerd and flannel CNI) as unprivileged processes on the host. This path uses unshare(1) or RootlessKit to create the user namespace before starting the node components.


Verification

After deploying with rootless mode enabled, confirm that the kubelet process is running as a non-root user on the host:

ps aux | grep kubelet | grep -v grep

The user column for the kubelet process should show the unprivileged user, not root. To inspect the UID mapping from within the user namespace:

cat /proc/$(pgrep -x kubelet)/status | grep -E '^Uid:|^Gid:'

On a correctly configured rootless node, the Uid line shows the unprivileged host UID (a value greater than 0), not 0 0 0 0.

For Kubernetes-level verification, the node should still register and become Ready normally:

kubectl get nodes

Pod scheduling, service account token projection, and network policies continue to function identically to a standard (rooted) node. The rootless configuration is transparent to workloads.


What This Feature Does Not Cover

  • Pods on the node are not automatically placed into user namespaces. The rootless node protects against escapes through the node components. Escapes through container runtimes that land inside the node's user namespace (rather than the host root namespace) are still constrained by the node's user namespace, but pod-level user namespace isolation still requires hostUsers: false on each pod spec.
  • Not all Kubernetes features have been validated in rootless mode. Some advanced features that rely on direct host interaction (certain CSI drivers, eBPF-based CNI plugins, device plugins) may require additional configuration or may not be supported.
  • The /etc/subuid and /etc/subgid UID range allocation is not managed by Kubernetes. Cluster administrators are responsible for provisioning adequate UID/GID ranges on each node. An insufficient range will cause the node to fail to start.


References

This article is based on information from the following official sources:

  1. Running Kubernetes Node Components as a Non-root User - Kubernetes Documentation
  2. Feature Gates — KubeletInUserNamespace - Kubernetes Reference Documentation