Kubernetes v1.36:细粒度 kubelet API 鉴权正式发布(GA)

我谨代表 Kubernetes SIG Auth 和 SIG Node 宣布, 细粒度 kubelet API 鉴权已在 Kubernetes v1.36 中正式发布(GA)!

KubeletFineGrainedAuthz 特性门控在 Kubernetes v1.32 中作为可选启用的 Alpha 特性引入,并在 v1.33 中进入 Beta 阶段(默认启用)。 如今,该特性已正式发布,且特性门控被锁定为启用状态。 此特性可针对 kubelet 的 HTTPS API 提供更精确、遵循最小权限原则的访问控制, 替代在常见监控与可观测性场景下授予范围过宽的 nodes/proxy 权限的做法。

动机:nodes/proxy 问题

kubelet 暴露了一个 HTTPS 端点,其中包含多个 API,可访问敏感程度不同的数据, 包括 Pod 列表、节点指标、容器日志,以及至关重要的在运行中的容器内执行命令的能力。

在此特性出现之前,kubelet 鉴权采用的是一种粗粒度模型。 启用 webhook 鉴权时,几乎所有 kubelet API 路径都会映射到单一的 nodes/proxy 子资源。 这意味着,任何需要从 kubelet 读取指标或健康状态的工作负载, 都必须拥有 nodes/proxy 权限,而这一权限同时也赋予了在节点上运行的任意容器中执行任意命令的能力。

这有什么问题?

nodes/proxy 授予监控代理、日志采集器或健康检查工具,违背了最小权限原则。 一旦这些工作负载中的任何一个被攻破,攻击者就能够在该节点上的每一个容器中执行命令。 nodes/proxy 权限实质上相当于节点级的超级用户能力, 而将其广泛授予会显著扩大安全事件的影响范围。

这个问题多年来一直被社区充分认识到(参见 kubernetes/kubernetes#83465), 也是推动此增强项 KEP-2862 的核心动因。

nodes/proxy GET 的 WebSocket 远程代码执行风险

实际情况比乍看起来更严重。安全研究人员在 2026 年初的研究中演示, 仅凭 nodes/proxy GET 这一通常授予监控工具的最小只读权限, 就可以被滥用来在可访问节点上的任意 Pod 中执行命令。

根本原因在于 WebSocket 连接的工作方式与 kubelet 将 HTTP 方法映射到 RBAC 动词的方式之间存在不匹配。 WebSocket 协议(RFC 6455) 要求在初始连接握手时发起一个 HTTP GET 请求。 kubelet 会将这个 GET 映射为 RBAC get 动词,并在鉴权时不进行二次检查, 以确认后续写操作所需的 create 权限是否同时存在。 攻击者借助 websocat 之类的 WebSocket 客户端, 可以直接访问 10250 端口上的 kubelet /exec 端点,并执行任意命令:

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)

细粒度 kubelet 鉴权如何工作

启用 KubeletFineGrainedAuthz 后,kubelet 现在会在回退到 nodes/proxy 子资源之前,先执行一次额外且更具体的鉴权检查。 多个常用的 kubelet API 路径被映射到了各自专属的子资源:

kubelet API资源子资源
/stats/*nodesstats
/metrics/*nodesmetrics
/logs/*nodeslog
/podsnodespods, proxy
/runningPods/nodespods, proxy
/healthznodeshealthz, proxy
/configznodesconfigz, proxy
/spec/*nodesspec
/checkpoint/*nodescheckpoint
其他所有路径nodesproxy

对于现在拥有细粒度子资源的端点(/pods/runningPods//healthz/configz),kubelet 会先针对具体子资源发送一次 SubjectAccessReview。 如果该检查成功,请求即获准通过;如果失败,kubelet 会再使用粗粒度的 nodes/proxy 子资源重试,以保持向后兼容。

这种双重检查方式确保了平滑的迁移路径。 已有的、依赖 nodes/proxy 权限的工作负载仍可继续运行, 而新的部署则可以从一开始就采用最小权限访问方式。

这在实践中意味着什么

以 Prometheus node exporter 或某个需要从 kubelet 抓取 /metrics 的监控 DaemonSet 为例。过去,你需要像下面这样配置一个 RBAC ClusterRole

# 旧方式:权限范围过宽
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-agent
rules:
- apiGroups: [""]
  resources: ["nodes/proxy"]
  verbs: ["get"]

这赋予了监控代理远超实际所需的访问权限。 有了细粒度鉴权后,你现在可以精确限定权限范围:

# 新方式:遵循最小权限原则
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-agent
rules:
- apiGroups: [""]
  resources: ["nodes/metrics", "nodes/stats"]
  verbs: ["get"]

这样一来,监控代理就可以从 kubelet 读取 metricsstats, 同时完全不具备在容器中执行命令的能力。

已更新的 system:kubelet-api-admin ClusterRole

启用 RBAC 鉴权时,内置的 system:kubelet-api-admin ClusterRole 会自动更新,以包含所有新的细粒度子资源权限。 这可确保已经在使用该角色的集群管理员(包括 API server 所使用的 kubelet 客户端) 无需任何手动配置变更,仍然保有完整访问能力。

该角色现在包含以下权限:

  • nodes/proxy
  • nodes/stats
  • nodes/metrics
  • nodes/log
  • nodes/spec
  • nodes/checkpoint
  • nodes/configz
  • nodes/healthz
  • nodes/pods

升级注意事项

由于 kubelet 会执行双重鉴权检查(先细粒度检查,再回退到 nodes/proxy), 升级到 v1.36 对大多数集群来说应当是无缝的:

  • 现有工作负载:已经拥有 nodes/proxy 权限的工作负载无需任何改动即可继续运行。 回退到 nodes/proxy 的机制保证了向后兼容。
  • API server:始终通过 system:kubelet-api-admin 拥有 nodes/proxy 权限,因此无论特性门控状态如何,kube-apiserverkubelet 之间的通信都不受影响。
  • 混合版本集群:也能被平滑处理。如果 kubelet 支持细粒度鉴权而 API server 不支持 (或反过来),nodes/proxy 权限就会作为回退机制发挥作用。

验证该特性是否已启用

你可以通过检查 kubelet 的 metrics 端点,确认该特性是否在某个节点上处于启用状态。 由于 10250 端口上的 metrics 端点需要鉴权, 你首先需要为发起请求的 Pod 或服务账号创建适当的 RBAC 绑定。

第 1 步:创建 ServiceAccountClusterRole

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"]

第 2 步:将 ClusterRole 绑定到 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

应用这些清单:

kubectl apply -f serviceaccount.yaml
kubectl apply -f clusterrole.yaml
kubectl apply -f clusterrolebinding.yaml

第 3 步:使用该 ServiceAccount 运行一个 Pod,并检查特性标志

kubectl run kubelet-check \
  --image=curlimages/curl \
  --serviceaccount=kubelet-metrics-checker \
  --restart=Never \
  --rm -it \
  -- sh

然后在 Pod 内获取节点 IP,并查询 metrics 端点:

# 获取令牌
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# 查询 kubelet 指标,并筛选特性门控
curl -sk \
  --header "Authorization: Bearer $TOKEN" \
  https://$NODE_IP:10250/metrics \
  | grep kubernetes_feature_enabled \
  | grep KubeletFineGrainedAuthz

如果该特性已启用,你应当会看到类似下面的输出:

kubernetes_feature_enabled{name="KubeletFineGrainedAuthz",stage="GA"} 1

注意: 请将 $NODE_IP 替换为你要检查的节点 IP 地址。 你可以使用 kubectl get nodes -o wide 获取节点 IP。

从 Alpha 到 GA 的历程

版本阶段说明
v1.32Alpha引入 KubeletFineGrainedAuthz 特性门控,默认禁用
v1.33Beta默认启用;为 /pods/runningPods//healthz/configz 增加细粒度检查
v1.36GA特性门控被锁定为启用状态;细粒度 kubelet 鉴权始终处于活动状态

下一步是什么?

随着细粒度 kubelet 鉴权现已 GA,Kubernetes 社区可以开始建议, 并最终强制要求监控与可观测性工作负载使用特定子资源,而不是 nodes/proxy。 之所以需要尽快推进这一迁移,是因为已有 研究表明 nodes/proxy GET 可通过 WebSocket 协议被滥用,从而实现无日志记录的远程代码执行。 这一风险存在于数十个被广泛部署的 Helm Chart 的默认 RBAC 配置中。 随着时间推移,我们预计会出现以下变化:

  • 生态系统采用:Prometheus、Datadog agent 以及其他 DaemonSet 等监控工具, 可以更新其默认 RBAC 配置,改用 nodes/metricsnodes/statsnodes/pods, 而不再使用 nodes/proxy。这会直接消除这些工作负载面临的 WebSocket RCE 攻击面。
  • 策略执行:当存在细粒度替代方案时,准入控制器与策略引擎可以标记或拒绝授予 nodes/proxy 的 RBAC 绑定,帮助组织以规模化方式落实最小权限访问。
  • 弃用路径:随着采用范围扩大,nodes/proxy 最终可能会在监控用途中被弃用, 从而进一步缩小 Kubernetes 集群的攻击面。

参与其中

这一增强项由 SIG Auth 和 SIG Node 推动。 如果你有兴趣为 Kubernetes 的安全与鉴权特性贡献力量,欢迎加入我们:

期待听到你对这一特性的反馈与使用经验!