隔离具有用户命名空间的容器

Linux 命名空间为正在运行的进程提供隔离,限制其访问 添加到系统资源中,而正在运行的进程不知道这些限制。 有关 Linux 命名空间的更多信息,请参阅 Linux 命名空间

防止来自容器内部的权限提升攻击的最佳方法是 将容器的应用程序配置为以非特权用户身份运行。为 其进程必须以容器内用户身份运行的容器,则您 可以将此用户重新映射到 Docker 主机上的低权限用户。映射的 user 被分配了一系列 UID,这些 UID 在命名空间中正常运行 UID 从 0 到 65536,但对主机本身没有权限。root

关于重新映射和从属用户和组 ID

重新映射本身由两个文件处理:和 。 每个文件的工作方式都相同,但一个文件与用户 ID 范围有关,而 other 替换为组 ID 范围。请考虑 中的以下条目:/etc/subuid/etc/subgid/etc/subuid

testuser:231072:65536

这意味着 将按顺序分配 65536 个整数的从属用户 ID 范围。UID 映射到 namespace (在本例中为容器内) 设置为 UID () 。UID 映射为 UID ,依此类推。如果进程尝试提升权限 在命名空间之外,进程作为非特权高号运行 主机上的 UID,它甚至没有映射到真实用户。这意味着 在 Host 系统上完全没有权限。testuser2310722310720root2310731

注意

可以为给定用户或组分配多个从属范围 通过在 OR 文件中为同一用户或组添加多个不重叠的映射。在这种情况下,Docker 只使用第一个 5 个映射,根据内核只有 5 个条目的限制 in 和 ./etc/subuid/etc/subgid/proc/self/uid_map/proc/self/gid_map

将 Docker 配置为使用该功能时,您可以选择 指定现有用户和/或组,也可以指定 。如果你 指定 ,将创建用户和组并将其用于此 目的。userns-remapdefaultdefaultdockremap

警告

某些分发不会自动将新组添加到 和 文件中。如果是这样的话,您可能已经 手动编辑这些文件并分配非重叠范围。此步骤为 在先决条件中介绍。/etc/subuid/etc/subgid

范围不重叠非常重要,这样过程就无法获得 访问。在大多数 Linux 发行版中,系统实用程序 在添加或删除用户时管理范围。

这种重新映射对容器是透明的,但会引入一些 容器需要访问 Docker 主机上的资源,例如将挂载绑定到文件系统的区域 系统用户无法写入的从安全的角度来看,最好 避免这些情况。

先决条件

  1. 从属 UID 和 GID 范围必须与现有用户关联。 即使关联是实现细节。用户拥有 下的命名空间存储目录。如果你没有 想要使用现有用户,Docker 可以为您创建一个并使用该用户。如果 要使用现有的用户名或用户 ID,它必须已经存在。 通常,这意味着相关条目需要位于 和 中,但如果您使用其他 authentication 后端,此要求可能会以不同的方式转换。/var/lib/docker//etc/passwd/etc/group

    要验证这一点,请使用以下命令:id

    $ id testuser
    
    uid=1001(testuser) gid=1001(testuser) groups=1001(testuser)
    
  2. 在主机上处理命名空间重新映射的方式是使用两个文件,而 .这些文件通常是托管的 在添加或删除用户或组时自动,但在某些 发行版,您可能需要手动管理这些文件。/etc/subuid/etc/subgid

    每个文件都包含三个字段:用户的用户名或 ID,后跟 开始的 UID 或 GID(在命名空间中被视为 UID 或 GID 0) 以及用户可用的 UID 或 GID 的最大数量。例如 给定以下条目:

    testuser:231072:65536

    这意味着由 由主机 UID 拥有(看起来像 namespace) 通过 296607 (231072 + 65536 - 1) 进行。这些范围不应重叠, 来确保命名空间进程无法访问彼此的命名空间。testuser2310720

    添加用户后,请检查 和 查看您的 user 在每个 URL 中都有一个条目。如果没有,您需要添加它,小心 避免重叠。/etc/subuid/etc/subgid

    如果您想使用 Docker 自动创建的用户, 在 After 之后检查这些文件中的条目 配置并重新启动 Docker。dockremapdockremap

  3. 如果 Docker 主机上有任何位置存在非特权 用户需要写入,调整这些位置的权限 因此。如果您想使用用户 自动创建,但您无法修改 权限,直到配置并重新启动 Docker 之后。dockremap

  4. 启用可有效屏蔽现有镜像和容器 层以及 .这是 因为 Docker 需要调整这些资源的所有权,实际上 将它们存储在 中的子目录中。最好启用 此功能在新的 Docker 安装上,而不是现有安装上。userns-remap/var/lib/docker//var/lib/docker/

    同样,如果禁用,则无法访问任何 启用时创建的资源。userns-remap

  5. 检查对 user 的限制 命名空间,以确保您的用例是可能的。

在守护程序上启用 userns-remap

您可以从 flag 开始,也可以按照 this 进行操作 使用配置文件配置守护程序的过程。 建议使用该方法。如果您使用该标志,请使用以下 命令作为模型:dockerd--userns-remapdaemon.jsondaemon.json

$ dockerd --userns-remap="testuser:testuser"
  1. 编辑。假设文件以前为空, 以下条目允许使用名为 的用户和组。您可以按 ID 或名称对用户和组进行寻址。您只需要 如果组名称或 ID 与用户名或 ID 不同,请指定组名称或 ID。如果 您提供用户和组名称或 ID,用冒号分隔 () 字符。以下格式都适用于该值,假设 的 UID 和 GID 为:/etc/docker/daemon.jsonuserns-remaptestuser:testuser1001

    • testuser
    • testuser:testuser
    • 1001
    • 1001:1001
    • testuser:1001
    • 1001:testuser
    {
      "userns-remap": "testuser"
    }

    注意

    要使用该用户并让 Docker 为您创建它, 将值设置为 ,而不是 。dockremapdefaulttestuser

    保存文件并重新启动 Docker。

  2. 如果您使用的是该用户,请验证 Docker 是否使用 命令。dockremapid

    $ id dockremap
    
    uid=112(dockremap) gid=116(dockremap) groups=116(dockremap)
    

    验证该条目是否已添加到 和 中:/etc/subuid/etc/subgid

    $ grep dockremap /etc/subuid
    
    dockremap:231072:65536
    
    $ grep dockremap /etc/subgid
    
    dockremap:231072:65536
    

    如果这些条目不存在,请以用户身份编辑文件,然后 分配一个起始 UID 和分配最高的 GID 加上 offset (在本例中为 )。请注意,不允许 范围。root65536

  3. 使用命令验证以前的镜像是否不可用。输出应为空。docker image ls

  4. 从镜像启动容器。hello-world

    $ docker run hello-world
    
  5. 验证 named 中是否存在命名空间目录 替换为命名空间用户的 UID 和 GID,该 UID 和 GID 拥有, 而不是 group-or-world-readable。一些子目录仍然是 拥有并具有不同的权限。/var/lib/docker/root

    $ sudo ls -ld /var/lib/docker/231072.231072/
    
    drwx------ 11 231072 231072 11 Jun 21 21:19 /var/lib/docker/231072.231072/
    
    $ sudo ls -l /var/lib/docker/231072.231072/
    
    total 14
    drwx------ 5 231072 231072 5 Jun 21 21:19 aufs
    drwx------ 3 231072 231072 3 Jun 21 21:21 containers
    drwx------ 3 root   root   3 Jun 21 21:19 image
    drwxr-x--- 3 root   root   3 Jun 21 21:19 network
    drwx------ 4 root   root   4 Jun 21 21:19 plugins
    drwx------ 2 root   root   2 Jun 21 21:19 swarm
    drwx------ 2 231072 231072 2 Jun 21 21:21 tmp
    drwx------ 2 root   root   2 Jun 21 21:19 trust
    drwx------ 2 231072 231072 3 Jun 21 21:19 volumes
    

    您的目录列表可能会有一些差异,尤其是当您 使用与 . 不同的容器存储驱动程序。aufs

    将改用重新映射的用户所拥有的目录 的 未使用的版本(如此处的示例中所示) 可以删除。Docker 不使用它们,而 启用。/var/lib/docker//var/lib/docker/tmp/userns-remap

禁用容器的命名空间重新映射

如果在守护进程上启用用户命名空间,则所有容器都使用 用户命名空间。在某些情况下,例如特权 containers 时,您可能需要为特定容器禁用用户命名空间。 有关其中一些限制,请参阅用户命名空间已知限制

要禁用特定容器的用户命名空间,请将该标志添加到 、 或 命令中。--userns=hostdocker container createdocker container rundocker container exec

使用此标志时有一个副作用:不会为该容器启用用户重新映射,但由于只读(镜像)层在容器之间共享,因此容器文件系统的所有权仍将被重新映射。

这意味着整个容器文件系统将属于守护进程配置中指定的用户(在上面的示例中)。这可能会导致容器内的程序出现意外行为。例如(检查其二进制文件是否属于 user )或带有标志的二进制文件。--userns-remap231072sudo0setuid

用户命名空间已知限制

以下标准 Docker 功能与运行 Docker 不兼容 启用了用户命名空间的守护进程:

  • 与主机共享 PID 或 NET 命名空间 ( 或 )。--pid=host--network=host
  • 不知道或无法使用的外部(卷或存储)驱动程序 守护程序用户映射。
  • 使用 mode 标志 on,而不指定 .--privilegeddocker run--userns=host

用户命名空间是一项高级功能,需要与其他 能力。例如,如果卷是从主机挂载的,则文件所有权 如果需要对卷内容的读或写访问权,则必须预先安排。

虽然用户命名空间容器进程中的 root 用户具有许多 超级用户在容器(Linux 内核)中的预期权限 根据内部知识施加限制,即这是一个用户命名空间 过程。一个值得注意的限制是无法使用该命令。 当 运行时,在容器内创建设备的权限被拒绝 用户。mknodroot