Kubernetes 1.30:用户命名空间 Pod 的 Beta 支持
Linux 提供了不同的命名空间来隔离进程。例如,一个典型的 Kubernetes Pod 在网络命名空间内运行以隔离网络标识,并在 PID 命名空间内运行以隔离进程。
有一个 Linux 命名空间被遗漏了,那就是用户命名空间。这个命名空间允许我们将容器内部使用的用户和组标识符(UID 和 GID)与主机上的标识符隔离开来。
这是一个强大的抽象,允许我们以“root”身份运行容器:我们在容器内部是 root 用户,并且可以在 pod 内部执行 root 用户可以执行的所有操作,但是我们与主机的交互仅限于非特权用户可以执行的操作。这对于限制容器突破的影响非常有用。
容器突破是指容器内部的进程可以使用容器运行时或内核中的某些未修补漏洞突破到主机上,并访问/修改主机或其他容器上的文件。如果我们使用用户命名空间运行 pod,容器对主机其余部分的权限将减少,并且它可以访问的容器外部文件也会受到限制。
在 Kubernetes v1.25 中,我们仅为无状态 pod 引入了用户命名空间的支持。Kubernetes 1.28 解除了该限制,现在,随着 Kubernetes 1.30 的发布,我们正在迈向 beta 阶段!
什么是用户命名空间?
注意:Linux 用户命名空间与Kubernetes 命名空间是不同的概念。前者是 Linux 内核功能;后者是 Kubernetes 功能。
用户命名空间是一个 Linux 功能,它可以将容器的 UID 和 GID 与主机上的 UID 和 GID 隔离开来。容器中的标识符可以以某种方式映射到主机上的标识符,从而使不同容器使用的主机 UID/GID 永远不会重叠。此外,标识符可以映射到主机上非特权的、不重叠的 UID 和 GID。这带来了两个关键好处
防止横向移动:由于不同容器的 UID 和 GID 映射到主机上不同的 UID 和 GID,即使它们逃脱了容器边界,容器也难以相互攻击。例如,假设容器 A 在主机上运行的 UID 和 GID 与容器 B 不同。在这种情况下,它可以对容器 B 的文件和进程执行的操作受到限制:只能读取/写入文件允许其他人的内容,因为它永远不会拥有权限所有者或组权限(保证不同容器在主机上的 UID/GID 是不同的)。
增强主机隔离:由于 UID 和 GID 映射到主机上的非特权用户,如果容器逃脱了容器边界,即使它在容器内部以 root 身份运行,它在主机上也没有任何权限。这大大保护了它可以读取/写入的主机文件、它可以向其发送信号的进程等。此外,授予的功能仅在用户命名空间内有效,而不在主机上,从而限制了容器逃逸可能产生的影响。
用户命名空间 ID 分配
如果不使用用户命名空间,则在容器突破的情况下,以 root 身份运行的容器在节点上具有 root 权限。如果某些功能被授予容器,则这些功能在主机上也有效。当使用用户命名空间时,这些都不是真的(当然,除了 bug 之外🙂)。
1.30 中的更改
在 Kubernetes 1.30 中,除了将用户命名空间移至 beta 版之外,为该功能做出贡献的贡献者还
- 引入了一种让 kubelet 使用自定义范围进行 UID/GID 映射的方法
- 添加了一种让 Kubernetes 强制运行时支持用户命名空间所需的所有功能的方法。如果不支持这些功能,Kubernetes 将在尝试使用用户命名空间创建 pod 时显示明确的错误。在 1.30 之前,如果容器运行时不支持用户命名空间,则可以创建没有用户命名空间的 pod。
- 添加了更多测试,包括 cri-tools 存储库中的测试。
您可以查看关于用户命名空间的文档,了解如何配置映射的自定义范围。
演示
几个月前,CVE-2024-21626 被披露。此漏洞评分为 8.6(高)。它允许攻击者逃脱容器,并读取/写入节点上的任何路径以及同一节点上托管的其他 pod。
Rodrigo 创建了一个演示,利用了 CVE 2024-21626,并展示了在没有用户命名空间的情况下有效的漏洞利用,在使用用户命名空间时如何得到缓解。
请注意,使用用户命名空间,攻击者可以在主机文件系统上执行“其他”权限位允许的操作。因此,该 CVE 并未完全阻止,但影响大大降低。
节点系统要求
要使用此功能,对 Linux 内核版本和容器运行时有要求。
在 Linux 上,您需要 Linux 6.3 或更高版本。这是因为该功能依赖于名为 idmap mounts 的内核功能,并且支持将 idmap mounts 与 tmpfs 一起使用已合并到 Linux 6.3 中。
如果您使用带有 crun 的 CRI-O;如往常一样,您可以期望 CRI-O 1.30 支持 Kubernetes 1.30。请注意,您还需要 crun 1.9 或更高版本。如果您将 CRI-O 与 runc 一起使用,则仍然不支持。
Containerd 支持目前的目标是 containerd 2.0,并且适用相同的 crun 版本要求。如果您将 containerd 与 runc 一起使用,则仍然不支持。
请注意,containerd 1.7 为用户命名空间添加了实验性支持,如 Kubernetes 1.25 和 1.26 中实现的那样。我们在 Kubernetes 1.27 中进行了重新设计,这需要容器运行时进行更改。这些更改不存在于 containerd 1.7 中,因此它仅适用于 Kubernetes 1.25 和 1.26 中的用户命名空间支持。
containerd 1.7 的另一个限制是它需要在 Pod 启动期间更改容器映像内每个文件和目录的所有权。这会带来存储开销,并可能显著影响容器启动延迟。Containerd 2.0 可能会包含一个实现,它将消除增加的启动延迟和存储开销。如果您计划在生产中使用带有用户命名空间的 containerd 1.7,请考虑这一点。
这些 containerd 1.7 的限制都不适用于 CRI-O。
我如何参与?
您可以通过多种方式联系 SIG Node
您也可以直接联系我们
- GitHub: @rata @giuseppe @saschagrunert
- Slack: @rata @giuseppe @sascha