使用用户命名空间隔离容器

Linux 命名空间为正在运行的进程提供隔离,限制它们对系统资源的访问,而正在运行的进程却意识不到这些限制。有关 Linux 命名空间的更多信息,请参阅 Linux 命名空间

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

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

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

testuser:231072:65536

这意味着testuser分配了一个从属用户 ID 范围231072 以及接下来的 65536 个整数。 UID231072在命名空间内(在本例中为容器内)映射为 UID 0( root)。 UID231073 映射为 UID 1,依此类推。如果进程尝试在命名空间之外提升权限,则该进程将作为主机上的非特权高编号 UID 运行,该 UID 甚至不会映射到真实用户。这意味着该进程在主机系统上根本没有特权。

笔记

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

当您配置 Docker 使用该userns-remap功能时,您可以选择指定现有用户和/或组,也可以指定default.如果您指定,则会创建default用户和组并将其用于此目的。dockremap

警告

某些发行版(例如 RHEL 和 CentOS 7.3)不会自动将新组添加到/etc/subuid/etc/subgid文件中。在这种情况下,您负责编辑这些文件并分配不重叠的范围。先决条件中介绍了此步骤 。

范围不重叠非常重要,这样进程就无法访问不同的命名空间。在大多数 Linux 发行版上,当您添加或删除用户时,系统实用程序会为您管理范围。

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

先决条件

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

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

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

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

    testuser:231072:65536

    这意味着由 启动的用户命名空间进程由主机 UID (看起来像命名空间内的 UID)到 296607 (231072 + 65536 - 1)testuser拥有。这些范围不应重叠,以确保命名空间进程无法访问彼此的命名空间。2310720

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

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

  3. 如果Docker主机上有非特权用户需要写入的位置,请相应调整这些位置的权限。如果您想使用 Docker 自动创建的用户,也是如此dockremap,但只有在配置并重新启动 Docker 后才能修改权限。

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

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

  5. 检查 用户命名空间的限制,以确保您的用例可行。

在守护进程上启用 userns-remap

您可以dockerd--userns-remap标志开始或按照此过程使用配置文件配置守护程序daemon.jsondaemon.json推荐该方法。如果使用该标志,请使用以下命令作为模型:

$ dockerd --userns-remap="testuser:testuser"
  1. 编辑/etc/docker/daemon.json。假设该文件以前为空,则以下条目允许userns-remap使用名为 的用户和组 testuser。您可以按 ID 或名称来称呼用户和组。如果组名或 ID 与用户名或 ID 不同,则只需指定组名或 ID。如果您同时提供用户和组名称或 ID,请用冒号 ( :) 字符分隔它们。以下格式均适用于该值,假设 UID 和 GIDtestuser1001

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

    笔记

    要使用该dockremap用户并让 Docker 为您创建该用户,请将值设置为default而不是testuser

    保存文件并重新启动 Docker。

  2. 如果您正在使用该dockremap用户,请验证 Docker 是否使用该id命令创建了该用户。

    $ 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
    

    如果这些条目不存在,请以用户身份编辑文件root并分配起始 UID 和 GID,即分配的最高的 UID 和 GID 加上偏移量(在本例中为65536)。请注意范围内不要有任何重叠。

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

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

    $ docker run hello-world
    
  5. 验证命名空间目录中是否存在/var/lib/docker/以命名空间用户的 UID 和 GID 命名的目录,该目录由该 UID 和 GID 拥有,并且不是组或世界可读的。某些子目录仍由其拥有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.

    使用重新映射的用户拥有的目录而不是直接位于其下的相同目录,并且可以删除/var/lib/docker/未使用的版本(例如此处的示例)。 Docker 在启用/var/lib/docker/tmp/时不会使用它们。userns-remap

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

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

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

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

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

用户命名空间已知限制

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

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

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

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