Kubernetes v1.35:细粒度补充组控制升至 GA

发布: (2025年12月24日 GMT+8 02:30)
7 min read

Source: Kubernetes Blog

毕业公告

代表 Kubernetes SIG Node,我们很高兴宣布 细粒度补充组控制 已在 Kubernetes v1.35正式发布 (GA)

  • 新的 Pod 字段 supplementalGroupsPolicy 最初在 v1.31 中作为 可选的 Alpha 特性引入,随后在 v1.33 中升级为 Beta,现在已达到 GA
  • 该特性让您能够对 Linux 容器中的补充组进行精确控制,增强安全性——尤其是在访问卷时。
  • 同时,它提升了容器内部 UID/GID 细节的透明度,提供更好的安全监控。

升级说明 – 如果您从 v1.32 或更早版本 升级,请注意自 Beta(v1.33)以来引入的 行为破坏性更改。有关详细信息,请参阅之前博客中的 行为更改升级注意事项 部分。

动机:在 /etc/group 中定义的隐式组成员

即使许多集群管理员/用户可能没有注意到,Kubernetes 默认会将 pod 的组信息与容器镜像内部的 /etc/group 文件合并

示例清单

apiVersion: v1
kind: Pod
metadata:
  name: implicit-groups-example
spec:
  securityContext:
    runAsUser: 1000          # UID
    runAsGroup: 3000         # 主 GID
    supplementalGroups: [4000]
  containers:
  - name: example-container
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: ["sh", "-c", "sleep 1h"]
    securityContext:
      allowPrivilegeEscalation: false

容器内部运行 id 会显示什么?

uid=1000 gid=3000 groups=3000,4000,50000

即使在 pod 清单中没有定义,50000 GID 仍然出现。

它来源于 容器镜像中的 /etc/group 文件

user-defined-in-image:x:1000:
group-defined-in-image:x:50000:user-defined-in-image

主用户(UID 1000)属于组 50000,因此 Kubernetes 隐式地将该组合并 到容器的补充组中。

为什么这会是个问题?

  • 隐式 GID 对 策略引擎是不可见的,因为它们没有出现在 pod 规范中。
  • 这可能导致 意外的访问控制问题,尤其是在访问卷时(参见 kubernetes/kubernetes#112879)。

细粒度补充组控制 – supplementalGroupsPolicy

Pod 的 .spec.securityContext 现在包含字段 supplementalGroupsPolicy。它决定 Kubernetes 如何为容器进程计算补充组。

策略描述
Merge(默认)保持历史行为 – 主用户在 /etc/group 中的组成员与 pod 中定义的组合并
Strict fsGroupsupplementalGroupsrunAsGroup 中列出的 GID 会被附加。来自 /etc/group 的组成员被忽略

严格策略示例

apiVersion: v1
kind: Pod
metadata:
  name: strict-supplementalgroups-policy-example
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    supplementalGroups: [4000]
    supplementalGroupsPolicy: Strict
  containers:
  - name: example-container
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: ["sh", "-c", "sleep 1h"]
    securityContext:
      allowPrivilegeEscalation: false

在容器内部运行 id,现在得到:

uid=1000 gid=3000 groups=3000,4000

不需要的组 50000 已不再出现。

强制使用 supplementalGroupsPolicy: Strict(例如通过策略引擎)可防止向 pod 添加隐式补充组。

注意: 即使容器拥有足够的权限,也仍然可以在启动后更改其进程身份。supplementalGroupsPolicy 只影响 初始 进程身份。

暴露 Pod 状态中的附加进程身份

该功能还会将初始进程身份添加到 Pod 状态中:

status:
  containerStatuses:
  - name: ctr
    user:
      linux:
        uid: 1000
        gid: 3000
        supplementalGroups:
        - 3000
        - 4000

重要提示: status.containerStatuses[].user.linux 中的值反映的是 首次附加的身份。如果容器能够调用 setuid(2)setgid(2)setgroups(2) 等系统调用,它随后可以修改自己的身份,从而使实际运行时的身份变为动态的。

缓解特权升级

为了限制容器更改其身份的能力,可考虑以下简易缓解措施:

  1. 在容器的 securityContext设置 privilege: false 并且 allowPrivilegeEscalation: false
  2. 使你的 Pod 符合 Pod Security Standards 中的 Restricted 策略。

TL;DR

  • supplementalGroupsPolicy(在 v1.35 中已 GA)允许您在历史的 Merge 行为和 Strict 模式之间进行选择,后者会消除隐藏的组成员关系。
  • 使用 Strict(或通过策略强制执行)以避免因 /etc/group 中的隐式组导致的安全意外。
  • 初始身份现在在 pod 状态中可见,帮助您审计和验证组分配。

概述

  • kubelet 无法看到 NRI 插件或容器运行时的内部工作原理。
  • 集群管理员(或拥有本地管理员权限的高特权工作负载)可以更改任何 Pod 的补充组。
  • 这超出了 Kubernetes 的控制范围,不应成为安全加固节点的关注点。

严格策略需要最新的容器运行时

高级容器运行时(例如 containerdCRI‑O)负责计算将附加到容器的补充组 ID。因此,supplementalGroupsPolicy: Strict 需要一个 支持 此功能的 CRI 运行时。

  • 较旧的行为(supplementalGroupsPolicy: Merge)在不支持该功能的运行时上也能工作,因为它完全向后兼容。

支持的 CRI 运行时

运行时最低版本
containerdv2.0 或更高
CRI‑Ov1.31 或更高

验证功能支持

您可以通过 status.features.supplementalGroupsPolicy 字段检查节点上是否启用了该功能(该字段不同于 KEP‑5328: Node Declared Features 中引入的 status.declaredFeatures)。

apiVersion: v1
kind: Node
...
status:
  features:
    supplementalGroupsPolicy: true

最佳实践

  • 随着容器运行时普遍采用此功能,许多安全策略将开始强制执行 Strict 行为,以实现更强的安全性。
  • 确保您的 Pod 已准备好接受此强制执行,方法是 在 Pod 规范中显式声明所有补充组,而不是依赖镜像中默认的设置。

参与方式

此功能的增强由 SIG Node 社区推动。
加入讨论,分享想法,并对该功能及相关主题提供反馈。社区期待您的参与!


了解更多

  • 为 Pod 或容器配置安全上下文 – 关于 supplementalGroupsPolicy 的详细指南。
  • KEP‑3619细粒度的 SupplementalGroups 控制
Back to Blog

相关文章

阅读更多 »