在Docker中访问主机目录的权限被拒绝


282

简而言之:我试图在Docker中挂载一个主机目录,但是即使访问权限看起来不错,我也无法从容器中访问它。

细节:

我在做

sudo docker run -i -v /data1/Downloads:/Downloads ubuntu bash

然后

ls -al

它给了我:

total 8892
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 .
drwxr-xr-x.  23 root root    4096 Jun 18 14:34 ..
-rwxr-xr-x.   1 root root       0 Jun 18 14:34 .dockerenv
-rwx------.   1 root root 9014486 Jun 17 22:09 .dockerinit
drwxrwxr-x.  18 1000 1000   12288 Jun 16 11:40 Downloads
drwxr-xr-x.   2 root root    4096 Jan 29 18:10 bin
drwxr-xr-x.   2 root root    4096 Apr 19  2012 boot
drwxr-xr-x.   4 root root     340 Jun 18 14:34 dev
drwxr-xr-x.  56 root root    4096 Jun 18 14:34 etc
drwxr-xr-x.   2 root root    4096 Apr 19  2012 home

以及更多类似的内容(我认为这是相关的部分)。

如果我做

cd /Downloads
ls

结果是

ls: cannot open directory .: Permission denied

主机是Fedora 20,带有Docker 1.0.0和go1.2.2。

怎么了?

Answers:


269

有关完整的故事,请参见有关Volumes和SELinux的Project Atomic博客文章

特别:

由于Docker最终合并了一个补丁,该补丁将在docker-1.7中显示(最近,我们一直在RHEL,CentOS和Fedora的docker-1.6中携带该补丁),因此最近这样做变得更加容易。

此修补程序在卷安装(-v)上增加了对“ z”和“ Z”的支持。

例如:

docker run -v /var/db:/var/db:z rhel7 /bin/sh

将自动执行chcon -Rt svirt_sandbox_file_t /var/db 手册页中所述。

更好的是,您可以使用Z。

docker run -v /var/db:/var/db:Z rhel7 /bin/sh

这将使用与容器一起运行的确切的MCS标签来标记容器中的内容,基本上,每个容器chcon -Rt svirt_sandbox_file_t -l s0:c1,c2 /var/dbs0:c1,c2不同的MCS标签上运行。


18
这就像一个魅力。其他解决方案大多是解决方法。
tuxdna'3

4
cf. 码头工人文档中的“ 卷标”部分
maxschlepzig,2016年

哦,伙计,这确实有效。我终于找到了。非常感谢!是否有任何官方文件?
柯比


1
通过同时使用两个选项(以逗号分隔),可以在同时将卷挂载为只读的同时修复SELinux下的权限-v $(pwd):/app:ro,Z。应该将其标记为正确答案。
danirod '19

263

这是SELinux问题。

您可以临时发出

su -c "setenforce 0"

在主机上访问或通过运行添加SELinux规则

chcon -Rt svirt_sandbox_file_t /path/to/volume

3
/ path / to / volume是主机的路径?如果是这样,那么该解决方案似乎不适用于数据容器吗?
Roy Truelove 2014年

6
别忘了做su -c“ setenforce 1” ...否则它将只因为SELinux仍被停用而
起作用

这解决了我的问题。谢谢,我希望他们对此有修正。
北斗星2015年

19
添加selinux规则是最好的方法,因为在大多数情况下以特权模式运行容器不是一个好主意。
Zoro_77 2015年

7
正如Zoro_77所说,添加一条规则并stopdisablingselinux.com ;)
GabLeRoux

71

警告:此解决方案具有安全风险。

尝试以特权用户身份运行容器:

sudo docker run --privileged=true -i -v /data1/Downloads:/Downloads ubuntu bash

另一个选择(我没有尝试过)是创建一个特权容器,然后在其中创建非特权容器。


1
@JBernardo这两个选项中的哪一个解决了问题?
user100464 2014年

@ user100464--privileged=true
JBernardo

1
在我的情况下没有帮助。Debian Whezzy具有向后移植的内核3.16,但未激活SELinux配置。:(
aholbreich 2014年

如果您使用的docker-composer添加了'privileged:
true'–莱昂内尔·莫里森

35
不要这样做。--privileged是一种安全风险
纳文

38

通常,主机卷装入的权限问题是因为根据主机上文件的uid / gid权限,容器内的uid / gid无法访问该文件。但是,此特定情况不同。

权限字符串末尾的点drwxr-xr-x.表示已配置SELinux。与SELinux一起使用主机安装时,您需要在卷定义的末尾传递一个额外的选项:

  • z选项指示绑定安装内容在多个容器之间共享。
  • Z选项指示绑定安装内容是私有的且未共享。

您的volume mount命令将如下所示:

sudo docker run -i -v /data1/Downloads:/Downloads:z ubuntu bash

在以下网址查看有关使用SELinux进行主机安装的更多信息:https : //docs.docker.com/storage/#configure-the-selinux-label


对于其他看到以其他用户身份运行容器的问题,您需要确保容器内用户的uid / gid有权访问主机上的文件。在生产服务器上,这通常是通过控制映像构建过程中的uid / gid来匹配具有访问文件权限的主机上的uid / gid来完成的(甚至更好的是,不要在生产环境中使用主机挂载)。

命名卷通常比主机挂载更可取,因为命名卷将从映像目录初始化卷目录,包括所有文件所有权和权限。当卷为空并且使用命名卷创建容器时,会发生这种情况。

MacOS用户现在拥有OSXFS,它可以在Mac主机和容器之间自动处理uid / gid。其中一个无济于事的地方是嵌入式VM内的文件已挂载到容器中,例如/var/lib/docker.sock。

对于每个开发人员可能更改主机uid / gid的开发环境,我的首选解决方案是使用以root身份运行的入口点启动容器,将用户的uid / gid固定在容器内以匹配主机卷uid / gid,并然后用于gosu从根目录降到容器用户以在容器内运行应用程序。重要的脚本fix-perms在我的基本图像脚本中,可以在以下位置找到:https : //github.com/sudo-bmitch/docker-base

fix-perms脚本的重要部分是:

# update the uid
if [ -n "$opt_u" ]; then
  OLD_UID=$(getent passwd "${opt_u}" | cut -f3 -d:)
  NEW_UID=$(stat -c "%u" "$1")
  if [ "$OLD_UID" != "$NEW_UID" ]; then
    echo "Changing UID of $opt_u from $OLD_UID to $NEW_UID"
    usermod -u "$NEW_UID" -o "$opt_u"
    if [ -n "$opt_r" ]; then
      find / -xdev -user "$OLD_UID" -exec chown -h "$opt_u" {} \;
    fi
  fi
fi

这样会将用户的uid和文件的uid放入容器内,如果不匹配,则调用usermod以调整uid。最后,它会进行递归查找以修复所有未更改uid的文件。我比使用带有-u $(id -u):$(id -g)标志的容器更好,因为上面的入口点代码不需要每个开发人员都运行脚本来启动容器,并且用户拥有的卷之外的任何文件都将得到其权限更正。


您还可以让docker通过使用执行绑定安装的命名卷从映像初始化主机目录。该目录必须预先存在,并且您需要提供主机目录的绝对路径,这与可能是相对路径的撰写文件中的主机卷不同。该目录也必须为空,以便docker对其进行初始化。定义绑定卷的命名卷的三个不同选项如下所示:

  # create the volume in advance
  $ docker volume create --driver local \
      --opt type=none \
      --opt device=/home/user/test \
      --opt o=bind \
      test_vol

  # create on the fly with --mount
  $ docker run -it --rm \
    --mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=none,volume-opt=o=bind,volume-opt=device=/home/user/test \
    foo

  # inside a docker-compose file
  ...
  volumes:
    bind-test:
      driver: local
      driver_opts:
        type: none
        o: bind
        device: /home/user/test
  ...

最后,如果您尝试使用用户名称空间,则会发现主机卷存在权限问题,因为容器的uid / gid已转移。在这种情况下,避免主机卷而仅使用命名卷可能是最容易的。


32

access.redhat.com:Sharing_Data_Across_Containers

主机音量设置不可移植,因为它们取决于主机,并且可能无法在任何其他计算机上使用。因此,没有等效于将主机目录挂载到容器的Dockerfile。另外,请注意,主机系统不了解容器SELinux策略。因此,如果强制执行SELinux策略,则无论rw设置如何,装入的主机目录均不可写入容器。当前,您可以通过将适当的SELinux策略类型分配给主机目录来解决此问题”:

chcon -Rt svirt_sandbox_file_t host_dir

其中host_dir是主机系统上已安装到容器的目录的路径。

看来这只是一种解决方法,但我尝试过并且可行。



13

尝试docker volume create

mkdir -p /data1/Downloads
docker volume create --driver local --name hello --opt type=none --opt device=/data1/Downloads --opt o=uid=root,gid=root --opt o=bind
docker run -i -v hello:/Downloads ubuntu bash

看看文档https://docs.docker.com/engine/reference/commandline/volume_create/


3
在SO上对这个问题尝试了很多答案,但是实际上这个有帮助。谢谢!
Paul

它解决了权限错误。但是现在,如果我要安装物理位置,它将安装voulme

1
@kunalverma是的。如果您不喜欢它,这是更简单的答案。 stackoverflow.com/a/31334443/4909388
六月

4

我有一个类似的问题,我的问题是由主机的UID和容器的用户的UID不匹配引起的。解决方法是将用户的UID作为参数传递给docker构建,并使用相同的UID创建容器的用户。

在DockerFile中:

ARG UID=1000
ENV USER="ubuntu"
RUN useradd -u $UID -ms /bin/bash $USER

在构建步骤中:

docker build <path/to/Dockerfile> -t <tag/name> --build-arg UID=$UID

之后,按照OP运行容器和命令给了我预期的结果。


1
如果直到运行时才知道UID怎么办?(我正在为同事构建映像,以打包一些写回其文件系统的工具,但它们具有不同的UID)。我想我可以保持它的根目录并且只在运行adduser吗?
辛格

不幸的是,我对此没有很好的答案。如果其他人有解决方案,我也会对此感兴趣。我怀疑Docker入口点功能可能会提供解决方案。
RoboCop87

0

我通过使用数据容器解决了该问题,这还具有将数据与应用程序层隔离的优点。您可以这样运行它:

docker run --volumes-from=<container-data-name> ubuntu

教程对数据容器的使用提供了很好的解释。


-1

在我的情况下,问题是不同的。我不知道为什么,但是即使在主机上chmod 777运行了目录,在docker内部它也显示为755

在容器内运行sudo chmod 777 my_volume_dir固定。


5
chmod 777几乎没有修复任何东西。
Erki Aring '19年

抱歉,您错过了重点。关键是降低了内部容器的特权,因此无法从外部进行修复。
CodeSandwich,

-2

sudo -s 在MAC上帮了我大忙


1
如果您不赞成,请发表评论并解释原因。我遇到了完全相同的问题,并且可以通过sudo -s解决此问题。
Nachiket Joshi

并非每个docker映像都有sudo,而且并非在每种情况下都可行。
SOFe

2
不要在容器上安装sudo。攻击者可以在容器内使用sudo。
阿诺德·巴留
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.