本文介绍了 Kubernetes 1.31 版本中的一个新特性,它可以帮助我们更细致地控制 Pod 里容器的补充组。之前,Kubernetes 会自动把 Pod 里设置的组信息和容器镜像里的 /etc/group
文件中的组信息合并起来,但这种做法有时候并不是管理员想要的。
举个例子,下面创建一个 Pod,并设置了运行用户是 1000,主组是 3000,还指定了一个补充组 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
按照这个配置创建 Pod 后,如果你在容器里执行 id
命令,会发现输出中除了我们设置的组,还多了一个 50000 的组。这个 50000 组就是从 /etc/group
文件中来的,即使你在 Pod 的配置里没有提到它。
执行命令查看输出:
# 创建 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
如果你想查看 /etc/group
文件里的内容,可以执行:
$ kubectl exec implicit-groups -- cat /etc/group
你会发现容器的主用户 1000 属于组 50000。这就是为什么即使没有在 Pod 配置中明确指定,这个组也会出现的原因。
这个自动合并的行为是从 Docker 继承来的,直到现在社区才开始重新考虑。通过 Kubernetes 1.31 的新特性,我们可以更精细地控制这个行为,以避免潜在的安全问题和权限管理上的混乱。
这有什么问题?
在容器镜像中,/etc/group
文件中的组信息可能会被隐式合并,这在某些情况下可能会引起安全关注,特别是在访问卷时(详见 kubernetes/kubernetes#112879),涉及到文件权限时。这是因为Linux系统的文件权限是由用户ID(uid)和组ID(gid)控制的。更让人担忧的是,这些隐式组(gids)不会被任何策略引擎检测或验证,因为它们在配置清单中没有明确的记录。
为了解决这个问题,Kubernetes 1.31版本引入了一个新字段supplementalGroupsPolicy
,它位于Pod的.spec.securityContext
中。这个字段允许你控制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
策略成功地排除了组50000
。
通过确保supplementalGroupsPolicy: Strict
被正确设置(可能由某些策略机制强制执行),可以帮助防止Pod中的隐式补充组问题。
Pod 状态中的附加进程身份
在Kubernetes中,Pod状态信息的.status.containerStatuses[].user.linux
字段提供了一个查看容器进程身份的窗口,这对于理解容器中运行的进程所具有的用户和组权限非常有用。这个字段显示了附加到容器的第一个进程的用户和组信息,包括UID(用户ID)、GID(主组ID)和补充组。
通过查看这个字段,你可以确认容器进程是否具有预期的用户和组权限,这有助于确保容器的安全性和正确的权限管理。例如,如果你看到以下输出:
...
status:
containerStatuses:
- name: ctr
user:
linux:
gid: 3000
supplementalGroups:
- 3000
- 4000
uid: 1000
...
这意味着名为ctr
的容器的第一个进程具有以下身份:
- UID: 1000
- GID: 3000
- 补充组: 3000, 4000
这个信息可以帮助你验证是否正确地应用了安全策略,比如supplementalGroupsPolicy
。如果策略设置为Strict
,那么只有明确指定的组ID才会被附加为补充组,这有助于防止未授权的访问和潜在的安全风险。
特性可用性
要充分利用Kubernetes的supplementalGroupsPolicy
功能,确保你的环境满足以下要求是非常重要的。
必需的组件和版本
-
Kubernetes: 需要使用v1.31或更高版本,并且需要启用
SupplementalGroupsPolicy
特性门控。在v1.31版本中,这个特性是标记为alpha的,意味着它可能还在开发中,可能需要额外的配置或注意。 -
CRI运行时:
- containerd: 需要v2.0或更高版本。
- CRI-O: 需要v1.31或更高版本。
检查特性支持
你可以通过查看节点的.status.features.supplementalGroupsPolicy
字段来确认你的节点是否支持supplementalGroupsPolicy
特性。如果该字段显示为true
,则表示该特性在你的节点上是受支持的。你可以使用以下命令来查看节点的状态:
kubectl get node -o yaml
在输出中,查找类似于以下的内容:
apiVersion: v1
kind: Node
...
status:
features:
supplementalGroupsPolicy: true
如果supplementalGroupsPolicy
字段显示为true
,那么你的环境已经准备好使用这个特性。
启用特性
如果你的环境还未启用SupplementalGroupsPolicy
特性,你可能需要在Kubernetes的配置文件中启用它。这通常涉及到编辑Kubernetes的启动参数,确保包含--feature-gates=SupplementalGroupsPolicy=true
。