【问题标题】:File permission in docker container with volume mount带有卷挂载的 docker 容器中的文件权限
【发布时间】:2019-12-26 16:51:14
【问题描述】:

我正在尝试让 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 特别是这个要点引起了我的注意:

容器操作系统对所有在 容器运行时根据自己的配置。例如, 如果主机和容器中都存在用户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

这使它在容器内看起来正确,但不会改变最终结果 - 仍然拒绝权限。

任何正确方向的帮助或指示将不胜感激,因为我的想法已经不多了。

【问题讨论】:

    标签: docker permissions docker-compose


    【解决方案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 命令)。这就是为什么我倾向于调整容器用户/组而不是完全替换这些文件。

    【讨论】:

      【解决方案2】:

      其实你的解决方案没有错。我做了同样的事情,但差别不大。

      这是我的 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:

      【讨论】:

      • 感谢您的回答。是的,我把它缩小到主机上的“证书组”与同一 GID 的 docker 中的“ping”冲突。至少这是我最好的猜测。
      • 是的,但是您是否从 docker-compose.yml 文件中删除了 user: "113:117"?我相当肯定这是不正确的。在 Dockerfile 中定义用户并坚持下去。
      • 据我所知并拥有 testet,docker-compose 文件中的用户条目将覆盖 dockerfile 中的 USER。我发现设置 user: "113:999" 实际上可以使它工作,即使这不是我希望最终得到的解决方案,因为 113 已经在组 999 中。
      • 但这不是您使用的实际 docker-compose.yml,对吗?它定义了一个图像,它没有使用您编写的 Dockerfile。
      • 不,我将撰写文件更改为 build: context: . dockerfile: Dockerfile.alpine
      【解决方案3】:

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

      “将 :Z 添加到您的卷挂载”

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

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

      【讨论】:

        猜你喜欢
        • 2015-11-27
        • 2017-06-30
        • 2019-03-24
        • 2019-11-30
        • 2020-04-27
        • 2015-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多