Docker и --userns-remap, как управлять разрешениями на том для обмена данными между хостом и контейнером?

96

В докере файлы, созданные внутри контейнеров, имеют тенденцию иметь непредсказуемое владение при проверке их с хоста. Владельцем файлов на томе по умолчанию является root (uid 0), но как только учетные записи пользователей без полномочий root включаются в контейнер и записывают в файловую систему, владельцы становятся более или менее случайными с точки зрения хоста.

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

Типичные обходные пути:

  • принудительное использование uID пользователей во время создания в Dockerfiles (не переносится)
  • передача UID пользователя хоста docker runкоманде в качестве переменной среды, а затем выполнение некоторых chownкоманд на томах в сценарии точки входа.

Оба эти решения могут дать некоторый контроль над фактическими разрешениями вне контейнера.

Я ожидал, что пространства имен пользователей станут окончательным решением этой проблемы. Я провел несколько тестов с недавно выпущенной версией 1.10 и параметром --userns-remap для моей учетной записи рабочего стола. Однако я не уверен, что это может упростить владение файлами на смонтированных томах, я боюсь, что на самом деле может быть наоборот.

Предположим, я запустил этот базовый контейнер

docker run -ti -v /data debian:jessie /bin/bash
echo 'hello' > /data/test.txt
exit

А затем проверьте содержимое хоста:

ls -lh /var/lib/docker/100000.100000/volumes/<some-id>/_data/

-rw-r--r-- 1 100000 100000 6 Feb  8 19:43 test.txt

Этот номер «100000» является суб-UID моего пользователя хоста, но поскольку он не соответствует UID моего пользователя, я все еще не могу редактировать test.txt без прав. Этот субпользователь, похоже, не имеет ничего общего с моим фактическим постоянным пользователем за пределами докера. Это не отображается на карте.

Обходные пути, упомянутые ранее в этом посте, которые заключались в выравнивании UID между хостом и контейнером, больше не работают из-за UID->sub-UIDсопоставления, которое происходит в пространстве имен.

Тогда есть ли способ запустить докер с включенным пространством имен пользователей (для повышения безопасности), при этом позволяя пользователю хоста, запускающему докер, владеть файлами, созданными на томах?

Стефан К.
источник
Я думаю, что если вы собираетесь совместно использовать тома между хостом и контейнером, эти пользовательские пространства имен не будут частью решения. Ваш второй вариант («передача UID пользователя хоста команде docker run в качестве переменной среды, а затем выполнение некоторых команд chown на томах в сценарии точки входа»), вероятно, является лучшим решением.
larsks 07
4
Сам Docker, похоже, не поощряет использование подключенных к хосту томов с возможностью записи. Поскольку я не использую облачную службу и использую только свои собственные доверенные образы, теперь мне интересно, стоит ли выгода от безопасности пользователя NS жертвовать таким удобством.
Стефан С.
@ StéphaneC. возможно, вы нашли лучший подход?
EightyEight
4
К сожалению, нет, я по-прежнему предпочитаю не использовать пространство имен пользователей и передавать UID с хоста. Я надеюсь, что в будущем появится правильный способ отображать пользователей. Сомневаюсь, но все же слежу за этим.
Стефан С.

Ответы:

46

Если вы можете заранее упорядочить пользователей и группы, то можно назначить UID и GID таким особым образом, чтобы пользователи хоста соответствовали пользователям с пространством имен внутри контейнеров.

Вот пример (Ubuntu 14.04, Docker 1.10):

  1. Создайте нескольких пользователей с фиксированными числовыми идентификаторами:

    useradd -u 5000 ns1
    
    groupadd -g 500000 ns1-root
    groupadd -g 501000 ns1-user1
    
    useradd -u 500000 -g ns1-root ns1-root
    useradd -u 501000 -g ns1-user1 ns1-user1 -m
    
  2. Ручное редактирование автоматически генерируемые подчиненные диапазонов ID в /etc/subuidи /etc/subgidфайлы:

    ns1:500000:65536
    

    (примечание нет записей для ns1-rootи ns1-user1из - за MAX_UIDи MAX_GIDограничение в /etc/login.defs)

  3. Включить пространства имен пользователей в /etc/default/docker:

    DOCKER_OPTS="--userns-remap=ns1"
    

    Перезапустите демон service docker restart, убедитесь, что /var/lib/docker/500000.500000каталог создан.

    Теперь внутри контейнеров у вас есть rootand user1, и на хосте - ns1-rootи ns1-user1с соответствующими идентификаторами

    ОБНОВЛЕНИЕ: чтобы гарантировать, что пользователи без полномочий root имеют фиксированные идентификаторы в контейнерах (например, user1 1000: 1000), создайте их явно во время сборки образа.

Тест-драйв:

  1. Подготовить каталог тома

    mkdir /vol1
    chown ns1-root:ns1-root /vol1
    
  2. Попробуйте из контейнера

    docker run --rm -ti -v /vol1:/vol1 busybox sh
    echo "Hello from container" > /vol1/file
    exit
    
  3. Попробуйте от хозяина

    passwd ns1-root
    login ns1-root
    cat /vol1/file
    echo "can write" >> /vol1/file
    

Не портативный и выглядит как хакерский, но работает.

Амартынов
источник
3
Очень интересно и заслуживает +1. Но вам все равно нужно убедиться, что пользователю user1 в вашем образе назначен UID 1000. В противном случае вы не можете быть уверены, что он получит UID 501000 на хосте. Между прочим, мы абсолютно уверены, что формула всегда верна, subUID lower bound + UID in imageесли мы запускаем много разных изображений с пользователем, у которого установлен идентификатор 1000?
Стефан С.
@ StéphaneC. Хорошая точка зрения! Добавлено примечание об исправлении идентификаторов внутри изображений. Что касается формулы, я собираюсь поэкспериментировать с моими собственными изображениями и
обновлю
1
Если вы размещаете пользователей и группы вручную на хосте и в контейнерах, действительно ли вам нужна функция «пространства имен пользователей»?
Тристан
1
Создаваемое вами пространство имен отделяет пользователей хостов от пользователей контейнера, но вам может потребоваться несколько пространств имен для контейнеров, особенно когда официальные изображения (например, mysql) создают пользователя без явного uid. Как вы справляетесь с несколькими пространствами имен, когда опция --userns-remap ожидает только одно?
Тристан
2
@amartynov Могу я спросить, почему вы потрудились указать UID (5000) для своего пользователя "ns1"? Поскольку это имя (а не UID), на которое вы ссылаетесь в файлах subuid и subgid, похоже, не имеет значения, какой UID получает этот пользователь. Не хватает ли мне какой-то связи, о чем может свидетельствовать сходство между 5000 и 500000?
Jollymorphic
2

Один из способов решения этой проблемы - динамически назначать uid пользователя во время сборки для соответствия хосту.

Пример Dockerfile:

FROM ubuntu
# Defines argument which can be passed during build time.
ARG UID=1000
# Create a user with given UID.
RUN useradd -d /home/ubuntu -ms /bin/bash -g root -G sudo -u $UID ubuntu
# Switch to ubuntu user by default.
USER ubuntu
# Check the current uid of the user.
RUN id
# ...

Затем создайте как:

docker build --build-arg UID=$UID -t mycontainer .

и запустить как:

docker run mycontainer

Если у вас есть контейнер, создайте контейнер-оболочку со следующим Dockerfile:

FROM someexistingcontainer
ARG UID=1000
USER root
# This assumes you've the existing user ubuntu.
RUN usermod -u $UID ubuntu
USER ubuntu

Это можно обернуть docker-compose.ymlтак:

version: '3.4'
services:
  myservice:
    command: id
    image: myservice
    build:
      context: .
    volumes:
    - /data:/data:rw

Затем соберите и запустите как:

docker-compose build --build-arg UID=$UID myservice; docker-compose run myservice
Kenorb
источник
1
Я бы не хотел выглядеть недружелюбно, но на самом деле это один из обходных путей, перечисленных в исходном вопросе, но не решение и не связанное с пространствами имен пользователей.
Стефан С.
@ StéphaneC. Можете ли вы прокомментировать этот связанный с этим вопрос? stackoverflow.com/questions/60274418/…
overxchange
-1

Вы можете избежать проблем с разрешениями, используя docker cpкоманду .

Право собственности устанавливается для пользователя и основной группы в месте назначения. Например, файлы, скопированные в контейнер, создаются UID:GIDпользователем root. Файлы, скопированные на локальный компьютер, создаются UID:GIDпользователем, который вызвал docker cpкоманду.

Вот ваш пример, переключенный на использование docker cp:

$ docker run -ti -v /data debian:jessie /bin/bash
root@e33bb735a70f:/# echo 'hello' > /data/test.txt
root@e33bb735a70f:/# exit
exit
$ docker volume ls
DRIVER              VOLUME NAME
local               f073d0e001fb8a95ad8d919a5680e72b21a457f62a40d671b63c62ae0827bf93
$ sudo ls -l /var/lib/docker/100000.100000/volumes/f073d0e001fb8a95ad8d919a5680e72b21a457f62a40d671b63c62ae0827bf93/_data
total 4
-rw-r--r-- 1 100000 100000 6 Oct  6 10:34 test.txt
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                          PORTS               NAMES
e33bb735a70f        debian:jessie       "/bin/bash"         About a minute ago   Exited (0) About a minute ago                       determined_hypatia
$ docker cp determined_hypatia:/data/test.txt .
$ ls -l test.txt 
-rw-r--r-- 1 don don 6 Oct  6 10:34 test.txt
$ cat test.txt
hello
$ 

Однако, если вы просто хотите читать файлы из контейнера, именованный том вам не нужен. В этом примере вместо именованного тома используется именованный контейнер:

$ docker run -ti --name sandbox1 debian:jessie /bin/bash
root@93d098233cf3:/# echo 'howdy' > /tmp/test.txt
root@93d098233cf3:/# exit
exit
$ docker cp sandbox1:/tmp/test.txt .
$ ls -l test.txt
-rw-r--r-- 1 don don 6 Oct  6 10:52 test.txt
$ cat test.txt
howdy
$ 

Я считаю именованные тома полезными, когда я хочу скопировать файлы в контейнер, как описано в этом вопросе .

Дон Киркби
источник
Но docker cpпредполагает дублирование данных. Более того, согласно документу, при копировании данных в контейнер он устанавливает идентификаторы владения в соответствии с пользователем root, который часто не является учетной записью, на которой запущено контейнерное приложение. Я не понимаю, как это решает нашу проблему.
Стефан С.
Вы правы, @ Stéphane, это связано с дублированием данных. Однако создание копий файлов позволяет вам назначать разные права собственности и разрешения на хосте и в контейнере. docker cpдает вам полный контроль над владением файлом при потоковой передаче tar-архива в контейнер или из него. Вы можете настроить права собственности и разрешения для каждой записи в tar-файле по мере его потоковой передачи, поэтому вы не ограничены пользователем root.
Дон Киркби