使用 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 恕我直言。
所以我从头开始,尝试为我的用例创建一个简单的概念证明。
我想要这个一般的工作流程:
- 用户 windows and/or linux 构建 Dockerfile
- 用户 运行 容器(如果可能,不是 root)
- 容器启动一个 crontab,其中 运行 脚本每分钟写入数据量
- 用户(在 linux 或 windows 上)从数据卷(不是 root)获取结果,因为权限已正确映射
我使用 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
调查结果
crontab 运行 with scrap user [OK]
UID/GID 本地用户映射到容器用户报废[OK]
导出的数据继续为 root [不正常]。
Windows / Linux [一半OK]
如果我使用绑定卷而不是命名卷,它就可以工作。但这不是所需的行为,我如何在 Win/Linux ...
上使用具有正确权限的命名卷
让我把答案分成两部分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
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 的 UID 是 1000。现在我们要在 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 文件中跳过该步骤。
参考文献:
我正在尝试一个简单的工作流程但没有成功,我花了很多时间来测试 SO 和 github 上的许多解决方案。 docker 中命名文件夹的权限和更一般的权限卷是一场噩梦 link1 link2 恕我直言。
所以我从头开始,尝试为我的用例创建一个简单的概念证明。
我想要这个一般的工作流程:
- 用户 windows and/or linux 构建 Dockerfile
- 用户 运行 容器(如果可能,不是 root)
- 容器启动一个 crontab,其中 运行 脚本每分钟写入数据量
- 用户(在 linux 或 windows 上)从数据卷(不是 root)获取结果,因为权限已正确映射
我使用 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
调查结果
crontab 运行 with scrap user [OK]
UID/GID 本地用户映射到容器用户报废[OK]
导出的数据继续为 root [不正常]。
Windows / Linux [一半OK]
如果我使用绑定卷而不是命名卷,它就可以工作。但这不是所需的行为,我如何在 Win/Linux ...
上使用具有正确权限的命名卷
让我把答案分成两部分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.
中声明为 VolumeIndeed, 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 的 UID 是 1000。现在我们要在 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 文件中跳过该步骤。
参考文献: