用户命名空间

特性状态: Kubernetes v1.30 [beta]

本页介绍如何在 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+ 版本)。

要在 Kubernetes 中使用用户命名空间,您还需要使用 CRI 容器运行时才能在 Kubernetes Pod 中使用此功能

  • CRI-O:版本 1.25(及更高版本)支持容器的用户命名空间。

containerd v1.7 与 Kubernetes v1.27 到 v1.31 中的用户命名空间支持不兼容。Kubernetes v1.25 和 v1.26 使用了更早的实现,该实现containerd v1.7 兼容,在用户命名空间支持方面也是如此。如果您使用的是除 1.31 以外版本的 Kubernetes,请查看该版本 Kubernetes 的文档以获取最相关的 信息。如果 containerd 的版本比 v1.7 更新,可供使用,请查看 containerd 文档以了解兼容性信息。

您可以在 GitHub 上的问题中查看 cri-dockerd 中用户命名空间支持的状态。

简介

用户命名空间是 Linux 的一项功能,它允许将容器中的用户映射到主机中的不同用户。此外,授予用户命名空间中 Pod 的功能仅在命名空间内有效,在命名空间外部无效。

Pod 可以通过将pod.spec.hostUsers 字段设置为false来选择使用用户命名空间。

kubelet 将选择 Pod 映射到的主机 UID/GID,并以保证同一节点上的两个 Pod 不使用相同映射的方式进行操作。

pod.spec 中的runAsUserrunAsGroupfsGroup 等字段始终引用容器内部的用户。

启用此功能后,有效的 UID/GID 范围为 0-65535。这适用于文件和进程(runAsUserrunAsGroup 等)。

使用超出此范围的 UID/GID 的文件将被视为属于溢出 ID,通常为 65534(在/proc/sys/kernel/overflowuid/proc/sys/kernel/overflowgid 中配置)。但是,即使是以 65534 用户/组的身份运行,也无法修改这些文件。

大多数需要以 root 身份运行但不需要访问其他主机命名空间或资源的应用程序,如果启用了用户命名空间,应该可以正常运行,而无需进行任何更改。

了解 Pod 的用户命名空间

许多容器运行时及其默认配置(如 Docker Engine、containerd、CRI-O)使用 Linux 命名空间进行隔离。其他技术也存在,并且可以与这些运行时一起使用(例如,Kata Containers 使用 VM 而不是 Linux 命名空间)。本页适用于使用 Linux 命名空间进行隔离的容器运行时。

创建 Pod 时,默认情况下,将使用几个新的命名空间进行隔离:一个网络命名空间用于隔离容器的网络,一个 PID 命名空间用于隔离进程的视图,等等。如果使用用户命名空间,这将隔离容器中的用户与节点中的用户。

这意味着容器可以以 root 身份运行,并映射到主机上的非 root 用户。在容器内部,进程会认为它以 root 身份运行(因此aptyum 等工具可以正常工作),而在现实中,进程在主机上没有权限。您可以通过以下方式验证这一点,例如,如果您通过从主机执行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 分配的 UID/GID 超过 0-65535 范围,这是基于主机文件和进程使用此范围内的 UID/GID 的假设,这 是大多数 Linux 发行版的标准。这种方法可以防止主机与 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 subuidman 5 subgid)。

此设置仅收集 UID/GID 范围配置,不会更改执行kubelet的用户。

您必须遵循为kubelet用户分配的从属 ID 范围的一些约束

  • 从属用户 ID(即 Pod 的 UID 范围的起始 ID)必须是 65536 的倍数,并且还必须大于或等于 65536。换句话说,您不能将 0-65535 范围内的任何 ID 用于 Pod;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 网站指南

您应该在提出添加额外第三方链接的更改之前阅读内容指南

上次修改时间:2024 年 3 月 7 日下午 12:46 PST:content: Add OCI runtime requirements for userns (89e0ec8a77)