本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
用户名字空间:现在支持在 Alpha 阶段运行有状态 Pod!
Kubernetes v1.25 引入了对用户名字空间的支持,但仅限于无状态 Pod。在 1.27 版本中进行了一些设计更改后,Kubernetes 1.28 取消了该限制。
此功能的美妙之处在于
- 它非常容易采用(你只需要在 Pod 规约中设置一个布尔值)
- 对于大多数应用程序,不需要任何更改
- 通过显著增强容器的隔离性并缓解被评为高危和严重级别的 CVE,提高了安全性。
本文解释了用户名字空间的基础知识,并展示了
- 最近 Kubernetes v1.28 版本中带来的变化
- 一个被评为高危漏洞的演示,该漏洞在使用用户名字空间时无法被利用
- 使用此功能所需的运行时要求
- 在未来版本中,你可以期待关于用户名字空间的哪些内容。
什么是用户名字空间?
用户名字空间是一项 Linux 功能,它将容器的用户和组标识符(UID 和 GID)与主机上的标识符隔离开来。容器中的标识符可以映射到主机上的标识符,使得用于不同容器的主机 UID/GID 永远不会重叠。更重要的是,这些标识符可以映射到主机上无特权的、不重叠的 UID 和 GID。这基本上意味着两件事
由于不同容器的 UID 和 GID 映射到主机上不同的 UID 和 GID,即使容器逃逸了容器边界,它们也很难相互攻击。例如,如果容器 A 在主机上运行的 UID 和 GID 与容器 B 不同,那么它对容器 B 的文件和进程可以执行的操作是有限的:只能读取/写入文件允许其他人访问的内容,因为它永远不会拥有所有者或组的权限(主机上的 UID/GID 保证对于不同容器是不同的)。
由于 UID 和 GID 映射到主机上的无特权用户,如果一个容器逃逸了容器边界,即使它在容器内以 root 身份运行,它在主机上也没有特权。这极大地保护了它能读/写哪些主机文件,能向哪些进程发送信号等。
此外,授予的能力仅在用户名字空间内有效,在主机上无效。
不使用用户名字空间,以 root 身份运行的容器在发生容器逃逸的情况下,在节点上拥有 root 权限。如果授予了容器某些能力,这些能力在主机上同样有效。当使用用户名字空间时,这些情况都不会发生(当然,除了 bug 🙂)。
1.28 的变化
如前所述,从 1.28 开始,Kubernetes 支持有状态 Pod 的用户名字空间。这意味着具有用户名字空间的 Pod 可以使用任何类型的卷,它们不再像以前那样仅限于某些卷类型。
用于激活此功能的特性门控已重命名,不再是 UserNamespacesStatelessPodsSupport
,从 1.28 开始,你应该使用 UserNamespacesSupport
。进行了许多更改,对节点主机的要求也发生了变化。因此,在 Kubernetes 1.28 中,特性标志被重命名以反映这一点。
演示
Rodrigo 创建了一个演示,利用了 CVE 2022-0492,并展示了在没有用户名字空间的情况下如何发生漏洞利用。他还展示了在容器使用此功能的 Pod 中无法使用此漏洞。
该漏洞被评为高危,并允许一个没有特殊权限的容器读/写主机上的任何路径,并在主机上以 root 身份启动进程。
如今,容器中的大多数应用程序都以 root 身份或半可预测的非 root 用户(用户 ID 65534 是一个相当普遍的选择)运行。当你运行一个使用 userns 的容器的 Pod 时,Kubernetes 会将这些容器作为无特权用户运行,而无需对你的应用程序进行任何更改。
这意味着两个以用户 65534 身份运行的容器实际上将被映射到主机上的不同用户,从而限制了它们在逃逸时可以对彼此做什么,如果它们以 root 身份运行,则在主机上的权限将降低为无特权用户的权限。
节点系统要求
要使用此功能,对 Linux 内核版本和容器运行时都有要求。
在 Linux 上,你需要 Linux 6.3 或更高版本。这是因为该功能依赖于一个名为 idmap 挂载的内核特性,并且在 Linux 6.3 中合并了对 tmpfs 使用 idmap 挂载的支持。
如果你正在使用带有 crun 的 CRI-O,CRI-O 1.28.1 和 crun 1.9 或更高版本支持此功能。如果你正在使用带有 runc 的 CRI-O,则尚不支持。
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 安全标准(PSS)和 Pod 安全准入中。目前,计划是在使用用户名字空间时放宽 PSS 策略中的检查。这意味着字段 spec[.*].securityContext
中的 runAsUser
、runAsNonRoot
、allowPrivilegeEscalation
和 capabilities
在使用用户名字空间时不会触发违规。该行为可能会通过使用 API Server 特性门控来控制,例如 UserNamespacesPodSecurityStandards
或类似名称。
我如何参与?
你可以通过多种方式联系 SIG Node
- Slack: #sig-node
- 邮件列表
- 开放的社区问题/PR
你也可以直接联系我们:
- GitHub: @rata @giuseppe @saschagrunert
- Slack: @rata @giuseppe @sascha