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