Fine-Grained Kubelet API Authorization Graduates to GA
On behalf of Kubernetes SIG Auth and SIG Node, we are pleased to announce the
graduation of fine-grained kubelet API authorization to General Availability
(GA) in Kubernetes v1.36!
The KubeletFineGrainedAuthz feature gate was introduced as an opt-in alpha
feature in Kubernetes v1.32, then graduated to beta (enabled by default) in
v1.33. Now, the feature is generally available and the feature gate is locked
to enabled. This feature enables more precise, least-privilege access control
over the kubelet's HTTPS API, replacing the need to grant the overly broad
nodes/proxy permission for common monitoring and observability use cases.
Motivation: the nodes/proxy problem
The kubelet exposes an HTTPS endpoint with several APIs that give access to data
of varying sensitivity, including pod listings, node metrics, container logs,
and, critically, the ability to execute commands inside running containers.
Prior to this feature, kubelet authorization used a coarse-grained model. When
webhook authorization was enabled, almost all kubelet API paths were mapped to a
single nodes/proxy subresource. This meant that any workload needing to read
metrics or health status from the kubelet required nodes/proxy permission,
the same permission that also grants the ability to execute arbitrary commands
in any container running on the node.
What's wrong with that?
Granting nodes/proxy to monitoring agents, log collectors, or health-checking
tools violates the principle of least privilege. If any of those workloads were
compromised, an attacker would gain the ability to run commands in every
container on the node. The nodes/proxy permission is effectively a node-level
superuser capability, and granting it broadly dramatically increases the blast
radius of a security incident.
This problem has been well understood in the community for years (see kubernetes/kubernetes#83465), and was the driving motivation behind this enhancement KEP-2862.
The nodes/proxy GET WebSocket RCE risk
The situation is more severe than it might appear at first glance. Security
researchers demonstrated in early 2026
that nodes/proxy GET alone, which is the minimal read-only permission routinely
granted to monitoring tools, can be abused to execute commands in any pod on
reachable nodes.
The root cause is a mismatch between how WebSocket connections work and how the
kubelet maps HTTP methods to RBAC verbs. The
WebSocket protocol (RFC 6455)
requires an HTTP GET request for the initial connection handshake. The kubelet
maps this GET to the RBAC get verb and authorizes the request without
performing a secondary check to confirm that CREATE permission is also present
for the write operation that follows. Using a WebSocket client like websocat,
an attacker can reach the kubelet's /exec endpoint directly on port 10250 and
execute arbitrary commands:
websocat --insecure \
--header "Authorization: Bearer $TOKEN" \
--protocol v4.channel.k8s.io \
"wss://$NODE_IP:10250/exec/default/nginx/nginx?output=1&error=1&command=id"
uid=0(root) gid=0(root) groups=0(root)
Fine-grained kubelet authorization: how it works
With KubeletFineGrainedAuthz, the kubelet now performs an additional, more
specific authorization check before falling back to the nodes/proxy
subresource. Several commonly used kubelet API paths are mapped to their own
dedicated subresources:
kubelet API |
Resource | Subresource |
|---|---|---|
/stats/* |
nodes | stats |
/metrics/* |
nodes | metrics |
/logs/* |
nodes | log |
/pods |
nodes | pods, proxy |
/runningPods/ |
nodes | pods, proxy |
/healthz |
nodes | healthz, proxy |
/configz |
nodes | configz, proxy |
/spec/* |
nodes | spec |
/checkpoint/* |
nodes | checkpoint |
| all others | nodes | proxy |
For the endpoints that now have fine-grained subresources (/pods,
/runningPods/, /healthz, /configz), the kubelet first sends a
SubjectAccessReview for the specific subresource. If that check succeeds, the
request is authorized. If it fails, the kubelet retries with the coarse-grained
nodes/proxy subresource for backward compatibility.
This dual-check approach ensures a smooth migration path. Existing workloads
with nodes/proxy permissions continue to work, while new deployments can adopt
least-privilege access from day one.
What this means in practice
Consider a Prometheus node exporter or a monitoring DaemonSet that needs to
scrape /metrics from the kubelet. Previously, you would need an RBAC
ClusterRole like this:
# Old approach: overly broad
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-agent
rules:
- apiGroups: [""]
resources: ["nodes/proxy"]
verbs: ["get"]
This grants the monitoring agent far more access than it needs. With fine-grained authorization, you can now scope the permissions precisely:
# New approach: least privilege
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-agent
rules:
- apiGroups: [""]
resources: ["nodes/metrics", "nodes/stats"]
verbs: ["get"]
The monitoring agent can now read metrics and stats from the kubelet without
ever being able to execute commands in containers.
Updated system:kubelet-api-admin ClusterRole
When RBAC authorization is enabled, the built-in system:kubelet-api-admin
ClusterRole is automatically updated to include permissions for all the new
fine-grained subresources. This ensures that cluster administrators who already
use this role, including the API server's kubelet client, continue to have
full access without any manual configuration changes.
The role now includes permissions for:
nodes/proxynodes/statsnodes/metricsnodes/lognodes/specnodes/checkpointnodes/configznodes/healthznodes/pods
Upgrade considerations
Because the kubelet performs a dual authorization check (fine-grained first,
then falling back to nodes/proxy), upgrading to v1.36 should be seamless for
most clusters:
- Existing workloads with
nodes/proxypermissions continue to work without changes. The fallback tonodes/proxyensures backward compatibility. - The API server always has
nodes/proxypermissions viasystem:kubelet-api-admin, sokube-apiserver-to-kubeletcommunication is unaffected regardless of feature gate state. - Mixed-version clusters are handled gracefully. If a
kubeletsupports fine-grained authorization but the API server does not (or vice versa),nodes/proxypermissions serve as the fallback.
Verifying the feature is enabled
You can confirm that the feature is active on a given node by checking the
kubelet metrics endpoint. Since the metrics endpoint on port 10250 requires
authorization, you'll first need to create appropriate RBAC bindings for the pod
or ServiceAccount making the request.
Step 1: Create a ServiceAccount and ClusterRole
apiVersion: v1
kind: ServiceAccount
metadata:
name: kubelet-metrics-checker
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kubelet-metrics-reader
rules:
- apiGroups: [""]
resources: ["nodes/metrics"]
verbs: ["get"]
Step 2: Bind the ClusterRole to the ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubelet-metrics-checker
subjects:
- kind: ServiceAccount
name: kubelet-metrics-checker
namespace: default
roleRef:
kind: ClusterRole
name: kubelet-metrics-reader
apiGroup: rbac.authorization.k8s.io
Apply both manifests:
kubectl apply -f serviceaccount.yaml
kubectl apply -f clusterrole.yaml
kubectl apply -f clusterrolebinding.yaml
Step 3: Run a pod with the ServiceAccount and check the feature flag
kubectl run kubelet-check \
--image=curlimages/curl \
--serviceaccount=kubelet-metrics-checker \
--restart=Never \
--rm -it \
-- sh
Then from within the pod, retrieve the node IP and query the metrics endpoint:
# Get the token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# Query the kubelet metrics and filter for the feature gate
curl -sk \
--header "Authorization: Bearer $TOKEN" \
https://$NODE_IP:10250/metrics \
| grep kubernetes_feature_enabled \
| grep KubeletFineGrainedAuthz
If the feature is enabled, you should see output like:
kubernetes_feature_enabled{name="KubeletFineGrainedAuthz",stage="GA"} 1
Note: Replace
$NODE_IPwith the IP address of the node you want to check. You can retrieve node IPs withkubectl get nodes -o wide.
The journey from alpha to GA
| Release | Stage | Details |
|---|---|---|
| v1.32 | Alpha | Feature gate KubeletFineGrainedAuthz introduced, disabled by default |
| v1.33 | Beta | Enabled by default; fine-grained checks for /pods, /runningPods/, /healthz, /configz |
| v1.36 | GA | Feature gate locked to enabled; fine-grained kubelet authorization is always active |
What's next?
With fine-grained kubelet authorization now GA, the Kubernetes community can
begin recommending and eventually enforcing the use of specific subresources
instead of nodes/proxy for monitoring and observability workloads. The urgency
of this migration is underscored by
research showing that nodes/proxy GET can be abused for unlogged remote code execution via the WebSocket protocol. This risk is present in the default RBAC
configurations of dozens of widely deployed Helm charts. Over time, we expect:
- Ecosystem adoption: Monitoring tools like Prometheus, Datadog agents, and
other
DaemonSetscan update their default RBAC configurations to usenodes/metrics,nodes/stats, andnodes/podsinstead ofnodes/proxy. This directly eliminates the WebSocket RCE attack surface for those workloads. - Policy enforcement: Admission controllers and policy engines can flag or
reject RBAC bindings that grant
nodes/proxywhen fine-grained alternatives exist, helping organizations adopt least-privilege access at scale. - Deprecation path: As adoption grows,
nodes/proxymay eventually be deprecated for monitoring use cases, further reducing the attack surface of Kubernetes clusters.
Getting involved
This enhancement was driven by SIG Auth and SIG Node. If you are interested in contributing to the security and authorization features of Kubernetes, please join us:
- SIG Auth
- SIG Node
- Slack:
#sig-authand#sig-node - KEP-2862: Fine-Grained Kubelet API Authorization
We look forward to hearing your feedback and experiences with this feature!