docker配置使用User Namespace

官方文档

概述

docker容器运行时,默认情况下容器内进程的运行用户就是docker host上的root用户,这样运行时如果将host中的某些目录挂载到容器中时,容器内的进程拥有这些目录的所有权限,这样时不安全的。

解决方法有两种,一种是使用非root用户运行容器,另一种就是使用下面讲的user namespace方式。

Linux的user namespace可以对运行的进程做用户隔离,在这些进程不感知的情况下,限制它们对系统资源的访问。可以通过Linux的user namespace功能,将容器内的root用户映射为docker host中的低权限用户。

docker启用user namespace

docker在1.10(2016-02-04)版本中才实现了使用user namespace的功能,称之为 userns-remap

  • 首先在docker host中创建用户和组

    1
    2
    3
    4
    [root@localhost ~]# groupadd -g 5000 dockertest
    [root@localhost ~]# useradd -u 5000 -g dockertest dockertest
    [root@localhost ~]# id dockertest
    uid=5000(dockertest) gid=5000(dockertest) groups=5000(dockertest)
  • 修改docker host中的 /etc/subuid 和 /etc/subgid 配置

    1
    2
    3
    4
    5
    6
    [root@localhost ~]# cat /etc/subuid
    dockertest:5000:1 # 容器中root用户(uid=0)映射为docker host中的uid为5000的用户
    dockertest:100000:65535 # 容器中的用户 1 ~ 65536 映射为 host中的 uid 为 100000 ~ 100000+65535
    [root@localhost ~]# cat /etc/subgid
    dockertest:5000:1 # 同subuid
    dockertest:100000:65535 # 同subuid

也可以直接配置dockertest:100000:65536,这样的话,容器的root映射为host的uid=100000的用户

  • 修改/etc/docker/daemon.json文件,增加 "userns-remap":"dockertest" 配置,然后重启docker服务

  • 启动容器进行检查

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [root@localhost ~]# docker run -d --rm centos:8 sleep 300
    152a7eed420e3d84eefebf1fadc1f1f9d8cfa2fd607d59c03a806d822f5e57d3
    [root@localhost ~]# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    152a7eed420e centos:8 "sleep 300" 6 seconds ago Up 3 seconds jovial_sutherland
    [root@localhost ~]# ps -ef |grep -i sleep
    dockert+ 12387 12371 0 20:20 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 300
    [root@localhost ~]# cat /proc/12387/uid_map
    0 5000 1 # 将host 上的 5000 用户映射为容器内的 0(root)用户
    1 100000 65535 # 将host 上的 100000 用户映射为容器内的 1 用户
    [root@localhost ~]# cat /proc/12387/gid_map
    0 5000 1
    1 100000 65535
    [root@localhost ~]#

docker使用userns-remap时,在docker_home目录下会自动创建一个5000.5000(根据实际情况会有变化)的目录

在修改daemon.json文件时,可以将userns-remap的值设置为已经存在的用户。也可以指定为默认值(default),如果指定为 default,那么会自动创建用户和组 dockremap。
如果使用default配置,RHEL/centos不会自动将 dockremap 添加到 /etc/subuid 和 /etc/subgid 中,需要手动添加。

容器中禁用namespace映射

当docker daemon启用了user namespace后,所有启动的容器默认都会使用user namespace功能。

此时如果想要禁用容器的user namespace(如想要启动一个具有特权的容器),可以在docker create/docker run/docker exec命令中增加 --userns=host 选项实现。

使用 --userns=host 选项时,不会为该容器进行用户映射,但是由于容器之间共享read-only(image) layers,容器的文件系统所有者仍然会映射为daemon中配置userns-remap用户。这样可能会导致容器内某些程序运行时出现一些意外,For instance sudo (which checks that its binaries belong to user 0) or binaries with a setuid flag。

docker使用user namespace的限制

当docker启用user namespace时,会与docker的下面的一些特性无法兼容:

  • sharing PID or NET namespaces with the host(–pid=host or –network=host)
  • external (volume or storage) drivers which are unaware or incapable of using daemon user mappings
  • Using the –privileged mode flag on docker run without also specifying –userns=host。(docker run中使用了–privileged但是没有使用–userns=host)

常见问题

检查Linux内核是否开启了user namespace功能

  • 首先确认内核版本,linux是在内核3.8开始才引入了user namespace功能
  • 查看内核编译选项

    1
    2
    [root@localhost ~]# cat /boot/config-$(uname -r) |grep -i config_user_ns
    CONFIG_USER_NS=y

CentOS7配置userns-remap后启动容器失败