经过数年的开发,Kubernetes 中的用户命名空间(User Namespaces)支持已随着 v1.36 发布进入正式发布(GA)阶段。 这是一个仅适用于 Linux 的特性。
对于从事底层容器运行时(Container Runtimes)和 rootless 技术的我们来说, 这是一个期待已久的里程碑。 我们终于走到了可以将 rootless 安全隔离用于 Kubernetes 工作负载的阶段。
这一特性还开启了一种关键模式:让工作负载在拥有特权的同时,依然被限制在用户命名空间内。
当设置 hostUsers: false 时,CAP_NET_ADMIN 这类权能(capabilities)会变成 被命名空间化(namespaced) 的权能,
这意味着它们只会授予容器本地资源的管理能力,而不会影响主机。
这实际上开启了此前只有运行完全特权容器(fully privileged container)才能实现的新用例。
在容器内以 root 身份运行的进程,从内核视角看,在主机上同样是 root。
如果攻击者成功逃逸出容器,无论是利用内核漏洞,还是借助配置错误的挂载(misconfigured mount),
他们都会在主机上获得 root 权限。
虽然运行容器时已经有许多安全防护措施,但这些措施并不会改变进程的底层身份,
它依然保留着 root 的某些“部分能力”。
通往 GA 的道路并不只是 Kubernetes API 的演进,更关键的是让内核真正为我们所用。
在早期阶段,最大的阻碍之一是卷的所有权问题。
如果你把容器映射到较高的 UID 范围,Kubelet 就必须递归地对挂载卷中的每个文件执行 chown,
这样容器才能对这些文件进行读写。
对于大型卷来说,这一操作的成本高得惊人,足以摧毁启动性能。
实现这一目标的关键,是 ID-mapped mounts(在 Linux 5.12 中引入,并在后续版本中持续完善)。 借助这一机制,内核可以在挂载时重映射文件所有权,而不必改写磁盘上的实际所有权信息。
当一个卷被挂载到启用了用户命名空间的 Pod 中时,内核会透明地转换 UID(user ids)和 GID(group ids)。
对容器来说,这些文件看起来像是由 UID 0 拥有。
而在磁盘上,文件所有权完全不会变化,因此不需要执行 chown。
这是一个 O(1) 操作,既即时又高效。
使用用户命名空间非常直接:你只需要在 Pod spec 中设置 hostUsers: false。
无需修改容器镜像,也不需要复杂配置。
它仍然使用 Alpha 阶段引入的同一套接口。
在 Pod(或 PodTemplate)的 spec 中,你可以显式选择不使用主机用户命名空间:
apiVersion: v1
kind: Pod
metadata:
name: isolated-workload
spec:
hostUsers: false
containers:
- name: app
image: fedora:42
securityContext:
runAsUser: 0
如果你想进一步了解用户命名空间在实践中的工作方式,以及它如何缓解被评为 HIGH 的 CVE, 请参阅此前的博客文章: User Namespaces alpha、 User Namespaces stateful pods in alpha、 User Namespaces beta,以及 User Namespaces enabled by default。
如果你对用户命名空间感兴趣,或希望参与贡献,这里有一些有用的链接:
这一特性历经多年才走到今天:第一份 KEP 在 10 年前由其他贡献者提出, 而我们在过去 6 年里一直积极推动它向前发展。 我们感谢所有在 SIG Node、容器运行时以及 Linux 内核领域参与贡献的人。 同时,也特别感谢那些在多个 Alpha 和 Beta 周期中帮助打磨设计的评审者与早期采用者。