Отказано в доступе к каталогу хоста в докере
Вкратце: я пытаюсь смонтировать каталог хоста в 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
Этот учебник дает хорошее объяснение по использованию контейнеров данных.
В моей ситуации проблема была в другом. Я не знаю почему, но даже если каталог на хосте chmod 777
беги по нему, внутри докера это было видно как 755
,
Бег внутри контейнера sudo chmod 777 my_volume_dir
починил это.