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


76

考虑以下琐碎的Dockerfile:

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

在没有其他任何工作目录中。构建docker映像:

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。让我们尝试修复bobhome目录的所有权。在容器上,运行:

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服务器)编写一个容器,该容器允许不同的用户登录,该容器仅将linux计算机中的用户名和凭据用作有效的用户名。这给我带来了想要创建多个用户的不寻常动机。我可以通过仅在运行时创建用户来解决此问题,一切都很好。但是,我使用的基础映像添加了一个docker用户,因此可以交互使用而无需以root用户身份运行(按照最佳做法)。由于该用户成为第一个用户,这破坏了一切用户并最终拥有所有内容,因此尝试在其他用户失败时尝试登录(该应用无法启动,因为它缺少写入权限)。chown首先运行启动脚本可以解决此问题,但是要以链接卷更改权限为代价(显然,如果我们要链接卷,这只是一个问题)。


1001应该映射到/ etc / passwd中的bob,如果是,则容器没有挂载/ etc / password。在内核级别,进程上下文将仅携带id /数字,而不包含名称。
resultsway

Answers:


28

那是对的吗?有人可以指出我的相关文档吗,我只是基于上述实验进行猜测。

也许这仅仅是因为它们在内核上具有相同的数值,如果我在我的家庭用户的ID不是1000的系统上进行测试,那么在每种情况下权限都会改变吗?

阅读info coreutils 'chown invocation',可能会使您更好地了解文件权限/所有权的工作方式。

但是,基本上,计算机上的每个文件都有一组附加的位,以定义其权限和所有权。当您chown创建文件时,只需设置这些位。

chown使用用户名或组名将文件发送到特定用户/组时,chown将查找/etc/passwd用户名和/etc/group组,以尝试将名称映射到ID。如果这些文件中不存在用户名/组名,chown则会失败。

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'

但是,chown无论您的计算机上是否存在使用这些ID的用户/组,都可以使用ID来实现所需的文件(当然,在某个正整数上限内)。

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

UID和GID位是在文件本身上设置的,因此,当您将这些文件安装在Docker容器中时,该文件具有与主机相同的所有者/组UID,但现在已映射到/etc/passwd容器中,即除非该用户归root(UID 0)所有,否则可能会成为其他用户。

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

看起来,在当前设置下,如果您要与安装的用户目录进行同一用户交互,则需要确保/etc/passwd主机上的UID>用户名与容器中的UID>用户名匹配/etc/passwd在主机上登录。

您可以使用创建具有特定用户ID的用户useradd -u xxxx。Buuuut,这似乎是一个混乱的解决方案...

您可能需要提出一种不装载主机用户主目录的解决方案。


感谢您提供详细的答案,这听起来确实像是对我的正确解释。就像您说的那样,并不是特别令人满意的解决方案-令人惊讶的是Docker没有更明确地解决此问题。没有在主机用户的主目录中挂载任何东西似乎有极大的限制。
cboettig 2014年

hokstad.com/docker/patterns我正在阅读此书,并意识到这个人解决了我上面建议的问题。毕竟还算不错吗?
克里斯·麦金奈尔

感谢您的链接。看起来这种方法将UID硬连线(无论如何,还是默认值),因此问题仍然会发生(例如,如果没有用户预先配置和构建Dockerfile,容器就无法移植)。如果Docker支持名称而不是uid值,那么这个问题的确可以解决。Docker开发人员提供的有关将其发布到
docker

1
是的,我想这毕竟不是一个坏策略。需要先释放一个UID userdel。首先,这是我在实际的dockerfile中采用的解决方案:github.com/rocker-org/rocker/issues/50#issuecomment-60306104
cboettig 2014年

63

我找到了两个选择:

搞定所有事情(做完工作之后)

我已经完成了docker run -v `pwd`/shared:/shared image,容器在其中创建pwd/shared了docker进程如何拥有的文件。但是,/shared仍归我所有。因此,在docker进程中,

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

stat -c "%u:%g" /shared1000:1000以我的情况返回,是uid:gid我的用户的。即使1000在docker容器中没有用户,ID也在那里(stat /shared如果您要求用户名,只会说“未知”)。

无论如何,chown乖乖地将内容的所有权转让/shared1000:1000(就其而言,不存在,但是在容器之外,是我)。因此,我现在拥有所有文件。如果容器愿意,它仍然可以修改内容,因为从它的角度来看,它是root

世界一切都很好。

docker run -u 因此创建的所有文件将自动具有正确的所有者

另一种方法是-u在docker run上标记。

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

这样,容器内的docker用户为youruid:yourgid

但是:这意味着放弃容器内的根权限(apt-get install,等等)。除非您使用该新uid创建用户并将其添加到root组中。


1
是的,我也倾向于大量使用chown。我也倾向于使用-u 1000。我认为使用不会%u有太大帮助,因为如果您的用户使用的UID在容器中不存在,则无论如何都会出错。
cboettig 2015年

它实际上并没有出错-只是向stderr投诉:)
Jared Forsyth,2015年

请注意,由于Docker的工作方式,该选项UIDGID通过该-u选项传递给Docker的对象必须存在于Docker容器本身内(即,ID,而不是与之关联的名称)。
code_dredd

4

因此,我最终在这篇文章中探讨了如何将以root身份运行的docker容器中的所有文件(由root拥有)的所有权还原到主机中的非特权用户。

我需要容器内的进程以root身份运行,所以我不能在docker run上使用-u。

我不为自己的所作所为感到骄傲,但是在bash脚本的结尾,我添加了以下内容:

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/'

让我们分解一些行:

  • 在容器中运行/ bin / sh:

--entrypoint / bin / sh

  • 将当前用户的uid作为环境变量传递给容器:

-e HOST_UID =`id -u`

  • 安装要重新自己回到你的用户无论文件夹(弥漫着由以前的容器由根,输出版拥有的文件跑了作为根),在这个新的容器的/tmp

-v $ {HOST_FOLDER_OWNED_BY_ROOT}:/ tmp

  • chown在目标目录(安装在中的容器内部/tmp)中,使用主机用户的uid递归运行:

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

因此,有了这个,我就将文件拥有权归还给当前用户,而不必将特权“升级”为root或sudo。

它很脏,但是可以用。希望我能帮上忙。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.