简体   繁体   English

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

[英]File permission in docker container with volume mount

I'm trying to let a docker container access a letsencrypt certificate from the host file system.我正在尝试让 docker 容器访问来自主机文件系统的 Letencrypt 证书。

I do not want to run the docker container as root, but rather as a user with very specific access rights.我不想以 root 身份运行 docker 容器,而是以具有非常特定访问权限的用户身份运行。 Neither do I want to change the permissions of the certificate.我也不想更改证书的权限。 All I want, is for the given user, to have access to read the certificate inside the docker container.我想要的只是让给定的用户有权读取 docker 容器内的证书。

The certificate has the following setup:该证书具有以下设置:

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

The user who's going to run the docker container, is in the cert-group:将要运行 docker 容器的用户位于 cert-group 中:

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

This works as long as we're on the host - I am able to read the file as expected with the user "myuser".只要我们在主机上,这就有效 - 我可以使用用户“myuser”按预期读取文件。

Now I want to do this within a docker container with the certificate mounted as a volume.现在我想在将证书安装为卷的 docker 容器中执行此操作。 I have done multiple test cases, but none with any luck.我已经完成了多个测试用例,但没有一个运气好。

A simple docker-compose file for testing:一个用于测试的简单 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"

This ouputs a lot, but most important is:这输出很多,但最重要的是:

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:

Here I assume that "ping" is an internal group for docker alpine, however, im getting some mixed information about how this collaborates with the host.在这里,我假设“ping”是 docker alpine 的一个内部组,但是,我得到了一些关于它如何与主机协作的混合信息。

From this article https://medium.com/@mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf my takeaway is, that there's a single kernel handling all permissions (the host) and therefore if the same uid and gid is used, the permissions would inherit from the host.从这篇文章https://medium.com/@mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf我的结论是,有一个内核处理所有权限(主机)和因此,如果使用相同的 uid 和 gid,则权限将从主机继承。 However, even though that the running user is 113:117, which on the host is part of the group 999 it still doesnt give me access to read the file.但是,即使正在运行的用户是 113:117,它在主机上是 999 组的一部分,它仍然没有给我读取文件的权限。

Next I found this article https://medium.com/@nielssj/docker-volumes-and-file-system-permissions-772c1aee23ca where especially this bullet point caught my attention:接下来我发现这篇文章https://medium.com/@nielssj/docker-volumes-and-file-system-permissions-772c1aee23ca特别是这个要点引起了我的注意:

The container OS enforces file permissions on all operations made in the container runtime according to its own configuration.容器操作系统根据自己的配置对容器运行时中进行的所有操作强制执行文件权限。 For example, if a user A exists in both host and container, adding user A to group B on the host will not allow user A to write to a directory owned by group B inside the container unless group B is created inside the container as well and user A is added to it.例如,如果主机和容器中都存在用户A,则将用户A添加到主机上的B组将不允许用户A写入容器内B组拥有的目录,除非在容器内也创建了B组并将用户 A 添加到其中。

This made me think, that maybe a custom Dockerfile was needed, to add the user inside docker, and make the user part of 999 (which is known as ping as earlier stated):这让我想到,也许需要一个自定义的 Dockerfile,将用户添加到 docker 中,并使用户成为 999 的一部分(如前所述,这称为 ping):

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

Running this gives me the exact same result, now with myuser appended to passwd though:运行它给了我完全相同的结果,但现在将 myuser 附加到 passwd :

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

This is just a couple of things that I've tried.这只是我尝试过的几件事。

Another is syncing /etc/passwd and /etc/group with volumes found in some other blog另一个是将 /etc/passwd 和 /etc/group 与其他博客中的卷同步

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

This makes it visually look correct inside the container, but it doesnt change the end result - still permission denied.这使得它在容器内在视觉上看起来是正确的,但它不会改变最终结果 - 仍然拒绝许可。

Any help or pointers in the right direction would be really appreciated since I'm running out of ideas.由于我的想法已经用完了,因此任何正确方向的帮助或指示都将不胜感激。

Docker containers do not know the uid/gid of the user running the container on the host. Docker 容器不知道在主机上运行容器的用户的 uid/gid。 All requests to run containers go through the docker socket, and then to the docker engine that is often running as root, and no uid/gid's are passed in those API calls.运行容器的所有请求都通过 docker 套接字,然后到达通常以 root 身份运行的 docker 引擎,并且在这些 API 调用中不会传递 uid/gid。 The docker engine is just running the container as the user specified in the Dockerfile or as part of the container create command (in this case, from the docker-compose.yml). docker 引擎只是以 Dockerfile 中指定的用户身份或作为容器创建命令的一部分(在这种情况下,来自 docker-compose.yml)运行容器。

Once inside the container, the mapping from uid/gid to names is done with the /etc/passwd and /etc/group file that is inside the container.一旦进入容器,从 uid/gid 到名称的映射是通过容器内的 /etc/passwd 和 /etc/group 文件完成的。 Importantly, at the filesystem level, uid/gid values are not being mapped between the container and the host (with the exception of user namespaces, but if implemented properly, that would only make this problem worse).重要的是,在文件系统级别,容器和主机之间没有映射 uid/gid 值(用户命名空间除外,但如果实施得当,只会使这个问题变得更糟)。 And all filesystem operations happen at the uid/gid level, not based on names.并且所有文件系统操作都发生在 uid/gid 级别,而不是基于名称。 So when you do a host volume mount, the uid/gid's are passed directly through.所以当你挂载主机卷时,uid/gid 会直接通过。

The issue you are encountering here is how you are telling the container to pick the uid/gid to run the container processes.您在这里遇到的问题是您如何告诉容器选择 uid/gid 来运行容器进程。 By specifying user: "113:117" you have told the container to not only specify the uid (113), but also the gid (117) of the process.通过指定user: "113:117"你告诉容器不仅要指定进程的 uid (113),还要指定进程的 gid (117)。 When that's done, none of the secondary groups from /etc/group are assigned to the user.完成后, /etc/group中的任何次要/etc/group都不会分配给用户。 To get those secondary groups assigned, you want to only specify the uid, user: "113" , which will then lookup the group assignments from the /etc/passwd and /etc/group file inside the container.要分配这些辅助组,您只想指定 uid、 user: "113" ,然后它将从容器内的/etc/passwd/etc/group文件中查找组分配。 Eg:例如:

user: "113"

Unfortunately, the lookup for group membership is done by docker before any volumes are mounted, so you have the following scenario.不幸的是,在安装任何卷之前,docker 会查找组成员身份,因此您有以下场景。

First, create an image with an example user assigned to a few groups:首先,使用分配给几个组的示例用户创建一个图像:

$ 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 .
...

Next, run that image, comparing the id on the host to the id inside the container:接下来,运行该图像,将主机上的 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.

Woops, docker doesn't see the entry from /etc/passwd, lets try with the test user we created in the image:糟糕,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

That works, and assigns the groups from the /etc/group file in the image, not the one we mounted.这有效,并从映像中的/etc/group文件分配组,而不是我们挂载的组。 We can also see that uid works too:我们还可以看到 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

As soon as we specify the gid, the secondary groups are gone:一旦我们指定了 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)

And if we run without overriding the /etc/passwd and /etc/group file, we can see the correct permissions:如果我们在不覆盖/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)

Likely the best option is to add a container user with the group membership matching the uid/gid values from the host.可能最好的选择是添加一个容器用户,该用户的组成员身份与主机的 uid/gid 值匹配。 For host volumes, I've also solved this problem with a base image that dynamically adjusts the user or group inside the container to match the uid/gid of the file mounted in a volume.对于主机卷,我还使用基本映像解决了这个问题,该映像动态调整容器内的用户或组以匹配卷中安装的文件的 uid/gid。 This is done as root, and then gosu is used to drop permissions back to the user.这是作为 root 完成的,然后 gosu 用于将权限放回给用户。 You can see that at sudo-bmitch/docker-base on github, specifically the fix-perms script that I would run as part of an entrypoint .您可以在 github 上的sudo-bmitch/docker-base 中看到,特别是我将作为entrypoint 的一部分运行的fix-perms 脚本

Also, be aware that mounting the /etc/passwd and /etc/group can break file permissions of other files within the container filesystem, and this user may have access inside that container that is not appropriate (eg you may have special access to the ping command that gives the ability to modify files or run ping commands that a normal user wouldn't have access to).另外,请注意挂载/etc/passwd/etc/group会破坏容器文件系统内其他文件的文件权限,并且该用户可能在该容器内拥有不适当的访问权限(例如,您可能对ping 命令,可以修改文件或运行普通用户无法访问的 ping 命令)。 This is why I tend to adjust the container user/group rather than completely replace these files.这就是为什么我倾向于调整容器用户/组而不是完全替换这些文件。

Actually your solution is not wrong.其实你的解决方案没有错。 I did the same with few differences.我做了同样的事情,但几乎没有区别。

This is my Dockerfile:这是我的 Dockerfile:

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

And my docker-compose.yml:还有我的 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"

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

IMHO, I think the problem in your docker-compose.yml that doesn't use your image.恕我直言,我认为您的 docker-compose.yml 中的问题没有使用您的图像。 You should remove the image: and add build:您应该删除image:并添加build:

I have gone through the same issue today and luckily, the below solution helped me.我今天遇到了同样的问题,幸运的是,下面的解决方案帮助了我。

"Add :Z to your volumes mounts" “将 :Z 添加到您的卷安装中”

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

Note: Unfortunately It's issue with only Centos, I didn't face any problem with Ubuntu.注意:不幸的是,这只是 Centos 的问题,我在 Ubuntu 上没有遇到任何问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM