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

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

Любые идеи о том, что происходит не так?

Ответ 1

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

Вы можете временно выпустить

su -c "setenforce 0"

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

chcon -Rt svirt_sandbox_file_t /path/to/volume

Ответ 2

См. Этот пост в блоге Project Atomic о томах и 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 отличается для каждого контейнера,

Ответ 3

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

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

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

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

Ответ 4

От access.redhat.com:Sharing_Data_Across_Containers:

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

chcon -Rt svirt_sandbox_file_t host_dir

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

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

Ответ 5

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

Это событие включено:

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

Ответ 6

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

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

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

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

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, которая автоматически обрабатывает uid/gid между хостом Mac и контейнерами. Единственное место, в котором это не помогает, это файлы из встроенной виртуальной машины, которые монтируются в контейнер, например /var/lib/docker.sock.

В средах разработки, где uid/gid хоста может меняться в зависимости от разработчика, мое предпочтительное решение состоит в том, чтобы запустить контейнер с точкой входа, работающей от имени пользователя root, исправить uid/gid пользователя внутри контейнера в соответствии с uid/gid тома хоста и затем используйте gosu для gosu от пользователя root к пользователю контейнера, чтобы запустить приложение внутри контейнера. Важным сценарием для этого является fix-perms в моих сценариях с базовым изображением, которые можно найти по адресу: https://github.com/sudo-bmitch/docker-base

Важный fix-perms скрипта 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

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

Ответ 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/

Ответ 8

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

Ответ 9

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

Запуск внутри контейнера sudo chmod 777 my_volume_dir исправил это.

Ответ 10

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

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

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

Ответ 11

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