使用 linux 和 win 提取非 root 命名卷权限数据的工作方法

A working how-to for data extraction of non-root named volume permissions working with linux and win

我正在尝试一个简单的工作流程但没有成功,我花了很多时间来测试 SO 和 github 上的许多解决方案。 docker 中命名文件夹的权限和更一般的权限卷是一场噩梦 link1 link2 恕我直言。

所以我从头开始,尝试为我的用例创建一个简单的概念证明。

我想要这个一般的工作流程:

我使用 supercronic 因为它 运行 在容器中没有 root 权限的 crontab。

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:

首先描述的策略here only使用绑定卷,而不是命名卷。根据 docker 运行.

给出的信息,我们使用 entrypoint.sh 来 chown 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"]

entrypoint.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 工具箱和合订本,i found the answer on 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 

调查结果

让我把答案分成两部分Linux部分和Docker部分。为了解决这个问题,你需要了解两者。

Linux 部分

在 Linux.

中 运行 以 root 以外的用户身份执行 cronjobs 很容易

这可以通过在 docker 容器中创建一个与主机中具有相同 UID 的用户并将 crontab 文件复制为 /var/spool/cron/crontabs/user_name.

来实现

来自man crontab

crontab is the program used to install, deinstall or list the tables used to drive the cron(8) daemon in Vixie Cron. Each user can have their own crontab, and though these are files in /var/spool/cron/crontabs, they are not intended to be edited directly.

由于Linux通过用户ID识别用户,在docker内部UID将绑定到新创建的用户,而在主机中,UID将与主机用户绑定。

因此,您没有任何权限问题,因为文件归 host_user 所有。现在你应该明白为什么我提到创建与主机中 UID 相同的用户了。

Docker 部分

Docker 将所有目录(或层)视为 UNION FILE SYSTEM。每当您构建图像时,每条指令都会创建一个层,并且该层被标记为只读。这就是 Docker 容器不持久化数据的原因。所以你必须明确地告诉 docker 一些目录需要使用 VOLUME 关键字来持久化数据。

您可以 运行 容器而无需明确提及体积。如果您这样做,docker 守护程序会将它们视为 UFS 并重置权限。 为了保留对 file/directory 的更改,包括所有权。相应的文件应在 Dockerfile.

中声明为 Volume

来自UNION FILE SYSTEM

Indeed, when a container has booted, it is moved into memory, and the boot filesystem is unmounted to free up the RAM used by the initrd disk image. So far this looks pretty much like a typical Linux virtualization stack. Indeed, Docker next layers a root filesystem, rootfs, on top of the boot filesystem. This rootfs can be one or more operating systems (e.g., a Debian or Ubuntu filesystem). Docker calls each of these filesystems images. Images can be layered on top of one another. The image below is called the parent image and you can traverse each layer until you reach the bottom of the image stack where the final image is called the base image. Finally, when a container is launched from an image, Docker mounts a read-write filesystem on top of any layers below. This is where whatever processes we want our Docker container to run will execute. When Docker first starts a container, the initial read-write layer is empty. As changes occur, they are applied to this layer; for example, if you want to change a file, then that file will be copied from the read-only layer below into the read-write layer. The read-only version of the file will still exist but is now hidden underneath the copy.

示例:

让我们假设我们有一个名为 host_user 的用户。 host_user 的 UID1000。现在我们要在 Docker 容器中创建一个名为 docker_user 的用户。所以我会给他分配 UID 为 1000。现在 Docker 容器中 docker_user 拥有的任何文件也由 host_user 拥有,如果这些文件可由 host_user 从主机访问(即通过卷)。

现在您可以与其他人共享绑定的目录,而不会出现任何权限问题。你甚至可以在相应的目录上授予777权限,允许其他人编辑数据。否则,您可以保留 755 权限,允许其他人复制但只有所有者可以编辑数据。

我已将目录声明为将更改保存为一个卷。这会保留所有更改。请注意,一旦您将目录声明为卷,在构建时对该目录所做的进一步更改将被忽略,因为这些更改将在不同的层中。因此,在目录中进行所有更改,然后将其声明为卷。

这是 Docker 文件。

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"]

这是 crontab

* * * * * /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

Whenever you mount myTestVolume to other container you can see the data under /usr/local/src/myscript/result is owned by UID 1000 if no user exist with that UID in that container or the username of corresponding 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** 所有。

当您执行 ls -al /temp 时,docker 容器中的 docker_user 将拥有相同的内容。 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 权限以便与大家分享。您可以根据自己的意愿在 Docker 文件中跳过该步骤。

参考文献:

  1. Crontab manual.
  2. User identiier - Wiki.
  3. User ID Definition.
  4. About storage drivers.
  5. UNION FILE SYSTEM.