Moby: 添加以非 root 用户身份挂载卷的功能

创建于 2013-10-17  ·  157评论  ·  资料来源: moby/moby

用例:将卷从主机安装到容器,供 apache 作为 www 用户使用。
问题是当前所有挂载都以 root 身份挂载在容器内。
例如,这个命令
docker run -v /tmp:/var/www ubuntu stat -c "%U %G" /var/www
将打印“根根”

我需要将它作为用户 www 安装在容器内。

areapi arekernel arevolumes exexpert kinenhancement

最有用的评论

我可以说不 - 强制用户添加一个帮助脚本

#!/bin/sh
chown -R redis:redis /var/lib/redis
exec sudo -u redis /usr/bin/redis-server

(感谢@bfirsh你的例子)

非常可怕。

这意味着容器必须以 root 身份启动,而不是以预期的redis用户身份运行。 (正如@aldanor 所暗示的那样)

这意味着用户不能执行以下操作:

docker run -v /home/user/.app_cfg/ -u user application_container application :(

所有157条评论

如果在绑定挂载之前chown卷(在主机端),它将起作用。
在这种情况下,您可以这样做:

mkdir /tmp/www
chown 101:101 /tmp/www
docker run -v /tmp/www:/var/www ubuntu stat -c "%U %G" /var/www

(假设101:101是您容器中www-data用户的 UID:GID。)

另一种可能性是进行绑定安装,然后在容器内进行chown

@mingfang chown 不适合你吗?

为此有一个快捷方式会很有用。 我经常发现自己在编写run脚本来设置卷的权限:

https://github.com/orchardup/docker-redis/blob/07b65befbd69d9118e6c089e8616d48fe76232fd/run

如果您没有chown的权利怎么办?

chown卷的帮助脚本会解决这个问题吗? 这个脚本可以是你的 Dockerfile 的ENTRYPOINT

我可以说不 - 强制用户添加一个帮助脚本

#!/bin/sh
chown -R redis:redis /var/lib/redis
exec sudo -u redis /usr/bin/redis-server

(感谢@bfirsh你的例子)

非常可怕。

这意味着容器必须以 root 身份启动,而不是以预期的redis用户身份运行。 (正如@aldanor 所暗示的那样)

这意味着用户不能执行以下操作:

docker run -v /home/user/.app_cfg/ -u user application_container application :(

有 _one_ 方法可以让它工作,但你需要提前在你的 Dockrfile 中做好准备。

RUN mkdir -p /var/lib/redis ; chown -R redis:redis /var/lib/redis
VOLUME ["/var/lib/redis"]
ENTRYPOINT ["usr/bin/redis-server"]
USER redis

(我没有测试这个例子,我正在研究一个铬容器,然后显示在一个 _separate_ X11 容器上......)

当然,该方法仅适用于直接新卷,不适用于绑定
挂载或卷-来自卷。 ;)

此外,使用volumes-from的多个容器对于同一用户将具有不同的 uid/gid,这也使事情变得复杂。

@SvenDowideit @tianon该方法也不起作用。 完整示例:

FROM ubuntu
RUN groupadd -r redis    -g 433 && \
useradd -u 431 -r -g redis -d /app -s /sbin/nologin -c "Docker image user" redis 
RUN mkdir -p /var/lib/redis
RUN echo "thing" > /var/lib/redis/thing.txt
RUN chown -R redis:redis /var/lib/redis
VOLUME ["/var/lib/redis"]
USER redis
CMD /bin/ls -lah /var/lib/redis

两次运行,有和没有 -v 卷:

bash-3.2$ docker run -v `pwd`:/var/lib/redis voltest 
total 8.0K
drwxr-xr-x  1 root root  102 Aug  7 21:30 .
drwxr-xr-x 28 root root 4.0K Aug  7 21:26 ..
-rw-r--r--  1 root root  312 Aug  7 21:30 Dockerfile
bash-3.2$ docker run  voltest 
total 12K
drwxr-xr-x  2 redis redis 4.0K Aug  7 21:30 .
drwxr-xr-x 28 root  root  4.0K Aug  7 21:26 ..
-rw-r--r--  1 redis redis    6 Aug  7 21:26 thing.txt
bash-3.2$ 

我们遇到了一个可以通过这个解决的问题(我认为)。 我们为开发人员的主目录提供了 NFS 共享。 开发人员想将/home/dev/git/project挂载到 Docker 中,但由于我们启用了 Root Squash,所以不能。

这禁止 root 访问/home/dev/git/project所以当我尝试运行 docker mount /home/dev/git/project我得到lstat permission denied错误。

@frankamp这是因为 docker 目前的偏好是不修改不在 Docker 自己控制范围内的主机内容。

您的“VOLUME”定义被您的-v pwd`:/var/lib/reds` 覆盖。
但是在您的第二次运行中,它使用的是 docker 控制的卷,该卷是在 /var/lib/docker 中创建的。 当容器启动时,docker 将图像中的数据复制到卷中,然后使用指定卷的目录的uid:gid来更改卷。

我不确定这里可以做很多事情,不幸的是绑定安装不支持(据我所知)安装为不同的 uid/gid。

我对此的解决方案是执行 SvenDowideit 上面所做的操作(在 dockerfile 中创建新用户并预先 chown),但不是挂载主机卷,而是使用纯数据容器,并将我想挂载的主机卷复制到带有tar cf - . | docker run -i --volumes-from app_data app tar xvf - -C /data的容器。 一旦https://github.com/docker/docker/pull/13171被合并(并且docker cp双向工作),这将变得更容易一些,但也许它可以成为-v host_dir:container_dir的替代品-vc host_dir:container_dir ,(用于卷复制的vc),其中host_dir的内容将被复制到数据容器中。 虽然我不能说我理解复制的文件为什么/如何继承容器用户的权限,但我可以告诉他们这样做,这是我设法提出的唯一合理的解决方案,它不会破坏可移植性。

acl呢?

是否有任何修复或解决方法? 我在使用 OpenShift 时遇到了同样的问题,挂载的文件夹归root:root所有,并且预先创建的图像不起作用。

我也在寻找解决方法。 如果所有已挂载的卷都归root所有,那么除了root之外的任何用户都无法运行 Docker 容器。

那么你可以试试s6-overlay 。 它包括专门用于帮助解决此类问题的功能。

@dreamcat4 :感谢您的指点。 修复所有权和权限似乎是一个有趣的解决方法,但我不是必须以 root 身份运行我的 Docker 容器才能使其工作吗?

@brikis98是的,这是真的。 但是 s6-overlay 还有另一个功能,它允许您在启动服务器/守护程序时再次删除权限。

@dreamcat4啊,明白了,谢谢。

我在容器内部和外部有相同的 uid/gid,这就是我得到的:

nonroot$ ls -l .dotfiles/
ls: cannot access .dotfiles/byobu: Permission denied
ls: cannot access .dotfiles/config: Permission denied
ls: cannot access .dotfiles/docker: Permission denied
ls: cannot access .dotfiles/vim: Permission denied
ls: cannot access .dotfiles/bashrc: Permission denied
ls: cannot access .dotfiles/muse.yml: Permission denied
ls: cannot access .dotfiles/my.cnf: Permission denied
ls: cannot access .dotfiles/profile: Permission denied
total 0
-????????? ? ? ? ?            ? bashrc
d????????? ? ? ? ?            ? byobu
d????????? ? ? ? ?            ? config
d????????? ? ? ? ?            ? docker
-????????? ? ? ? ?            ? muse.yml
-????????? ? ? ? ?            ? my.cnf
-????????? ? ? ? ?            ? profile
d????????? ? ? ? ?            ? vim
nonroot$ ls -l .ssh
ls: cannot access .ssh/authorized_keys: Permission denied
total 0
-????????? ? ? ? ?            ? authorized_keys
nonroot$

@darkermatter你能打开一个单独的问题吗?

没问题,但这在这里不相关吗?

@darkermatter这是一个功能请求,而不是错误报告,将您的案例与其他案例混合起来很难进行讨论,您的问题也可能不直接相关

@thaJeztah好吧,正如@frankamp和其他人所做的那样,我只是演示了在 Dockerfile 中运行 chmod 等之后会发生什么。 我会将其作为错误报告提交,但它与此讨论相关。

类似于@ebuchman提出的建议,无需复制主机卷,您可以先创建一个仅数据容器,该容器执行
chown 1000:1000 /volume-mount启动时作为 root 用户。
例如在 docker compose v2 语法中

version: '2'
services:
  my-beautiful-service:
    ...
    depends_on:
      - data-container
    volumes_from:
      - data-container

  data-container:
    image: same_base_OS_as_my-beautiful-service
    volumes:
      - /volume-mount
    command: "chown 1000:1000 /volume-mount"

这样您的容器就可以作为非 root 用户运行。 仅数据容器仅运行一次。
假设你事先知道 my-beautiful-service 使用的 uid 和 gid。 通常是 1000,1000。

由于您可以(在 1.11 中)为要在docker volume create中使用的卷指定挂载选项,我想说这似乎非常接近准备关闭。

您不能直接指定 uid/gid,因为绑定挂载不支持这一点,但是可以与新挂载选项一起使用的许多文件系统可以与 uid/gid 选项一起使用。

我认为如果您想在容器内安装 CIFS 驱动器,问题仍然存在,但也许这应该是另一张票?

@michaeljs1990你可以这样做,只是不是每个容器(除非你为你想要的每个 uid/gid 组合创建单独的卷)。

@cpuguy83 ,您能否说明必须如何使用docker volume create来避免此问题?

我今天刚刚在 docker 1.11 中遇到了这个问题,不得不进行一些痛苦的重新调整以说服 docker 映像让我写入已安装驱动器上的文件。 如果我再也不需要那样做就太好了,更不用说向别人解释了。

不确定这是否是您要问的,但是...

FROM busybox
RUN mkdir /hello && echo hello > /hello/world && chown -R 1000:1000 /hello

构建名为“test”的上述图像

$ docker volume create --name hello
$ docker run -v hello:/hello test ls -lh /hello

上例中的/hello/hello/world都归 1000:1000 所有

我知道了。 所以,我做了一些类似但有点不同的事情,这可能值得分享。 基本上,我向 Dockerfile 添加了一个用户,该用户为容器外的用户共享了我的 UID、GID、用户名和组。 所有<...>都是由相关值替换的东西。

FROM <some_image>
RUN groupadd -g <my_gid> <my_group> && \
    useradd -u <my_uid> -g <my_gid> <my_user>

在此之后,可以使用USER或稍后使用su进行切换(例如,入口点脚本或使用 shell 时)。 这让我可以写入已安装的卷,因为我是创建的同一用户。 还可以在容器内使用chown以确保对相关事物具有权限。 此外,安装sudo通常也是一个明智之举。

虽然它解决了问题,但我不知道我是否喜欢它,因为任何用户都需要这样做。 此外,我硬编码了一些东西(糟糕!),但也许可以使用模板来使这更流畅一些。 我想知道这个垫片是否可以以某种方式吸收到docker run中。 如果已经有更好的方法可以做到这一点,我很想知道它是什么。

可以选择将主机用户 uids/gids 与容器用户 uids/gids 与--userns-remap进行映射。 我个人没有尝试过。 请参阅有关此主题的精彩讨论http://stackoverflow.com/questions/35291520/docker-and-userns-remap-how-to-manage-volume-permissions-to-share-data-betwee

@cpuguy83

您不能直接指定 uid/gid,因为绑定挂载不支持这一点,但是可以与新挂载选项一起使用的许多文件系统可以与 uid/gid 选项一起使用。

您认为哪些文件系统可以接受 uid/gid 参数? 我知道 FAT 可以,但这感觉就像这个线程中提出的任何其他内容一样糟糕。

IMO,Docker有两种选择:

  1. 官方支持将卷挂载为指定的用户/组(使用容器内定义的用户/组名称,不需要主机了解容器的内部结构)。
  2. 或者...摆脱USER指令(和相关的运行时标志)。

能够以非 root 用户身份运行而只能挂载 root 拥有的卷是一种错误功能。 在主机和容器之间共享 uid/gid 是另一个错误功能。

@mehaase卷拥有容器路径中已经存在的任何内容的所有权。 如果容器中的位置由 root 拥有,则该卷将获得 root。 如果容器中的位置由其他人拥有,则卷将获得该位置。

某种解决方法会很棒。 除非容器特别期望它,否则很难在不编写设置权限的自定义 Dockerfile 的情况下将卷添加到标准容器(如 elasticsearch、redis、couchDB 和许多其他容器)中。 这主要使 docker-compose 中的docker run -v命令或volume:指令无用。

@chrisfosterelli为什么没用? 我不认为设置您希望使用的文件/目录的所有权是不寻常的。

@cpuguy83因为如果不使用设置权限和卷的自定义 Dockerfile 似乎无法设置所有权,这就是为什么认为它们对定义卷没有用处。 如果相关的话,我不会将容器绑定到我的主机文件系统。

@chrisfosterelli但是所有这些标准的 Dockerfile 都应该已经设置了权限。

我认为@chrisfosterelli 想说的是@cpuguy83 (如果我错了@chrisfosterelli,请纠正我)很明显,这些变量(UID、GID 等)是动态的,需要设置为运行时(特别是对内部拥有的文件和已安装卷的文件),但我们目前缺乏这样做的方法。 到目前为止的响应似乎是它们不应该由运行时确定,但这是忽略了这种建议所提出的基本可用性问题。 再次,如果我对此有任何误解,请随时纠正我。

@jakirkham我一定不明白可用性问题是什么。
文件在映像中,它们应该具有应用程序运行所需的所有权和权限。 它与音量本身无关。 音量只是采用图像中设置的内容。

@cpuguy83我做了更多的挖掘并将其隔离为:假设我有一个弹性搜索容器,它将在启动时创建一个目录/data (如果没有数据存在),然后使用docker run -v /data elasticsearch . 目录/dataroot:root拥有,并且在容器内作为elasticsearch运行的守护程序现在将无法启动,因为它无法写入/data

如果我可以在不需要自定义 Dockerfile 的情况下将此卷设置为由 elasticsearch 拥有,那将是理想的……尽管我猜您可能会争辩说此类问题应该在上游映像中解决。

@chrisfosterelli在内核邮件列表中有一些关于拥有可以更改所有权的覆盖之类的驱动程序的讨论,但是如果没有类似的东西我们无能为力。 我很好奇,你能不能让你的卷世界中的所有文件读写并适当地设置umasks,所以新文件也是如此? (我还没试过)。

@justincormack我相信是这样,但我认为当我期望容器在卷(而不是主机)中创建数据时,这不起作用。 我知道这是一个奇怪的问题,所以我目前正在通过在上游 Dockerfile 本身中将其修复到mkdir -p && chmod目录来解决它。

@chrisfosterelli这就是我说设置 umask 的原因,如果您的 umask 是000 (在容器中),所有新文件都将使用666777权限创建,如果挂载点是777开始应该可以吗? 如果权限始终是世界读写,那么 uid 和 gid 应该无关紧要吗?

@justincormack是的,这听起来是正确的......在创建具有非主机安装卷的 docker 容器时如何做到这一点?

@chrisfosterelli嗯,这是一个好问题。 在我看来,新卷上的权限是默认 umask 所赋予的权限,因此您可以尝试使用000 umask 运行 docker 守护程序,然后查看该卷是否是世界可写的。 也许我们应该在docker volume create上有一些权限选项。

(您可以使用执行chmod并退出的根容器来修复它,但那很难看)

在创建是没有好处的。 问题是如果容器没有路径,则使用 root 创建路径。 可以说,无论传入的用户是什么,这都可以完成。

@ cpuguy83我认为在用户使用 -u 传入时创建它会更有意义,因为那可能是用户试图从容器内部写入卷,对吗?

我可以使用以下步骤安装为我选择的用户:

  • 在 Docker 中创建与拥有主机上文件的用户具有相同 UID/GID 的用户。
  • 预先创建挂载点,并在容器中将它们分配给目标用户

引用@chrisfosterelli

我做了更多的挖掘并将其隔离到这一点:假设我有一个弹性搜索容器,它将在启动时创建一个目录 /data(如果没有数据存在),然后使用 docker run -v /data elasticsearch。 目录 /data 由root:root拥有,并且在容器内作为 elasticsearch 运行的守护程序现在将无法启动,因为它无法写入 /data。

这是一个很好的例子! 我有一个与 Solr 图像类似的示例。 Solr 需要一个或多个“核心”,每个“核心”都是相关配置文件和索引片段的集合。 每个核心都放置在具有用户指定名称的目录中。 例如,如果我想创建一个名为products的核心,则路径为/opt/solr/server/solr/products 。 核心的名字是我自己选的,所以 Solr 镜像维护者不能在镜像中预先创建这个目录。

我希望保存我的索引数据,这样我就可以将我的图像升级到更新的 Solr 而无需重新索引我的所有文档,但是如果我将一个卷挂载到/opt/solr/server/solr/products ,那么它归root和 Solr(作为solr运行)实际上无法向其写入任何内容。 父目录/opt/solr/server/solr包含其他文件,因此我也无法在那里安装卷。 (在最新的 Docker 用语中,我相信我的卷被称为“命名卷”,即未安装到主机上指定路径但完全由 Docker 为我管理的卷。)

我已经与 Solr 图像维护者讨论过这个问题,并且有一些解决方法(他对图像进行了一些更改以提供帮助),但这一切都非常老套,需要对上游图像进行逐个更改。 拥有此线程中讨论的功能将使_所有图像_更具可扩展性,而无需创建新的 Dockerfile。

@ctindel也许......如果该目录尚不存在。

@cpuguy83是的,我同意。 那绝对是我的用例。 如果在为运行容器明确指定了用户 ID 时目录不存在,那么以 root 身份创建目录似乎没有意义。

@cpuguy83它仅适用于命名卷。

@kamechen什么才有效?

@cpuguy83当您使用命名卷时,文件将安装在您需要的用户下

@eciuca好吧....这取决于。 如果命名卷为空,或者命名卷中的数据是由您碰巧需要的同一用户创建的。

@andrewmichaelsmith 提出的问题是否有解决方案?

我们遇到了一个可以通过这个解决的问题(我认为)。 我们为开发人员的主目录提供了 NFS 共享。 开发人员想将 /home/dev/git/project 挂载到 Docker 中,但不能,因为我们启用了 Root Squash。

这禁止 root 访问 /home/dev/git/project 所以当我尝试运行 docker mount /home/dev/git/project 我得到一个 lstat 权限被拒绝错误。

我认为可以使用bindfs解决这个问题。
使用 docker 的-v ...将卷挂载到临时位置,然后 bindfs 将其挂载为另一个用户需要的位置。

@piccaso ,我理解@andrewmichaelsmith的方式是,问题是主机端的绑定安装由于rootsquash 而失败。 但实际上 bindfs 仍然可以用作解决方法,但这次是在主机端。 首先,在主机上,您使用 FUSE 以非 root 用户身份将 nfs 共享绑定挂载到临时位置,然后使用-v ...将该临时文件夹挂载到 docker 中。

请注意,bindfs(至少使用 FUSE)有相当多的 CPU 开销。

是的,bindfs 是非常不可取的。 它甚至比 CoW 文件系统还要慢。
内核中正在进行一些工作,以允许在挂载时进行 uid/gid 转换,这可能会有所帮助。

内核中正在进行一些工作,以允许在挂载时进行 uid/gid 转换,这可能会有所帮助。

这也可能只有助于解决我想在容器内重新映射 uid/gid 的用例。 由 docker 守护进程执行的挂载本身仍将在主机上以 root 身份执行。 我的理解是,内核绑定挂载只能以 root 身份创建。 不确定是否有工作可以改变它以允许非 root 用户执行挂载(我对 Linux 处理挂载以判断是否有意义的方式知之甚少)。

@NikolausDemmel我怀疑这会改变。 mount系统调用需要 CAP_SYS_ADMIN,这不是给非 root 用户的东西,甚至不是我们给容器的 root 用户的东西。

@cpuguy83感谢您的澄清。 这意味着将 docker volumens 安装到使用 root-squash 进行 NFS 安装的主机文件夹在可预见的将来将无法工作(由于您所说的安装系统调用的限制),除非使用 FUSE 和bindfs之类的变通方法。

抱歉,这有点 OT,因为 OP 要求更改容器内的 UID/GID。 但它是硬币的另一面,它已经在上面的讨论中出现了。 只是想澄清这种区别。

我正在为 Mac 运行 Docker,并且已经安装了一个卷,但我似乎无法为 Web 服务设置访问文件的权限。 我将如何解决这个问题? 我尝试更改烫发并将组设置为员工,但似乎 Alpine 没有员工组。

抱歉,如果这不是放置它的最佳位置,我已经挣扎了好几天,想不出更好的地方。

@NikolausDemmel :我们正在尝试使用 Docker 进行一些生物信息学工作。 我们有多个通过 NFS 进行根压缩的大型文件系统。 我们读入巨大的序列数据(fastq),并写出一个较小的 BAM 文件,其中基因组读取与数据存储对齐。 目前,我们可以通过自定义镜像来使用 docker 在容器中创建一个用户,并在最后使用 USER 使其工作,但这有几个原因是有问题的:

  1. 如果我们想使用别人的 Docker 镜像,我们必须使用自定义的 Dockerfile 重新构建
  2. 我们要么需要为每个本地用户创建一个自定义 docker 映像,要么我们使用单个“服务”帐户,我们将无法区分用户在 FS 中的活动。

Bindfs 或 userNS 可以让我们解决这个问题吗?

我想我遇到了同样的问题,我的用例是:
Docker 镜像为给定项目保存了我们的可移植构建工具,我们使用具有相对路径的卷挂载,或者使用docker run -v ./:/src/ image之类的东西或 docker-compose 文件中的等价物。 构建会自动启动,并在链接卷的子文件夹中生成新文件。
有时我们喜欢使用来自主机的那些构建文件,但事实上它们仍然由 docker 中的用户拥有,而不是运行 docker 的主机用户,这往往会使事情变得棘手。

我在这里做错了什么吗?

@rlabrecque请参阅我之前关于将 docker 用户的 id 与主机的 id 匹配的帖子。 我使用了这种方法,它对我们非常有效。 基本上,运行HOST_UID=$(id -u)HOST_GID=$(id -g)并在以下两个命令中生成扩展 $HOST_GID 和 $HOST_UID 的 Dockerfile:

RUN groupadd -g $HOST_GID mygroup
RUN useradd -l -u $HOST_UID -g mygroup myuser

使用生成的 Dockerfile 并填写 ID 来构建您的映像。

@haridsv我做过类似的事情,它在 Linux 上运行良好。 但这在 Windows 上似乎对我不起作用:挂载内的文件仍归 root 所有。

我通过使用inotifywait解决了这个问题。 您需要安装 inotify-tools 才能在 docker 映像中运行它。 可以在您的主机系统上运行它,但我想要一个可移植的解决方案。

RUN export DEBIAN_FRONTEND=noninteractive \
  && apt -y update \
  && apt -y install inotify-tools \
  && inotifywait -m -r /mount -e create --format '%w%f' \
    | while read f; do chown $(stat -c '%u' /mount):$(stat -c '%g' /mount) $f; done

这通过指示 inotifywait 监视在目录 /mount 中创建的任何新文件或目录来工作。 当它注意到一个时,它会将所有权更改为与 /mount 文件夹相同的用户和组。 我使用了两者的整数表示,以防容器中不存在主机用户/组。 在容器内部,谁拥有它并不重要,因为一切都以 root 身份运行。 在容器之外,主机文件系统显示与挂载在 /mount 的任何目录相同的所有权。

我故意将它设计为只设置新创建的文件和目录的所有权,以保留预先存在的文件和目录的所有权。 这比每次挂载文件系统时都用 chown -R 语句吹走所有这些更安全。 如果统一权限适用于您的项目,并且您想要一个更简单、运行更高效的解决方案,请查看inotify-hookable

警告:由于每个子目录会建立一个 inotify 手表,因此可能会达到每个用户的最大 inotify 手表数量。 默认最大值为 8192; 可以通过写入 /proc/sys/fs/inotify/max_user_watches 来增加它。

我们使用了主机端脚本来chown安装的卷,这避免了重建映像的需要:

#!/bin/bash
set -e

DOCKER_IMAGE=<docker_image>
COMMAND=<internal_command>

DOCKER_USER=docker-user
DOCKER_GROUP=docker-group

HOME_DIR=/work
WORK_DIR="$HOME_DIR/$(basename $PWD)"

PARAMS="$PARAMS -it --rm"
PARAMS="$PARAMS -v $PWD:$WORK_DIR"
PARAMS="$PARAMS -w $WORK_DIR"

USER_ID=$(id -u)
GROUP_ID=$(id -g)

run_docker()
{
  echo \
    groupadd -f -g $GROUP_ID $DOCKER_GROUP '&&' \
    useradd -u $USER_ID -g $DOCKER_GROUP $DOCKER_USER '&&' \
    chown $DOCKER_USER:$DOCKER_GROUP $WORK_DIR '&&' \
    sudo -u $DOCKER_USER HOME=$HOME_DIR $COMMAND
}

if [ -z "$DOCKER_HOST" ]; then
    docker run $PARAMS $DOCKER_IMAGE "$(run_docker) $*"
else
    docker run $PARAMS $DOCKER_IMAGE $COMMAND "$*"
fi

在主机目录上使用文件系统 ACL 怎么样? 这样,您可以告诉文件系统将特定权限应用于目录中新创建的文件。 如果您在主机级别设置 ACL,如果您从容器中修改数据,也会发生这种情况。

@thaJeztah @justincormack @cpuguy83

@kamechen似乎是正确的,命名卷“正常工作”。 在命名卷的情况下,现有权限“适得其反”并更改卷权限,我个人认为这是一个错误 (#28041)。

@thegecko ,为什么不进一步采用这种方法并且不在入口点内创建用户?

这是我的示例,它检测到已安装目录的所有者,创建具有相同 UID 的用户并在此用户下运行命令:

Dockerfile

FROM ubuntu

RUN mkdir /project
VOLUME /project

ENV GOSU_VERSION 1.9
RUN set -x \
    && apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \
    && dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
    && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
    && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc" \
    && export GNUPGHOME="$(mktemp -d)" \
    && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
    && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
    && rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \
    && chmod +x /usr/local/bin/gosu \
    && gosu nobody true \
    && apt-get purge -y --auto-remove ca-certificates wget

ADD entrypoint.sh /

ENTRYPOINT ["/entrypoint.sh"]

CMD /project/run.sh

入口点.sh

#!/bin/sh

USER=dockeruser
VOLUME=/project
UID="$(stat -c '%u' $VOLUME)" && \
useradd --uid "$UID" "$USER" && \
ls -l "$VOLUME" && \
exec gosu "$USER" "$@"

运行.sh

#!/bin/sh

echo "Running as \"$(id -nu)\""

当我运行sudo docker build -t test . && sudo docker run --rm -v /tmp/docker-test/:/project test:latest它输出:

total 12
-rw-r--r-- 1 dockeruser dockeruser 990 Dec 12 10:55 Dockerfile
-rwxr-xr-x 1 dockeruser dockeruser 156 Dec 12 11:03 entrypoint.sh
-rwxr-xr-x 1 dockeruser dockeruser  31 Dec 12 11:01 run.sh
Running as "dockeruser"

有没有人考虑过这个问题? 确保您的卷具有与您的容器相同的 gid 和 uid,这使得管理您的容器以不使用 root 变得更加困难。 Docker 的最佳实践建议尽可能不要使用 root 运行服务,但不必在主机和容器上设置 gid 和 uid,这会破坏使 Docker 流行的易用性?

@AndreasBackx使用卷只是假设图像在您的安装路径上有数据。
使用绑定使用主机路径的 UID/GID。

目前没有办法(因为没有内核支持)在不更改原始文件的情况下将文件/目录的 UID/GID 映射或更改为其他内容,除非使用 FUSE 之类的东西,这非常慢。

但是,让我们退后一步。
Docker 并没有真正让事情变得更加困难。
容器中的 UID/GID 与主机上的 UID/GID 相同。即使用户/组名不匹配,UID/GID 也很重要。
就像没有 docker 一样,你需要想出一个你想用于你的服务的 uid/gid 并打破常规使用它。
请记住,在/etc/passwd/etc/group中不需要存在 uid/gid 来设置文件所有权。

@cpuguy83谢谢你的解释。

我今天在创建node管道时遇到了这个问题,我的主机用户 UID 是 1000 并且node图像创建了一个具有该特定 UID 的自定义用户,甚至还有一个问题

我会使用节点用户并继续前进,但感觉有点脏。 我真的分享了你在@cpuguy83上写的关于“让我们退后一步”的内容,但有时它可能会成为一个难以解决的问题。

刚刚发现usermod上的-o选项允许重复的 IDS,这似乎是一个合法的选项。

RUN usermod -o -u 1000 <user>

不确定为什么没有以任何合理的方式解决这个问题。

docker run -it -u 1000:4211 -v /home/web/production/nginx_socks:/app/socks -e SOCKS_PATH=/app/socks --name time_master  time_master

登录查看:

drwxr-xr-x    8 geodocr_ geodocr       4096 Jun  4 18:51 .
drwxr-xr-x   57 root     root          4096 Jun  6 21:17 ..
-rwxrwx---    1 geodocr_ geodocr        140 Jun  4 18:49 .env
-rwxrwx--x    1 geodocr_ geodocr         78 Jun  4 18:49 entrypoint.sh
drwxrwxr-x    2 geodocr_ geodocr       4096 Jun  4 18:51 handlers
-rwxrwx---    1 geodocr_ geodocr        242 Jun  4 18:49 requirements.txt
-rwxrwx---    1 geodocr_ geodocr       1270 Jun  4 18:49 server.py
drwxr-xr-x    2 root     root          4096 Jun  6 21:00 socks
drwxr-xr-x   10 geodocr_ geodocr       4096 Jun  4 18:51 utils

dokefile 专门做

RUN adduser  -D -u 1000 $USER 
#
RUN addgroup $GROUP -g 4211 
#
RUN addgroup $USER $GROUP 
RUN mkdir /app/socks
USER $USER  
#

当既没有选择容器中的用户,也没有选择运行命令的用户时,将此卷作为 root 安装是没有任何意义的。 我可以理解 RUN 命令是作为运行命令的用户安装的,还是作为拥有该目录的用户,甚至是在 Dockerfile 中指定的用户。

这些都不是root,因此以root身份安装似乎是一个错误。

也刚刚检查过,创建一个卷,然后安装它。 所以还是个bug。

@disarticulate如果您希望主机路径不是根目录,那么您应该更改主机路径。

我认为之前没有提到过这个问题,但是当你依赖 Docker 为你创建主机卷时,这个 bug 特别烦人。 Docker 似乎总是使用 root 创建主机卷,即使您要挂载的目录具有不同的所有者。

似乎正确的做法是创建具有属于图像USER的所有权权限的卷。

@jalaziz当容器的用户在主机上不存在时该怎么办? 容器的主要好处之一是您不必将它们的依赖项(包括用户)暴露给主机。

@taybin我希望Docker只是使用容器用户的uid:gid创建文件夹,或者如果该文件夹存在于容器内,它将创建具有相同uid:gid和掩码的主机文件夹。

注意:如果主机上已经存在该文件夹,我不希望 Docker 更改权限。

@taybin@frol描述的完全一样。 它应该只使用容器中的uid:gid

但是,这揭示了我对当前方法的普遍问题。 我必须知道容器用来允许写入并根据该uid:gid 设置主机目录权限的 uid。 如果上游作者更改了 uid,权限将中断。 这一切似乎都非常脆弱。

就我而言,我不一定能明确控制正在使用的 Docker 映像(我不能只根据自己的喜好编辑 Dockerfile)。

所以,我尝试了这个:

docker run -it -u $(id -u $USER):$(id -g $USER) -v $(pwd):/src -w /src node:latest npm run build

这会创建一个名为./built-app的文件夹。 但是,所有者仍然是具有严格权限的root

我的解决方法是这样的:

docker run -it -v $(pwd):/src -w /src node:latest /bin/bash -c "npm run build; chmod -R 777 ./built-app"

它仍然拥有root所有者,但权限宽松。 然后我的主机操作系统(Ubuntu)能够在没有 sudo 权限的情况下访问./built-app

@rms1000watt您是否尝试过以下命令?

docker run -it -v $(pwd):/src -w /src node:latest /bin/bash -c "npm run build; chown -R ${UID}:${GID} ./built-app"

这应该可以工作,因为它会将主机的UIDGID直接用于文件本身。 使用chmod -R 777通常是不好的做法。

@saada哎呀! 好决定。 我会试一试。

我可以通过阅读这篇文章并了解 _UID 和 GID 在 Docker 容器中的工作方式_来完成我的方法
https://medium.com/@mccode/understanding -how-uid-and-gid-work-in-docker-containers-c37a01d01cf

基本上只有一个内核和一个共享的 uid 和 gid 池,这意味着本地计算机的根与容器的根相同,它们都共享相同的 UID


我有一个 apache 服务器,我想与 apache 容器共享我的 webapp 文件以在主机上修改它(开发,使用文本编辑器更改它们)并通过容器中运行的进程查看结果。 有时,该过程会写入新文件,如果我不使用 root 用户生成文件的权限更改默认行为,我的本地用户将无法再修改它们。

我所做的是,生成一个自定义图像,将其添加到我的 dockerfile 中:

RUN adduser -D -u 1002 dianjuar -G www-data
USER dianjuar

也许为了让我的docker-compose.yml可移植到任何人都可以使用它是在构建过程中放置一些参数

这是一个容器模式,用于在运行时以一种易于移植的方式分配 userid / groupid。 https://github.com/Graham42/mapped-uid-docker

我遵循的方式:

1-在主机服务器上创建目录
2-将其权限更改为具有 userid 和 groupid = 1000 的用户
3-运行docker-compose up
检查了容器,一切都很好。

注意:我在主机服务器上使用 root 用户,我假设如果我使用的是 uid = 1000 的非 root 用户,我将能够挂载该卷而无需担心权限,但我还没有测试它。 有没有人遵循类似的方式?

典型问题:

  • docker swarm,所以 CAPP_ADD 不可用,bind-mount 不是解决方案
  • 两个不同图像的两个容器共享同一个卷,因此两者上的不同用户/组数据库
  • 例如,必须具有访问权限 www-data(即让我们加密证书下载器)
  • 另一个也使用www-data(即nginx)
  • 但第三个需要从用户 openldap 访问(即 openldap 服务器)
  • 如此简单的 chmod ist 也不是解决方案

这意味着,我有一个网络服务器,它从 let's encrpt 和同一域的 OpenLDAP 服务器获取其域的 SSL 证书,我想在其中重用证书。

还有其他组合会遇到完全相同的问题。

任何想法,如何解决这个问题?

如果没有 Docker,你将如何解决这个问题? 这不是 Docker 特定的
问题。

2018 年 1 月 12 日星期五上午 10:24,Marc Wäckerlin [email protected]
写道:

典型问题:

  • docker swarm,所以 CAPP_ADD 不可用,bind-mount 不是
    解决方案
  • 两个不同图像的两个容器共享相同的卷,所以
    两者上的不同用户/组数据库
  • 例如,必须具有访问权限 www-data(即让我们加密
    证书下载器)
  • 另一个也使用www-data(即nginx)
  • 但第三个需要用户 openldap 访问(即 openldap
    服务器)
  • 如此简单的 chmod ist 也不是解决方案

这意味着,我有一个网络服务器,可以为其域获取 SSL 证书
来自 let's encrpt 和我想要的同一域的 OpenLDAP 服务器
重用证书。

还有其他组合会遇到完全相同的问题。

任何想法,如何解决这个问题?


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/moby/moby/issues/2259#issuecomment-357267193或静音
线程
https://github.com/notifications/unsubscribe-auth/AAwxZgyvdCwGGVkUqCxK9nDFw1zxSKjUks5tJ3kXgaJpZM4BGxv9
.

--

  • 布莱恩·高夫

即使没有 swarm,我也可以在 docker:bind-mount 中解决它。

这是一个 docker-swarm 特定的问题,因为没有 CAP_ADD。

@mwaeckerlin绑定挂载无法映射到不同的用户 ID。
但即使使用 swarm 你也可以绑定 mount.... 为什么需要 CAP_ADD?

如果没有 CAP_ADD,则在 docker 内挂载会失败。

但是通过写我的评论,我得到了一个可能的解决方案,但不幸的是,这需要更改两个图像中的Dockerfile ,因此它不适用于库或其他第 3 方图像:

  • 在所有 Dockerfile 中创建一个具有明确指定的公共组 ID 的组
  • 赋予群组权利

@mwaeckerlin为什么需要安装在容器内?

因为我无法使用 docker 选项-v指定用户/组。

上面指定的想法是:在容器内绑定挂载,然后目标上的 chown 不应该更改源。

@mwaeckerlin如果您更改它,它会随处更改。 这是本期问题的症结所在。
Chowing/Chmoding 绑定挂载的文件/目录会改变这两个地方。

也没有必要能够安装在容器内你可以--mount type=bind,source=/foo,target=/bar

是的,我只是在docker之外测试过,所以上面的想法是错误的。

我在 docker 中经常看到的主要问题是,不同图像中的用户、组并不相同,即使两者中存在相同的用户名或组名,它们也经常具有不同的 id。

在某些情况下,这样的事情至少会有所帮助: --mount type=bind,source=/foo,target=/bar,user=me,group=mine

关于此主题的任何建议或最佳实践:docker swarm 中不同图像中不同用户的共享卷用户?

  1. 不共享卷
  2. 同步您的 uid/gids
  3. 确保所有共享的权限都足够大
  4. 使用主机上的保险丝挂载来为每个容器绑定到不同的 uid/gid

你能详细说明第4点吗?
一个实际的例子可能是关于如何做到这一点?

2018 年 1 月 12 日星期五 17:27,Brian Goff, notifications @github.com 写道:

>

  1. 不共享卷
  2. 同步您的 uid/gids
  3. 确保所有共享的权限都足够大
  4. 使用主机上的保险丝安装来为每个绑定到不同的 uid/gid
    容器


您收到此消息是因为您订阅了此线程。
直接回复此邮件,在 GitHub 上查看
https://github.com/moby/moby/issues/2259#issuecomment-357282169或静音
线程
https://github.com/notifications/unsubscribe-auth/AHSjvgjb0BFbJhZ1VWM-pLGfa7tRBvDNks5tJ4VPgaJpZM4BGxv9
.

使用类似https://bindfs.org/的东西——甚至至少有一个 Docker 卷插件已经实现了它(https://github.com/lebokus/docker-volume-bindfs 是我通过谷歌找到的第一个结果) .

安装卷后我无法更改权限,有人知道吗?

一种解决方法:
将此添加到 Dockerfile
RUN echo "if [ -e container_volume_path ]; then sudo chown user_name container_volume_path; fi" >> /home/user_name/.bashrc
container_volume_path的所有权在卷安装后更改。

能够映射 uid 和 gid 似乎是 docker 卷处理的一个神秘缺失元素。 最不意外的方法是包含它,并且建议的修复程序笨重且难以发现,同时没有提供最佳实践好处:

回覆:
1) 不要共享卷

  • 很好,但对映射 uid/gid 的讨论无关紧要
    2) 同步你的 uid/gids
  • 这就是该功能的目的,但不会在 Dockerfile 中强制使用 chown
    3) 确保所有共享的权限都足够大
  • 这再次依赖于 Dockerfile 中定义的行为,当它可能是一个简单的映射时
    4) 在主机上使用熔断器挂载为每个容器绑定不同的 uid/gid
  • 好建议,这也像是刮牦牛。

@colbygk

当它可能是一个简单的映射时

这就是问题所在,因为 vfs 层不支持它,所以无法进行“简单映射”。
一些文件系统提供了映射所有权的能力(例如,bindfs 或 nfs),但在一般情况下实现这一点目前是不可能的。

我需要共享卷,例如在以下情况下:

共享证书

  • 容器 1 是一个反向代理,它处理所有托管域的 let's encrypt
  • 容器 2 是一个 ldap 服务器,还需要提供其域的证书

解决方法:镜像容器2比容器1继承同一个镜像,公共基础镜像创建一个公共组,那么两个容器具有相同的组访问权限

Dockerfile共同基数

RUN groupadd -g 500 ssl-cert

让我们加密图像中的letsencrypt-config.sh

chgrp -R ssl-cert /etc/letsencrypt

Dockerfilemwaeckerlin/reverse-proxy

RUN usermod -a -G ssl-cert www-data

Dockerfilemwaeckerlin/openldap

RUN usermod -a -G ssl-cert openldap

而已。

所有这些都说明了如何在入口点或构建过程中更改用户权限,以使整个 docker 在不同的用户中运行。

但也许我在过去 3 天的网上搜索后错过了一个重要的点。
上述或其他链接的建议和(解决方法)都不会以任何方式起作用。

安装到容器的所有卷始终由容器内的root:root拥有。 不管我是否在主机上预先更改了具有匹配 UID/GID 的所有者。

从我的角度来看,我不能放松我试图做一些非常基本的事情是愚蠢的感觉。

  • Windows 10 专业版 1803 (17134.112)
  • Docker for Windows 18.03.1-ce-win65 (17513)
  • 带有 Hyper-V 和 Ubuntu 的 Windows WSL

尝试启动一个普通的 apache2 容器,在该容器中将文档根目录安装到主机,这样我就可以在 php 源代码上进行开发,同时立即在 docker 容器上对其进行测试。

root<strong i="16">@win10</strong>:# docker run --rm -v /c/Users/<MyUser>/Development/www-data:/var/www/html -it httpd:2.4 /bin/bash

在 docker 容器内,directoy _/var/www/html_ 始终归 _ root:root_ 所有,因此我的 php 应用程序永远无法打开或写入该文件夹内的任何数据。
还没有任何效果... :(

对于那些寻找相当优雅的解决方案的人,请查看@elquimista在此处提出的建议。 我已经对此进行了测试并且运行良好

我们一直很幸运地使用https://github.com/boxboat/fixuid#specify -paths-and-behavior-across-devices。 此外,它在容器内设置一个用户以匹配主机上的用户。

这是图像中的示例配置:

$ cat /etc/fixuid/config.yml
user: lion
group: lion
paths:
  - /home/lion
  - /home/lion/.composer/cache
  - /tmp

并运行:

$ docker run --rm -it --init \
    -u 1000:1000 \
    -v `pwd`:/app \
    -v "$HOME/.composer/cache:/home/lion/.composer/cache" \
    --entrypoint='fixuid' \
    php:7.2-cli \
        /bin/bash

请注意,在使用不支持 unix 权限和所有权的存储系统时,这也是一个烦恼。 在这种情况下,必须完成存储的安装,以便它获得正确的 uid 以在容器内使用,因为任何 chown 文件的尝试都会失败。 如果有一种方法可以告诉 docker 将文件呈现为由特定 uid 拥有,而不管容器外部的所有权如何,这将简化事情。

@tlhonmey

如果有办法告诉 docker 将文件显示为特定 uid 所拥有

没有,也没有自定义文件系统(例如 bindfs)。

@tlhonmey是的,我能够通过一些符号链接解决“不支持 unix 权限的存储系统”的问题。

基本上,从 NTFS 驱动器安装,我会把东西放在-v ./HostNtfsStuff:/data/ntfsMount中,然后创建一个符号链接并 chown 那个ln -s -T /data/ntfsMount /var/lib/myApp && chown -Rh myApp:myApp /var/lib/myApp/

你也可以测试: su myApp -c 'echo foo > /var/lib/myApp/bar' && cat /data/ntfsMount/bar

我的用途是让 Windows 开发人员能够运行 MySQL 容器并在已安装的卷上持久保存,但它适用于大量应用程序。

因此解决方案是手动管理一堆uid:gid对,并希望它们不会在主机或辅助脚本上发生冲突,或者:

有 _one_ 方法可以让它工作,但你需要提前在你的 Dockrfile 中做好准备。

RUN mkdir -p /var/lib/redis ; chown -R redis:redis /var/lib/redis
VOLUME ["/var/lib/redis"]
ENTRYPOINT ["usr/bin/redis-server"]
USER redis

(我没有测试这个例子,我正在研究一个铬容器,然后显示在一个 _separate_ X11 容器上......)

直到今天,当我尝试绑定挂载容器卷时,我一直在使用最后一种技术,但它坏了。 显然你不能这样做。 该卷以 root 身份创建,然后内部的应用程序无法以用户身份对其进行写入。 VOLUME文档中描述的自动填充似乎也不适用于绑定安装。

我看到这篇阅读Dockerfile Best Practices和他们推荐的帮助脚本:

#!/usr/bin/env bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"

因此,递归 chown 确保您在每次启动时都拥有所有权,然后以用户身份运行您的应用程序。 exec也强制 PID 1 使信号起作用。 而且,如果我想使用辅助脚本之类的内容填充卷,以便在容器外部对结果数据使用,它可能也必须放入辅助脚本中。 但是,想知道如果您的应用程序在一个卷上存储大量文件,容器启动是否会影响性能,尤其是在存储不是本地存储的情况下。

似乎确实有更好的解决方案。 可能类似于将容器 uid 和 gid 映射到主机上指定的用户名和组。 docker 可以查看容器的 /etc 并找出答案吗?

您不能在文件系统级别映射 uids/gids,至少在没有 fuse 的情况下不能。

您不能在文件系统级别映射 uids/gids,至少在没有 fuse 的情况下不能。

有点像我害怕的。 知道如果 docker 像这样使用保险丝,性能损失可能是多少吗?

@mdegans

所以一个递归的 chown 来确保你在每次开始时都拥有所有权,

您不需要在每次开始时都执行chown 。 相反,请检查数据目录的所有者,如果不正确,则仅执行递归chown 。 像这样:

 [ $(stat -c %U "$PG_DATA") == "postgres" ] || chown -R postgres "$PG_DATA"

所以理想情况下,这只会在第一次开始时发生。

并且在使用这样的入口点脚本运行容器时要非常小心; 如果您将(例如)您的主目录安装到容器中,您的所有文件都将被分配到 postgres

在一个好的 docker 镜像设计中,运行时用户不是 root,因此不能chown文件......!

在一个好的 docker 镜像设计中,运行时用户不是 root,因此不能 chown 文件……!

正确,但不应该有任何东西阻止经常需要的root之间的切换......就像你通常不应该像root一样运行任何东西,直到你需要它,但是当您这样做时,您可以执行以下一项或多项操作:

  • sudo
  • su
  • USER root

根据: https: //f1.holisticinfosecforwebdevelopers.com/chap03.html#vps -countermeasures-docker-the-default-user-is-root

以我的拙见,Docker 映像的用户应确保他/她设置已安装卷的权限。

这与我们在容器出现之前的传统做法非常相似,例如,当我想运行 nginx 并且我需要确保静态 HTML 目录归正确的用户所有时。 为了知道我需要打开我的 nginx.conf 文件,检查工作人员的用户并相应地设置权限。 实际上,这在 nginx 文档中都有描述。

这只是一个 Unix 权限问题,这里的 Docker 没有什么新鲜事。 所以也许这个问题的解决方案是为每个 Docker 镜像提供更好的文档,说明挂载卷的所有权。 我不记得 nginx 启动守护程序确保目录具有正确的所有权,如果设置不正确,它只会失败。

然而这是真的,因为我们现在可能在容器内部而不是外部定义了用户,这使得事情看起来不同(而事实并非如此)。 但是内部和外部的 UID 是等价的,因此 UID 为 2000 的用户 foobar 可能存在于容器内部而不是外部,但 UID 2000 仍然可以设置在外部的文件和目录上。 我们必须根据 UID/GID 而不是我们过去处理的人类友好名称来改变我们的想法。
如果您需要在由 2 个不同作者编写的 2 个容器之间共享一个卷,这也会使事情变得更加困难。 使用传统的 Unix 系统(用户、组和其他)设置权限可能不足以解决问题(没有通用的 UID 或 GID)。 我承认,自从我使用 Docker 以来,我更多地使用了 POSIX ACL。 因此,我可以为同一个文件分配 3 个不同的用户权限。 例如,具有 rw 权限的容器编写器、具有 r 权限的容器读取器和具有 r 权限的主机用户。

另一种选择:可以使用共享目录的 setgid 标志强制执行公共 GID。 可以使用 ACL 强制执行文件掩码。

在 Docker 容器中执行任何操作之前,请运行:

```
掩码 0000
````

https://en.wikipedia.org/wiki/Umask

迟到此线程以重申此功能将有多大帮助。

老实说,我已经部署容器大约一年了,我认为这已成为一个真正的问题。 在这个级别提供解决方案,在这里,似乎是唯一明智的选择。

就目前而言,相当多的 Docker 映像选择继续以root的形式运行其入口点,因此它们可以引导目录和文件权限,仅在运行应用程序进程之前删除权限。

当您意识到并非每个人都可以采用这种做法时,就会出现一个真正的问题。 对于一些流行的平台,如 Kubernetes 或 OpenShift,其中一些环境可能被配置为不允许特权容器......因为......安全。 在我的脑海中,我不可能看到一个大型金融机构甚至会考虑采用一个容器平台来处理没有这种限制的敏感信息。

_entrypoint-as-root_ 实践引发的安全问题驱使大量 Kubernetes helm chart 提供initContainers可以在应用容器启动之前提供 $ chownchmod卷. 这似乎是一个不错的方法,但是当我这样说时请相信我:它不是.

尤其是 Helm 图表,充斥着硬编码的uidsgids ,因为它们需要从应用程序运行时偷偷地删除。 该信息隐藏在容器内,在部署期间无法立即获得。

虽然有很多方法可以解决这个问题,但它继续困扰着整个部署配置,就像一个 _hack to make things work_。 受此影响的部署数量正在迅速增加,人们所采用的技术与容器带来的所有其他好处背道而驰。

我希望有一种方法可以将其作为 OCI 规范的一部分来实现,以便其他依赖 Docker 的解决方案可以使用它来优雅地提供完全自动化的部署。

那么问题就变成了:他们在互联网上的其他什么地方开发了通用的 OCI 规范,应该在哪里进行讨论? 假设这不是将此功能引入 docker 的最佳方式(最终,通过对未来的合规性要求,即普遍同意的标准采用)。

由于问题绝对不会自行消失,解决方案需要一些非常根本的改变。

initContainers 可以在应用程序容器启动之前 chown 和 chmod 卷。 这似乎是一个不错的方法,但是当我这样说的时候相信我:它不是。

FWIW; 只有在多个命名空间之间共享文件的情况下才需要此功能(“主机”上存在的文件(预先),或者在作为不同用户运行的多个容器之间共享的公共文件位置)。 在主机上预先创建文件的情况下,可以通过在与容器共享文件之前确保这些文件具有正确的所有权和权限来缓解这种情况。 实际上,这与(例如)在主机上运行 nginx 并确保 webroot 中的文件具有正确的权限没有任何不同。

在以不同用户身份运行的容器之间共享时,请使用相同的uid (或gid运行两个容器,并设置正确的组权限,类似于运行两个容器时的工作方式需要访问相同资源的非容器化进程)。

其中一些环境可能被配置为不允许特权容器......因为......安全。 在我的脑海中,我不可能看到一个大型金融机构甚至会考虑采用一个容器平台来处理没有这种限制的敏感信息。

只是为了防止混淆; 作为root运行的容器与“特权”容器( --privileged或选项,例如--cap-add集)不同。 特权( --privileged )容器非常不安全,而以root运行的容器被完全包含并且无法突破; 传递它绑定挂载的文件/目录是在其中打孔,所以_will_让它访问您作为绑定挂载传递的文件/目录。

尤其是 Helm 图表,到处都是硬编码的 uid 和 gid,因为它们需要从应用程序运行时中偷偷地删除。 该信息隐藏在容器内,在部署期间无法立即获得。

想知道:如果那些 uids/gids 是未知的; 用户体验会是什么样子? (因为我必须提供一个映射 uid/gid 用于将主机 uid/gid 映射到(未知)容器 uid/gid ?

那么问题就变成了:他们在互联网上的其他什么地方开发了通用的 OCI 规范,应该在哪里进行讨论?

我不认为(一目了然)需要更改 OCI 规范。 这可以在 OCI 规范之外解决; 主要问题是内核中当前缺少映射 uids/gids 的机制(或存在(例如shiftfs ),但不常用)

这是传递职责的经典五角星/其他人可以或应该解决这个问题。 要么是:

  • 用户
  • Docker/容器化平台的具体实现
  • OCI 规范
  • 核心
  • 文件系统

问题已经得到有效说明:让用户这样做既笨重又不安全。 然而,让用户对每个图像进行黑客攻击的连锁效应也很重要:

这就是您不能如此轻松地互操作和共享/混合图像以从不同的用户一起工作。 所以它要么:

  • 打破社区共享(相当多)。 因为不同的用户从同一个全局命名空间池中定义了他们各自开发的图像的 uid 和 gid
  • 迫使用户制定自己的临时标准,并希望其他人遵循他们自己选择的约定
  • 强制用户对所有内容使用root 。 这肯定不太安全。 因为您正在剥离您本来拥有的额外的特权升级保护层。 并且使容器突破漏洞更容易被利用,因为用户已经在容器内root 。 更不用说能够在同一个容器内运行其他服务,这也是另一种横盘前行的方式。

所以这是一个交易。 以上是当前的权衡。 然而,您将有一组不同的权衡将责任转移到其他地方,转交给上面列出的一个或多个其他实体。

顺便说一句,在仔细研究基于文件系统的解决方案时,发现蜘蛛链接的这个“可能有用”的评论:

https://github.com/docker/compose/issues/3270#issuecomment -365644540

其中对相同的一般功能(对其他项目/地点)有几个不同的引用,包括对分布式文件系统(称为“Lustre”)以及有关 ZFS 的其他问题。 好吧,我只是碰巧在这里自己使用 ZFS。

然后还在 ubuntu/launchpad 上发现了相同 bug 的另一个副本。 引用相同的 ZOL #4177 问题,

https://bugs.launchpad.net/ubuntu/+source/zfs-linux/+bug/1567558

这表示有问题的错误已在 zfs 版本 0.6.5.7+ SO 中修复。 这是否意味着我们可以使用 zfs 和 ACL 作为某种后备存储,以某种方式重新映射 uid 和 gid? 好吧,这不是我以前听说过的。

哦,也许这个解决方案只适用于 LXC 容器。 因为他还在他的评论中说(LXC 项目的负责人),“我们使用 setuid 助手(newuidmap 和 newgidmap)”,然后可以“设置一个 uid 和 gid 映射”。 所以想必LXC本身也有一些必要的机制,不然zfs acls部分就不能利用了? 或者也许我错了。 我不完全确定我是否一直遵循这一点。

另一个有趣的链接,这次是关于shiftfs ,以及关于将其功能吸收到 overlayfs 中的可能性的讨论。 这当然是 docker 已经使用的底层文件系统。

但是,如果重新映射功能被实现到overlayfs中会发生什么,但我想使用zfs存储驱动程序来代替我的底层文件系统? 如果它是在每个文件系统的基础上实现的,那么我是否必须被排除在重新映射 uids/gids 的能力之外? 或者我们可以分别实现吗? 抱歉,我有点不清楚 Docker 守护进程是否需要了解此类重新映射,并提供通用 api 和标志(以传递给 fs 驱动程序层)。 或者,如果我们改为在主机端(在文件系统中,在 docker 之外)手动执行此类重新映射。 这方面对我来说也有点不清楚。

[编辑] 哎呀,忘了包含链接! 这里是

https://lists.linuxfoundation.org/pipermail/containers/2018-June/039172.html

这个问题与卷/绑定挂载有关,因此与容器的文件系统分开

如果 overlay 包含 shiftfs 功能,我们将过度使用 uid/gid shift 进行 bindmount,但在不受支持的系统上必须回退到其他东西(或什么都没有)。

Podman 是一个无根的 Docker 插件替代https://www.youtube.com/watch?v=N0hSn5EwW8w https://podman.io/ 。 使用 podman,不使用 root,因此可以正确处理用户权限。 由于这个问题,我们的团队切换到 Podman 并且工作得很好。

这是没有意义的。
同样的问题也适用。
请注意,docker 也有无根模式。

您可以使用以下命令测试 Podman。 与 Docker 不同,Podman 没有单独的守护进程,一切都在执行podman命令的用户下运行。 因此,在 podman 中创建的文件归运行podman run ...命令的用户所有。

kkimdev<strong i="8">@ubuntu</strong>:~$ mkdir podman_test
kkimdev<strong i="9">@ubuntu</strong>:~$ ls -agh podman_test
total 8.0K
drwxrwxr-x 2 kkimdev 4.0K Jun 27 04:23 .
drwxr-xr-x 8 kkimdev 4.0K Jun 27 04:23 ..

kkimdev<strong i="10">@ubuntu</strong>:~$ podman run --rm -it -v ~/podman_test:/podman_test alpine
/ # cd /podman_test/
/podman_test # touch test_file
/podman_test # ls -agh
total 8K
drwxrwxr-x    2 root        4.0K Jun 27 02:24 .
drwxr-xr-x   20 root        4.0K Jun 27 02:24 ..
-rw-r--r--    1 root           0 Jun 27 02:24 test_file

/podman_test #

kkimdev<strong i="11">@ubuntu</strong>:~$ ls -agh podman_test/
total 8.0K
drwxrwxr-x 2 kkimdev 4.0K Jun 27 04:24 .
drwxr-xr-x 8 kkimdev 4.0K Jun 27 04:23 ..
-rw-r--r-- 1 kkimdev    0 Jun 27 04:24 test_file

这不是为podman做广告的合适地方——如果有关于它如何工作的具体技术细节可以帮助解决这个问题,那些将是相关的讨论,特别是作为你问题的潜在解决方案'目前正在评论。 到目前为止,情况还不是这样,所以请在其他地方进行讨论。

podman与 Docker 具有非常不同的架构,这使得这个问题不那么严重/痛苦这一事实并不能神奇地让 Docker 完全改变它的工作方式来解决这个问题。 我可以向你保证,为什么 Docker 的结构是这样的,有很多原因,坦率地说,忽略所有这些历史是不可信的。

@tianon是的,这两种方法都各有利弊。 我之所以提到 podman,只是因为在目标用户下使用 podman 运行容器专门解决了这个技术问题,即“以非 root 用户身份挂载卷”。

请查看我在上述评论中创建的“test_file”的权限。 它首先挂载“~/podman_test”目录,并在 podman 容器中写入“test_file”文件。 然后,一旦用户离开容器,您可以看到该文件归“kkimdev”所有,而不是 root。

问题在于,您对解决 Docker 问题的建议是“不要使用 Docker”,这对 Docker 的问题跟踪器没有太大的建设性。

是的, podman的设计不同,这使得这个问题对那个工具没有意义——这很好,但在这里完全偏离主题。 Rootless 有不同的权衡,其中一些对某些人来说很好,而另一些则不然。 它随着时间的推移而变得更好(主要是内核改进),但并不是这里每个人的通用解决方案。

这需要内核修改或用于通用解决方案的填充程序,如上面详细讨论的(并且@cpuguy83和其他人一直在努力尝试以通用方式帮助解决此问题)。

Docker 自 2013 年以来一直存在这个特殊问题,近六年后,还没有看到任何简单的改进。 Podman 旨在获得与 Docker 的兼容性,但也解决了 Docker 的设计缺陷(包括作为不需要超级用户 Docker 守护程序的非特权用户运行)。

如果用户能够就 GitHub 问题向其他人提供建议,那完全没问题。 这是一个社区。 随意推荐任何可能有帮助的东西。

我可以向你保证,为什么 Docker 的结构是这样的,原因有很多

grep也是如此。 但是如果有人需要更快地搜索,我仍然会推荐ripgrep 。 即使在grep问题跟踪器上。 它是谁的问题跟踪器无关紧要,只要它解决了用户的问题并让他们满意。

如果 Podman 不适合您:很好! 但是如果可以帮助其他人,因为他们只需要在他们的基础设施中用podman替换docker :就让他们这样做。

Podman 的主要论点是它不运行守护进程,这是我反对它的主要论点。 重启后如何恢复我的容器? 我不会手工做,其他的都是糟糕的设计。 此外,我不希望我的 docker 容器归用户所有,但归系统所有,这意味着 root。
如果您是唯一使用 Podman 的人,那么 Podman 是有意义的。

并解决您的问题:使用COPY --chown ...:...构建一个容器!

Docker 也没有这样的问题,你可以远程控制 docker 服务器,这对我来说也很重要。

还有一些工具可以从正在运行的容器生成 pod,但我不建议您这样做,因为您应该以一种干净的方式从头开始构建它们。

我想我们现在应该回到主题:恕我直言,第一个建议还可以,但其他一切只会把这个问题搞砸,不会解决任何问题。


@SuperSandro2000 ,不过,您可以单击此处获取对您声明的回复。

重启后如何恢复我的容器? 我不会手工做,其他的都是糟糕的设计。

好吧, Podman 与 systemd 具有原生集成(就像几乎所有现代 GNU Linux 发行版上的几乎所有其他东西一样)。 因此,您不必维护“两个”引导系统(例如首先让 systemd 启动 Docker 守护程序,然后必须以不同的配置进行另一轮启动容器)。 因此,使用 Podman,您可以使用 systemd 控制一切(意思是:您很可能已经安装并运行的系统)。

此外,我不希望我的 docker 容器归用户所有,但归系统所有,这意味着 root。

如果你不想要它完全没问题。 您仍然可以以超级用户身份运行 Podman,但您不再_必须_。 一般来说,这被认为是一个坏主意并增加了攻击面,因为如果有人能够利用您的 Docker 守护程序,他就可以控制系统上的_everything_。

如果您是唯一使用 Podman 的人,那么 Podman 是有意义的。

这种说法没有任何意义。 Podman 使您能够在单个系统上进行扩展,如果您有很多人在同一个系统上工作,这个功能尤其有意义。

并解决您的问题:使用COPY --chown ...:...构建一个容器!

恕我直言,这里的问题是_mounting_ _runtime_ 上的容器的卷。 这与建立图像关系不大。

Docker 也没有这样的问题,你可以远程控制 docker 服务器,这对我来说也很重要。

有趣的是,您确切地提到了包含此帖子的博客。 但是,我对这两种实现的网络细节都不是很有经验,但据我所知,podman 从尽可能少的网络规则开始,非特权用户无法设置veth

需要明确的是,您应该能够使用无根 docker 获得与使用 podman 相同的效果。
这是因为 dockerd 以您的用户身份运行,并且容器中的 root 映射到您的 UID。

这确实有缺点,当然在与多个用户共享守护程序时不起作用。
https://get.docker.com/rootless

2019 年 6 月 27 日上午 7:52,Alexander Adam [email protected]写道:

我想我们现在应该回到主题:恕我直言,第一个建议还可以,但其他一切只会把这个问题搞砸,不会解决任何问题。

@SuperSandro2000 https://github.com/SuperSandro2000 ,不过,您可以单击此处获取对您声明的回复。
https://podman.io/blogs/2018/09/13/systemd.html https://osric.com/chris/accidental-developer/2018/12/docker-versus-podman-and-iptables/ https:// /osric.com/chris/accidental-developer/2018/12/using-docker-to-get-root-access/

你收到这个是因为你被提到了。
回复此电子邮件直接,查看它在GitHub上https://github.com/moby/moby/issues/2259?email_source=notifications&email_token=AAGDCZXX2UQCG7LUVH57V6LP4TH2DA5CNFSM4AI3DP62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODYXL2XI#issuecomment-506379613 ,或静音线程https://github.com/notifications/取消订阅-auth/AAGDCZX437HJP4M6XG3SEY3P4TH2DANCNFSM4AI3DP6Q

@alexanderadam

恕我直言,这里的问题是在运行时为容器安装卷。 这与建立图像关系不大。

我的解决方案是不挂载目录,但如果可能的话,将其烘焙到容器中。

我的意思是 podman 听起来不错,但我暂时不会切换原因,我看不出对我有任何好处。 无论如何感谢您的解释。

如果容器内的 Apache 在www用户下运行,$ podman也会遇到同样的问题。 https://github.com/containers/libpod/issues/3990

如果容器内没有root用户,解决方案可能是将容器中的www用户映射到主机上的 UID。 我不知道这是否可能。

如果您想使用--read-only运行(与readOnlyRootFilesystem Kubernetes 策略相同),可以执行以下操作。 它建立在@jpetazzo建议的解决方法之上:

  • 我的 docker 映像创建并使用 uid=1001 和 gid=1001 的用户
  • 另外,创建一个 docker 卷
  • uid:gid 改成1001
  • 在运行应用程序时挂载该图像。

Dockerfile:

FROM ubuntu

RUN groupadd -g 1001 appgroup && \
    useradd -u 1001 -g appgroup appuser

USER appuser

然后:

$ docker build . -t test
$ docker volume create somedir
$ docker run -v somedir:/some_dir alpine chown -R 1001:1001 /some_dir

现在,在运行 docker 映像并挂载卷时,/some_dir 属于我想要的用户。

$ docker run -it --read-only -v somedir:/some_dir test ls -lrt

...
dr-xr-xr-x  13 root    root        0 Nov  4 15:22 sys
drwxr-xr-x   2 appuser appgroup 4096 Nov  5 09:45 some_dir
drwxr-xr-x   1 root    root     4096 Nov  5 09:45 etc
...

$ docker run -it --read-only -v somedir:/some_dir test touch /some_dir/hello
$ docker run -it --read-only -v somedir:/some_dir test ls -lrt /some_dir

-rw-r--r-- 1 appuser appgroup 0 Nov  5 09:52 hello

我将再次指出,因为它很容易在线程中丢失,一个 chowned 符号链接可能适用于大多数情况。 缺点是您需要以某种方式对其进行设置,这通常意味着将入口点替换为然后运行原始命令的脚本。

https://github.com/moby/moby/issues/2259#issuecomment -466094263

+1

我认为这是迄今为止我在 docker 上遇到的最烦人的问题,并且看到它已经打开了多长时间表明这对许多其他人来说并非如此?

如果您知道解决方法,这不是问题。 我的案例:

  • 主机是 Linux

    • 容器中的 uid == 主机上所需的 uid - 不需要解决方法
    • 容器中的 uid != 主机上所需的 uid - 只需运行几个setfacl命令并为主机用户和容器用户提供rw访问权限
  • 主机是 MacOS——官方 Docker 应用程序开箱即用。

只需运行几个setfacl命令并为主机用户和容器用户提供rw访问权限

这是问题。 我不想为每个 docker 映像运行几个setfacl命令并检测操作系统。

这实际上也是一个巨大的安全问题。

示例场景:

  • host1已安装 docker
  • host1有一个在 docker 容器中运行的多个服务 - 所有这些都是/docker/my-service-01|02|03|etc下的挂载本地路径
  • 每个容器由不同的供应商构建,每个容器都遵循自己的uidguid政策,因此要求您相应地使用chown -R uid.gid /docker/my-service-01...

结果:

  • 在某些时候,在host上创建的普通用户或服务用户将拥有对/docker/my-service-01|02|03|etc的完全访问权限,这不是预期的,也不是所希望的。
  • 如果您想在来自不同供应商的两个容器上将卷安装为“只读”-它将失败,因为uid.gid与所需的不匹配并且您将无法chown因为每个容器都有自己的uid.gid政策,而且它们是不同的 :)

是的,我们之前详细讨论了这个问题,并且(当时)传达的关键事实是 linux 内核没有提供可重新映射的 uid 和 gid 的底层支持机制。 因此,需要添加到内核中才能让这个项目(moby / docker)实现这一非常理想的功能。 否则我们会在前一段时间已经获得此功能。 回到第一次看的时候。

所以(今天)继续这个讨论的最有效的方法是:看看这种情况是否已经改变了。 在 vger.org 上查找来自 linux 内核主线开发人员的技术评论。 在内核上查找过去的补丁集/合并请求以获取此底层缺失功能。 等等。

希望更好地了解在较低级别发生的事情。 什么是绊脚石? 是性能问题吗? 这是对安全模型/弱化的反对吗? 它仍然在桌面上还是在未来的路线图中,但只有在其他功能 B 和 C 可以实现之后才有意义? 所有这些内核开发都在其他地方进行。 在其他渠道。

@DXist事实上,它在 OSX 上而不是在 Linux 上神奇地工作,这本身就是一个问题,令人惊讶。

根据@dreamcat4的最后评论,有没有人重新尝试看看这是什么状态? 我们现在在内核中支持可重映射的 uid 和 gid 吗? 这里的总体情况如何?

我已经使用 Linux 用户命名空间完美地解决了这个问题。 与其他平台完全相同(AFAICT)(容器将绑定安装的卷视为 root,主机将其视为运行 docker 的用户)。

指南在这里: https ://www.jujens.eu/posts/en/2017/Jul/02/docker-userns-remap/

@patrobinson +1

此页面是否有帮助?
0 / 5 - 0 等级