Moby: 为共享卷设置 uid 和 gid

创建于 2014-07-23  ·  141评论  ·  资料来源: moby/moby

请提供一个选项,使共享卷的所有权可配置。

例如,我当前的用例是让 logstash-forwarder 在容器中运行,该容器将 /var/lib/docker 作为来自主机的卷共享为只读。

由于 /var/lib/docker 在主机上设置为 0700 root:root我无法以非 root 用户身份访问该卷。

我想要的是类似于 NFS 的东西,可以将 uid 和 gid 从主机映射到客户端上的用户和组。

即 docker run -v /var/lib/docker:/var/lib/ docker:ro :$user:$group 将使容器中的卷作为只读可用,属于 $user:$group。

kinfeature

最有用的评论

我们真的需要一个跨平台的 go-to(不是 gosu)解决方案来映射 uid/gid。 仅此问题就对新手对 docker 的看法造成了巨大的损害。

所有141条评论

我不知道_如何_他们实现了这一点,如果 Linux 上存在类似的功能,但在 OS X 上,存在一个“忽略所有权”的功能。 实际上,这将使 _any_ 用户看到文件/目录,就好像他们是所有者一样。

虽然这不会适用于所有情况,但它可能是一个不错的补充

+1

相关: http :

gh#5910 从 SELinux 端处理这个问题。

链接:#5910 ;)

SELinux 的变化实际上是在改变内容上的标签。 您可能可以在主机上进行更改。 我知道没有其他方法可以为 UID/GID 做到这一点。 但是在挂载点上执行 chown -R $UID:$GID 。

或者您可以使用 ACL 添加这种类型的访问。 但我认为您需要更改挂载点上的每个 INODE。

我也需要这个功能。

例如,我想创建 Web 容器,并将带有网站和配置的卷附加到它,以便该容器对于任何数量的网站都是完全通用的。

但是,我需要 git 访问权限才能将代码推送到网站存储库。 因为我想让我的应用程序被隔离 - 我想让每个网站目录由单独的用户/组拥有,如果由 Docker 容器写入卷的文件将由该单独的用户/组拥有,那就太好了。

+1 此功能。
我不明白没有它读/写卷如何工作。 期望镜像和主机上的 guid/uid 是相同的,这是一个与 Docker 的隔离原则不兼容的强烈要求。

我个人正在为我的 Dockerized 开发工具使用丑陋和缓慢的 useradd/groupadd 命令解决这个问题: https :

我可能完全没有抓住重点。 但是我在一个类似的问题上苦苦挣扎,我想确保 http 用户对 /var/log 具有写权限,这是一个卷,很可能来自以root:root为所有者的主机。

我通过设置一个入口点来解决它,以确保创建日志目录并具有正确的权限。 我想这是有效的,因为入口点脚本以 root 身份运行。

(删除评论 - 错误的标签,抱歉)

我在 Docker 之外的 Ubuntu 中解决了这个问题。 安装包 bindfs 并将包含卷内容的目录绑定到另一个路径,同时将 UID 和 GID 映射到容器内使用的那些:

sudo bindfs -u UID -g GID oldpath newpath

然后使用 newpath 作为 docker 卷。 Oldpath 仍然为主机显示正确的所有权,为来宾显示 newpath。

@jjv问题是 bindfs 真的很慢。

@ cpuguy83是的,它远非最佳,但可能会帮助处于类似情况的人。 这是为了让开发虚拟机(vmhgfs 不允许设置 UID/GID)内部工作,而在生产中,UID 和 GID 仍然必须在主机和来宾之间匹配......

如果在 docker 实现此功能时使用一种类型的 bindfs 功能实际上会很好,假设它不会导致太多的性能损失。 这样,您就不必确保容器以正确的用户身份运行。 也应该可以使用逻辑名称而不是文字 uid/gid。

@jsternberg这是一个巨大的性能打击。 非常类似于使用 vbox 共享文件夹。

+1

对于本地开发用例,我认为 Docker 肯定需要这个功能。 在这种情况下,我希望此功能同时支持 Windows 和 OSX。

Vagrant 似乎通过将主机用户的 UID/PID 映射到流浪用户的 UID/PID 来支持这一点。 但是出于开发目的,我真的想使用 Docker 而不是 Vagrant,因为它在运行多主机应用程序方面比 Vagrant 轻得多。

请告诉我我在这里缺少什么(我没有任何使用 Go 的经验),但是 Go Mount() 函数不接受标志吗? 难道我们不能允许这样的命令
-v host/folder:container/folder -mount-as user:group
你不能通过查找来获取 uid/gid (https://golang.org/src/os/user/lookup_unix.go)
然后将它们 (uid=1,gid=1) 作为标志传递给 Mount()? (https://golang.org/src/syscall/syscall_linux.go?s=19154:19249#L754)

@krisgraham绑定安装不支持这样设置 uid/gid。

当有多个 -v 选项时,将 -v 选项与 --mount-as 选项分开会导致混淆

对此有什么好的解决方法? 我喜欢使用 Docker 进行主动开发,并且没有某种类型的安装卷并不是真正的选择,因为每次我对代码进行更改时我都必须重新构建。

我想使用 Docker 进行主动开发的原因是它与我的生产环境一致。

@berfarah我每天都使用
很少有我需要弄乱烫发的情况。
如果您在 OSX 上使用 boot2docker,请确保您的工作目录位于 /Users 中,您应该没问题。

@cpuguy83感谢您的快速回复。 我在无法写入日志的 rails 环境中遇到权限问题,并且由于权限偶尔会出现故障点。 这是因为我的服务与其中一个文件具有不同的 UID。

@berfarah我的解决方法是编写我的 dockerfiles,以便拥有代码的容器用户/组与我的主机用户具有相同的 UID/GUID。 例如,由于我的主机用户 UID 是 1000:

RUN \
    groupadd code_executor_group && \
    useradd code_executor_user -g code_executor_group -u 1000

@berfarah是否有日志可用于 RAILS_ENV=development 的 stdout/stderr?

@cpuguy83此问题不影响 OSX; thaJeztah在 2014 年 7 月 24 日评论:

在 OS X 上,存在“忽略所有权”卷的功能。 实际上,这将使任何用户看到文件/目录,就好像他们是所有者一样。

@ncjones实际上,这同样适用于 OS X。我在那里谈论的“卷”是 OS X 本身使用的硬盘/分区(卷)。 我怀疑这对使用 Boot2Docker 有什么影响,但我不确定。

我的评论是为了通知 Linux 中是否有 _similar_ 可能(因此在 Boot2Docker VM 中)

很抱歉那里的混乱。

@ryneeverett谢谢,这很有帮助。 然后,您是否会在需要的地方分别将权限从 755 修改为 775,将 664 从 644 修改为 664? 编辑:阅读技巧!

@cpuguy83谢谢! 这似乎比@ryneeverett的解决方案更具限制性,因为我无法将其

@berfarah很高兴你觉得这很有帮助。 是的,我只是确保我的主机文件具有正确的权限,并且 Docker 将它们保留在卷中。

+1

这对于通过 docker 共享卷非常有用,并解决了我的安全问题:

  • 容器 uid 和 gid 意外映射到主机上的特权非 root 用户的共享卷。 (例如,主机上的容器创建的文件映射到可以 sudo w/o passwd 的用户,访问其他受限内容等)

对于运行大量带有 Apache 或一些中间件的容器并共享日志记录卷和/或各种内容/上传目录的企业人员来说,修复此问题将非常方便。 在 Linux 中容器化最终用户应用程序(如 Syncomm/spotify)时,我个人也对这些权限感到头疼。 今天的许多变通方法本身就存在问题。 最终,这必须在 docker 中修复。 我尤其不喜欢将 root shell 脚本作为入口点运行,尤其是当这个问题突出显示容器中的 0:0 uid/gid 将如何映射到我主机上的 root 时。 我喜欢最初的提议“docker run -v /var/lib/docker:/var/lib/ docker:ro :$user:$group”。

@syncomm我会采取完全相反的方法。
Docker 无法对数据的所有权做出假设。

如果我们将这些真实文件自动映射到基于保险丝的 fs,我们可以在其中更改 uids/gids,那么您在评论底部突出显示的方法将是可行的……这会导致显着的性能影响。

很快,容器中的 uid 0 将不再是主机上的 uid 0 ......这也使得文件所有权更加棘手。

这是#2259 的副本吗?

@cpuguy83感谢您的反馈,尽管我不确定您所说的完全相反的方法是什么意思。 云你解释一下? 我同意数据的所有权不应该由 docker 承担,但我相信提供从容器到主机的一致映射肯定会让 docker 消费者更轻松地监管这些数据。

我同意,就像 bindfs 解决方法一样,使它成为保险丝包装器会产生一些深刻的开销。 但是,必须有办法摆脱这个难题,而不会受到巨额罚款。 本质上,容器的行为是正确的(就好像它是与主机不同的唯一机器),并且当我们将主机的目录作为卷挂载时,它(正确地)看到了那里的 POSIX 文件系统、权限和所有内容。 不幸的是,这使得以一致的方式在两个“主机”之间共享数据变得非常困难。 诸如 nfs、cifs 之类的东西已经习惯了这一点,并支持 uid 和 gid 映射——我认为在解决这个问题时,这里可能有一个相似之处。 老实说,我需要更多地深入研究 repo 以找出代码中发生的情况并更好地理解它。

您提到的对文件所有权的更改何时会下降? 容器和主机的 uid 0 不同实际上会让我觉得制作根 shell 脚本的入口点更舒服。 然后只需执行建议的解决方法,将正确的 uid/gid 作为 env 传递并在容器启动时执行 adduser。

@syncomm不幸的是,不是我的知识,不是没有将它们包装在可以使用 uid/gid 设置挂载的其他类型的文件系统中。

最终,这看起来像需要内核支持对 UID 和 GID 进行完全双向重新映射的东西,甚至看起来并不容易管理。

对于非性能敏感的场景,也许可以使用 FUSE 来完成一些事情。

对于开发用例仍然 +1,因为我最终在我的家中拥有根拥有的文件。

对于我的用例,如果 docker 可以在创建主机卷时将自定义 uid/gid 和 selinux 标签应用于主机卷就足够了,如果目录已经存在,则单独保留权限和标签。 目前,我通过为作为 root 运行的图像设置一个单独的入口点来解决这个问题,只是为了修复卷权限。

@ibukanov它不能应用 uid/gid,但 selinux 标签即将到来。

@cpuguy83创建主机卷目录时无法应用自定义 uid/gid 的技术原因是什么?

@ibukanov见上面的评论。

@ cpuguy83我只是没有从评论中看出问题是什么。 对于我的用户案例,我不需要对 uid/gid 进行任何重新映射。 当前在容器内以 root 身份运行时,我可以chown uid:gid /host_volume && chmod 770 /host_volume 。 如果 docker 在 /host_volume 后面的主机上创建目录时可以自行完成,那该多好。 这样我就不需要在容器中提供额外的入口点来作为容器根执行上述操作,并且始终可以使用非根帐户运行代码。

@ibukanov啊,那是另一回事。
Docker 不会故意更改主机目录。
如果我们执行 #9092,那么如果 docker 创建了目录,它会将其视为普通卷(即,将容器中卷路径中的所有内容复制到主机和 chmod/chown 上,就像它在容器中一样)。

@ cpuguy83 - 好吧,

@cpuguy83写道:

很快,容器中的 uid 0 将不再是主机上的 uid 0 ......这也使得文件所有权更加棘手。

是否存在跟踪此问题的现有问题? 我找不到一个。

谢谢!

是否存在跟踪此问题的现有问题? 我找不到一个。

@dato寻找“用户命名空间”; 可以在此处找到初始实现: https : https://github.com/docker/docker/pull/11253

但是在此之前有各种讨论/问题:)

+1

只是为了让我知道我和每个人都在同一条船上:

我正在使用 nginx 映像并将卷安装到 /usr/shared/nginx/html 以进行本地开发。 Nginx 以www-data ,但卷安装为1000:staff ,所以我得到403 forbidden

@MrMMorris我不认为这是同一条船 - 这个问题是当在容器内创建的文件(在已安装的卷上)在没有 sudo 访问权限的情况下无法在容器外读取时。 您的 www-data 用户只需要对挂载的文件进行读取访问,因此您应该可以对文件进行chmod a+r访问。

@ncjones chmod/chown 似乎对容器中的权限没有影响

我很困惑,因为我发誓这在我使用过程中的多个点都有效......

好吧,这很尴尬......

似乎在升级到 compose 1.3.0rc-1(可能不是)或删除我所有的容器/图像并再次运行docker-compose up后,似乎已经修复了所有内容......没有更多的403 forbidden

我之前就知道它可以正常工作,所以我采用了 ol' rm -rf *方法..

我也需要以某种方式解决这个问题。 我有一个 docker 镜像,我用它来执行一些必须隔离的繁重编译工作。 所以容器必须上升,接收一些外部输入,处理然后它会产生二进制文件(输出)。 如果没有适当的权限管理,试图让它发挥作用是一场噩梦。 我的图像必须是可移植的,所以我不能对主机假设太多。

@alercunha在您的情况下,在

@alercunha
我们在构建设置中通过让 Docker 容器 chown 构建输出解决了这个问题。 我们的 Dockerfile 将包含如下内容:

env USER_ID 1000
env GROUP_ID 1000
cmd bash -lc '\
  build.sh && \
  chown -R $USER_ID:$GROUP_ID build \
'

然后可以在运行时将 Docker 主机用户的实际 uid/gid 提供给容器。 例如,Jenkins 构建命令将使用以下内容:

docker run \
  -e USER_ID=`id -u` \
  -e GROUP_ID=`id -g` \
  -v $basedir:/workspace

@motin以 root

@ncjones谢谢! 这实际上是一个好主意,在我的特定情况下可能会很好地作为解决方案。

@alercunha在这种情况下,可能值得考虑为每个第三方项目使用单独的图像/容器。 这也可以更好地使开源社区受益,例如,如果您提交了用于构建每个第三方项目的官方 docker-images。

要授予 www-data 的所有权限,您可以使用:

RUN usermod -u 1000 www-data

在已安装卷上的 docker 中运行作业时遇到问题。 结果文件归 root 所有,不能由运行 docker 命令的用户管理。 当前的解决方法是在作业之后运行以修复权限。

docker run -t --rm \
    -v $PWD:/usr/src/app \
    -w /usr/src/app \
    debian:wheezy chown -R $(id -u):$(id -g) ./

是否可以让 docker 更改基于主机的挂载卷的权限,使其归在 DockerFile 的 USER 命令中指定的用户所有? 显然权限必须是可配置的,但我们可以让它具有默认行为。

@mig-foxbat
就我而言,如果 docker 仅在第一次创建主机目录时从映像中的挂载点设置主机目录的所有权和权限就足够了。 如果主机目录已经存在,docker 可以不理会它。

@mig-foxbat & @ibukanov对我来说,在打开这个问题时的强制性先决条件是主机文件系统上的权限不会改变(因为在容器映射中有一些虚拟,类似于人们可以用 _NFS_ 做的事情)。

您现在可以通过在容器内的某些启动脚本中运行 _chown_ 轻松完成您正在尝试做的事情。

我喜欢即将推出的卷插件架构,但我更希望 Docker 团队添加此功能。 Docker 运行良好,直到您在容器中使用具有非 root 用户的卷。 然后,当事情(例如,日志记录)不起作用时,您要么将笔记本电脑扔过 Starbucks 窗口,要么找到上面提到的 stackoverflow 项目。

+1 我想让容器以非 root 用户身份运行应用程序,由于这个问题,这变得很复杂。

为什么不使用 LXC 的 id_map 功能来实现这个功能呢?

我刚刚注意到@thaJeztah 已经在上面提到了“id_map”(又名用户命名空间支持)的答案

虽然设置权限需要一些额外的工作,但我们正在以非 root 用户身份运行数十个容器并避免权限问题。 我们的大多数数据容器都以以下内容开头:
ENV CONTAINER_USER 领事
环境容器_UID 312312
运行 adduser -D $CONTAINER_USER -u $CONTAINER_UID
运行 mkdir -p /var/consul/
CMD chown $CONTAINER_USER.$CONTAINER_USER /var/consul/

实际运行容器在设置 UID/useradd 信息方面是相似的。 这是一些重复的工作(必须在数据容器和运行容器上设置 UID),但它相当小。 动态更改 docker 权限或映射权限会增加一些不错的复杂性,用户必须从安全角度更加小心。 我也支持任何有助于降低命令行复杂性的东西,所以如果一切都包含在 dockerfile 中,那么正在发生的事情会更直接一些。

现在有上面的注释,尝试使用某种映射访问容器中的主机文件。 我认为这当然是一个冒险的提议。 通常尝试在没有直接主机映射的情况下共享文件可能是最好的解决方案,否则您会在某种程度上颠倒 docker 安全性。 而不是具有比其他权限更少的容器,否则您正在谈论以非 root 身份运行的容器,该容器具有对主机文件的根级访问权限(假定为常见用例)。 在大多数情况下,我认为组权限或其他解决方案应该会胜出。

我使用入口点 shell 脚本解决了这个问题,该脚本对挂载的目录进行了修改,并通过 su 执行子命令。

solr 容器中的示例,其中 /data 目录作为来自主机的卷挂载:

#!/bin/bash

chown -R $SOLR_USER:$SOLR_USER /data

su -c "$@" -m $SOLR_USER

_用户投票_

_在此讨论中有更改时获得通知的最佳方式是单击右上角的订阅按钮。_

下面列出的人通过随机 +1 对您有意义的讨论表示赞赏:

@jcercurati
@iangelov
@scue
@razvanphp
@鬼
@andrerocker
@anandnalya
@多普森
@哈森
@哈迪姆
@iyn
@cgrantcharovtp
@sandytrinh
@gomex
@本顿博士
@tehmaspc
@segphault
@avaz
@edavism
@ayeo
@斯坦尼斯拉夫
@smebberson
@托尼-克尔兹
@msierks
@pdonorio
@samsong8610
@qm78
@joshughes
@roelvanduijnhoven
@vladimirbright
@ava-dylang

在这些线程上说任何话都会自动订阅用户,所以是的,所有这些人都会订阅,除非他们另外有意取消订阅。 (也不确定 _who_ 你指的是your

这绝对是必须的。 Docker 必须正确地重新映射 UID。 否则,从容器到主机操作系统卷创建的所有内容都归 root 所有。 解决方法很糟糕 - 在容器内更改用户会导致各种复杂情况,不得不到处 chown,Dockerfile 变得越来越丑陋。 最糟糕的是,硬编码 UID 使容器无法移植!

+1 此功能将受到高度赞赏,因为我想避免在 docker 容器和我的主机系统之间切换,只是为了避免覆盖文件上的用户 ID。

现在可以进行 UID 自定义,但需要遵守以下限制:

  • 通过使容器通过环境变量接受它需要的 uid,可以自定义 Uid。 这使主机可以完全控制容器使用的 uid,并有一个额外的好处:多个变量可以设置为相同的 uid 值。 Uid 重映射无法支持将多个容器 uid 映射到单个主机 uid,因为映射必须是 1:1。 例如,如果所需的映射实际上是应用程序应以 uid=0 运行,则可以通过注入变量来实现,但不能通过 uid 重新映射来实现。 请参阅上面的@ncjones@mitchcapper解决方案。
  • 但是,root uid=0 不能更改为不同的值,除非重新映射:#12648。

几乎所有现代 Web 开发框架都有一个日志文件夹和一个位于项目内部的缓存文件夹,在项目配置中定义并通过安装的卷共享。 我首先使用 vboxfs 但速度太慢。 我发现docker-machine-nfs可以创建 NFS 挂载,而且速度很快,但是现在我在容器中的 Web 文件归 502:dialout 所有,不能是 chmod 或 chown,因此我的 Web 应用程序无法运行。 我尝试了docker vagrant box,但它需要所有不同的环境变量和配置的 vagrant,并且还不能与 docker-machine 一起使用。 即使是短期的手动过程,是否有将用户 502 添加到 www-data 或其他内容的解决方法? 这就是我做生意所需要的一切!

+1

对于那些遵循这一点的人; 目前有一个实验性功能的 PR,它将递归地更改绑定安装文件的所有权。 但是应该小心使用,因为该功能会更改主机上的文件(这可能不是您在所有情况下都想要的); 见https://github.com/docker/docker/pull/14632 (和 https://github.com/docker/docker/pull/16767)

+1

顺便说一句,我在使用 ACL 时解决了这个问题

@kvaps ,要详细说明吗?

@posita ,我敢打赌@kvaps正在谈论POSIX ACL 。 如果我是对的,那与 OP 请求不同。

@posita@denydias ,抱歉回答太长。 是的,我正在谈论这个。
我有很多容器,比如 owncloud、samba 和 minidlna。 每个人都使用不同的 uid 和 gid。
它们都使用相同的文件进行操作。 每个人都可以读写文件,我将带有acl选项的文件系统挂载到主机中,并为每个人授予此文件的 uid 和 gid 权限,就像chown命令一样:

# give access for owncloud (apache uid 33):
setfacl -R -m "u:33:rwx" /data
# give access for samba (uid 1000):
setfacl -R -m "u:1000:rwx" /data
# give access for minidlna (uid 997):
setfacl -R -m "u:997:r-x" /data
# preserve this permissions for new files and folders:
setfacl -R -d -m "u:33:rwx" /data
setfacl -R -d -m "u:1000:rwx" /data
setfacl -R -d -m "u:997:r-x" /data

@kvaps ,命令setfaclDockerfile不起作用。 例子:

FROM nginx

ADD data/conf /etc/nginx

RUN mkdir -p /etc/nginx/sites-enabled

VOLUME /etc/nginx

RUN setfacl -dR -m u:1000:rwx /etc/nginx && setfacl -R -m u:1000:rwx /etc/nginx

结果:

root<strong i="12">@3975ac4fba98</strong>:/etc/nginx# getfacl sites-enabled/
# file: sites-enabled/
# owner: root
# group: root
user::rwx
group::r-x
other::r-x

ACL 仅在主机上挂载并运行setfacl后才起作用。 码头工人版本:

Client version: 1.7.1
Client API version: 1.19
Go version (client): go1.4.2
Git commit (client): 786b29d
OS/Arch (client): linux/amd64
Server version: 1.7.1
Server API version: 1.19
Go version (server): go1.4.2
Git commit (server): 786b29d
OS/Arch (server): linux/amd64

这很容易做对,也很容易搞砸。 所以不要搞砸了!
正确的做法是:第一次运行容器时,主机注入容器应该使用的 uid(如上所述)。
更糟糕的方法是:期望容器将其 uid 要求强加给主机。 这是错的,错的,错的。 例如,考虑共享一个公共挂载的两个容器:一个容器写入文件以供服务,另一个容器运行 httpd 来提供文件。 现在,如果这两个容器对 uid 有相互竞争的定义,那么这个系统在最坏的情况下将被破坏,或者在最好的情况下会出现缺陷。 设置的 ACL 对主机没有任何意义,而且它们可能比必要的更宽,这意味着它现在是一个安全问题。 请不要这样做!! 以正确的方式去做。

同意。 将 UID 与容器之间的映射留给容器是一个足够好的解决方案。 只是希望 docker 以身作则,并在官方 docker 镜像中支持此类 UID/GID 映射。

运行时解决,无需chown

我使用了接近@ncjones的解决方案,但我不想chown文件,因为我不想在主机上修改它们。 所以我选择在容器启动时更改UID。

我在Dockerfile创建了一个专用用户:

RUN adduser --no-create-home --disabled-login --gecos "" username --uid 1000

我在Dockerfile定义了一个启动脚本:

CMD ["/run.sh"]

我的/run.sh脚本中有这一行:

usermod -u $USER_ID username

现在我可以在容器启动时选择USER_ID

docker run -e USER_ID=$(id -u)

设法使用新的 dockerfile args 解决了这个问题。 构建容器后不需要做任何特别的事情,所以我想我会分享。 (需要 Docker 1.9)

在 Dockerfile 中:

# Setup User to match Host User, and give superuser permissions
ARG USER_ID=0
RUN useradd code_executor -u ${USER_ID} -g sudo
RUN echo 'code_executor ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER ${USER_ID} 

然后构建:

docker build --build-arg USER_ID=$(id -u)

其他一切都正常:)

编辑:
我在错误的问题上写了这篇文章(这是另一个与 boot2docker 相关的问题)。
对不起。

@pa-de-solminihac usermod -u $USER_ID username将更改username的用户 ID,并将触发username的 HOME 中文件的所有权更改,但以前拥有的每个文件username在他家之外可能会变得不可读/不可写,因为它们现在属于不同的用户

@riquito我将它与在Dockerfile创建的专用用户一起使用:

adduser --no-create-home --disabled-login --gecos "" username --uid 1000

因此username之前没有任何文件。 所以我想没有问题;)

仅供参考, setfacl _not_ 处理可追溯到 OS X 的卷:

root<strong i="7">@0da3c867240d</strong>:~# setfacl --default --recursive --modify u:500:rwx --modify g:500:rwx /opt/test
setfacl: /opt/test: Operation not supported

(在这种情况下, /opt/test是通过 Docker 机器和 Boot2Docker 从 OS X 托管的。另见 boot2docker/boot2docker#581。)

@posita您是否使用 virtualbox 来托管您的 boottodocker 映像? 如果是这样,那实际上可能是 virtualbox 执行共享文件夹的方式的限制。 通过虚拟框共享文件夹,我在做任何类型的常规渗透时遇到了很多麻烦。

@ava-dylang,这是个好点子。 以上是通过带有Parallels 驱动程序的Docker Machine ,它使用 Parallels 的本机共享文件夹实现,这似乎同样受到限制。 (另请参阅此 https://github.com/docker/machine/issues/13#issuecomment-164320881 关于这些类型环境的提案。)

FWIW,我在我的实用程序Scuba中遇到文件所有权问题。

我通过添加一个“init”脚本来解决这个问题,该脚本在容器中创建一个用户,该用户与主机中的调用用户具有相同的 UID/GID。 见 JonathonReinhart/scuba#11 和 JonathonReinhart/scuba#13。

现在在容器中创建的文件看起来与在主机上创建的完全一样。

_更新:_ 这引起了问题(JonathonReinhart/scuba#22)。 我改为通过基于主机用户的 uid/gid 生成我自己的/etc/passwd和朋友来修复它,并将其注入容器(参见 JonathonReinhart/scuba#24)。

+1

当主机在多租户模式下运行时,使其更易于维护和安全。

当您将整个 Wordpress 从主机共享到带有卷的容器时,我遇到了此类 Wordpress 安装的权限问题。

在我的堆栈中,我有一个基本映像,它是一个经过我基本修改的 debian,所有其他映像都将从该映像构建。

在基本图像中,我有这部分:

### Start of Nginx WEBSERVER setup
RUN mkdir -p /var/www
# Modify www-data user and set UID, GID to 500
# https://muffinresearch.co.uk/linux-changing-uids-and-gids-for-user/
RUN groupmod -g 500 www-data \
    && usermod -u 500 www-data \
    #&& `find / -user 33 -exec chown -h 500 {} \;` \
    #&& `find / -group 33 -exec chgrp -h 500 {} \;` \
    && usermod -g 500 www-data \
    && chown -R www-data:www-data /var/www \
    && chmod g+s /var/www
### End of Nginx WEBSERVER setup

www-data不是由 php 或 nginx 安装创建的。 它是 Debian 和其他发行版中默认定义的用户/组。 一些 PHP、Nginx 安装建议在他们的配置文件中使用这个用户。

如果您的主机用户的 UID/GID 是 500(大多数默认的 linux 第一个非 root 用户的 UID/GID 是 500 或 1000),那么这个脚本将www-data用户的 UID/GID 从 33 更改为 500。这样如果您从主机共享任何内容,Docker 认为这些文件和文件夹归www-data用户所有!

在您的 PHP-FPM 设置文件中,还将用户和组设置为www-data

在您的 nginx Dockerfile 中,您还必须设置:

# Allow Nginx to access /var/run/php-fpm/php-fpm.sock
RUN usermod -aG www-data nginx

这样nginx用户就可以访问www-data拥有的文件(你可以在 nginx 配置文件中定义 nginx 的用户名)。

在这次黑客攻击之后,我的 Wordpress 安装没有任何权限问题! 所有文件都驻留在主机上 + 更新 Wordpress 工作完美!

@DJviolin我不确定像这样的问题是否适合 Wordpress 教程,我不愿在这里详细说明。 然而,对于那些可能以某种方式偶然发现的不幸的新手来说:

  1. 您发布的内容对安装做出了很多假设 - 即 UID/GID 值、用户和组名称等。它在跨发行版中的可移植性不是非常好。
  2. 更重要的是,为整个 Wordpress 安装提供世界可写 (0777) 访问权限是非常危险的,并且在官方 WP 文档中明确建议不要这样做。 此外,所有这些都是从主机安装的,您只是允许从 Internet 任意上传到 _host_ 文件系统。

@jantman对不起,我的错。 看起来第一个 hack 也解决了权限问题。 无需更改任何默认权限。

显然每个人都需要找出自己的主机用户UID:GID,并据此更改配置。 此外,还可以与任务中的现有用户或新用户建立联系。 我的是一个活堆栈的工作示例。

在 docker 没有给出这个问题的解决方案之前,假设将保持不变。

我的解决方案与@JonathonReinhart或@pa-de-solminihac 的解决方案相同。

你可以通过创建本地卷来做到这一点,它刚刚登陆 master https://github.com/docker/docker/pull/20262。

将此作为固定关闭。

嗨,我还必须在我的 dockerfile 中做一个解决方法:

...
COPY start.sh /root/start.sh
CMD /root/start.sh

然后在 start.sh

usermod -u $USER_ID www-data
exec php-fpm

由于 $USER_ID 可以作为环境参数注入,因此可以不加修改地使用该 docker 镜像。

无论如何,我认为应该为这类东西提供更多内置功能,我想知道如何使用@calavera建议的本地卷,有人可以提供一个例子吗?

@keywan-ghadami 我也对本地卷感到困惑。 问题是您必须先创建文件系统(或使用 tmpfs)。 所以它不使用/var/lib/docker。 实际上,您不能使用 _any_ 现有 FS 的子目录,因为绑定安装不支持uid选项。 我不知道本地卷是干什么用的; 您同样可以自己创建和挂载 FS,然后使用普通主机卷 ( -v my-created-fs:container-mount-point )。

我对这个线程已经很晚了,但是这个问题已经解决了吗? 从这里引用的所有不同问题来看,并不是 100% 清楚。 @brthor似乎拥有我所看到的最佳解决方案,但涉及命令行选项,这些选项可以更轻松地添加到 Dockerfile 中并在幕后完成。

对我来说,硬编码 UID 和 GID 对于环境之间的可移植性来说是一个坏主意,而且我认为每次启动容器或构建映像时都不需要添加命令行参数,因为它可能是一个简单的选项文件。 在 Dockerfile 中或者通过 docker-compose.yml 中是否没有一个简单的选项,您可以在其中指定某种“从主机映射 uid”选项?

可能已经有一个很好的解决方案,但我无法从文档或此线程中真正弄清楚这一点。

谢谢@alvinchevolleaux,我可以确认我们已经成功使用了我在上面在 CI 中为https://github.com/dotnet/cli发布的解决方案几个月了。

一定要推荐它!

@brthor是的,这就是我所用的,我将export USER_ID=$(id -u)到我的 .bash_profile 中,因此它对于我的各种环境都是自动的。 非常感谢。

如上所述,将您的主文件夹中的内容共享到容器中时,请添加一个与您的 UID 具有相同 UID 的用户帐户。 如果您的 UID 不是 1000,这里有一个应对技巧。我假设每个用户都从 Dockerfile 构建自己的 docker 镜像。

您的Dockerfile应包含:

RUN useradd --uid 1000 -m vagrant
USER vagrant

常量1000使用 _git filter_ 替换您的实际 UID。 在您的主机上运行以下命令:

git config filter.uidfix.smudge "sed s/1000/$UID/g"
git config filter.uidfix.clean "sed s/$UID/1000/g"

最后,添加一个.gitattributes文件以应用 git 过滤器:

Dockerfile filter=uidfix

当您通过 _smudging_ 文件检出 Dockerfile 时,将1000替换1000 )。 在容器中运行的任何命令都以具有正确 UID 的vagrant用户身份运行。

应重新打开此票证的接缝,因为此处提供的所有解决方案都只是解决方法
我在https://github.com/rocker-org/rocker/blob/master/rstudio/userconf.sh 中找到了一个很好的文档化和更完整的解决方案
它使用运行时参数,这使得可以使用 git 过滤器无法使用的预构建图像

@calavera我不明白你的解决方案。

例如

$ mkdir /tmp/test
$ sudo docker volume create --name=test -d local --opt type=nfs --opt device=/tmp/test:/data --opt o=notime,nosuid,uid=11459,git=11459
$ sudo docker run -t -i -v test:/tmp fedora bash -i
[.. $] touch /tmp/foo.txt
[.. $] exit
$ ls -la /tmp/test

当我查看 /tmp/test 目录时,没有文件。 似乎 /tmp/test 文件夹甚至都没有安装在容器中......而是创建了一个容器卷。 这不是我想要的。

我找不到任何文档告诉我什么是有效的 --opt 选项。 所以我真的在猜测这是如何工作的。

请重新打开此问题。 新的本地驱动程序似乎根本没有解决使用选定用户 ID 挂载本地主机目录的问题。

@卡拉维拉
您能否评论一下我们如何解决本地主机卷以 root 身份删除文件的问题? 我经常看到这个问题出现,尤其是在 CI 场景中使用 docker 时。

查看链接的 PR 我没有看到任何明显的东西,但我确定我错过了一些东西😄

我一直在我的容器中使用这种解决方法的变体,例如 docbill/docker-force 。 然而,我发现一个更清洁的解决方案是一个只负责做南瓜的容器......

对本地使用诸如 bindfs 之类的东西应该会有所帮助。 您可以使用 -o map=$(id -u)/root:@$(id -g)/ @root (假设没有用户命名空间)运行它,并且您应该在容器外部看到属于您的文件,而它们归您所有根在它里面。

@ktosiek谢谢,这听起来很有潜力。 你能发布一个完整的例子docker run命令或控制台会话吗?

有人可以发布有关此问题的状态更新:它是否因“无法修复”而关闭,或者是否已实际实施? 线程很长,所以请原谅我一目了然无法看到分辨率是什么。

@quinncomendanthttps://github.com/docker/docker/issues/7198#issuecomment -191990887
如果无效就报告。

谢谢@LK4D4。

总而言之,该问题已通过修复解决了问题,该local卷驱动程序。

据我所知,没有设置user:group的 _host-directory 挂载为 volume_ 的选项(如果该选项存在,请纠正我 - 这是我来这里学习的内容)。

@quinncomendant正确; 在绑定挂载主机目录时,您希望容器按原样使用包含权限的文件。 如果要更改这些权限,则需要在主机上进行,然后再绑定挂载它们

@quinncomendant或者更简单地说,这是一个
提供的解决方案实际上并没有解决问题
报道,也没有计划解决这个问题。

2016 年 7 月 6 日 17:20,Sebastiaan van Stijn通知@ github.com
写道:

@quinncomendant https://github.com/quinncomendant正确; 什么时候
绑定挂载主机目录,您希望容器使用以下文件
是否按原样存在,其中包括权限。 如果你想改变那些
权限,您需要在主机上执行此操作,然后再绑定安装它们


您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/docker/docker/issues/7198#issuecomment -230909967,或者静音
线程
https://github.com/notifications/unsubscribe/ADBcWLihVjI1FoSMLKu7DyhwFOpGX0KKks5qTBwMgaJpZM4CQMjR
.

这似乎是一个 WON'T FIX 问题。 容器化的 _development_ 方面似乎总是被忽视,仍然需要我们编写 hacks。

我特别遇到了这个问题,因为我支持的开发人员正在从容器内部运行项目文件生成命令,并输出到主机安装的卷(在主机上安装这些工具完全违背了使用容器的目的,对吗?)。

他们想快速迭代这些文件,但正如该线程中的其他人指出的那样,它们作为容器运行的 uid/gid 被写出,这要求它们在主机上正确地被 chown 以进一步操作(通过例如IDE)。

在这种情况下,确实需要一种 _docker 特定的方式来填充临时 uid/gid。

@boj这是我在开发Scuba 时遇到的确切场景。 我当前的解决方案在启动期间在容器中创建一个与主机用户具有相同 uid/gid 的用户。 这不是最简单的解决方案,但效果很好。

@JonathonReinhart谢谢,你给了我一点灵感。

我最终编写了一个像这样从主机调用的脚本包装器(在这种情况下他们使用 django):

# bin/manage.py
#!/bin/sh

docker run -v $(pwd):/usr/local/prj -it --entrypoint="/usr/bin/python3.4" -w /usr/local/prj -u $(id -u):$(id -g) prj src/prj/manage.py $@

@boj该解决方案( -u $(id -u):$(id -g) )的潜在问题是容器中该 uid/gid 的/etc/passwd/etc/group没有条目: https://github .com/JonathonReinhart/scuba/issues/11

@JonathonReinhart注意到。 在这种特殊情况下,我只关心写出到开发人员主机的文件,并且它们与主机用户具有相同的权限。

不用担心运行时实际发生的事情,这就是我真正需要的。

@乔纳森莱因哈特

如果容器中的软件需要 /etc/passwd 或 /etc/group 中的条目,请将这些条目设置为可在映像中全局写入,然后在启动时在其中添加必要的条目。

所以@JonathonReinhart提到的解决方案(uid/gid 映射)解决了这个问题。

这似乎在 runc 中已经得到支持(https://github.com/opencontainers/runc/blob/8c9db3a7a5145f6b26c8051af319eee6f72c9ca8/libcontainer/configs/config.go#L19-24)。 在 Docker 中有用于守护进程的userns-remap配置,在这里我们基本上需要让它更细粒度(容器级别而不是守护进程级别),是否有任何兴趣/计划支持这个?

这个docker-compose.yml不起作用:

version: '2'

services:
  app:
    build: ./app
    container_name: myapp
    volumes:
      #- "../app:/root/www/myapp:rw"
      - myapp:/root/www/myapp:rw

volumes:
  myapp:
    driver: local
    driver_opts:
      o: uid=500
      device: ../app

有人能告诉我为什么吗? 我遵循官方指南: https: //docs.docker.com/engine/reference/commandline/volume_create/#/driver -specific-options

是否可以从主机附加命名卷? 使用driver_opts您可以定义uid (也许gid ?)。

+1

+1

@lazyuser @xiaods请停止 +1。 除了向所有这些 ---> 参与者发送垃圾邮件之外,它什么也做不了。

@bamarni是的,我认为新的用户命名空间功能可以解决这个问题,但正如您所说,它需要针对每个容器实施。 最终结果将是:一个容器以它认为是“root”的方式运行,但实际上是在docker run命令行上传递的 UID/GID。 然后文件将从相应用户拥有的容器中“出来”。

@lazyuser @xiaods @JonathonReinhart你应该点击问题描述下的 +1 按钮

或者如果您只想要通知,只需单击右侧的订阅...

@JonathonReinhart :当然,我已经再次阅读了文档,但不幸的是,由于限制,使用映射守护进程而不是每个容器是一个有意识的决定:

注意:现在每个守护进程的单一映射限制已经到位,因为 Docker 在引擎实例上运行的所有容器之间共享来自其本地缓存的图像层。 由于共享同一层内容的所有容器的文件所有权必须相同,因此决定将 docker pull 上的文件所有权映射到守护程序的用户和组映射,以便在下载内容后运行容器没有延迟。 这种设计为 docker pull、docker push 和容器启动保留了与用户期望的相同的性能,同时禁用了用户命名空间。

_(https://docs.docker.com/engine/reference/commandline/dockerd/#/daemon-user-namespace-options)_

亲爱的@ JonathonReinhart 、@pa-de-solminihac 和@nalipaz
感谢您努力教育我和其他人不要通过这样做留下与问题无关的评论! 仅供参考,Github 不允许搜索订阅的问题而不至少对其发表评论。 有关更多信息,请参阅https://github.com/isaacs/github/issues/283。 具有讽刺意味的是,Github 的问题与 Docker 的问题几乎相同,而且两者的优先级似乎相似。

对于所有垃圾邮件,抱歉。 第一次是针对上述 github 错误的解决方法,这一次我忍不住讽刺了这种情况。

我通过使用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 来增加。

@codekitchen-ws 另一个警告:文件可以在创建后和所有者更改之前移动。 根据外壳,您还需要引用"$f" (以防止路径中的分词)。

+1

@briansrepo这是一种有趣的方法。 如果这是在 Dockerfile RUN语句中,则在构建时执行。 它如何在执行时知道docker run用户?

@btiernay谢谢! 它不使用启动图像的用户的 UID。 它专门复制挂载在 /mount 的任何主机目录的主机用户和主机组。 它不查看任何其他文件或子目录。 由用户确保将权限设置为他们可以在主机系统上写入的内容。

示例:假设主机目录 /var/www/html 归brian:www-data 所有。 您启动一个映像,该映像将主机系统目录 /var/www/html 装入映像目录 /mount。 然后从图像内部创建 /mount/index.html。 如果您去检查主机系统上 /var/www/html/index.html 的所有权,它将归brian:www-data 所有。

在该示例的基础上,假设您有一个主机目录 /var/www/html/upload 归www-data:www-data 所有。 请记住,挂载的主机目录 /var/www/html 仍然归brian:www-data 所有。 现在进入图像并创建/mount/upload/file.pdf。 如果您检查主机文件 /var/www/html/upload/file.pdf 它将属于brian:www-data ,而不是www-data:www-data ,因为挂载的主机目录 /var/www/html 是布赖恩所有:www-data。 有道理?

TL; DR:您通过用户:组要通过chowning安装主目录使用的用户:组。

@briansrepo感谢您的解释。 这一切都说得通,但仍然不明白这是如何在RUN 。 我认为这需要在执行容器时在后台执行(即docker run )。

@btiernay我也喜欢这个主意。
@briansrepo至少涵盖了容器中发生的事情。
构建过程仍然可以通过类似的方式解决

RUN usermod -u 1000 www-data

不过,这些仍然是解决方法。

在我的 LEMP 堆栈中,我的基础 Dockerfile 中有这个 Nginx 预配置:

### Start of Nginx WEBSERVER setup
RUN mkdir -p /var/www
# Modify www-data user and set UID, GID to 500
# https://muffinresearch.co.uk/linux-changing-uids-and-gids-for-user/
RUN groupmod -g 500 www-data \
    && usermod -u 500 www-data \
    && usermod -g 500 www-data \
    && chown -R www-data:www-data /var/www \
    && chmod g+s /var/www
### End of Nginx WEBSERVER setup

这是一个 hack,其中来自主机 ssh 会话的每个新创建的文件获取 uid、gid 500 和 nginx 最终将无法访问这些文件(因为容器中不存在 id 为 500 的用户或组)。 您可以检查哪些UID,GID号码,你必须为您的www-data用户,当您复制新的文件从一个SSH会话主机上的共享卷,以后你docker exec到这个容器中,查看文件 uid、gid。

我发现的问题是,如果我将新文件复制到主机上的共享文件夹中(您通过 ssh 会话访问该文件夹,例如在 DigitalOcean 上的 CoreOS 实例中),Nginx 将无法访问这些新创建的文件。 因此,如果您想要与特权绝对兼容,则必须在创建容器时将您的网络服务器文件共享到 Docker 容器中(至少在 1 年前是这种情况,当我遇到这些共享卷的 uid、gid 问题时) .

或者,您也可以将 ssh 服务安装到 docker 容器中,该容器将文件共享到 nginx 容器,这样如果您复制/修改文件,它们就会获得正确的 uid、gid。 但这违背了 Docker 最佳实践,您应该使用docker inspect而不是 ssh 会话,因为“Docker 不是 VM”(这太简单了,对吧?)。

在我看来,Docker 容器应该充当服务或可执行文件,如果我不想,不应该劫持我的网络服务器文件。 数据库是一种不同的东西(有时),但我不明白为什么不可能实现相同的圣杯容器基础设施,其中所有静态、网络服务器、数据库文件都位于容器之外(但容器可以修改它们(如删除、创建、修改),您也可以修改它们而不会受到主机的任何权限问题。

使用docker volume create --opt您可以定义 uid、gid,但docker-compose不正确: https :

我们真的需要一个跨平台的 go-to(不是 gosu)解决方案来映射 uid/gid。 仅此问题就对新手对 docker 的看法造成了巨大的损害。

我们已经为这个问题创建了一个解决方法,将构建时设置的 Docker 容器的用户/组和文件权限更改为容器在运行时启动时使用的 UID/GID。

项目和安装说明位于: https :

示例:Docker 容器是使用用户/组dockeruser:dockergroup作为 UID/GID 1000:1000构建的。 主机作为 UID/GID 1001:1002 。 图像使用docker run -u 1001:1002fixuid将:

  • dockeruser UID 更改为 1001
  • dockergroup GID 更改为 1002
  • 将旧dockeruser:dockergroup所有文件权限更改为 1001:1002
  • 将容器内的 $HOME 更新为dockeruser $HOME
  • 现在容器和主机 UID/GID 匹配,并且在主机挂载的容器中创建的文件将匹配

它可以作为ENTRYPOINT或作为启动脚本的一部分运行。 它作为root拥有的带有setuid 位的二进制文件安装在容器中,并提升权限以进行适当的更改。 它应该只用于开发容器。

如果这证明有用,Docker 引擎可能能够通过docker run标志合并部分或全部逻辑

通过使用docker volume

@hashar怎么样? 你能举个例子吗

那么除了使用gosu和入口点脚本之外,还有没有其他解决方案?!

目前,看起来有 2 个选项可以解决此问题,直到 Docker 团队对此进行官方更新。

太可惜了,这仍然是一个问题。 理论上有 userns-remap 但它不是用户友好的。

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