访问docker中的主机目录时拒绝权限

问题描述 投票:219回答:11

简而言之:我正在尝试在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。

出了什么问题?

directory file-permissions docker mount permission-denied
11个回答
237
投票

这是一个selinux问题。

你可以暂时发出

su -c "setenforce 0"

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

chcon -Rt svirt_sandbox_file_t /path/to/volume

-1
投票

sudo -s在MAC上为我做了伎俩


-1
投票

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

在容器内运行sudo chmod 777 my_volume_dir修复它。


230
投票

有关完整故事,请参阅this Project Atomic blog post about Volumes and SELinux

特别:

由于Docker最终合并了一个将在docker-1.7中出现的补丁(我们一直在RHEL,CentOS和Fedora上的docker-1.6中运行补丁),最近这变得容易了。

此补丁添加了对“z”和“Z”的支持,作为卷安装(-v)上的选项。

例如:

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/db,其中s0:c1,c2对于每个容器不同。


66
投票

警告:此解决方案存在安全风险。

尝试以特权运行容器:

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

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


29
投票

来自access.redhat.com:Sharing_Data_Across_Containers

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

chcon -Rt svirt_sandbox_file_t host_dir

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

它似乎只是一种解决方法,但我尝试了它的工作原理


25
投票

通常,主机卷装入的权限问题是因为容器内的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从root用户删除到容器用户以在容器内运行应用程序。这个重要的脚本是我的基本图像脚本中的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已移位。在这种情况下,最简单的方法是避免使用主机卷并仅使用命名卷。


13
投票

我确认chcon -Rt svirt_sandbox_file_t /path/to/volume确实有效,你不必作为特权容器运行。

这是:

  • Docker版本0.11.1-dev,构建02d20af / 0.11.1
  • centos7作为启用了selinux的主机和容器。

7
投票

试试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/


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运行容器和命令给了我预期的结果。


0
投票

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

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

这个tutorial提供了关于数据容器使用的很好的解释。

© www.soinside.com 2019 - 2024. All rights reserved.