Каков (лучший) способ управления разрешениями для общих томов Docker?
Некоторое время я играл с Docker и продолжаю находить ту же проблему при работе с постоянными данными.
Я создаю свой Dockerfile
и открываю том или использую --volumes-from
для монтирования папки хоста внутри моего контейнера.
Какие разрешения я должен применить к общему тому на хосте?
Я могу придумать два варианта:
До сих пор я предоставил всем доступ на чтение/запись, поэтому я могу писать в папку из контейнера Docker.
Сопоставьте пользователей с хоста в контейнере, чтобы я мог назначить более детальные разрешения. Не уверен, что это возможно, хотя и не нашел много об этом. Пока что все, что я могу сделать, это запустить контейнер от имени какого-то пользователя: docker run -i -t -user="myuser" postgres
, но у этого пользователя UID, отличный от моего хоста myuser
, поэтому разрешения не работают. Кроме того, я не уверен, что сопоставление пользователей будет представлять некоторую угрозу безопасности.
Есть ли другие альтернативы?
Как вы, ребята/девочки, имеете дело с этой проблемой?
Ответы
Ответ 1
ОБНОВЛЕНИЕ 2016-03-02. Начиная с Docker 1.9.0, Docker имеет имена томов, которые заменить контейнеры только для данных. Ответ ниже, а также мое связанное сообщение в блоге по-прежнему имеют значение в смысле того, как думать о данных внутри докеров, но подумайте об использовании именованных томов для реализации шаблона, описанного ниже, а не контейнеров данных.
Я считаю, что канонический способ решить это - использовать контейнеры только для данных. При таком подходе весь доступ к данным тома осуществляется через контейнеры, которые используют -volumes-from
контейнер данных, поэтому хост uid/gid не имеет значения.
Например, один вариант использования, указанный в документации, представляет собой резервную копию тома данных. Для этого другой контейнер используется для резервного копирования через tar
, и он также использует -volumes-from
для установки тома. Поэтому я считаю, что ключевым моментом для этого является: вместо того, чтобы думать о том, как получить доступ к данным на хосте с соответствующими разрешениями, подумайте о том, как делать все, что вам нужно - резервные копии, просмотр и т.д. - через другой контейнер, Сами контейнеры должны использовать согласованные uid/gids, но им не нужно отображать что-либо на хосте, оставаясь при этом переносимым.
Это относительно новое для меня, но если у вас есть конкретный случай использования, не стесняйтесь комментировать, и я попытаюсь расширить ответ.
ОБНОВЛЕНИЕ. Для данного варианта использования комментариев вы можете иметь изображение some/graphite
для запуска графита и изображение some/graphitedata
в качестве контейнера данных. Таким образом, игнорируя порты и т.д., Dockerfile
изображения some/graphitedata
выглядит примерно так:
FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
&& useradd -r -g graphite graphite
RUN mkdir -p /data/graphite \
&& chown -R graphite:graphite /data/graphite
VOLUME /data/graphite
USER graphite
CMD ["echo", "Data container for graphite"]
Создайте и создайте контейнер данных:
docker build -t some/graphitedata Dockerfile
docker run --name graphitedata some/graphitedata
Файл some/graphite
Dockerfile должен также получать одинаковые uid/gids, поэтому он может выглядеть примерно так:
FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
&& useradd -r -g graphite graphite
# ... graphite installation ...
VOLUME /data/graphite
USER graphite
CMD ["/bin/graphite"]
И он будет запускаться следующим образом:
docker run --volumes-from=graphitedata some/graphite
Итак, теперь это дает нам наш графитовый контейнер и связанный контейнер с данными только с правильным пользователем/группой (обратите внимание, что вы можете повторно использовать контейнер some/graphite
для контейнера данных, а также переопределить ввод /cmd при запуске это, но наличие их как отдельных изображений IMO яснее).
Теперь скажем, вы хотите что-то изменить в папке с данными. Поэтому вместо того, чтобы связывать установку с хостом и редактировать его там, создайте новый контейнер для выполнения этой задачи. Давайте назовем его some/graphitetools
. Позволяет также создать соответствующий пользователь/группу, как и изображение some/graphite
.
FROM debian:jessie
# add our user and group first to make sure their IDs get assigned consistently, regardless of other deps added later
RUN groupadd -r graphite \
&& useradd -r -g graphite graphite
VOLUME /data/graphite
USER graphite
CMD ["/bin/bash"]
Вы можете сделать это DRY, наследуя от some/graphite
или some/graphitedata
в файле Docker, или вместо создания нового изображения просто повторно используйте один из существующих (переопределяя точку входа /cmd при необходимости).
Теперь вы просто запускаете:
docker run -ti --rm --volumes-from=graphitedata some/graphitetools
а затем vi /data/graphite/whatever.txt
. Это отлично работает, потому что все контейнеры имеют один и тот же графитовый пользователь с соответствующими uid/gid.
Поскольку вы никогда не монтируете /data/graphite
с хоста, вам все равно, как хост uid/gid сопоставляется с uid/gid, определенным внутри контейнеров graphite
и graphitetools
. Теперь эти контейнеры могут быть развернуты на любом хосте, и они будут продолжать работать отлично.
В этой статье описывается, что graphitetools
может иметь всевозможные полезные утилиты и скрипты, которые теперь можно развертывать переносимым образом.
ОБНОВЛЕНИЕ 2. После написания этого ответа я решил написать более полное сообщение в блоге об этом подходе. Надеюсь, это поможет.
ОБНОВЛЕНИЕ 3. Я исправил этот ответ и добавил более подробную информацию. Ранее он содержал некоторые неправильные предположения о владении и perms - право собственности обычно присваивается при создании объема, т.е. В контейнере данных, потому что именно тогда создается тома. Смотрите этот блог. Однако это не является обязательным требованием - вы можете просто использовать контейнер данных в качестве "ссылки/дескриптора" и установить права собственности /perms в другом контейнере через chown в точке входа, которая заканчивается gosu, чтобы запустить команду как правильный пользователь. Если кто-то заинтересован в этом подходе, прокомментируйте, и я могу предоставить ссылки на образец, используя этот подход.
Ответ 2
Очень элегантное решение можно увидеть на официальном redis image и вообще на всех официальных изображениях.
Описано в пошаговом процессе:
- Создайте пользователя/группу redis перед чем-либо еще
Как видно из комментариев Dockerfile:
сначала добавьте нашего пользователя и группу, чтобы убедиться, что их идентификаторы назначаются последовательно, независимо от того, какие зависимости добавляются
- Установите gosu с помощью Dockerfile
gosu является альтернативой su
/sudo
для легкого ухода от пользователя root. (Redis всегда запускается с пользователем redis
)
- Настройте громкость
/data
и установите его как рабочий каталог
Благодаря настройке тома /data с помощью команды VOLUME /data
у нас теперь есть отдельный том, который может быть либо томом докера, либо подключенным к каталогу хоста.
Конфигурирование его как рабочего каталога (WORKDIR /data
) делает его каталогом по умолчанию, из которого выполняются команды.
- Добавьте файл docker-entrypoint и установите его как ENTRYPOINT с CMD-редис-сервером по умолчанию
Это означает, что все выполнения контейнера будут выполняться через сценарий docker-entrypoint, и по умолчанию команда, которую нужно запустить, - это redis-server.
docker-entrypoint
- это скрипт, выполняющий простую функцию: измените владельца текущего каталога (/data) и перейдите с root
на redis
пользователя, чтобы запустить redis-server
. (Если выполненная команда не является redis-сервером, она будет запущена непосредственно.)
Это имеет следующий эффект
Если каталог /data привязан к хосту, точка входа docker подготовит права пользователя перед запуском redis-server под пользователем redis
.
Это дает вам легкое представление о том, что для запуска контейнера в любой конфигурации тома не требуется никаких настроек.
Конечно, если вам нужно разделить объем между разными изображениями, вам нужно убедиться, что они используют один и тот же идентификатор пользователя /groupid, в противном случае последний контейнер перехватит права пользователя из предыдущего.
Ответ 3
Это, возможно, не лучший способ для большинства обстоятельств, но он еще не упоминался, поэтому, возможно, это поможет кому-то.
-
Связать хост с хостом
Host folder FOOBAR is mounted in container /volume/FOOBAR
-
Измените запуск вашего контейнера script, чтобы найти GID интересующего тома.
$ TARGET_GID=$(stat -c "%g" /volume/FOOBAR)
-
Убедитесь, что ваш пользователь принадлежит к группе с этим GID (возможно, вам придется создать новую группу). В этом примере я буду притворяться, что мое программное обеспечение работает как пользователь nobody
, когда внутри контейнера, поэтому я хочу, чтобы nobody
принадлежал к группе с идентификатором группы, равным TARGET_GID
EXISTS=$(cat /etc/group | grep $TARGET_GID | wc -l)
# Create new group using target GID and add nobody user
if [ $EXISTS == "0" ]; then
groupadd -g $TARGET_GID tempgroup
usermod -a -G tempgroup nobody
else
# GID exists, find group name and add
GROUP=$(getent group $TARGET_GID | cut -d: -f1)
usermod -a -G $GROUP nobody
fi
Мне это нравится, потому что я могу легко изменять групповые разрешения на своих томах хоста и знаю, что эти обновленные разрешения применяются в контейнере докера. Это происходит без каких-либо разрешений или прав собственности на мои папки/файлы хоста, что делает меня счастливым.
Мне это не нравится, потому что он не предполагает опасности добавлять себя к произвольным группам внутри контейнера, которые используют GID, который вы хотите. Он не может использоваться с предложением USER
в файле Docker (если у этого пользователя нет привилегий root). Кроме того, он кричит о взломе, -)
Если вы хотите быть хардкором, вы можете, очевидно, расширить его разными способами - например, поиск всех групп на любых подфайлах, нескольких томах и т.д.
Ответ 4
Хорошо, теперь это отслежено на докеровской проблеме # 7198
В настоящее время я имею дело с этим, используя ваш второй вариант:
Сопоставьте пользователей из хоста в контейнер
Dockerfile
#=======
# Users
#=======
# TODO: Idk how to fix hardcoding uid & gid, specifics to docker host machine
RUN (adduser --system --uid=1000 --gid=1000 \
--home /home/myguestuser --shell /bin/bash myguestuser)
CLI
# DIR_HOST and DIR_GUEST belongs to uid:gid 1000:1000
docker run -d -v ${DIR_HOST}:${DIR_GUEST} elgalu/myservice:latest
ОБНОВЛЕНИЕ Я сейчас более склонен к Hamy ответить p >
Ответ 5
Попробуйте добавить команду в Dockerfile
RUN usermod -u 1000 www-data
кредиты передаются https://github.com/denderello/symfony-docker-example/issues/2#issuecomment-94387272
Ответ 6
То же, что и вы, я искал способ сопоставления пользователей/групп с хостом на контейнеры докеров, и это самый короткий способ, который я нашел до сих пор:
version: "3"
services:
my-service:
.....
volumes:
# take uid/gid lists from host
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
# mount config folder
- path-to-my-configs/my-service:/etc/my-service:ro
.....
Это выдержка из моего docker-compose.yml.
Идея состоит в том, чтобы монтировать (в режиме только для чтения) список пользователей/групп из хоста в контейнер, таким образом, после запуска контейнера он будет иметь одинаковые идентификаторы uid- > username (а также для групп) с хост. Теперь вы можете настроить параметры пользователя/группы для своей службы внутри контейнера, как если бы она работала на вашей хост-системе.
Когда вы решите переместить свой контейнер на другой хост, вам просто нужно изменить имя пользователя в файле конфигурации службы на то, что у вас есть на этом хосте.
Ответ 7
Здесь используется подход, который все еще использует контейнер только для данных, но не требует его синхронизации с контейнером приложения (с точки зрения наличия того же uid/gid).
Предположительно, вы хотите запустить какое-то приложение в контейнере как не-root $USER без оболочки входа.
В файле Docker:
RUN useradd -s /bin/false myuser
# Set environment variables
ENV VOLUME_ROOT /data
ENV USER myuser
...
ENTRYPOINT ["./entrypoint.sh"]
Затем в entrypoint.sh:
chown -R $USER:$USER $VOLUME_ROOT
su -s /bin/bash - $USER -c "cd $repo/build; [email protected]"
Ответ 8
Для обеспечения безопасности и изменения корневого каталога для докер-контейнера хост-докеры попробуйте использовать --uidmap
и --private-uids
options
https://github.com/docker/docker/pull/4572#issuecomment-38400893
Также вы можете удалить несколько возможностей (--cap-drop
) в контейнере докеров для обеспечения безопасности
http://opensource.com/business/14/9/security-for-docker
Поддержка UPDATE должна появиться в docker > 1.7.0
UPDATE Версия 1.10.0
(2016-02-04) добавить --userns-remap
флаг
https://github.com/docker/docker/blob/master/CHANGELOG.md#security-2
Ответ 9
Базовое изображение
Используйте это изображение: https://hub.docker.com/r/reduardo7/docker-host-user
или
Важно: это разрушает переносимость контейнера по хостам.
1) init.sh
#!/bin/bash
if ! getent passwd $DOCKDEV_USER_NAME > /dev/null
then
echo "Creating user $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME"
groupadd --gid $DOCKDEV_GROUP_ID -r $DOCKDEV_GROUP_NAME
useradd --system --uid=$DOCKDEV_USER_ID --gid=$DOCKDEV_GROUP_ID \
--home-dir /home --password $DOCKDEV_USER_NAME $DOCKDEV_USER_NAME
usermod -a -G sudo $DOCKDEV_USER_NAME
chown -R $DOCKDEV_USER_NAME:$DOCKDEV_GROUP_NAME /home
fi
sudo -u $DOCKDEV_USER_NAME bash
2) Dockerfile
FROM ubuntu:latest
# Volumes
VOLUME ["/home/data"]
# Copy Files
COPY /home/data/init.sh /home
# Init
RUN chmod a+x /home/init.sh
3) run.sh
#!/bin/bash
DOCKDEV_VARIABLES=(\
DOCKDEV_USER_NAME=$USERNAME\
DOCKDEV_USER_ID=$UID\
DOCKDEV_GROUP_NAME=$(id -g -n $USERNAME)\
DOCKDEV_GROUP_ID=$(id -g $USERNAME)\
)
cmd="docker run"
if [ ! -z "${DOCKDEV_VARIABLES}" ]; then
for v in ${DOCKDEV_VARIABLES[@]}; do
cmd="${cmd} -e ${v}"
done
fi
# /home/usr/data contains init.sh
$cmd -v /home/usr/data:/home/data -i -t my-image /home/init.sh
4) Постройте с помощью docker
4) Запустите!
sh run.sh
Ответ 10
Мой подход заключается в том, чтобы обнаружить текущий UID/GID, затем создать такого пользователя/группу внутри контейнера и выполнить скрипт под ним. В результате все файлы, которые он создаст, будут соответствовать пользователю на хосте (который является сценарием):
# get location of this script no matter what your current folder is, this might break between shells so make sure you run bash
LOCAL_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# get current IDs
USER_ID=$(id -u)
GROUP_ID=$(id -g)
echo "Mount $LOCAL_DIR into docker, and match the host IDs ($USER_ID:$GROUP_ID) inside the container."
docker run -v $LOCAL_DIR:/host_mount -i debian:9.4-slim bash -c "set -euo pipefail && groupadd -r -g $GROUP_ID lowprivgroup && useradd -u $USER_ID lowprivuser -g $GROUP_ID && cd /host_mount && su -c ./runMyScriptAsRegularUser.sh lowprivuser"
Ответ 11
Чтобы разделить папку между хостом Docker и контейнером Docker, попробуйте команду ниже
$ docker run -v "$(pwd):$(pwd)" -i -t ubuntu
Флаг -v монтирует текущий рабочий каталог в контейнер. Если каталог хоста подключенного тома тома не существует, Docker автоматически создаст этот каталог на хосте для вас,
Однако здесь есть две проблемы:
- Вы не можете выполнять запись на том, смонтированный на томе, если вы были пользователем без полномочий root, поскольку общий файл будет принадлежать другому пользователю хоста,
- Вы не должны запускать процесс внутри ваших контейнеров с правами root, но даже если вы работаете как пользователь с жестким кодом, он все равно не будет соответствовать пользователю на вашем ноутбуке /Jenkins,
Решение:
Контейнер:
создайте пользователя с именем "testuser", по умолчанию идентификатор пользователя будет начинаться с 1000,
Ведущий:
создайте группу, скажем "testgroup" с идентификатором группы 1000, и добавьте каталог в новую группу (testgroup
Ответ 12
В моем конкретном случае я пытался создать мой пакет node с образцом docker node, чтобы мне не пришлось устанавливать npm на сервере развертывания. Он работал хорошо, за пределами контейнера и на главном компьютере, я попытался переместить файл в каталог node_modules, который создал образ docker node, которому было отказано в разрешениях, поскольку оно принадлежало root. Я понял, что могу обойти это, скопировав каталог из контейнера на хост-машину. Через docker docs...
Файлы, скопированные на локальный компьютер, создаются с помощью UID: GID пользователь, который вызвал команду docker cp.
Это код bash, который я использовал для изменения права собственности на каталог, созданный контейнером docker и внутри него.
NODE_IMAGE=node_builder
docker run -v $(pwd)/build:/build -w="/build" --name $NODE_IMAGE node:6-slim npm i --production
# node_modules is owned by root, so we need to copy it out
docker cp $NODE_IMAGE:/build/node_modules build/lambda
# you might have issues trying to remove the directory "node_modules" within the shared volume "build", because it is owned by root, so remove the image and its volumes
docker rm -vf $NODE_IMAGE || true
При необходимости вы можете удалить каталог со второго контейнера докеров.
docker run -v $(pwd)/build:/build -w="/build" --name $RMR_IMAGE node:6-slim rm -r node_modules
Ответ 13
Если вы используете Docker Compose, запустите контейнер в превалированном режиме:
wordpress:
image: wordpress:4.5.3
restart: always
ports:
- 8084:80
privileged: true