这篇文章已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否已不再正确。

用户命名空间:Alpha 版本现已支持运行有状态 Pod!

Kubernetes v1.25 引入了仅支持无状态 Pod 的用户命名空间。Kubernetes 1.28 在 1.27 中进行了一些设计更改后,取消了这一限制。

此功能的妙处在于

  • 采用起来很简单 (只需在 Pod spec 中设置一个布尔值)
  • 大多数应用程序无需任何更改
  • 通过显著增强容器隔离并缓解评级为 HIGH 和 CRITICAL 的 CVE 漏洞,从而提升安全性。

本文解释了用户命名空间的基础知识,并展示了

  • 最新 Kubernetes v1.28 版本中引入的更改
  • 一个评级为 HIGH 的漏洞演示,使用用户命名空间时该漏洞无法被利用
  • 使用此功能的运行时要求
  • 在未来版本中关于用户命名空间的期望。

什么是用户命名空间?

用户命名空间是 Linux 的一项特性,它将容器的用户和组标识符 (UIDs 和 GIDs) 与宿主机的标识符隔离开来。容器中的标识符可以映射到宿主机上的标识符,这样不同容器使用的宿主机 UID/GIDs 永远不会重叠。此外,标识符可以映射到宿主机上的无特权非重叠 UID 和 GID。这基本上意味着两件事:

  • 由于不同容器的 UID 和 GID 映射到宿主机上不同的 UID 和 GID,因此即使容器逃逸出容器边界,它们也更难互相攻击。例如,如果容器 A 在宿主机上使用的 UID 和 GID 与容器 B 不同,则容器 A 对容器 B 文件和进程的操作会受到限制:它只能进行文件允许其他人进行的读/写操作,因为它永远不会拥有所有者或组的权限 (宿主机上的 UID/GID 保证对不同容器是不同的)。

  • 由于 UID 和 GID 映射到宿主机上的无特权用户,如果容器逃逸出容器边界,即使它在容器内以 root 身份运行,它在宿主机上也没有特权。这极大地保护了它能读/写哪些宿主机文件、可以向哪些进程发送信号等。

此外,授予的能力 (capabilities) 只在用户命名空间内有效,而不在宿主机上有效。

如果不使用用户命名空间,以 root 身份运行的容器在容器逃逸的情况下,在节点上拥有 root 权限。如果容器被授予了某些能力 (capabilities),这些能力在宿主机上也有效。而使用用户命名空间时,这些都不成立 (当然,不考虑 bug 🙂)。

1.28 中的变化

如前所述,从 1.28 版本开始,Kubernetes 支持带有状态 Pod 的用户命名空间。这意味着带有用户命名空间的 Pod 可以使用任何类型的卷,不再像以前那样仅限于某些卷类型。

激活此功能的特性门已重命名,不再是 UserNamespacesStatelessPodsSupport,而是从 1.28 开始应使用 UserNamespacesSupport。进行了许多更改,并且对节点宿主机的要求也发生了变化。因此,在 Kubernetes 1.28 中,特性标志被重命名以反映这一点。

演示

Rodrigo 创建了一个演示,该演示利用了 CVE 2022-0492,并展示了如何在没有用户命名空间的情况下发生此漏洞利用。他还展示了如何无法从未开启此功能的 Pod 中的容器利用此漏洞。

此漏洞被评级为 HIGH,并允许一个没有任何特殊特权的容器在宿主机上读/写任何路径,并在宿主机上以 root 身份启动进程。

今天,容器中的大多数应用程序都以 root 身份运行,或者以半可预测的非 root 用户身份运行 (用户 ID 65534 是一个相当受欢迎的选择)。当您运行一个使用 userns 的容器的 Pod 时,Kubernetes 会将这些容器作为无特权用户运行,而无需更改您的应用程序。

这意味着以用户 65534 身份运行的两个容器实际上会映射到宿主机上的不同用户,限制了它们在逃逸情况下能对彼此做什么,并且如果它们以 root 身份运行,其在宿主机上的特权会降至无特权用户级别。

节点系统要求

使用此功能对 Linux 内核版本和容器运行时都有要求。

在 Linux 上,您需要 Linux 6.3 或更高版本。这是因为此功能依赖于一个名为 idmap mounts 的内核特性,并且在 Linux 6.3 中合并了在 tmpfs 中使用 idmap mounts 的支持。

如果您使用 CRI-O 配合 crun,此功能在 CRI-O 1.28.1 及更高版本的 crun 1.9 中受支持。如果您使用 CRI-O 配合 runc,此功能尚不受支持。

containerd 支持目前的目标是 containerd 2.0;无论您是使用 crun 还是 runc,可能都不重要。

请注意,containerd 1.7 添加了对 Kubernetes 1.25 和 1.26 中实现的用户命名空间的实验性支持。1.27 中进行的设计更改不受 containerd 1.7 支持,因此,就用户命名空间支持而言,它仅适用于 Kubernetes 1.25 和 1.26。

containerd 1.7 中存在一个限制,即在 Pod 启动期间需要更改容器镜像内每个文件和目录的所有权。这意味着它会产生存储开销,并且会显著影响容器启动延迟。Containerd 2.0 可能会包含一个实现,以消除额外的启动延迟和存储开销。如果您计划在生产环境中使用 containerd 1.7 和用户命名空间,请考虑此点。

这些 containerd 限制均不适用于 CRI-O 1.28

下一步是什么?

展望 Kubernetes 1.29,计划是与 SIG Auth 合作,将用户命名空间集成到 Pod Security Standards (PSS) 和 Pod Security Admission 中。目前,计划是在使用用户命名空间时放宽 PSS 策略中的检查。这意味着如果正在使用用户命名空间,字段 spec[.*].securityContext runAsUserrunAsNonRootallowPrivilegeEscalationcapabilities 将不会触发违规。此行为可能会通过使用 API Server 特性门来控制,例如 UserNamespacesPodSecurityStandards 或类似的名称。

如何参与?

您可以通过以下几种方式联系 SIG Node

您也可以直接联系我们

  • GitHub:@rata @giuseppe @saschagrunert
  • Slack:@rata @giuseppe @sascha