用户命名空间
Kubernetes v1.30 [测试版]
本页面解释了如何在 Kubernetes Pod 中使用用户命名空间。用户命名空间将容器内运行的用户与主机中的用户隔离。
在容器中以 root 身份运行的进程可以在主机中以不同的(非 root)用户身份运行;换句话说,该进程对用户命名空间内的操作具有完全权限,但对命名空间外的操作没有权限。
您可以使用此功能来减少受损容器对主机或同一节点中其他 Pod 可能造成的损害。有一些被评为**高**或**严重**的安全漏洞在用户命名空间处于活动状态时无法被利用。预计用户命名空间也将缓解未来的一些漏洞。
开始之前
这是一项仅限 Linux 的功能,并且在 Linux 中需要对所使用的文件系统进行 idmap 挂载支持。这意味着
- 在节点上,您用于
/var/lib/kubelet/pods/
的文件系统(或您为此配置的自定义目录)需要 idmap 挂载支持。 - Pod 卷中使用的所有文件系统都必须支持 idmap 挂载。
实际上,这意味着您至少需要 Linux 6.3,因为 tmpfs 从该版本开始支持 idmap 挂载。这通常是必需的,因为几个 Kubernetes 功能使用 tmpfs(默认情况下挂载的服务帐户令牌使用 tmpfs,Secrets 使用 tmpfs 等)
在 Linux 6.3 中支持 idmap 挂载的一些流行文件系统是:btrfs、ext4、xfs、fat、tmpfs、overlayfs。
此外,容器运行时及其底层 OCI 运行时必须支持用户命名空间。以下 OCI 运行时提供支持
- crun 版本 1.9 或更高版本(建议使用 1.13+ 版本)。
注意
许多 OCI 运行时不包括在 Linux Pod 中使用用户命名空间所需的支持。如果您使用托管 Kubernetes 或从软件包下载并设置了它,则集群中的节点很可能使用不支持此功能的运行时。例如,使用最广泛的 OCI 运行时是 runc
,而 1.1.z
版本的 runc 不支持 Kubernetes 用户命名空间实现所需的所有功能。
如果有比 1.1 更新的 runc 版本可用,请查看其文档和发行说明以了解兼容性(特别要注意 idmap 挂载支持,因为这是缺少的功能)。
要将用户命名空间与 Kubernetes 一起使用,您还需要使用 CRI 容器运行时 在 Kubernetes Pod 中使用此功能
- CRI-O:版本 1.25(及更高版本)支持容器的用户命名空间。
containerd v1.7 与 Kubernetes v1.27 到 v1.30 中的用户命名空间支持不兼容。Kubernetes v1.25 和 v1.26 使用了较早的实现,该实现**与** containerd v1.7 在用户命名空间支持方面**兼容**。如果您使用的 Kubernetes 版本不是 1.30,请查看该版本 Kubernetes 的文档以获取最相关的信息。如果有比 v1.7 更新的 containerd 版本可用,请同时查看 containerd 文档以获取兼容性信息。
您可以在 GitHub 上的一个问题中查看 cri-dockerd 中用户命名空间支持的状态。
简介
用户命名空间是一项 Linux 功能,允许将容器中的用户映射到主机中的不同用户。此外,授予用户命名空间中 Pod 的功能仅在该命名空间内有效,在命名空间外无效。
Pod 可以通过将 pod.spec.hostUsers
字段设置为 false
来选择使用用户命名空间。
kubelet 将选择 Pod 映射到的主机 UID/GID,并以保证同一节点上的两个 Pod 不使用相同映射的方式进行选择。
pod.spec
中的 runAsUser
、runAsGroup
、fsGroup
等字段始终指的是容器内的用户。
启用此功能时,有效的 UID/GID 范围为 0-65535。这适用于文件和进程(runAsUser
、runAsGroup
等)。
使用此范围之外的 UID/GID 的文件将被视为属于溢出 ID,通常为 65534(在 /proc/sys/kernel/overflowuid
和 /proc/sys/kernel/overflowgid
中配置)。但是,即使以 65534 用户/组身份运行,也无法修改这些文件。
如果激活了用户命名空间,则大多数需要以 root 身份运行但不需要访问其他主机命名空间或资源的应用程序应该可以继续正常运行,而无需进行任何更改。
了解 Pod 的用户命名空间
几个默认配置的容器运行时(如 Docker Engine、containerd、CRI-O)使用 Linux 命名空间进行隔离。其他技术也存在,并且可以与这些运行时一起使用(例如,Kata Containers 使用虚拟机而不是 Linux 命名空间)。本页面适用于使用 Linux 命名空间进行隔离的容器运行时。
创建 Pod 时,默认情况下会使用几个新的命名空间进行隔离:用于隔离容器网络的网络命名空间、用于隔离进程视图的 PID 命名空间等。如果使用用户命名空间,则会将容器中的用户与节点中的用户隔离。
这意味着容器可以以 root 身份运行并映射到主机上的非 root 用户。在容器内部,进程会认为它以 root 身份运行(因此 apt
、yum
等工具可以正常工作),而实际上该进程在主机上没有权限。例如,如果您从主机执行 ps aux
来检查容器进程以哪个用户身份运行,则可以验证这一点。ps
显示的用户与您在容器内执行 id
命令时看到的不一样。
这种抽象限制了可能发生的事情,例如,如果容器设法逃逸到主机。鉴于容器在主机上以非特权用户身份运行,因此它可以对主机执行的操作受到限制。
此外,由于每个 Pod 上的用户将被映射到主机中不同的非重叠用户,因此它们可以对其他 Pod 执行的操作也受到限制。
授予 Pod 的功能也仅限于 Pod 用户命名空间,并且在该命名空间之外大部分无效,有些甚至完全无效。以下是两个例子
- 如果将
CAP_SYS_MODULE
授予使用用户命名空间的 Pod,则该功能没有任何效果,Pod 无法加载内核模块。 CAP_SYS_ADMIN
仅限于 Pod 的用户命名空间,在该命名空间之外无效。
如果不使用用户命名空间,则在容器突破的情况下,以 root 身份运行的容器在节点上具有 root 权限。如果向容器授予了一些功能,则这些功能在主机上也有效。当我们使用用户命名空间时,这些都不是真的。
如果您想了解更多关于使用用户命名空间时会发生哪些变化的详细信息,请参阅 man 7 user_namespaces
。
设置节点以支持用户命名空间
默认情况下,kubelet 会为 Pod 分配 0-65535 范围以上的 UID/GID,前提是主机的文件和进程使用此范围内的 UID/GID,这是大多数 Linux 发行版的标准做法。这种方法可以防止主机的 UID/GID 与 Pod 的 UID/GID 之间发生任何重叠。
避免重叠对于减轻CVE-2021-25741等漏洞的影响非常重要,在这些漏洞中,Pod 可能会读取主机中的任意文件。如果 Pod 和主机的 UID/GID 不重叠,则 Pod 可以执行的操作将受到限制:Pod UID/GID 将与主机的文件所有者/组不匹配。
kubelet 可以为 Pod 使用自定义的用户 ID 和组 ID 范围。要配置自定义范围,节点需要具有
- 系统中的
kubelet
用户(您不能在此处使用任何其他用户名) - 安装了
getsubids
二进制文件(shadow-utils的一部分)并且位于 kubelet 二进制文件的PATH
中。 kubelet
用户的从属 UID/GID 配置(请参阅man 5 subuid
和man 5 subgid
)。
此设置仅收集 UID/GID 范围配置,不会更改执行 kubelet
的用户。
为 kubelet
用户分配的从属 ID 范围必须遵循一些限制
启动 Pod UID 范围的从属用户 ID 必须是 65536 的倍数,并且必须大于或等于 65536。换句话说,您不能对 Pod 使用 0-65535 范围内的任何 ID;kubelet 强制执行此限制是为了防止意外创建不安全的配置。
从属 ID 计数必须是 65536 的倍数
从属 ID 计数必须至少为
65536 x <maxPods>
,其中<maxPods>
是可以在节点上运行的最大 Pod 数。您必须为用户 ID 和组 ID 分配相同的范围,其他用户是否具有与组 ID 范围不对齐的用户 ID 范围并不重要。
任何分配的范围都不应与任何其他分配重叠。
从属配置必须只有一行。换句话说,您不能有多个范围。
例如,您可以将 /etc/subuid
和 /etc/subgid
定义为对 kubelet
用户都包含以下条目
# The format is
# name:firstID:count of IDs
# where
# - firstID is 65536 (the minimum value possible)
# - count of IDs is 110 (default limit for number of) * 65536
kubelet:65536:7208960
与 Pod 安全准入检查集成
Kubernetes v1.29 [alpha]
对于启用用户命名空间的 Linux Pod,Kubernetes 以受控方式放宽了 Pod 安全标准 的应用。此行为可以通过 功能门控 UserNamespacesPodSecurityStandards
来控制,该门控允许最终用户提前选择加入。如果使用功能门控,管理员必须确保集群中的所有节点都启用了用户命名空间。
如果启用关联的功能门控并创建一个使用用户命名空间的 Pod,则即使在强制执行“基线”或“受限”pod 安全标准的上下文中,以下字段也不会受到限制。此行为不会带来安全问题,因为具有用户命名空间的 Pod 内部的 root
实际上指的是容器内的用户,该用户永远不会映射到主机上的特权用户。以下是这些情况下 Pod 不检查的字段列表
spec.securityContext.runAsNonRoot
spec.containers[*].securityContext.runAsNonRoot
spec.initContainers[*].securityContext.runAsNonRoot
spec.ephemeralContainers[*].securityContext.runAsNonRoot
spec.securityContext.runAsUser
spec.containers[*].securityContext.runAsUser
spec.initContainers[*].securityContext.runAsUser
spec.ephemeralContainers[*].securityContext.runAsUser
限制
当为 Pod 使用用户命名空间时,不允许使用其他主机命名空间。特别是,如果设置 hostUsers: false
,则不允许设置以下任何一项
hostNetwork: true
hostIPC: true
hostPID: true
下一步
此页面上的项目指的是提供 Kubernetes 所需功能的第三方产品或项目。Kubernetes 项目作者不对这些第三方产品或项目负责。有关更多详细信息,请参阅 CNCF 网站指南。
在提议添加额外第三方链接的更改之前,您应该阅读 内容指南。