Рабочее руководство по извлечению данных разрешений на именованные тома без полномочий root, работающих с linux и win

Я безуспешно пробую простой рабочий процесс, и мне требуется много времени, чтобы протестировать множество решений на SO и github. Разрешение для именованной папки и более общий объем разрешений в докере — это кошмар link1 link2 имхо.

Поэтому я перезапускаю с нуля, пытаясь создать простое доказательство концепции для моего варианта использования.

Я хочу этот общий рабочий процесс:

  • пользователь в Windows и/или Linux создает Dockerfile
  • пользователь запускает контейнер (если возможно, не как root)
  • контейнер запускает crontab, который каждую минуту запускает скрипт, записывающий объем данных
  • пользователи (в Linux или Windows) получают результаты из тома данных (не root), потому что разрешения правильно сопоставлены

Я использую supercronic, потому что он запускает crontab в контейнере без разрешения root.

Dockerfile :

FROM artemklevtsov/r-alpine:latest as baseImage

RUN mkdir -p /usr/local/src/myscript/
RUN mkdir -p /usr/local/src/myscript/result

COPY . /usr/local/src/myscript/

WORKDIR /usr/local/src/myscript/

RUN echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories
RUN apk --no-cache add busybox-suid curl

ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.1.$
    SUPERCRONIC=supercronic-linux-amd64 \
    SUPERCRONIC_SHA1SUM=9aeb41e00cc7b71d30d33c57a2333f2c2581a201

RUN curl -fsSLO "$SUPERCRONIC_URL" \
 && echo "${SUPERCRONIC_SHA1SUM}  ${SUPERCRONIC}" | sha1sum -c - \
 && chmod +x "$SUPERCRONIC" \
 && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
 && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic

CMD ["supercronic", "crontab"]

Файл crontab:

* * * * * sh /usr/local/src/myscript/run.sh > /proc/1/fd/1 2>&1

Скрипт run.sh

#!/bin/bash

name=$(date '+%Y-%m-%d-%s')

echo "some data for the file" >> ./result/fileName$name

Команды:

# create the volume for result, uid/gid option are not possible for windows 
docker volume create --name myTestVolume

docker run --mount type=volume,source=myTestVolume,destination=/usr/local/src/myscript/result test

docker run --rm -v myTestVolume:/alpine_data -v $(pwd)/local_backup:/alpine_backup alpine:latest tar cvf /alpine_backup/scrap_data_"$(date '+%y-%m-%d')".tar /alpine_data

Когда я делаю это, папка результата local_backup и содержащиеся в ней файлы имеют права доступа root:root, поэтому пользователь, запускающий этот контейнер, не может получить доступ к файлам.

Есть ли работающее решение, позволяющее пользователям Windows/Linux/mac, которые запускают один и тот же сценарий, легко получать доступ к файлам в томе без проблем с разрешениями?

ИЗМЕНИТЬ 1:

Стратегия, впервые описанная только здесь, работает с привязанным томом, и не именованный том. Мы используем entrypoint.sh для выбора uid/gid папок контейнера на основе информации, полученной при запуске докера.

Я копирую и вставляю измененный Dockerfile:

FROM artemklevtsov/r-alpine:latest as baseImage

RUN mkdir -p /usr/local/src/myscript/
RUN mkdir -p /usr/local/src/myscript/result

COPY . /usr/local/src/myscript/

ENTRYPOINT [ "/usr/local/src/myscript/entrypoint.sh" ]

WORKDIR /usr/local/src/myscript/

RUN echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories
RUN apk --no-cache add busybox-suid curl su-exec

ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.1.$
    SUPERCRONIC=supercronic-linux-amd64 \
    SUPERCRONIC_SHA1SUM=9aeb41e00cc7b71d30d33c57a2333f2c2581a201

RUN curl -fsSLO "$SUPERCRONIC_URL" \
 && echo "${SUPERCRONIC_SHA1SUM}  ${SUPERCRONIC}" | sha1sum -c - \
 && chmod +x "$SUPERCRONIC" \
 && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
 && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic

CMD ["supercronic", "crontab"]

Точка входа.sh

#!/bin/sh
set -e

addgroup -g $GID scrap && adduser -s /bin/sh -D -G scrap -u $UID scrap

if [ "$(whoami)" == "root" ]; then
    chown -R scrap:scrap /usr/local/src/myscript/
    chown --dereference scrap "/proc/$$/fd/1" "/proc/$$/fd/2" || :
    exec su-exec scrap "$@"
fi

Процедура сборки, запуска, экспорта:

docker build . --tag=test                                                       

docker run -e UID=1000 -e GID=1000 --mount type=volume,source=myTestVolume,destination=/usr/local/src/myscript/result test

 docker run --rm -v myTestVolume:/alpine_data -v $(pwd)/local_backup:/alpine_backup alpine:latest tar cvf /alpine_backup/scrap_data_"$(date '+%y-%m-%d')".tar /alpine_data

ИЗМЕНИТЬ 2:

Для Windows с помощью панели инструментов Docker и связанного тома я нашел ответ на SO. Я для привязки использую папку c:/Users/MyUsers, так проще.

docker run --name test -d -e UID=1000 -e GID=1000 --mount type=bind,source=/c/Users/myusers/localbackup,destination=/usr/local/src/myscript/result dockertest --name rflightscraps 

Результат расследования

  • crontab запустить с пользователем лома [OK]

  • UID/GID локального пользователя сопоставляются с запиской пользователя контейнера [OK]

  • Экспортированные данные остаются корневыми [НЕ ОК].

  • Windows/Linux [НА ПОЛОВИНУ ОК]

    Если я использую связываемый том, а не именованный том, он работает. Но это нежелательное поведение, как я могу использовать именованный том с правильным разрешением в Win/Linux...


person reyman64    schedule 20.09.2018    source источник
comment
Это кажется почти тривиальной проблемой с использованием демона cron хоста, пока вы не представите Docker. Что вы надеетесь получить, используя Docker?   -  person David Maze    schedule 21.09.2018
comment
Я использую этот вариант использования для курсов, каждый студент сначала запускает этот рабочий процесс на своем локальном компьютере, а после мы отправляем изображения на некоторые серверы.   -  person reyman64    schedule 21.09.2018
comment
Вы не можете предоставить мне льготный период публикации вознаграждения, поскольку ответ не был принят в то время :-(   -  person Mani    schedule 11.10.2018


Ответы (1)


Позвольте мне разделить ответ на две части: часть Linux и часть Docker. Вы должны понимать оба, чтобы решить эту проблему.

Часть Linux

В Linux легко запускать cronjobs от имени пользователя, отличного от root.

Этого можно добиться, создав пользователя в контейнере докеров с тем же UID, что и на хост-компьютере, и скопировав файл crontab как /var/spool/cron/crontabs/user_name.

С 1_

crontab — это программа, используемая для установки, удаления или отображения таблиц, используемых для управления демоном cron(8) в Vixie Cron. У каждого пользователя может быть свой crontab, и хотя это файлы в /var/spool/cron/crontabs, они не предназначены для непосредственного редактирования.

Поскольку Linux идентифицирует пользователей по идентификатору пользователя, внутри докера UID будет привязан к вновь созданному пользователю, тогда как на хост-компьютере он будет привязан к пользователю хоста.

Итак, у вас нет проблем с разрешениями, поскольку файлы принадлежат host_user. Теперь вы бы поняли, почему я упомянул о создании пользователя с тем же UID, что и на хост-компьютере.

Докер Часть

Docker считает все каталоги (или слои) UNION FILE SYSTEM. Всякий раз, когда вы создаете образ, каждая инструкция создает слой, и этот слой помечается как доступный только для чтения. По этой причине контейнеры Docker не сохраняют данные. Таким образом, вы должны явно указать докеру, что некоторые каталоги должны сохранять данные, используя ключевое слово VOLUME.

Вы можете запускать контейнеры без явного указания объема. Если вы это сделаете, демон docker считает их UFS и сбрасывает разрешения. Чтобы сохранить изменения в файле/каталоге, включая права собственности. Соответствующий файл должен быть объявлен как Volume в Dockerfile.

Из UNION FILE SYSTEM

Действительно, когда контейнер загружается, он перемещается в память, а загрузочная файловая система размонтируется, чтобы освободить оперативную память, используемую образом диска initrd. Пока это очень похоже на типичный стек виртуализации Linux. Действительно, Docker затем накладывает корневую файловую систему, rootfs, поверх загрузочной файловой системы. Такой rootfs может быть одна или несколько операционных систем (например, файловая система Debian или Ubuntu). Docker называет образы каждой из этих файловых систем. Изображения можно накладывать друг на друга. Изображение ниже называется родительским изображением, и вы можете перемещаться по каждому слою, пока не достигнете нижней части стека изображений, где окончательное изображение называется базовым изображением. Наконец, когда контейнер запускается из образа, Docker монтирует файловую систему для чтения и записи поверх любых слоев ниже. Здесь будут выполняться любые процессы, которые мы хотим, чтобы наш контейнер Docker запускал. Когда Docker впервые запускает контейнер, начальный уровень чтения-записи пуст. По мере возникновения изменений они применяются к этому слою; например, если вы хотите изменить файл, этот файл будет скопирован из нижележащего слоя только для чтения в слой для чтения и записи. Версия файла только для чтения по-прежнему будет существовать, но теперь будет скрыта под копией.

Пример:

Предположим, у нас есть пользователь с именем host_user. UID host_user равен 1000. Теперь мы собираемся создать пользователя с именем docker_user в контейнере Docker. Поэтому я назначу ему UID 1000. Теперь любые файлы, принадлежащие пользователю docker_user в контейнере Docker, также принадлежат пользователю host_user, если эти файлы доступны пользователю host_user с хоста (т. е. через тома).

Теперь вы можете поделиться привязанным каталогом с другими без каких-либо проблем с разрешениями. Вы даже можете дать разрешение 777 на соответствующий каталог, что позволит другим редактировать данные. В противном случае вы можете оставить разрешения 755, которые позволяют другим копировать, но только владельцу редактировать данные.

Я объявил каталог для сохранения изменений в виде тома. Это сохраняет все изменения. Будьте осторожны, так как после того, как вы объявите каталог как том, дальнейшие изменения, внесенные в этот каталог при создании, будут проигнорированы, поскольку эти изменения будут в отдельных слоях. Следовательно, сделайте все свои изменения в каталоге, а затем объявите его как том.

Вот файл докера.

FROM alpine:latest
ARG ID=1000 
#UID as arg so we can also pass custom user_id
ARG CRON_USER=docker_user
#same goes for username

COPY crontab /var/spool/cron/crontabs/$CRON_USER
RUN adduser -g "Custom Cron User" -DH -u $ID $CRON_USER && \
    chmod 0600 /var/spool/cron/crontabs/$CRON_USER && \
    mkdir /temp && \
    chown -R $ID:$ID /temp && \
    chmod 777 /temp

VOLUME /temp
#Specify the dir to be preserved as Volume else docker considers it as Union File System

ENTRYPOINT ["crond", "-f", "-l", "2"]

Вот кронтаб

* * * * * /usr/bin/whoami >> /temp/cron.log

Создание образа

docker build . -t test

Создать новый том

docker volume create --name myTestVolume

Запуск с объемом данных

docker run --rm --name test -d -v myTestVolume:/usr/local/src/myscript/result test:latest

Всякий раз, когда вы монтируете myTestVolume в другой контейнер, вы можете видеть, что данные под /usr/local/src/myscript/result принадлежат UID 1000, если в этом контейнере не существует пользователя с этим UID или именем пользователя соответствующего UID.

Запуск с привязкой тома

docker run --rm --name test - -dv $PWD:/usr/local/src/myscript/result test:latest

Когда вы выполните ls -al /home/host_user/temp, вы увидите, что файл с именем cron.log создан и принадлежит **host_user**.

То же самое будет принадлежать docker_user в контейнере docker, когда вы выполните ls -al /temp. Содержимое cron.log будет docker_user.

Таким образом, ваш эффективный Dockerfile должен быть

FROM artemklevtsov/r-alpine:latest as baseImage

ARG ID=1000 

ARG CRON_USER=docker_user

RUN adduser -g "Custom Cron User" -DH -u $ID $CRON_USER && \
    chmod 0600 /var/spool/cron/crontabs/$CRON_USER && \
    echo http://nl.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories && \
    apk --no-cache add busybox-suid curl && \
    mkdir -p /usr/local/src/myscript/result && \
    chown -R $ID:$ID /usr/local/src/myscript/result && \
    chmod 777 /usr/local/src/myscript/result

COPY crontab /var/spool/cron/crontabs/$CRON_USER

COPY . /usr/local/src/myscript/

VOLUME /usr/local/src/myscript/result
#This preserves chown and chmod changes.

WORKDIR /usr/local/src/myscript/

ENTRYPOINT ["crond", "-f", "-l", "2"] 

Теперь всякий раз, когда вы подключаете том Data/bind к /usr/local/src/myscript/result, он будет принадлежать пользователю с UID 1000, и он будет постоянным во всех контейнерах, в зависимости от того, какой из них смонтировал один и тот же том с соответствующим пользователем с 1000 в качестве владельцев файлов.

Обратите внимание: я дал 777 разрешений, чтобы поделиться со всеми. При желании вы можете пропустить этот шаг в Dockerfle.

Рекомендации:

  1. Руководство по Crontab.
  2. Идентификатор пользователя — Вики.
  3. Определение идентификатора пользователя.
  4. О драйверах устройств хранения.
  5. ФАЙЛОВАЯ СИСТЕМА UNION.
person Mani    schedule 09.10.2018
comment
Хм, вы правы, это работает, но вы используете связанный том (например, в редактировании моего вопроса), а не новый именованный том каноническим способом, как я ищу решение в своем вопросе. - person reyman64; 09.10.2018
comment
Спасибо за ваши усилия по решению этой проблемы. Я не вижу части, где вы создаете именованный том : docker volume create --name myTestVolume - person reyman64; 10.10.2018