了解 docker 中的用户文件所有权: 如何避免更改链接卷的权限

考虑下面这个简单的 Dockerfile:

FROM debian:testing
RUN  adduser --disabled-password --gecos '' docker
RUN  adduser --disabled-password --gecos '' bob

在一个没有其他东西的工作目录里:

docker build -t test .

然后在容器上运行 bash 脚本,将工作目录链接到 bob 主目录的一个新子目录中:

docker run --rm -it -v $(pwd):/home/bob/subdir test

谁拥有集装箱上 subdir的内容? 在集装箱上,运行:

cd /home/bob/subdir
ls -l

我们看到:

-rw-rw-r-- 1 docker docker 120 Oct 22 03:47 Dockerfile

天啊!docker拥有这些东西!回到容器外的主机上,我们看到原始用户仍然拥有 Dockerfile。让我们尝试修复 bob主目录的所有权。在集装箱上,运行:

chown -R bob:bob /home/bob
ls -l

我们看到:

-rw-rw-r-- 1 bob bob 120 Oct 22 03:47 Dockerfile

但是等等! 在集装箱外面,我们现在运行 ls -l

-rw-rw-r-- 1 1001 1001 120 Oct 21 20:47 Dockerfile

我们不再拥有自己的档案,坏消息!


如果我们在上面的例子中只添加了一个用户,那么一切都会进行得更加顺利。出于某种原因,Docker 似乎正在创建它遇到的 第一非根用户所拥有的任何主目录(即使该用户是在早期映像上声明的)。同样,这个 第一用户与我的主用户拥有相同的所有权权限。

问题1是这样吗?有人能给我指出这个文件吗,我只是基于上述实验的推测。

问题2 : 也许这只是因为它们在内核上具有相同的数值,如果我在一个我的主用户不是 id 1000的系统上测试,那么在每种情况下权限都会被更改?

问题3 : 真正的问题当然是,‘我该怎么办?如果 bob在给定的主机上以 bob的身份登录,那么他应该能够以 bob的身份运行容器,并且在他的主机帐户下没有更改文件权限。实际上,他需要以用户 docker的身份运行容器,以避免他的帐户被更改。

我听到你问 我怎么会有这么奇怪的 Dockerfile 文件?。有时候我也想知道。我正在为一个 webapp (RStudio-server)编写一个容器,该容器允许不同的用户登录,它只使用来自 linux 机器的用户名和凭据作为有效的用户名。这给我带来了想要创建多个用户的可能不同寻常的动机。我可以通过仅在运行时创建用户来解决这个问题,一切都很好。但是,我使用的基本映像已经添加了一个单独的 docker用户,这样就可以交互式地使用它,而无需以 root 用户身份运行(按照最佳实践)。这会破坏一切,因为该用户成为 第一用户,并最终拥有一切,所以尝试登录其他用户失败(应用程序无法启动,因为它没有写权限)。让启动脚本运行 chown首先解决了这个问题,但是代价是链接卷更改权限(显然只有在链接卷时才会出现问题)。

120814 次浏览

Is that correct? Can someone point me to documentation of this, I'm just conjecturing based on the above experiment.

Perhaps this is just because they both have the same numerical value on the kernel, and if I tested on a system where my home user was not id 1000 then permissions would get changed in every case?

Have a read of info coreutils 'chown invocation', that might give you a better idea of how file permissions / ownership works.

Basically, though, each file on your machine has a set of bits tacked on to it that defines its permissions and ownership. When you chown a file, you're just setting these bits.

When you chown a file to a particular user/group using the username or group name, chown will look in /etc/passwd for the username and /etc/group for the group to attempt to map the name to an ID. If the username / group name doesn't exist in those files, chown will fail.

root@dc3070f25a13:/test# touch test
root@dc3070f25a13:/test# ll
total 8
drwxr-xr-x  2 root root 4096 Oct 22 18:15 ./
drwxr-xr-x 22 root root 4096 Oct 22 18:15 ../
-rw-r--r--  1 root root    0 Oct 22 18:15 test
root@dc3070f25a13:/test# chown test:test test
chown: invalid user: 'test:test'

However, you can chown a file using IDs to whatever you want (within some upper positive integer bounds, of course), whether there is a user / group that exists with those IDs on your machine or not.

root@dc3070f25a13:/test# chown 5000:5000 test
root@dc3070f25a13:/test# ll
total 8
drwxr-xr-x  2 root root 4096 Oct 22 18:15 ./
drwxr-xr-x 22 root root 4096 Oct 22 18:15 ../
-rw-r--r--  1 5000 5000    0 Oct 22 18:15 test

The UID and GID bits are set on the file itself, so when you mount those files inside your docker container, the file has the same owner / group UID as it does on the host, but is now mapped to /etc/passwd in the container, which is probably going to be a different user unless it's owned by root (UID 0).

The real question is, of course, 'what do I do about this?' If bob is logged in as bob on the given host machine, he should be able to run the container as bob and not have file permissions altered under his host account. As it stands, he actually needs to run the container as user docker to avoid having his account altered.

It seems like, with your current set-up, you'll need to make sure your UIDs > usernames in /etc/passwd on your host match up to your UIDs > usernames in your containers /etc/passwd if you want to interact with your mounted user directory as the same user that's logged in on the host.

You can create a user with a specific user id with useradd -u xxxx. Buuuut, that does seem like a messy solution...

You might have to come up with a solution that doesn't mount a host users home directory.

Two options I've found:

CHOWN all the things (after doing your work)

I've done docker run -v `pwd`/shared:/shared image, and the container has created files within pwd/shared that are how owned by the docker process. However, /shared is still owned by me. So within the docker process, I do

chown -R `stat -c "%u:%g" /shared` /shared

stat -c "%u:%g" /shared returns 1000:1000 in my case, being the uid:gid of my user. Even though there is no user 1000 within the docker conatainer, the id is there (and stat /shared just says "unknown" if you ask for the username).

Anyway, chown obediently transfers ownership of the contents of /shared to 1000:1000 (which, as far as it is concerned, doesn't exist, but outside the container, it's me). So I now own all the files. The container can still modify things if it wants to, because from its perspective, it's root.

And all is well with the world.

docker run -u so all files created will automatically have the right owner

Another way to do this is the -u flag on docker run.

docker run -v `pwd`/shared:/shared -u `stat -c "%u:%g" /shared` ubuntu bash

This way, the docker user inside the container is youruid:yourgid.

However: this means giving up your root authority within the container (apt-get install, etc.). Unless you create a user with that new uid and add it to the root group.

So, I ended up in this post looking on how to restore ownership of all the files (owned by root) that came out of a docker container running as root, to my non-privileged user in the host.

I needed the process inside the container to run as root, so I can't use -u on docker run.

I'm not proud of what I did, but at the end of my bash script, I added this:

docker run --rm -it \
--entrypoint /bin/sh \
-e HOST_UID=`id -u` \
-v ${HOST_FOLDER_OWNED_BY_ROOT}:/tmp \
alpine:latest \
-c 'chown -R ${HOST_UID}:${HOST_UID} /tmp/'

Let's break some of the lines down:

  • Run /bin/sh inside the container:

--entrypoint /bin/sh

  • Pass the current user's uid as an environment variable to the container:

-e HOST_UID=`id -u`

  • Mount whatever folder you want to re-own back to your user (filled with files owned by root, output-ed by the previous container that ran as root), under this new container's /tmp:

-v ${HOST_FOLDER_OWNED_BY_ROOT}:/tmp

  • Run chown recursively with the host user's uid over the target directory (mounted inside the container in /tmp):

-c 'chown -R ${HOST_UID}:${HOST_UID} /tmp/'

So, with this, I got the files owned back to my current user without having to "escalate" privileges to root or to sudo.

It's dirty, but it worked. Hope I helped.