В доступе к каталогу хоста в Docker отказано

283

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

Что идет не так?

user3753011
источник

Ответы:

269

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

gregswift
источник
18
Это работает как шарм. Другие решения в основном обходные пути.
Tuxdna
4
ср раздел меток томов в документации докера
maxschlepzig
О, чувак, это действительно работает. Я наконец нашел это. Большое спасибо! Есть ли официальная документация по этому поводу?
Кирби
1
В апстриме
gregswift
1
Это возможно исправить разрешения при SELinux при монтаже громкости , как только для чтения , в то же время, используя оба варианта одновременно разделенных запятой: -v $(pwd):/app:ro,Z. Это должно быть помечено как правильный ответ.
Данирод
264

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

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

su -c "setenforce 0"

на хосте, чтобы получить доступ или добавить правило SELinux, запустив

chcon -Rt svirt_sandbox_file_t /path/to/volume
user3761313
источник
3
такое / путь / к / тому путь хоста? Если так, то не похоже, что это решение будет работать с контейнерами данных?
Рой Truelove
6
не забудьте выполнить su -c "setenforce 1" ... иначе это будет работать только потому, что SELinux по-прежнему деактивирован
vcarel
это решило мою проблему. спасибо, я надеюсь, что они исправят это.
Хокутосей
19
Добавление правила selinux - лучший способ, так как в большинстве случаев не рекомендуется запускать контейнеры в привилегированном режиме.
Zoro_77
7
Как сказал Zoro_77, добавьте правило и stopdisablingselinux.com ;)
GabLeRoux
71

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

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

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

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

Джон Филлипс
источник
1
@JBernardo Какой из двух вариантов решил проблему?
user100464
@ user100464--privileged=true
JBernardo
1
Не поможет в моем случае. Debian Whezzy с портированным ядром 3.16, но без активированной конфигурации SELinux. :(
aholbreich
если вы используете docker-composer, добавьте 'privileged: true'
Лионель Моррисон,
35
Не делай этого. --privilegedриск для безопасности
Навин
38

Как правило, проблемы с разрешениями при монтировании тома хоста связаны с тем, что 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/#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для перехода от пользователя 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

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

BMitch
источник
32

От access.redhat.com:Sharing_Data_Across_Containers :

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

chcon -Rt svirt_sandbox_file_t host_dir

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

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

Thomas8
источник
14

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

Это на:

  • Docker версия 0.11.1-dev, сборка 02d20af / 0.11.1
  • CentOS 7 в качестве хоста и контейнера с включенным SELinux.
Джефф Маккормик
источник
2
См. Github.com/docker/docker/pull/5910 для официальной поддержки перемаркировки в Docker.
cpuguy83
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/

cupen
источник
3
Пробовал много ответов об этой проблеме на SO, но на самом деле этот помог. Спасибо!
Пол
Это решило ошибку разрешения. Но теперь, если я пытаюсь смонтировать физическое местоположение, он монтирует voulme ???? @ cupen
kunal verma
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 дало мне ожидаемый результат.

RoboCop87
источник
1
Что делать, если вы не знаете UID до времени выполнения? (Я создаю образ для коллег, чтобы упаковать некоторые инструменты, которые записывают обратно в свою файловую систему, но у них разные UID). Я думаю, я мог бы держать его как root и только adduser на ходу?
Звонок
У меня нет хорошего ответа на это, к сожалению. Если у кого-то есть решение, я бы тоже заинтересовался. Я подозреваю, что функциональность точки входа Docker может обеспечить решение.
RoboCop87
0

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

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

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

tmsss
источник
-1

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

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

CodeSandwich
источник
5
chmod 777вряд ли что-нибудь исправит.
Эрки Аринг
Извините, но вы упустили момент. Дело в том, что внутри контейнера привилегии были снижены, и это нельзя было исправить снаружи.
CodeSandwich
-2

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

Начикет Джоши
источник
1
Если вы отрицаете, оставьте комментарий и объясните, почему. Я столкнулся с точно такой же проблемой, и я смог решить ее с помощью sudo -s.
Начикет Джоши
Не у каждого образа докера есть sudo, и это не возможно в каждом сценарии.
СОФЭ
2
Не устанавливайте sudo на контейнерах. Злоумышленник может использовать sudo внутри контейнера.
Арнольд Баллиу