这篇文章讨论了 Kubernetes 1.31 中的一个新特性,用于改善 Pod 中容器的补充组(Fine-grained SupplementalGroups control)处理。
动机:在容器镜像中的 /etc/group
定义的隐式组成员身份
尽管这种行为可能并不受许多 Kubernetes 集群用户/管理员的欢迎,但 Kubernetes 默认情况下会将 Pod 中的组信息与容器镜像中 /etc/group
定义的信息进行合并。
让我们来看一个例子,下面的 Pod 在其安全上下文中指定了 runAsUser=1000
、runAsGroup=3000
和 supplementalGroups=4000
。
apiVersion: v1
kind: Pod
metadata:
name: implicit-groups
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
supplementalGroups: [4000]
containers:
- name: ctr
image: registry.k8s.io/e2e-test-images/agnhost:2.45
command: [ "sh", "-c", "sleep 1h" ]
securityContext:
allowPrivilegeEscalation: false
在 ctr
容器中执行 id
命令的结果是什么?
# 创建 Pod:
$ kubectl apply -f https://k8s.io/blog/2024-08-22-Fine-grained-SupplementalGroups-control/implicit-groups.yaml
# 验证 Pod 的容器正在运行:
$ kubectl get pod implicit-groups
# 检查 id 命令
$ kubectl exec implicit-groups -- id
然后,输出应该类似于这样:
uid=1000 gid=3000 groups=3000,4000,50000
即使在 Pod 的清单中根本没有定义 50000
,补充组中的组 ID 50000
来自哪里?答案是容器镜像中的 /etc/group
文件。
检查容器镜像中的 /etc/group
的内容应该显示如下:
$ kubectl exec implicit-groups -- cat /etc/group
...
user-defined-in-image:x:1000:
group-defined-in-image:x:50000:user-defined-in-image
啊哈!容器的主用户 1000
在最后一个条目中属于组 50000
。
因此,容器镜像中为容器的主用户定义的组成员身份被隐式地合并到来自 Pod 的信息中。请注意,这是当前 CRI 实现从 Docker 继承的设计决策,社区直到现在才真正重新考虑它。
这有什么问题?
容器镜像中的 /etc/group
隐式合并的组信息可能会引起一些关注,特别是在访问卷时(详见 kubernetes/kubernetes#112879),因为文件权限由 Linux 中的 uid/gid 控制。更糟糕的是,来自 /etc/group
的隐式 gids 无法被任何策略引擎检测/验证,因为在清单中没有隐式组信息的线索。这也可能是 Kubernetes 安全方面的一个关注点。
为了解决上述问题,Kubernetes 1.31 引入了 Pod 的 .spec.securityContext
中的新字段 supplementalGroupsPolicy
。
此字段提供了一种控制如何计算 Pod 中容器进程的补充组的方法。可用的策略如下:
-
合并:容器的主用户在
/etc/group
中定义的组成员身份将被合并。如果没有指定,将应用此策略(即向后兼容的当前行为)。 -
严格:它只附加在
fsGroup
、supplementalGroups
或runAsGroup
字段中指定的组 ID 作为容器进程的补充组。这意味着容器镜像中为主用户定义的任何组成员身份都不会被合并。
让我们看看 Strict
策略是如何工作的。
# 创建 Pod:
$ kubectl apply -f https://k8s.io/blog/2024-08-22-Fine-grained-SupplementalGroups-control/strict-supplementalgroups-policy.yaml
# 验证 Pod 的容器正在运行:
$ kubectl get pod strict-supplementalgroups-policy
# 检查进程身份:
kubectl exec -it strict-supplementalgroups-policy -- id
输出应该类似于这样:
uid=1000 gid=3000 groups=3000,4000
你可以看到 Strict
策略可以排除 groups
中的组 50000
!
因此,确保 supplementalGroupsPolicy: Strict
(由某些策略机制强制执行)有助于防止 Pod 中的隐式补充组。
Pod 状态中的附加进程身份
此功能还通过 .status.containerStatuses[].user.linux
字段公开了附加到容器的第一个进程的进程身份。这将有助于查看是否附加了隐式组 ID。
...
status:
containerStatuses:
- name: ctr
user:
linux:
gid: 3000
supplementalGroups:
- 3000
- 4000
uid: 1000
...
特性可用性
要启用 supplementalGroupsPolicy
字段,必须使用以下组件:
- Kubernetes:v1.31 或更高版本,启用了
SupplementalGroupsPolicy
特性门控。截至 v1.31,该门控标记为 alpha。 - CRI 运行时:
- containerd:v2.0 或更高版本
- CRI-O:v1.31 或更高版本
你可以在节点的 .status.features.supplementalGroupsPolicy
字段中查看特性是否受支持。
apiVersion: v1
kind: Node
...
status:
features:
supplementalGroupsPolicy: true