具有卷挂载的docker容器中的文件权限

时间:2019-08-21 11:58:24

标签: docker permissions docker-compose

我正在尝试让docker容器从主机文件系统访问letencrypt证书。

我不想以root用户身份运行docker容器,而是以具有非常特定访问权限的用户身份运行。 我也不想更改证书的权限。 我想要的只是给定用户的访问权限,可以读取docker容器中的证书。

证书具有以下设置:

-rw-r----- 1 root cert-group

将要运行docker容器的用户位于cert-group中:

uid=113(myuser) gid=117(myuser) groups=117(myuser),999(cert-group),998(docker)

只要我们在主机上,此方法就可以工作-我可以按预期与用户“ myuser”一起读取文件。

现在,我想在将证书作为卷安装的docker容器中执行此操作。 我已经完成了多个测试用例,但运气不佳。

用于测试的简单docker-compose文件:

version: '3.7'

services:

  test:
    image: alpine:latest
    volumes:
      - /etc/ssl/letsencrypt/cert.pem:/cert.pem:ro
    command: > 
      sh -c 'ls -l / && cat /etc/passwd && cat /etc/group && cat /cert.pem'
    user: "113:117"
    restart: "no"

输出很多,但是最重要的是:

test_1  | -rw-r-----    1 root     ping          3998 Jul 15 09:51 cert.pem
test_1  | cat: can't open '/cert.pem': Permission denied
test_1  | ping:x:999:

在这里,我假设“ ping”是docker alpine的一个内部组,但是,我获得了一些有关如何与主机协作的混合信息。

本文https://medium.com/@mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf的要点是,有一个内核处理所有权限(主机),因此,如果使用相同的uid和gid,则权限将从主机继承。但是,即使正在运行的用户是113:117,该用户在主机上是组999的一部分,它仍然无法授予我读取文件的权限。

接下来,我发现这篇文章https://medium.com/@nielssj/docker-volumes-and-file-system-permissions-772c1aee23ca特别引人注目:

  

容器OS对在其中进行的所有操作强制执行文件权限   容器运行时根据其自身的配置。例如,   如果主机和容器中都存在用户A,则将用户A添加到组中   主机上的B不允许用户A写入其拥有的目录   除非容器内部已创建组B,否则容器内的组B   容器,并将用户A添加到其中。

这使我认为,可能需要一个自定义Dockerfile才能将用户添加到docker中,并使用户成为999的一部分(如前所述,称为ping):

FROM alpine:latest
RUN adduser -S --uid 113 -G ping myuser
USER myuser

运行此命令可以得到完全相同的结果,现在将myuser附加到passwd上:

test_1  | myuser:x:113:999:Linux User,,,:/home/myuser:/sbin/nologin

这只是我尝试过的几件事。

另一个正在将/ etc / passwd和/ etc / group与其他博客中的卷同步

volumes:
  - /etc/passwd:/etc/passwd
  - /etc/group:/etc/group

这使它在容器内在视觉上看起来是正确的,但它不会改变最终结果-仍被拒绝。

由于我用尽了所有想法,因此向正确方向的任何帮助或指示将不胜感激。

3 个答案:

答案 0 :(得分:1)

Docker 容器不知道在主机上运行容器的用户的 uid/gid。运行容器的所有请求都通过 docker 套接字,然后到达通常以 root 身份运行的 docker 引擎,并且在这些 API 调用中不会传递 uid/gid。 docker 引擎只是以 Dockerfile 中指定的用户身份或作为容器创建命令的一部分(在本例中,来自 docker-compose.yml)运行容器。

一旦进入容器,从 uid/gid 到名称的映射是通过容器内的 /etc/passwd 和 /etc/group 文件完成的。重要的是,在文件系统级别,容器和主机之间没有映射 uid/gid 值(用户命名空间除外,但如果实施得当,只会使这个问题变得更糟)。并且所有文件系统操作都发生在 uid/gid 级别,而不是基于名称。所以当你挂载主机卷时,uid/gid 会直接通过。

您在这里遇到的问题是您如何告诉容器选择 uid/gid 来运行容器进程。通过指定 user: "113:117",您已经告诉容器不仅要指定进程的 uid (113),还要指定 gid (117)。完成后,/etc/group 中的任何次要组都不会分配给用户。要分配这些次要组,您只需指定 uid user: "113",然后它将从容器内的 /etc/passwd/etc/group 文件中查找组分配。例如:

user: "113"

不幸的是,在挂载任何卷之前,docker 会查找组成员身份,因此您会遇到以下情况。

首先,使用分配给几个组的示例用户创建一个图像:

$ cat df.users 
FROM alpine:latest

RUN addgroup -g 4242 group1 \
 && addgroup -g 8888 group2 \
 && adduser  -u 1000 -D -H test \
 && addgroup test group1 \
 && addgroup test group2

$ docker build -t test-users -f df.users .
...

接下来,运行该图像,将主机上的 id 与容器内的 id 进行比较:

$ id
uid=1000(bmitch) gid=1000(bmitch) groups=1000(bmitch),24(cdrom),25(floppy),...

$ docker run -it --rm -u bmitch -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro test-users:latest id
docker: Error response from daemon: unable to find user bmitch: no matching entries in passwd file.

糟糕,docker 没有看到 /etc/passwd 中的条目,让我们尝试使用我们在映像中创建的 test 用户:

$ docker run -it --rm -u test -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro test-users:latest id
uid=1000(bmitch) gid=1000(bmitch) groups=4242,8888

这可行,并从映像中的 /etc/group 文件分配组,而不是我们挂载的组。我们还可以看到 uid 也有效:

$ docker run -it --rm -u 1000 -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro test-users:latest id
uid=1000(bmitch) gid=1000(bmitch) groups=4242,8888

一旦我们指定了 gid,次要组就消失了:

$ docker run -it --rm -u 1000:1000 -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro test-users:latest id
uid=1000(bmitch) gid=1000(bmitch)

如果我们在不覆盖 /etc/passwd/etc/group 文件的情况下运行,我们可以看到正确的权限:

$ docker run -it --rm -u test test-users:latest id
uid=1000(test) gid=1000(test) groups=4242(group1),8888(group2)

可能最好的选择是添加一个容器用户,该用户的组成员身份与主机的 uid/gid 值匹配。对于主机卷,我还使用基本映像解决了这个问题,该映像动态调整容器内的用户或组以匹配卷中安装的文件的 uid/gid。这是作为 root 完成的,然后 gosu 用于将权限放回给用户。您可以在 github 上的 sudo-bmitch/docker-base 中看到这一点,特别是我将作为 fix-perms script 运行的 part of an entrypoint

另外,请注意挂载 /etc/passwd/etc/group 会破坏容器文件系统内其他文件的文件权限,并且该用户可能在该容器内拥有不适当的访问权限(例如,您可能具有对 ping 命令的特殊访问权限,从而能够修改文件或运行普通用户无法访问的 ping 命令)。这就是为什么我倾向于调整容器用户/组而不是完全替换这些文件。

答案 1 :(得分:0)

实际上,您的解决方案没有错。我做了相同的事情,几乎没有什么区别。

这是我的Dockerfile:

FROM alpine:latest
RUN addgroup -S cert-group -g 117 \
    && adduser -S --uid 113 -G cert-group myuser
USER myuser

还有我的docker-compose.yml:

version: '3.7'

services:

  test:
    build: 
      dockerfile: ./Dockerfile
      context: .
    command: >
      sh -c 'ls -l / && cat /etc/passwd && cat /etc/group && cat /cert.pem'
    volumes:
      - "/tmp/test.txt:/cert.pem:ro"
    restart: "no"

我的'/tmp/test.txt'被分配给113:117。

恕我直言,我认为您的docker-compose.yml问题不使用您的图片。您应该删除image:并添加build:

答案 2 :(得分:0)

我今天遇到了同样的问题,幸运的是,以下解决方案帮助了我。

“将 :Z 添加到您的卷安装中”

参考:https://github.com/moby/moby/issues/41202

注意:不幸的是只有 Centos 有问题,我在 Ubuntu 上没有遇到任何问题。