Отказано в доступе к каталогу хоста в докере

Вкратце: я пытаюсь смонтировать каталог хоста в 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.

Есть идеи, что не так?

13 ответов

Смотрите этот пост в блоге Project Atomic о Voumes и SELinux для полной информации.

В частности:

В последнее время это стало легче, так как Docker наконец-то объединил патч, который будет отображаться в docker-1.7 (мы несли патч в docker-1.6 на RHEL, CentOS и Fedora).

Этот патч добавляет поддержку "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 отличается для каждого контейнера.

Это проблема selinux.

Вы можете временно выдать

su -c "setenforce 0"

на хосте для доступа или добавить правило selinux, запустив

chcon -Rt svirt_sandbox_file_t /path/to/volume

Как правило, проблемы с разрешениями при монтировании тома хоста связаны с тем, что uid/gid внутри контейнера не имеет доступа к файлу в соответствии с разрешениями uid/gid файла на хосте. Тем не менее, этот конкретный случай отличается.

Точка в конце строки разрешения, drwxr-xr-x., указывает, что SELinux настроен. При использовании монтирования хоста с SELinux вам необходимо передать дополнительную опцию в конец определения тома:

  • z Параметр указывает, что содержимое монтирования привязки совместно используется несколькими контейнерами.
  • Z Параметр указывает, что содержимое подключения bind является частным и не общим.

Ваша команда монтирования тома будет выглядеть так:

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

Подробнее о монтировании хоста с помощью SELinux читайте по адресу: https://docs.docker.com/storage/.


Для других, которые видят эту проблему с контейнерами, работающими как другой пользователь, вы должны убедиться, что uid/gid пользователя внутри контейнера имеют разрешения на файл на хосте. На производственных серверах это часто делается путем управления uid/gid в процессе построения образа, чтобы соответствовать uid/gid на хосте, который имеет доступ к файлам (или, что еще лучше, не использовать монтирование хоста в рабочей среде).

Именованный том часто предпочтительнее для размещения подключений, потому что он инициализирует каталог тома из каталога образа, включая любые права доступа к файлам и разрешения. Это происходит, когда том пуст и контейнер создан с указанным томом.

Пользователи MacOS теперь имеют OSXFS, которая автоматически обрабатывает uid/gid между хостом Mac и контейнерами. Единственное место, в котором это не помогает, это файлы из встроенной виртуальной машины, которые монтируются в контейнер, например /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 инициализировал каталог хоста из образа, используя именованный том, который выполняет подключение. Этот каталог должен существовать заранее, и вам необходимо указать абсолютный путь к каталогу хоста, в отличие от томов хоста в файле составления, которые могут быть относительными путями. Каталог также должен быть пустым, чтобы докер мог его инициализировать. Три различных варианта определения именованного тома для монтирования привязки выглядят так:

  # 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 контейнеров смещены. В этом случае, вероятно, проще всего избежать томов хоста и использовать только именованные тома.

ВНИМАНИЕ: это решение имеет риски для безопасности.

Попробуйте запустить контейнер как привилегированный:

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

Другой вариант (который я не пробовал) - создать привилегированный контейнер, а затем создать внутри него непривилегированные контейнеры.

От access.redhat.com:Sharing_Data_Across_Containers:

Параметры тома хоста не являются переносимыми, поскольку они зависят от хоста и могут не работать на любом другом компьютере. По этой причине не существует эквивалента Dockerfile для монтирования каталогов хоста в контейнер. Также имейте в виду, что хост-система не знает политики контейнера SELinux. Следовательно, если применяется политика SELinux, подключенный каталог хоста не будет доступен для записи в контейнер, независимо от значения параметра rw. В настоящее время вы можете обойти это, назначив правильный тип политики SELinux каталогу хоста ":

chcon -Rt svirt_sandbox_file_t host_dir

Где host_dir - это путь к каталогу в хост-системе, который монтируется в контейнер.

Кажется, это только обходной путь, но я попытался, и это работает

Я подтвердил, что chcon -Rt svirt_sandbox_file_t /path/to/volumeработает, и вам не нужно работать как привилегированный контейнер.

Это на:

  • Docker версия 0.11.1-dev, сборка 02d20af / 0.11.1
  • centos7 в качестве хоста и контейнера с включенным selinux.

Пытаться 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/

У меня была похожая проблема, моя была вызвана несоответствием между 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 дало мне ожидаемый результат.

Эта проблема связана с тем, что на вашем компьютере включен SELinux. проверьте ниже и отключите его.

      [root@nfs-server ~]# getenforce
Enforcing
[root@nfs-server ~]# setenforce 0
[root@nfs-server ~]# getenforce
Permissive

Если вы хотите просто отключить SELinux, вы можете сделать это с помощью флага --security-opt label: disable.

      docker run --security-opt label:disable -v /run/docker.sock:/run/docker.sock POWERFULLCONTAINER

Я бьюсь головой об стену об этом. Использование Ubuntu 20.04, Docker версии 20.10.8 и отключенного SELinux.

      $ getenforce
Disabled

Итак, я запускаю следующее:

      $ docker run -it -v $PWD:/mount -w /mount ubuntu
root@aca654833def:/mount# ls -l
total 564
-rw-rw-r--.  1 nobody nogroup    210 Aug 20 05:50 Brewfile

Из предыдущего ответа я понимаю "." в конце -rw-rw-r--. означать, что SELinux включен. И вроде бы:

      root@aca654833def:/mount# date > date.txt
bash: date.txt: Permission denied

Любые идеи?

Как бы то ни было, я использую систему Windows / Ubuntu с двойной загрузкой.

Я решил эту проблему с помощью контейнера данных, это также имеет преимущество в изоляции данных от прикладного уровня. Вы можете запустить его так:

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

Этот учебник дает хорошее объяснение по использованию контейнеров данных.

sudo -s сделал трюк для меня на MAC

В моей ситуации проблема была в другом. Я не знаю почему, но даже если каталог на хосте chmod 777 беги по нему, внутри докера это было видно как 755,

Бег внутри контейнера sudo chmod 777 my_volume_dir починил это.

Другие вопросы по тегам