Compose: Docker Compose монтирует именованные тома исключительно как root

Созданный на 5 апр. 2016  ·  33Комментарии  ·  Источник: docker/compose

Речь идет об именованных томах (без «контейнера тома данных» и «от томов») и docker-compose.yml.

Цель здесь состоит в том, чтобы использовать docker-compose для управления двумя сервисами appserver и server-postgresql в двух отдельных контейнерах и использовать функцию «volume:» docker-compose.yml, чтобы сделать данные из сервиса server-postgresql постоянными. .

Dockerfile для server-postgresql выглядит так:

FROM        ubuntu:14.04
MAINTAINER xxx

RUN apt-get update && apt-get install -y [pgsql-needed things here]
USER        postgres
RUN         /etc/init.d/postgresql start && \
            psql --command "CREATE USER myUser PASSWORD 'myPassword';" && \
            createdb -O diya diya
RUN         echo "host all  all    0.0.0.0/0  md5" >> /etc/postgresql/9.3/main/pg_hba.conf
RUN         echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf
CMD         ["/usr/lib/postgresql/9.3/bin/postgres", "-D", "/var/lib/postgresql/9.3/main", "-c", "config_file=/etc/postgresql/9.3/main/postgresql.conf"]

И docker-compose.yml выглядит так:

version: '2'
services:
    appserver:
        build: appserver
        depends_on:
            - server-postgresql
        links:
            - "server-postgresql:serverPostgreSQL"
        ports:
            - "1234"
            - "1235"
        restart: on-failure:10
    server-postgresql:
        build: serverPostgreSQL
        ports:
            - "5432"
        volumes:
            - db-data:/volume_data
        restart: on-failure:10
volumes:
    db-data:
        driver: local

Затем я начинаю все с docker-compose up -d , я ввожу свой контейнер server-postgresql с docker-compose exec server-postgresql bash , быстрый ls действительно показывает /volume_data , я затем cd и попробуйте touch testFile и получил "разрешение отклонено. Это нормально, потому что быстрое ls -l показывает, что volume_data принадлежит root:root .

Теперь, как мне кажется, происходит следующее: поскольку у меня есть USER postgres в Dockerfile, когда я запускаю docker-compose exec я вхожу в систему как пользователь postgres (а демон postgresql запускается как пользователь postgres). также, поэтому он не сможет писать на /volume_data ).
Это подтверждается , потому что , когда я запускаю это вместо: docker-compose exec --user root server-postgresql bash и повторить попытку на cd /volume_data и touch testFile , это делает работу (это не ошибка разрешения между хостом и контейнером, а Иногда, когда контейнер монтирует папку хоста, это типичная ошибка разрешения unix, потому что /volume_data монтируется как 'root: root', пока пользователь 'postgres' пытается писать).

Таким образом, в docker-compose.yml должен быть способ монтировать именованные тома от имени конкретного пользователя, например:

version: '2'
services:
    appserver:
        [...]
    server-postgresql:
        [...]
        volumes:
            - db-data:/volume_data:myUser:myGroup
        [...]
volumes:
    db-data:
        driver: local

Единственный _dirty_ обходной путь, который я могу придумать, - это удалить директиву USER posgres из файла Docker и изменить ENTRYPOINT так, чтобы он указывал на настраиваемый "init_script.sh" (wihch будет запускаться как 'root', поскольку я удален USER postgres ), этот сценарий изменит права доступа /volume_data чтобы postgres мог писать на нем, затем su postgres и запустил демон postgresql (на переднем плане). Но на самом деле это очень грязно, потому что он связывает Dockerfile и docker-compose.yml нестандартным способом (ENTRYPOINT во время выполнения будет полагаться на тот факт, что подключенный том доступен через docker-compose.yml).

arevolumes statu0-triage

Самый полезный комментарий

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

В моем Dockerfile _перед переходом на пользователя postgres_ я добавил следующее:

# ...
RUN    mkdir /volume_data
RUN   chown postgres:postgres /volume_data

USER postgres
# ...

Это создает каталог / volume_data и изменяет его разрешения, чтобы пользователь postgres мог писать в нем.
Это часть Dockerfile .

Теперь я ничего не изменил в docker-compose.yml : поэтому docker-compose по-прежнему создает именованный том directory_name_db-data и монтирует его в /volume_data а разрешения сохраняются! .
Это означает, что теперь мой именованный том смонтирован в _pre-существующем_ каталоге /volume_data с сохраненными разрешениями, поэтому postgres может писать в него.

Итак, если это предполагаемое поведение или нарушение безопасности? (Впрочем, в данном случае мне это пригодится!)

Все 33 Комментарий

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

version: '2'
services:
  web:
    image: alpine:3.3
    volumes: ['random:/path']

volumes:
  random:
$ docker-compose run web sh
/ # touch /path/foo
/ # ls -l /path
total 0
-rw-r--r--    1 root     root             0 Apr  5 16:11 foo
/ # chown postgres:postgres /path/foo
/ # ls -l /path
total 0
-rw-r--r--    1 postgres postgres         0 Apr  5 16:11 foo
/ # 
$ docker-compose run web sh
/ # ls -l /path
total 0
-rw-r--r--    1 postgres postgres         0 Apr  5 16:11 foo

Проблема, с которой вы столкнулись, связана с инициализацией именованного тома. По общему признанию, это не то, что обрабатывается Compose (потому что это несколько выходит за рамки), но вы можете легко использовать docker cli для инициализации именованного тома перед запуском docker-compose up .

Я действительно не был уверен, была ли это проблема Docker или Compose, извините, если я ошибся.
Это план в Docker API, следует ли мне сообщать о проблеме?

Я понимаю возможность ручного входа в контейнер и chown передачи тома пользователю postgres. Но дело в том, что в моем случае я использую Compose, поэтому я могу сразу создавать экземпляры новых контейнеров для новых клиентов ( docker-compose -p client_name up ), а Compose создаст настраиваемую сеть client_name_default , она создаст контейнеры с именами client_name _appserver_1 и client_name _server-postgresql_1 и, что еще важнее, будет создан том client_name_db-data . Все это мне не нужно делать вручную, поэтому его можно запустить с помощью сценария, обрабатывающего регистрацию клиентов.

С помощью описанного вами решения (_вручную_ «ведение журнала» в контейнере с sh и chown запись метки тома) у меня не может быть простой процедуры для добавления новых клиентов, и это должно заботиться вручную.

Вот почему я считаю, что эту функцию следует реализовать. В Docker API мы можем указать разрешения ro или rw (только для чтения или чтения-записи) при монтировании тома, я думаю, мы можем указать user:group .

Как вы думаете, имеет ли смысл моя просьба?

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

В моем Dockerfile _перед переходом на пользователя postgres_ я добавил следующее:

# ...
RUN    mkdir /volume_data
RUN   chown postgres:postgres /volume_data

USER postgres
# ...

Это создает каталог / volume_data и изменяет его разрешения, чтобы пользователь postgres мог писать в нем.
Это часть Dockerfile .

Теперь я ничего не изменил в docker-compose.yml : поэтому docker-compose по-прежнему создает именованный том directory_name_db-data и монтирует его в /volume_data а разрешения сохраняются! .
Это означает, что теперь мой именованный том смонтирован в _pre-существующем_ каталоге /volume_data с сохраненными разрешениями, поэтому postgres может писать в него.

Итак, если это предполагаемое поведение или нарушение безопасности? (Впрочем, в данном случае мне это пригодится!)

Я считаю, что это было добавлено в Docker 1.10.x, чтобы именованные тома инициализировались из первого контейнера, который их использовал. Я думаю, это ожидаемое поведение.

Я также делаю именованные тома, права собственности которых устанавливаются в Dockerfile, а в compose я добавляю user: postgres чтобы даже PID 1 принадлежал пользователю без полномочий root.

Docker-compose для volumes имеет driver_opts для предоставления параметров.
Было бы очень приятно увидеть там такие варианты, как chmod , chown даже для драйвера local .
И особенно я хочу, чтобы он также был применен для локально созданных каталогов хоста, если его нет при запуске.

Связанный (в некоторой степени) https://github.com/moby/moby/pull/28499

Кто-то уже открыл вопрос по проекту Moby?

Ответ от @dnephin не работает. Проблема в том, что мы запускаем контейнер как стандартный пользователь, команды chown или chmod не работают, том принадлежит пользователю root, стандартный пользователь не может изменить разрешения.

@jcberthon предлагаемый метод состоит в том, чтобы запустить контейнер с root в качестве начального пользователя, а затем использовать команду USER ПОСЛЕ chown / chmod, так что это в основном "удаление привилегий".

Это нормально, если вы контролируете образ Docker, но если вы используете существующие образы, это не вариант.

@ dragon788 и @micheljung , я решил свою проблему.

На самом деле реальная проблема заключалась в том, что в моем Dockerfile я объявил VOLUME а затем изменил права собственности и права доступа к файлам в этом томе. Эти изменения потеряны. Просто переместив объявление VOLUME в конец файла Docker (или удалив его, поскольку это необязательно), моя проблема решена. Разрешения правильные.

Итак, ошибка была:

FROM blabla
RUN do stuff
VOLUME /vol
RUN useradd foo && chown -R foo /vol
USER foo
CMD ["blabla.sh"]

chown в приведенном выше примере файла Dockerfile теряются во время сборки, потому что мы объявляем перед ним VOLUME . При запуске контейнера dockerd копирует в названный том содержимое /vol перед объявлением VOLUME (то есть с правами root). Следовательно, запущенные процессы не могут изменять или изменять разрешения, поэтому даже принудительное использование chown в сценарии blabla.sh не может работать.

Изменив файл на:

FROM blabla
RUN do stuff
RUN useradd foo && chown -R foo /vol
USER foo
VOLUME /vol
CMD ["blabla.sh"]

проблема решена.

@jcberthon, не могли бы вы поделиться, как вы связываете свой том / том с хост-системой в вашем docker-compose.yml?

Я работаю с Docker в Fedora (значит, с включенным SELinux), и ни один из вышеупомянутых методов у меня не сработал. В идеале я хочу запускать приложения в своих контейнерах в контексте пользователя (без root), но проблема с объемом является препятствием для этого.

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

Привет, @renanwilliam и @ egee-irl

Я использовал вышеупомянутое решение на нескольких ОС, в т.ч. Fedora 26 и CentOS 7 оба с принудительным SELinux, Ubuntu 16.04, 17.10 и Raspbian 9 все три с активированным AppArmor (со смесью платформ amd64 и armhf).

Итак, как я уже сказал, я переместил объявление VOLUME ... в конец моего файла Docker, но вы можете удалить его полностью, это не нужно. Что я также обычно делаю, так это исправляю идентификатор пользователя при создании пользователя в Dockerfile (например, useradd -u 8002 -o foo ). Затем я могу просто повторно использовать этот UID на хосте, чтобы предоставить соответствующие разрешения для папки.

Итак, следующим шагом будет создание «подвески» каталога /vol на хосте, допустим, это /opt/mycontainer1/vol , так что это

$ sudo mkdir -p /opt/mycontainer1/vol
$ sudo chown -R 8002 /opt/mycontainer1/vol
$ sudo chmod 0750 /opt/mycontainer1/vol

Затем при запуске контейнера от имени пользователя foo он сможет писать в каталог /opt/mycontainer1/vol . Что-то вроде:

$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol mycontainer1-img

На хостах на основе SELinux вы можете добавить параметр :z :Z для тома, чтобы Docker соответствующим образом пометил папку. Разница между z и Z заключается в том, что строчная буква z помечает том, так что потенциально всем контейнерам этого хоста может быть разрешен доступ SELinux к этому каталогу (но, очевидно, только если вы привяжете его к другому контейнеру), тогда как верхний регистр Z пометит его так, чтобы только этот конкретный контейнер мог получить доступ к каталогу. Итак, в Fedora с SELinux вы можете попробовать:

$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol:Z mycontainer1-img

Обновление : вы можете проверить мое репо здесь https://github.com/jcberthon/unifi-docker Я использую этот метод и объясняю, как настроить хост и запустить ваш контейнер. Надеюсь, это поможет в дальнейшем решении ваших проблем.

Кстати, прошу прощения @renanwilliam за долгую задержку с

Короче для нетерпеливых:

RUN mkdir /volume_data
RUN chown postgres:postgres /volume_data

Предварительно создав каталог тома, и chown решит эту проблему, потому что том сохранит разрешения существующего каталога.

Это плохой обходной путь, поскольку он неочевиден (выполнение команды chown в файле Docker с последующим наследованием этого владения во время монтирования). Открытие управления владельцем и группой в интерфейсе командной строки docker-compose и docker было бы наименее неожиданным для команд стиля unix.

@villasv

Небольшой совет: объедините 2 RUN ... в один, это позволяет избежать создания дополнительных слоев и является наилучшей практикой. Итак, ваши 2 строки должны быть

RUN mkdir /volume_data && chown postgres:postgres /volume_data

Но будьте осторожны (как я упоминал в комментарии выше), что вам нужно выполнить указанную выше команду RUN ... перед объявлением объема (или фактически не объявлением объема) с помощью VOLUME ... . Если вы выполните (как я) смену владельца после объявления тома, эти изменения не будут записаны и потеряны.

@colbygk это действительно было бы удобно, но Linux работает не так. Docker использует пространство имен монтирования Linux для создания различных иерархий с одним каталогом ( / и подпапки), но, AFAIK, нет сопоставлений пользователей / групп или разрешений, переопределяющих в настоящее время в пространстве имен монтирования Linux. Эти «монтирования» внутри контейнера (включая тома с подключением к подключению) находятся в файловой системе на хосте (если, конечно, вы не используете какие-либо другие подключаемые модули томов Docker), и эта файловая система соблюдает уровень Linux VFS, который делает все проверка прав доступа к файлам. На хосте может быть даже какой-то MAC (например, SELinux, AppArmor и т. Д.), Который может мешать контейнеру обращаться к файлам внутри контейнера. На самом деле, если вы выполните chroot , вы можете столкнуться с аналогичными проблемами, поскольку вы можете привязать папки монтирования в chroot, у вас также есть проблема, что процессы, запущенные в среде chroot, могут иметь неправильный эффективный UID / GID для доступа к файлам в привязка-крепление.

К контейнеру применяются простые правила Linux (а также, собственно, и Unix). Хитрость заключается в том, чтобы увидеть и понять возможности и ограничения пространств имен Linux сегодня, и тогда становится яснее, как решать проблемы, подобные этой. Я решил это полностью с помощью классических команд Unix.

@jcberthon Спасибо за вдумчивый ответ:

Я бы сказал, что это должна быть проблема, которая переносится на уровень плагина, как вы предлагаете, и, следовательно, может стать частью общего плагина обработчика тома, который поставляется с Docker. Мне кажется очень uncloud / container, как мне заставить внешний ресурс (внешний по отношению к конкретному контейнеру) придерживаться по существу статических отношений, которые определены в образе, из которого происходит контейнер.

Есть и другие примеры такого точного сопоставления uid / gid, которое встречается в других подобных областях "unix":

Пожалуйста, поправьте меня, если я ошибаюсь; https://github.com/zfsonlinux/zfs/issues/4177, по- видимому, был создан «лидером LXC / LXD» как проблема, связанная с ZFS в Linux, которая неправильно предоставляет информацию UID / GID, чтобы можно было сопоставить их с контейнер почти таким же _exact_ способом, который мы здесь обсуждаем. При внимательном рассмотрении https://github.com/zfsonlinux/zfs/issues/4177 кажется, что тип тома zfs на самом деле уже может поддерживать это сопоставление uid / gid между пространствами имен, но не предоставляет элементы управления для этого.

большинство людей, использующих Docker, предназначено для разработки / CI, мы используем «общие» образы, такие как php / nginx (runner) или gradle / python (builder), поэтому хорошее решение будет:

  1. нет необходимости создавать / редактировать Dockerfile, чтобы переопределить изображение
  2. использовать простой синтаксис в файле yml для создания докеров

Поскольку мы можем легко определить разрешение на запись для тома (X: X: OPTION_READ_WRITE), как насчет добавления таким же образом владельца?

SOURCE:TARGET:RW:OWNER

У меня такая же проблема. Вероятно, это лучшая практика, и мой метод _не_:

version: '3.5'
services:
    something:
        image: someimage
        user: '1000'
        expose:
            - 8080
        volumes:
            - dev:/app

volumes:
    dev:

Это вызывает EACCES: permission denied, access '/app' .

Как нам это сделать? Т.е. определить новый том и иметь к нему доступ с пользователем без полномочий root?

Привет @Redsandro

Было бы лучше, если бы вы установили в образе someimage Docker UID равным 1000 для /app . Или, если вы не можете контролировать это, вы должны использовать для user: ... в вашем файле создания UID или GID, которые были предназначены автором изображения.

Конечно, если автор образа использовал UID 0, а вы не хотите запускать службу как root (и она может работать как непривилегированный пользователь), тогда поднимите вопрос перед автором образа Docker.

так как это не то, что нужно докеру для управления, вы можете создать другой контейнер для предоставления ваших томов прямо перед запуском связанных контейнеров, например, используя https://hub.docker.com/r/hasnat/volumes-provisioner

version: '2'
services:
  volumes-provisioner:
    image: hasnat/volumes-provisioner
    environment:
      PROVISION_DIRECTORIES: "65534:65534:0755:/var/data/prometheus/data"
    volumes:
      - "/var/data:/var/data"

  prometheus:
    image: prom/prometheus:v2.3.2
    ports:
      - "9090:9090"
    depends_on:
      - volumes-provisioner
    volumes:
      - "/var/data/prometheus/data:/prometheus/data"

Я не понимаю, почему Docker не исправляет это, я согласен, что мы можем взломать его для целей разработки, но в продакшене - никак!

IMHO podman может работать как (непривилегированный) пользователь (см. Также здесь ) и, вероятно, решит эту проблему. Кто-то также работает над решением для компоновки, и Podman API намеренно совместим с Docker в большинстве частей.

[podman] может здесь помочь некоторым людям, потому что он в значительной степени совместим с Docker.

Полностью согласен, к сожалению подман не работает на Mac

Полностью согласен, к сожалению подман не работает на Mac


Ну ИМХО ни Docker, ни podman нативно работать не будут. Но установка Docker на OS / X очень хорошо скрывает содержимое виртуальных машин.
Но я согласен с тем, что настройка виртуальных машин вручную для получения надлежащей системы разработки может быть болезненной.
Хотя здесь это немного не по теме.

Я больше не пользуюсь OS / X, но только что увидел, что

Я предполагаю, что в ближайшем будущем может развиться какая-то подобная экосистема, потому что уже есть возможность получить доступ к программному обеспечению podman или даже к podman-compose .

Это особенно становится проблемой, когда пользователи без прав sudo в общем кластере случайно создают некоторые папки, принадлежащие root, через docker-compose, а затем они даже не могут сами удалить эти папки.

Я тоже столкнулся с этой проблемой. Мы пытаемся использовать докер в докере, как указано в сообщении jpetazzo .

Мы хотим запустить этот контейнер из файла docker-compose и иметь возможность монтировать сокет докера с хост-машины на машину-контейнер в группе docker. Это сделано для того, чтобы мы могли использовать другого пользователя, кроме root, для запуска докера из контейнера.

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

Положи сюда мои два цента:

Для "родного" решения для docker-compose я сделал это, поскольку у большинства людей alpine уже есть в их библиотеке изображений:

volumes:
  media:
services:
  mediainit:
    image: alpine
    entrypoint: /bin/sh -c "chown -v nobody:nogroup /mnt/media && chmod -v 777 /mnt/media"
    container_name: mediainit
    restart: "no"
    volumes: 
      - "media:/mnt/media"

Не самый безопасный из методов, конечно, но только контейнеры, которым предоставлены привилегии для контейнера, будут видеть его, так что это не такая уж большая проблема, но вы можете легко сделать его chown user: user или сделать какую-нибудь причуду setacl, если ваше ядро ​​поддерживает Это.

РЕДАКТИРОВАТЬ: Кажется, вы должны сначала перерезать папку перед chmod, иначе она не «прилипнет», по крайней мере, в моем тестировании.

объем владения не находится под контролем docker-compose, поэтому это обсуждение должно происходить в репозитории проекта moby.

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

#Dockerfile
FROM alpine
RUN addgroup -S nicolas && adduser -S nicolas -G nicolas
RUN mkdir /foo && chown nicolas:nicolas /foo  
# empty, but owned by `nicolas`. Could also have some initial content
VOLUME /foo  
USER nicolas

Такой образ при запуске без явного тома (который будет создан специально со случайным идентификатором) или с именованным томом, который еще не существует, будет «распространять» разрешения на том:

➜  docker run --rm -it -v some_new_volume:/foo myimage
/ $ ls -al /foo
total 8
drwxr-xr-x    2 nicolas  nicolas       4096 Oct 18 08:30 .
drwxr-xr-x    1 root     root          4096 Oct 18 08:30 ..

То же самое применимо при использовании томов, объявленных в файле создания:

#docker-compose.yml
version: "3"
services:
  web:
    image: myimage
    command: ls -al /foo
    volumes:
      - db-data:/foo
volumes:
    db-data:

➜  docker-compose up
Creating volume "toto_db-data" with default driver
Creating toto_web_1 ... done
Attaching to toto_web_1
web_1  | total 8
web_1  | drwxr-xr-x    2 nicolas  nicolas       4096 Oct 18 08:30 .
web_1  | drwxr-xr-x    1 root     root          4096 Oct 18 08:37 ..
toto_web_1 exited with code 0

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

Надеюсь это поможет.
Я закрываю эту проблему, поскольку compose не управляет созданием тома, кроме открытого API движка.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги