Kubernetes v1.35:细粒度补充组控制升至 GA
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 | 仅 fsGroup、supplementalGroups 或 runAsGroup 中列出的 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)等系统调用,它随后可以修改自己的身份,从而使实际运行时的身份变为动态的。
缓解特权升级
为了限制容器更改其身份的能力,可考虑以下简易缓解措施:
- 在容器的
securityContext中 设置privilege: false并且allowPrivilegeEscalation: false。 - 使你的 Pod 符合 Pod Security Standards 中的 Restricted 策略。
TL;DR
supplementalGroupsPolicy(在 v1.35 中已 GA)允许您在历史的 Merge 行为和 Strict 模式之间进行选择,后者会消除隐藏的组成员关系。- 使用 Strict(或通过策略强制执行)以避免因
/etc/group中的隐式组导致的安全意外。 - 初始身份现在在 pod 状态中可见,帮助您审计和验证组分配。
概述
- kubelet 无法看到 NRI 插件或容器运行时的内部工作原理。
- 集群管理员(或拥有本地管理员权限的高特权工作负载)可以更改任何 Pod 的补充组。
- 这超出了 Kubernetes 的控制范围,不应成为安全加固节点的关注点。
严格策略需要最新的容器运行时
高级容器运行时(例如 containerd、CRI‑O)负责计算将附加到容器的补充组 ID。因此,supplementalGroupsPolicy: Strict 需要一个 支持 此功能的 CRI 运行时。
- 较旧的行为(
supplementalGroupsPolicy: Merge)在不支持该功能的运行时上也能工作,因为它完全向后兼容。
支持的 CRI 运行时
| 运行时 | 最低版本 |
|---|---|
| containerd | v2.0 或更高 |
| CRI‑O | v1.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 控制。