Розуміння власності користувацьких файлів у docker: як уникнути зміни дозволів пов’язаних томів


77

Розглянемо такий тривіальний Dockerfile:

FROM debian:testing
RUN  adduser --disabled-password --gecos '' docker
RUN  adduser --disabled-password --gecos '' bob 

у робочому каталозі ні з чим іншим. Створіть образ докера:

docker build -t test .

а потім запустіть скрипт bash на контейнері, зв’язавши робочий каталог з новим підкаталогом домашнього каталогу bob:

docker run --rm -it -v $(pwd):/home/bob/subdir test 

Кому належить вміст subdirконтейнера? На контейнері запустіть:

cd /home/bob/subdir
ls -l

Оголошення, яке ми бачимо:

-rw-rw-r-- 1 docker docker 120 Oct 22 03:47 Dockerfile

Святі дими! dockerволодіє вмістом! Повернувшись на хост-машині поза контейнером, ми бачимо, що наш початковий користувач все ще володіє Dockerfile. Давайте спробуємо виправити право власності на bobдомашній каталог. На контейнері запустіть:

chown -R bob:bob /home/bob
ls -l 

і ми бачимо:

-rw-rw-r-- 1 bob bob 120 Oct 22 03:47 Dockerfile

Але почекай! за межами контейнера, ми тепер запускаємоls -l

-rw-rw-r-- 1 1001 1001 120 Oct 21 20:47 Dockerfile

ми більше не володіємо власним файлом. Страшні новини!


Якби ми додали лише одного користувача у наведеному вище прикладі, все пройшло б більш гладко. З якоїсь причини Docker, здається, створює будь-який домашній каталог, що належить першому некореневому користувачеві, якого він зустрічає (навіть якщо цей користувач оголошений на попередньому зображенні). Так само, цей перший користувач відповідає тим самим дозволам власності, що і мій домашній користувач.

Питання 1 Чи правильно? Хтось може вказати мені документацію на це, я просто припускаю, спираючись на вищезазначений експеримент.

Запитання 2 : Можливо, це просто тому, що вони обидва мають одне і те ж числове значення в ядрі, і якби я тестував у системі, де мій домашній користувач не був ідентифікатором, 1000тоді дозволи змінювались би в кожному випадку?

Питання 3 : Справжнє питання, звичайно, "що я з цим роблю?" Якщо bobви ввійшли як bobна даній хост-машині, він повинен мати можливість запускати контейнер як bobі не мати дозволів на файли, змінених під своїм обліковим записом хоста. По суті, йому фактично потрібно запустити контейнер як користувач, dockerщоб уникнути зміни облікового запису.

Я чую, як ви запитуєте, навіщо мені такий дивний Dockerfile? . Мені теж часом цікаво. Я пишу контейнер для веб-додатка (RStudio-сервер), який дозволяє різним користувачам входити, який просто використовує імена користувачів та облікові дані з машини Linux як дійсні імена користувачів. Це приносить мені, можливо, незвичну мотивацію бажання створити декількох користувачів. Я можу обійти це, створивши користувача лише під час виконання, і все добре. Однак я використовую базове зображення, яке додало одного dockerкористувача, щоб його можна було використовувати в інтерактивному режимі, не виконуючи як root (згідно з найкращою практикою). Це руйнує все, оскільки цей користувач стає першимкористувач і в кінцевому підсумку володіє всім, тому намагається увійти в систему, оскільки інші користувачі зазнають невдачі (програма не може запуститись, оскільки їй бракує дозволів на запис). chownСпочатку запущений сценарій запуску вирішує цю проблему, але ціною пов’язаних томів, що змінюють дозволи (очевидно, проблема лише в тому випадку, якщо ми пов’язуємо томи).


1001 слід зіставити з bob в / etc / passwd, якщо так, то контейнер не змонтував / etc / password. на рівні ядра контекст процесу буде містити лише ідентифікатор / номер, а не ім'я.
resultway

Дивіться також: unix.stackexchange.com/a/154092/43610
Everett

Відповіді:


28

Це правильно? Хтось може вказати мені документацію на це, я просто припускаю, спираючись на вищезазначений експеримент.

Можливо, це просто тому, що вони обидва мають одне і те ж числове значення в ядрі, і якби я тестував у системі, де мій домашній користувач не мав ідентифікатора 1000, тоді дозволи змінювались би в кожному випадку?

Почитайте info coreutils 'chown invocation', що може дати вам краще уявлення про те, як працюють дозволи / права власності на файли.

В основному, однак, кожен файл на вашому комп'ютері має набір бітів, який прикріплюється до нього, що визначає його дозволи та право власності. Коли ви chownстворюєте файл, ви просто встановлюєте ці біти.

Коли ви chownпередаєте файл певному користувачеві / групі, використовуючи ім’я користувача чи ім’я групи, chownбуде шукати /etc/passwdім’я користувача та /etc/groupгрупу, яка намагатиметься зіставити ім’я з ідентифікатором. Якщо ім’я користувача / групи не існує у цих файлах, chownце не вдасться.

root@dc3070f25a13:/test# touch test
root@dc3070f25a13:/test# ll
total 8
drwxr-xr-x  2 root root 4096 Oct 22 18:15 ./
drwxr-xr-x 22 root root 4096 Oct 22 18:15 ../
-rw-r--r--  1 root root    0 Oct 22 18:15 test
root@dc3070f25a13:/test# chown test:test test
chown: invalid user: 'test:test'

Однак ви можете chownподати файл, використовуючи ідентифікатори, що завгодно (звичайно, у межах верхніх додатних цілих чисел), незалежно від того, є на вашому комп'ютері користувач / група, що існує з цими ідентифікаторами.

root@dc3070f25a13:/test# chown 5000:5000 test
root@dc3070f25a13:/test# ll
total 8
drwxr-xr-x  2 root root 4096 Oct 22 18:15 ./
drwxr-xr-x 22 root root 4096 Oct 22 18:15 ../
-rw-r--r--  1 5000 5000    0 Oct 22 18:15 test

Біти UID та GID встановлюються у самому файлі, тому, коли ви монтуєте ці файли всередині контейнера докера, файл має такий самий UID власника / групи, що і на хості, але тепер він відображається /etc/passwdу контейнері, який ймовірно, це буде інший користувач, якщо він не належить root (UID 0).

Справжнє питання, звичайно, "що я з цим роблю?" Якщо bob ввійшов як bob на даній хост-машині, він повинен мати можливість запускати контейнер як bob і не мати дозволів на файли, змінених під своїм обліковим записом хоста. По суті, йому насправді потрібно запустити контейнер як докер користувача, щоб уникнути зміни облікового запису.

Здається, що при поточній настройці вам потрібно буде переконатися, що ваші UID> імена користувачів /etc/passwdна хості збігаються з вашими UID> імена користувачів у ваших контейнерах, /etc/passwdякщо ви хочете взаємодіяти з вашим змонтованим каталогом користувачів як той самий користувач який увійшов на хост.

Ви можете створити користувача з певним ідентифікатором користувача за допомогою useradd -u xxxx. Ось, це здається брудним рішенням ...

Можливо, вам доведеться придумати рішення, яке не монтує домашній каталог користувачів хостів.


Дякую за детальну відповідь, це, безумовно, для мене звучить як правильне пояснення. Як ви вже сказали, не особливо задовільне рішення - дивно, що Docker не працює над цим більш явно. Не монтувати нічого в домашньому каталозі хост-користувача здається надзвичайно обмежуючим.
cboettig

hokstad.com/docker/patterns Я читав це і зрозумів, що цей хлопець вирішує вашу проблему, як я пропоную вище. Може, все-таки це не так погано?
Chris McKinnel

дякую за посилання. Схоже, такий підхід прив'язує UID (у будь-якому випадку до значення за замовчуванням), щоб проблема все одно виникала (наприклад, контейнер не переноситься без попередньої настройки та побудови файлу Docker). Якби Docker підтримував імена замість значень uid, ця проблема справді зникне. Більше інформації від розробників Docker про розміщення цього в групі
docker

1
так, я думаю, це насправді це не погана стратегія, зрештою. Потрібно спочатку звільнити UID, userdelхоча. fwiw ось рішення, яке я беру у своєму фактичному файлі docker: github.com/rocker-org/rocker/issues/50#issuecomment-60306104
cboettig

63

Я знайшов два варіанти:

ЗБЕРІГТИ всі речі (після виконання роботи)

Я зробив docker run -v `pwd`/shared:/shared image, і контейнер створив у них файли pwd/shared, які належать процесу докера. Однак /sharedвсе ще належить мені. Отже, у процесі докера я це роблю

chown -R `stat -c "%u:%g" /shared` /shared

stat -c "%u:%g" /sharedповертається 1000:1000в моєму випадку, будучи uid:gidмоїм користувачем. Незважаючи на те, що 1000в докер-контейнері немає користувача , ідентифікатор є (іstat /shared просто говорить "невідомо", якщо ви запитаєте ім'я користувача).

У будь-якому випадку, chownслухняно передає право власності на вміст /sharedдо 1000:1000(який, наскільки це стосується, не існує, але поза контейнером, це я). Отже, тепер я володію усіма файлами. Контейнер все ще може змінювати речі, якщо захоче, тому що з його точки зору це так root.

І зі світом все добре.

docker run -u тому всі створені файли автоматично матимуть відповідного власника

Інший спосіб зробити це - -uпрапор при запуску докера.

docker run -v `pwd`/shared:/shared -u `stat -c "%u:%g" /shared` ubuntu bash

Таким чином, користувач докера всередині контейнера знаходиться youruid:yourgid.

Однак: це означає відмову від кореневих повноважень у контейнері ( apt-get installтощо). Якщо ви не створите користувача з новим uid та не додасте його до rootгрупи.


1
Так, я, як правило, в кінцевому підсумку активно використовую chownтакож. Я також схильний використовувати -u 1000. Я не думаю, що використання %uдопомагає так сильно, оскільки якщо у вашого користувача є UID, якого не існує в контейнері, він все одно просто помилиться.
cboettig

Насправді це не помилка - просто скаржиться stderr :)
Джаред Форсайт

Слід зазначити, що з - за того , як працює Docker, то UIDі GIDпередається в Докер по -uвибору повинні існувати всередині самого Докер контейнера (тобто ідентифікатори, а не імена , пов'язані з ними).
code_dredd

4

Отже, я опинився в цій публікації, розглядаючи, як відновити право власності на всі файли (що належать root), що вийшли з контейнера докера, що працює як root , моєму непривілейованому користувачеві на хості.

Мені потрібно було, щоб процес всередині контейнера запускався як root, тому я не можу використовувати -u під час запуску докера.

Я не пишаюся тим, що зробив, але наприкінці мого сценарію bash я додав наступне:

docker run --rm -it \
    --entrypoint /bin/sh \
    -e HOST_UID=`id -u` \
    -v ${HOST_FOLDER_OWNED_BY_ROOT}:/tmp \
    alpine:latest \
    -c 'chown -R ${HOST_UID}:${HOST_UID} /tmp/'

Давайте розберемо деякі рядки:

  • Запустити / bin / sh всередині контейнера:

--entrypoint / bin / sh

  • Передати uid поточного користувача як змінну середовища до контейнера:

-e HOST_UID = `id -u`

  • З’єднайте будь-яку папку, якою ви хочете повторно володіти, користувачеві ( заповнену файлами, що належать root, виведеними попереднім контейнером, який виконувався як root ), під цим новим контейнером /tmp:

-v $ {HOST_FOLDER_OWNED_BY_ROOT}: / tmp

  • Запустіть chownрекурсивно з ідентифікатором користувача хосту над цільовим каталогом (змонтованим всередині контейнера в /tmp):

-c 'chown -R $ {HOST_UID}: $ {HOST_UID} / tmp /'

Отже, завдяки цьому я повернув файли, що належать моєму поточному користувачеві, без необхідності "ескалації" привілеїв на root або sudo.

Це брудно, але це спрацювало. Сподіваюся, я допоміг.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.