Docker和--userns重新映射,如何管理卷权限以在主机和容器之间共享数据?

Docker和--userns重新映射,如何管理卷权限以在主机和容器之间共享数据?,docker,Docker,在docker中,在容器中创建的文件在从主机检查时往往具有不可预测的所有权。默认情况下,卷上文件的所有者是root(uid 0),但一旦容器中涉及到非root用户帐户并写入文件系统,从主机的角度来看,所有者或多或少会变得随机 当您需要使用调用docker命令的同一用户帐户从主机访问卷数据时,这是一个问题 典型的解决办法是 在DockerFile中强制用户在创建时使用UID(不可移植) 将主机用户的UID作为环境变量传递给docker run命令,然后在入口点脚本中的卷上运行一些chown命令

在docker中,在容器中创建的文件在从主机检查时往往具有不可预测的所有权。默认情况下,卷上文件的所有者是root(uid 0),但一旦容器中涉及到非root用户帐户并写入文件系统,从主机的角度来看,所有者或多或少会变得随机

当您需要使用调用docker命令的同一用户帐户从主机访问卷数据时,这是一个问题

典型的解决办法是

  • 在DockerFile中强制用户在创建时使用UID(不可移植)
  • 将主机用户的UID作为环境变量传递给
    docker run
    命令,然后在入口点脚本中的卷上运行一些
    chown
    命令
这两种解决方案都可以在一定程度上控制容器外部的实际权限

我希望用户名称空间是这个问题的最终解决方案。我已经用最近发布的1.10版运行了一些测试,并将--userns重新映射设置为我的桌面帐户。但是,我不确定它是否能使装载卷上的文件所有权更容易处理,我担心它实际上可能恰恰相反

假设我启动这个基本容器

docker run -ti -v /data debian:jessie /bin/bash
echo 'hello' > /data/test.txt
exit
docker run --rm -ti -v /vol1:/vol1 busybox sh
echo "Hello from container" > /vol1/file
exit
然后检查来自主机的内容:

ls -lh /var/lib/docker/100000.100000/volumes/<some-id>/_data/

-rw-r--r-- 1 100000 100000 6 Feb  8 19:43 test.txt
ls-lh/var/lib/docker/100000.100000/volumes//u数据/
-rw-r--r--1 100000 100000 2月6日19:43 test.txt
这个数字“100000”是我的主机用户的子UID,但由于它与我的用户的UID不对应,我仍然无法在没有权限的情况下编辑test.txt。此子用户似乎与docker之外的实际常规用户没有任何关联。它没有映射回来

由于命名空间中出现了
UID->sub-UID
映射,本文前面提到的解决方法(在主机和容器之间对齐UID)不再有效


那么,是否有一种方法可以在启用用户名称空间的情况下运行docker(以提高安全性),同时仍然使运行docker的主机用户能够拥有卷上生成的文件

如果您可以预先安排用户和组,那么就可以以这种特定的方式分配UID和GID,以便主机用户与容器中的命名空间用户相对应

下面是一个例子(Ubuntu 14.04,Docker 1.10):

  • 创建具有固定数字ID的某些用户:

    useradd -u 5000 ns1
    
    groupadd -g 500000 ns1-root
    groupadd -g 501000 ns1-user1
    
    useradd -u 500000 -g ns1-root ns1-root
    useradd -u 501000 -g ns1-user1 ns1-user1 -m
    
  • /etc/subuid
    /etc/subgid
    文件中手动编辑自动生成的下级ID范围:

    ns1:500000:65536
    
    (注意,由于
    /etc/login.defs
    中的
    MAX_UID
    MAX_GID
    限制,没有关于
    ns1 root
    ns1-user1
    的记录)

  • /etc/default/docker
    中启用用户名称空间:

    DOCKER_OPTS="--userns-remap=ns1"
    
    重新启动守护程序
    服务docker Restart
    ,确保创建了
    /var/lib/docker/500000.500000
    目录

    现在,在容器内部有
    root
    user1
    ,在主机上--
    ns1 root
    ns1-user1
    ,具有匹配的ID

    更新:为了保证非root用户在容器中有固定的ID(例如user1 1000:1000),请在映像构建期间显式创建它们

  • 试驾:

  • 准备卷目录

    mkdir /vol1
    chown ns1-root:ns1-root /vol1
    
  • 从容器中试一下

    docker run -ti -v /data debian:jessie /bin/bash
    echo 'hello' > /data/test.txt
    exit
    
    docker run --rm -ti -v /vol1:/vol1 busybox sh
    echo "Hello from container" > /vol1/file
    exit
    
  • 从主机上尝试

    passwd ns1-root
    login ns1-root
    cat /vol1/file
    echo "can write" >> /vol1/file
    

  • 不是便携式的,看起来像黑客,但很有效

    您可以通过使用来避免权限问题

    所有权设置为目标的用户和主组。例如,复制到容器中的文件是使用root用户的
    UID:GID
    创建的。复制到本地计算机的文件是使用调用
    docker cp
    命令的用户的
    UID:GID
    创建的

    以下是切换到使用
    docker cp
    的示例:

    $ docker run -ti -v /data debian:jessie /bin/bash
    root@e33bb735a70f:/# echo 'hello' > /data/test.txt
    root@e33bb735a70f:/# exit
    exit
    $ docker volume ls
    DRIVER              VOLUME NAME
    local               f073d0e001fb8a95ad8d919a5680e72b21a457f62a40d671b63c62ae0827bf93
    $ sudo ls -l /var/lib/docker/100000.100000/volumes/f073d0e001fb8a95ad8d919a5680e72b21a457f62a40d671b63c62ae0827bf93/_data
    total 4
    -rw-r--r-- 1 100000 100000 6 Oct  6 10:34 test.txt
    $ docker ps -a
    CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                          PORTS               NAMES
    e33bb735a70f        debian:jessie       "/bin/bash"         About a minute ago   Exited (0) About a minute ago                       determined_hypatia
    $ docker cp determined_hypatia:/data/test.txt .
    $ ls -l test.txt 
    -rw-r--r-- 1 don don 6 Oct  6 10:34 test.txt
    $ cat test.txt
    hello
    $ 
    
    但是,如果只想从容器中读取文件,则不需要命名卷。此示例使用命名容器而不是命名卷:

    $ docker run -ti --name sandbox1 debian:jessie /bin/bash
    root@93d098233cf3:/# echo 'howdy' > /tmp/test.txt
    root@93d098233cf3:/# exit
    exit
    $ docker cp sandbox1:/tmp/test.txt .
    $ ls -l test.txt
    -rw-r--r-- 1 don don 6 Oct  6 10:52 test.txt
    $ cat test.txt
    howdy
    $ 
    

    我发现命名卷在将文件复制到容器中时非常有用,如中所述。

    一种解决方法是在构建时动态分配用户的uid以匹配主机

    示例
    Dockerfile

    FROM ubuntu
    # Defines argument which can be passed during build time.
    ARG UID=1000
    # Create a user with given UID.
    RUN useradd -d /home/ubuntu -ms /bin/bash -g root -G sudo -u $UID ubuntu
    # Switch to ubuntu user by default.
    USER ubuntu
    # Check the current uid of the user.
    RUN id
    # ...
    
    FROM someexistingcontainer
    ARG UID=1000
    USER root
    # This assumes you've the existing user ubuntu.
    RUN usermod -u $UID ubuntu
    USER ubuntu
    
    然后构建为:

    docker build --build-arg UID=$UID -t mycontainer .
    
    并运行为:

    docker run mycontainer
    
    docker-compose build --build-arg UID=$UID myservice; docker-compose run myservice
    

    如果已有容器,请使用以下
    Dockerfile
    创建包装容器:

    FROM ubuntu
    # Defines argument which can be passed during build time.
    ARG UID=1000
    # Create a user with given UID.
    RUN useradd -d /home/ubuntu -ms /bin/bash -g root -G sudo -u $UID ubuntu
    # Switch to ubuntu user by default.
    USER ubuntu
    # Check the current uid of the user.
    RUN id
    # ...
    
    FROM someexistingcontainer
    ARG UID=1000
    USER root
    # This assumes you've the existing user ubuntu.
    RUN usermod -u $UID ubuntu
    USER ubuntu
    
    这可以包装在
    docker compose.yml
    中,如:

    version: '3.4'
    services:
      myservice:
        command: id
        image: myservice
        build:
          context: .
        volumes:
        - /data:/data:rw
    
    然后构建并运行为:

    docker run mycontainer
    
    docker-compose build --build-arg UID=$UID myservice; docker-compose run myservice
    

    我认为,如果要在主机和容器之间共享卷,那么用户名称空间将不会成为解决方案的一部分。第二个选项(“将主机用户的UID作为环境变量传递给docker run命令,然后在入口点脚本中的卷上运行一些chown命令”)可能是最好的解决方案。docker本身似乎不鼓励使用主机安装的可写卷。由于我没有运行云服务,只使用我自己的可信映像,我现在想知道,用户NS的安全优势是否值得牺牲这么多便利。@StéphaneC。您是否找到了更好的方法?不幸的是,不使用用户名称空间和从主机传递UID仍然是我的选择。我希望将来会有一个正确的方法来映射用户。我对此表示怀疑,但我仍然保持警惕。非常有趣,应该得到+1。但是您仍然需要确保图像中的user1被分配了uid1000。否则,您无法确保它将在主机上接收UID 501000。顺便说一句,如果我们正在运行许多不同的图像,并且用户ID设置为1000,那么我们绝对确定公式总是
    subUID lower bound+UID in image
    。说得好!添加了关于修复图像内部ID的说明。作为