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

时间:2016-02-09 11:58:41

标签: docker

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

当您需要使用调用docker命令的同一用户帐户从主机访问卷数据时,会出现问题。

典型的解决方法是

  • 在Dockerfiles(非可移植)中创建时强制用户uID
  • 将主机用户的UID作为环境变量传递给docker run命令,然后在入口点脚本中的卷上运行一些chown命令。

这两种解决方案都可以控制容器外的实际权限。

我希望用户名称空间成为此问题的最终解决方案。我已经使用最近发布的版本1.10和--userns-remap设置到我的桌面帐户运行了一些测试。但是,我不确定它是否可以使安装卷上的文件所有权更容易处理,我担心它实际上可能正好相反。

假设我启动这个基本容器

docker run -ti -v /data debian:jessie /bin/bash
echo 'hello' > /data/test.txt
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

此号码&#39; 100000&#39;是我的主机用户的子UID,但由于它与我的用户的UID不对应,我仍然无法在没有权限的情况下编辑test.txt。此子用户似乎与docker之外的实际常规用户没有任何关联。它没有映射回来。

由于名称空间中出现UID->sub-UID映射,本文前面提到的由主机和容器之间的UID对齐组成的变通方法不再起作用。

那么,有没有办法在启用用户名称空间的情况下运行docker(为了提高安全性),同时仍然可以让运行docker的主机用户拥有在卷上生成的文件?

3 个答案:

答案 0 :(得分:36)

如果您可以提前预先安排用户和组,则可以以特定方式分配UID和GID,以便主机用户对应容器内的命名空间用户。

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

  1. 创建一些具有固定数字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
    
  2. /etc/subuid/etc/subgid个文件中手动修改自动生成的从属ID范围:

    ns1:500000:65536
    

    (请注意,由于ns1-root中的ns1-user1MAX_UID限制,MAX_GID/etc/login.defs没有记录

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

    DOCKER_OPTS="--userns-remap=ns1"
    

    重启守护进程service docker restart,确保创建/var/lib/docker/500000.500000目录。

    现在,在您拥有rootuser1的容器内,以及主机上的ns1-rootns1-user1,匹配ID < / p>

    更新以保证非root用户在容器中具有固定ID(例如user1 1000:1000),在图像构建期间显式创建它们。

  4. 试验驱动器:

    1. 准备卷目录

      mkdir /vol1
      chown ns1-root:ns1-root /vol1
      
    2. 从容器中试用

      docker run --rm -ti -v /vol1:/vol1 busybox sh
      echo "Hello from container" > /vol1/file
      exit
      
    3. 从主持人那里试试

      passwd ns1-root
      login ns1-root
      cat /vol1/file
      echo "can write" >> /vol1/file
      
    4. 不便携,看起来像黑客,但有效。

答案 1 :(得分:2)

您可以使用docker cp command来避免权限问题。

  

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

以下示例已切换为使用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
$ 

当我想将文件复制到容器中时,我发现命名卷很有用,如this question中所述。

答案 2 :(得分:1)

一种解决方法是在构建时动态分配用户的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
# ...

然后将其构建为:

docker build --build-arg UID=$UID -t mycontainer .

并运行为:

docker run mycontainer

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

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-compose build --build-arg UID=$UID myservice; docker-compose run myservice