Kubernetes 1.36 verscheen op 22 april en daarmee bereikte fine-grained kubelet API-authorisatie de GA-status. De release notes beschrijven het als "preciezere, least-privilege access control over de HTTPS-API van de kubelet". Dat verkoopt het te zuinig. De afgelopen vijf jaar kreeg elke Prometheus exporter, elke Datadog Agent en elk observability platform dat metrics of pod-status uit een kubelet wilde lezen nodes/proxy-permissie toebedeeld. Dat ene RBAC-werkwoord is ook precies het werkwoord waarmee je een willekeurig commando in elke pod op de node kunt uitvoeren. De helft van de cluster-operators in productie heeft het nooit doorgehad. De andere helft zag het wel, maar haalde de schouders op, want het alternatief was zelf admission policy bouwen.
KEP-2862 zet dat patroon nu eindelijk op de schop. In dit artikel: wat er precies verandert, waarom nodes/proxy überhaupt een privilege-escalation primitive werd, en wat je moet doen met de bindings die nu al in je cluster zitten.
TL;DR
nodes/proxyis het RBAC-werkwoord dat elke monitoring tool aanvroeg. Het gaf volledige toegang tot de kubelet API, inclusiefexecin elke pod op de node, en omzeilde Kubernetes audit logging en admission control.- Publiek onderzoek in januari 2026 toonde aan dat alleen al
nodes/proxy GETgenoeg is voor een remote-code-execution-keten. Het Kubernetes Security Team bevestigde dat dit "working as intended" is en heeft geen CVE uitgegeven. - KEP-2862 splitst de kubelet-authorisatie in acht fine-grained subresources:
nodes/stats,nodes/metrics,nodes/log,nodes/pods,nodes/healthz,nodes/configz,nodes/specennodes/checkpoint. - De feature gate staat in 1.36 vergrendeld aan. Workloads die nog op
nodes/proxystaan blijven gewoon werken via fallback. De migratielast ligt bij jou. - Dit haalt de vraag naar
nodes/proxyweg uit monitoring stacks. Het werkwoord zelf blijft bestaan. Tooling dieexecofrunnodig heeft, gaat er nog steeds doorheen.
Inhoudsopgave
- Wat nodes/proxy daadwerkelijk geeft
- De GET die al een RCE was
- Wat KEP-2862 daadwerkelijk verandert
- Wegmigreren van nodes/proxy
- Wat dit niet oplost
- Belangrijkste punten
Wat nodes/proxy daadwerkelijk geeft
Elke kubelet draait een HTTPS-API op poort 10250. Die API levert metrics, logs, pod-status en de runtime control endpoints (exec, run, attach, port-forward) die kubectl exec onder de motorkap gebruikt. Tot 1.36 verzamelde de authorisatiemap van de API server vrijwel alles achter die API in één RBAC-subresource: nodes/proxy.
Een workload nodes/proxy geven, betekent toegang geven tot de hele kubelet API. Dat omvat:
- Metrics lezen uit
/stats/*en/metrics/*. Daarvoor wilden monitoring tools het. - Container logs lezen uit
/logs/*. Daarvoor wilden log shippers het. - Een willekeurig commando draaien in elke container op de node via
/exec/*. En dat had niemand zomaar willen toegeven.
De Kubernetes-documentatie waarschuwt hier al jaren voor. Onder RBAC good practices staat dat gebruikers met nodes/proxy "have rights to the Kubelet API, which allows for command execution on every pod on the node(s) to which they have rights. This access bypasses audit logging and admission control." Lees dat nog eens. Een workload die aan nodes/proxy is gekoppeld, verschijnt niet als exec-event in je audit log, en je ValidatingAdmissionPolicy wordt niet eens geraadpleegd voordat het commando draait.
Met andere woorden: nodes/proxy aan een Helm chart geven kwam neer op die chart de reikwijdte van een privileged container geven over elke node die hij kon zien, met de audit pipeline ondertussen uitgezet.
De GET die al een RCE was
Het operationele beeld van de afgelopen vijf jaar was dat nodes/proxy met alleen get feitelijk read-only was. Veel security teams zetten create, update en delete achter admission policy en lieten get/list/watch als laag risico erdoorheen. Dat beeld stortte in januari 2026 in.
Graham Helton documenteerde dat de kubelet de WebSocket-handshake autoriseert op de initiële GET, niet op de daadwerkelijke exec-operatie die volgt. Zodra de connectie is geüpgraded, accepteert de server vrolijk een command= parameter en draait die binnen de gekozen container. De exploit is één request: wss://$NODE_IP:10250/exec/$NS/$POD/$CONTAINER?command=.... Sweet Security publiceerde dezelfde bevinding onafhankelijk, met een keten die eindigde in toegang tot kube-apiserver en etcd. Nirmata volgde de melding via Kubernetes' HackerOne-programma. Het antwoord van het Kubernetes Security Team was dat dit "working as intended" is. Er is geen CVE uitgegeven.
De ontvangst in de security-community was harder. DataDog's Stratus Red Team-catalogus lijst k8s.privilege-escalation.nodes-proxy als een standaard adversary technique onder MITRE Privilege Escalation. Aqua Security demonstreert in een walk-through de volledige keten van een ServiceAccount met nodes/proxy naar root in een willekeurige pod. En Stream Security auditte publieke Helm charts en vond nodes/proxy in 69 breed uitgerolde charts, waaronder Prometheus, Grafana Promtail, de Datadog Agent, Elastic Agent, New Relic Infrastructure, de OpenTelemetry Operator en de Trivy Operator.
De zin waar de security industrie uiteindelijk op uitkwam was de juiste: "Many security teams consider GET permissions read-only and safe to grant. This assumption is wrong."
Wat KEP-2862 daadwerkelijk verandert
KEP-2862 heeft een rustig pad afgelegd. Alpha in 1.32, beta in 1.33, GA in 1.36. De graduatie zet een KubeletFineGrainedAuthz feature gate vast die nu zowel in de kubelet als in de kube-apiserver verplicht aanstaat.
De inhoudelijke wijziging zit in de authorisatiemap. Acht nieuwe subresources zitten nu tussen RBAC en de kubelet API:
| Kubelet pad | Nieuwe subresource | Oude catch-all |
|---|---|---|
/stats/* |
nodes/stats |
nodes/proxy |
/metrics/* |
nodes/metrics |
nodes/proxy |
/logs/* |
nodes/log |
nodes/proxy |
/pods, /runningPods/ |
nodes/pods |
nodes/proxy |
/healthz |
nodes/healthz |
nodes/proxy |
/configz |
nodes/configz |
nodes/proxy |
/spec/* |
nodes/spec |
nodes/proxy |
/checkpoint/* |
nodes/checkpoint |
nodes/proxy |
Een monitoring agent die voorheen nodes/proxy nodig had voor /metrics heeft nu genoeg aan nodes/metrics. Een log shipper heeft genoeg aan nodes/log. De werkwoorden die nodes/proxy overbodig maken voor elke praktische monitoring- of observability-stack zijn precies de werkwoorden die die workloads toch al gebruikten.
Wat geen eigen subresource heeft gekregen: /exec, /run en /attach. Die blijven met opzet gegated door nodes/proxy. De strategie van de KEP is vraag verminderen, niet verwijderen. Workloads die echt exec in pods nodig hebben (debugging tooling, break-glass agents) vragen nog steeds om nodes/proxy. Het idee is dat dat een kleine, geauditteerde set is, en niet de hele monitoring vloot.
Backward compatibility gaat automatisch. Wanneer de kubelet een request krijgt, doet hij eerst een SubjectAccessReview tegen de nieuwe fine-grained subresource. Als die wordt geweigerd, valt hij terug op nodes/proxy. Bestaande ClusterRoles blijven gewoon werken. De migratie doe je in je eigen tempo.
Wegmigreren van nodes/proxy
De 1.36-upgrade zelf raakt je bindings niet aan. De migratie moet je zelf trekken.
Begin met inventariseren. De snelste manier om workloads in kaart te brengen die nu nog op nodes/proxy leunen:
kubectl get clusterroles -o json \
| jq '[.items[]
| select(.rules[]?
| .resources[]? == "nodes/proxy")
| .metadata.name]'
Vergelijk elke ClusterRole met de bindings die ernaar verwijzen. Tools als kubectl-who-can en rbac-lookup maken er een one-liner van: kubectl-who-can get nodes/proxy.
Daarna vervangen. Een typische Prometheus-stijl ClusterRole zoals deze:
rules:
- apiGroups: [""]
resources: ["nodes/proxy"]
verbs: ["get", "list", "watch"]
wordt:
rules:
- apiGroups: [""]
resources: ["nodes/metrics", "nodes/stats"]
verbs: ["get", "list", "watch"]
De exacte subresources hangen af van welke kubelet-endpoints de workload echt aanspreekt. Voor een Datadog Agent of node-exporter-equivalent dekken nodes/metrics en nodes/stats de metrics-scrape; voeg nodes/log toe als je ook logs verstuurt. Vendor charts lopen in verschillend tempo bij: DataDog opende eind januari helm-charts issue #2338 met een tracking PR, en vergelijkbaar werk loopt door het hele observability-ecosysteem heen. Tot je vendor de defaults aanpast, kun je zelf de RBAC-values van de chart overschrijven.
Om afdrijven tegen te gaan, leg je het vast op de cluster-grens. Met MutatingAdmissionPolicy die in 1.36 GA is geworden, kun je een ValidatingAdmissionPolicy schrijven die elke nieuwe ClusterRoleBinding flagt die nodes/proxy toekent aan een ServiceAccount die niet op de allowlist staat. Hetzelfde patroon werkt prima via Kyverno of OPA Gatekeeper als die toch al in je stack zitten. Het bredere admission-policy verhaal beschreef ik in mijn Kyverno CNCF-graduatie post.
Wat dit niet oplost
Dan de eerlijke sectie, want dit is echte vooruitgang en geen complete fix.
nodes/proxy bestaat in 1.36 nog steeds als werkwoord en is niet deprecated. Het is bovendien nog steeds nodig voor kubectl exec via de API server proxy, wat betekent dat ingebouwde rollen als system:kubelet-api-admin het blijven dragen. Verwijderen staat ook niet op de roadmap, want dat zou legitieme cluster-administratie breken.
De migratielast ligt bij jou, niet bij de upgrade. Een 1.36 cluster met ongewijzigde ClusterRoles staat er precies even open bij als een 1.35 cluster. De bescherming komt pas zodra die bindings zijn herschreven. In een multi-tenant cluster is dat werk groter; over wie er in zo'n cluster eigenaar is van wat schreef ik in mijn post over multi-tenant Kubernetes governance.
En het onderliggende probleem, dat autoriseren op de GET-handshake van een WebSocket-upgrade de aanvrager de rest van de connectie meegeeft, is een klasse bug die het Kubernetes Security Team expliciet niet als CVE wil afdoen. De fine-grained subresources zorgen dat minder workloads die handshake nodig hebben. Wat de handshake zelf autoriseert, blijft gelijk.
Het bijbehorende stuk bij dit verhaal is user namespaces die in dezelfde release stable zijn geworden. Beide zitten in 1.36. Beide verkleinen de blast radius voor dezelfde klasse incident: een gecompromitteerde workload die voorheen gewoon node-niveau erfde. Samen zijn dit de twee operationeel bruikbaarste security-stappen die het project in twee releases heeft geleverd. Allebei zijn ze opt-in.
Belangrijkste punten
nodes/proxywas een privilege-escalation primitive in elk cluster dat je ooit hebt geauditteerd. Publiek onderzoek uit januari 2026 liet zien dat hij met één enkele GET om te bouwen is tot een RCE op nodelevel, en het Kubernetes Security Team behandelt dat als bedoeld gedrag.- Kubernetes 1.36 introduceert acht fine-grained kubelet subresources (
nodes/stats,nodes/metrics,nodes/log,nodes/pods,nodes/healthz,nodes/configz,nodes/spec,nodes/checkpoint) die elk monitoring- en observability-scenario afdekken. - Bestaande
nodes/proxy-bindings blijven werken doordat de kubelet terugvalt op de oude SubjectAccessReview als de nieuwe wordt geweigerd. De upgrade zelf doet niets voor je security. - Inventariseer je ClusterRoles, vervang
nodes/proxydoor de minimale subresource-set en zet er een admission policy bij die nieuwenodes/proxy-grants flagt. - Dit haalt de vraag naar
nodes/proxyweg uit monitoring agents. Het werkwoord blijft bestaan. Workloads die echtexec-toegang nodig hebben gaan er nog steeds doorheen, en dat is precies de bedoeling.