如何解决在执行docker run命令时挂载卷出现的权限被拒绝问题?

3

我试图运行一个容器,绑定一个目录,但是出现了权限被拒绝的错误,所以容器根本没有启动。

当前目录中有一个名为main的目录,在容器中有一个目录/builder/project

david@localhost ~/contrib_archives $ docker run -v `realpath main`:/build/project builder
docker: Error response from daemon: OCI runtime create failed: container_linux.go:346: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:58: mounting \\\"/home/david/contrib_archives/main\\\" to rootfs \\\"/var/lib/docker/100000.100000/overlay2/2d14c3752819ff12891bace539ae4ef0039c42e6deb00170432c934d681b842e/merged\\\" at \\\"/build/project\\\" caused \\\"stat /home/david/contrib_archives/main: permission denied\\\"\"": unknown.
ERRO[0000] error waiting for container: context canceled 

如果我不使用-v参数运行容器,它可以正常运行(但是这样没有意义,因为我无法获取数据)。有人知道问题可能出在哪里吗?我正在使用用户命名空间。
3个回答

3
问题在于我正在使用用户名空间,但没有正确的映射。在我的发行版中,文件/etc/subuid包含以下内容:
david:100000:65536

但我必须让它看起来像这样:

david:1000:1
david:100000:65536

这样我的uid就会在容器内被映射为root。

一个不错的指南似乎是这个https://www.jujens.eu/posts/en/2017/Jul/02/docker-userns-remap/


2

假设我们有一个容器绑定挂载,即-v <host bind>:<container bind>,并且使用默认的docker userns重新映射,那么我也会遇到访问被拒绝的错误,其中关键细节如下:

mounting "<host bind>" to rootfs at "<container bind>" caused: stat <host bind>: permission denied: unknown.

显然docker试图检查<host bind>的状态,但缺少权限,“unknown”帐户名称部分是因为docker重新映射的uid在/etc/passwd中没有设置条目。

我通过确保<host bind>所在的父目录允许目录执行权限来解决了我的问题。例如,在bind mount目录的父目录中:

setfacl -m u:<userns-remap-uid>:X,g:<userns-remap-gid>:X .

其中.是`./.`的父目录。

请参阅Container with mounts fails to start with --userns-remap=default option #28859

通常被忽视的问题是Unix上对于目录的执行权限含义,它提供了“搜索”权限,更好地解释为(参见维基百科: 文件系统权限):

如果文件名已知,则可以访问文件内容和元信息。

大多数使用弱umask 0002的安装程序不会遇到此问题,因为默认情况下,其他用户有执行权限,但我运行了更安全的设置,不允许系统上的每个帐户都访问我的主目录,而且其他用户既没有执行权限也没有读取权限。无论如何,Docker容器绑定挂载代码无法获取宿主机绑定目录源的元数据,因为由于父目录上缺少访问权限,它缺少获取元数据的权限。可以说,Docker可以在执行绑定挂载后仅在容器初始化期间切换到重新映射的uid,以避免需要父目录的执行访问权限。

作为一个更大的示例,假设您想将目录绑定到容器中,请尝试以下操作。例如,给定docker-root作为主机绑定目录:

mkdir /tmp/test-dock-userns-remap
chmod o-rwx /tmp/test-dock-userns-remap
mkdir /tmp/test-dock-userns-remap/docker-root
chmod o-rwx /tmp/test-dock-userns-remap/docker-root

请注意,docker-root 父目录的权限位为 770

$ cd /tmp/test-dock-userns-remap
$ stat --format='type=%F perm=%A(%a)' .
type=directory perm=drwxrwx---(770)

如果在/etc/docker/daemon.json中使用默认设置"userns-remap": "default",那么以下操作可以使docker-root可绑定挂载,并能够被以root或低特权用户(例如容器内的uid 1000)运行的容器进程使用:

# Get the base/root rempped UID and GID
dockremap_root_uid=$(grep dockremap /etc/subuid | cut -d ':' -f 2)
dockremap_root_gid=$(grep dockremap /etc/subgid | cut -d ':' -f 2)
# Calculate the remapped standard normal user
user_uid=1000
user_gid=1000
dockremap_user_uid=$((dockremap_root_uid + user_uid))
dockremap_user_gid=$((dockremap_root_gid + user_gid))
# Ensure the parent dir premits the remapped root UID with rX access or else a mounting rootfs "permission denied: unknown." issue can occur
setfacl --modify "u:$dockremap_root_uid:rX,g:$dockremap_root_gid:X" .
# Set ACL to permit read and/or write access where needed
setfacl --recursive --modify "u:$dockremap_root_uid:rwX,g:$dockremap_root_gid:rwX" ./docker-root
setfacl --recursive --default --modify "u:$dockremap_root_uid:rwX,g:$dockremap_root_gid:rwX" ./docker-root
# Allow non-priv uid 1000 in container to have access
setfacl --recursive --modify "u:$dockremap_user_uid:rwX,g:$dockremap_user_gid:rwX" ./docker-root
setfacl --recursive --default --modify "u:$dockremap_user_uid:rwX,g:$dockremap_user_gid:rwX" ./docker-root
# Ensure own user has access
setfacl --recursive --modify "u:$(id -u):rwX,g:$(id -u):rwX" ./docker-root
setfacl --recursive --default --modify "u:$(id -u):rwX,g:$(id -u):rwX" ./docker-root
# Set ownership to the remapped docker root UID
sudo chown --recursive "$dockremap_root_uid:$dockremap_root_gid" ./docker-root

请注意,大多数人可能希望将扩展ACL设置为默认ACL。这可以确保在目录内创建的文件(无论是由主机进程还是容器进程创建)都具有相同的默认ACL传播。
一旦像上面那样做了某些事情,接下来的操作就可以在容器从其自己的角度看到根访问权限而没有错误。
$ sudo docker container run --rm --volume $(pwd)/docker-root:/root alpine sh -c "touch /root/test; stat -c 'name=%n inode=%i user=%U(%u) group=%G(%g) perm=%A(%a)' /root/test"
name=/root/test inode=262164 user=root(0) group=root(0) perm=-rw-rw----(660)

但是在主机上,该目录由重新映射的Docker根ID拥有,并且由于扩展ACLs,仍然可以在我的普通用户下访问:

$ stat -c 'name=%n inode=%i user=%U(%u) group=%G(%g) perm=%A(%a)' docker-root/test
name=docker-root/test inode=262164 user=UNKNOWN(165536) group=UNKNOWN(165536) perm=-rw-rw----(660)
$ getfacl docker-root/test
# file: docker-root/test
# owner: 165536
# group: 165536
user::rw-
user:myuser:rwx     #effective:rw-
user:165536:rwx         #effective:rw-
user:166536:rwx         #effective:rw-
group::rwx          #effective:rw-
group:myuser:rwx        #effective:rw-
group:165536:rwx        #effective:rw-
group:166536:rwx        #effective:rw-
mask::rw-
other::---

考虑到我的情况,这是有道理的:

$ grep dockremap /etc/sub{u,g}id
/etc/subuid:dockremap:165536:65536
/etc/subgid:dockremap:165536:65536

如果想要在主机上重新映射用户,请在 /etc/passwd 和 /etc/group 中创建条目。例如:

if ! getent passwd dockremap-root; then
    sudo groupadd --system --gid "$dockremap_root_gid" dockremap-root
    sudo useradd --system --no-create-home --home-dir "/var/lib/docker/$dockremap_root_uid.$dockremap_root_gid" --shell /bin/false --uid "$dockremap_root_uid" --gid "$dockremap_root_gid" --comment 'userns remap for docker root' dockremap-root
fi
if ! getent passwd dockremap-user; then
    sudo groupadd --system --gid "$dockremap_user_gid" dockremap-user
    sudo useradd --system --no-create-home --home-dir "/var/lib/docker/$dockremap_root_uid.$dockremap_root_gid" --shell /bin/false --uid "$dockremap_user_uid" --gid "$dockremap_user_gid" --comment 'userns remap for docker user' dockremap-user
fi

然而,Docker userns重新映射的整个目的是使容器的帐户不映射到操作系统上的任何有效帐户,因此上述方法可能会适得其反并增加暴露风险。


1
我认为dockremap_root_gid应该从文件/etc/subgid中读取,所以命令/行 dockremap_root_gid=$(grep dockremap /etc/subuid | cut -d ':' -f 2) 应该改为 dockremap_root_gid=$(grep dockremap /etc/subgid | cut -d ':' -f 2) - undefined
感谢 @CDuv,已经纠正了重复使用 uid 而不是 gid 的错误。 - undefined

0

我遇到了类似的问题,我所做的是给目标和源路径提供完全相同的路径,只是为了测试,结果成功了。

请尝试以下操作并查看是否能够运行,不用担心在容器中创建相同的路径,它会自动创建,请运行以下命令一次,将路径编辑为您的主文件夹,并告诉我是否适用于您:

docker run -it -v /path/to/main/:/path/to/main builder


这看起来非常奇怪。 - undefined
@LtWorf 我知道它可以这样做,但你试过像这样运行吗?并且它有效果吗? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接