假设我们有一个容器绑定挂载,即-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)运行的容器进程使用:
dockremap_root_uid=$(grep dockremap /etc/subuid | cut -d ':' -f 2)
dockremap_root_gid=$(grep dockremap /etc/subgid | cut -d ':' -f 2)
user_uid=1000
user_gid=1000
dockremap_user_uid=$((dockremap_root_uid + user_uid))
dockremap_user_gid=$((dockremap_root_gid + user_gid))
setfacl --modify "u:$dockremap_root_uid:rX,g:$dockremap_root_gid:X" .
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
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
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
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
user::rw-
user:myuser:rwx
user:165536:rwx
user:166536:rwx
group::rwx
group:myuser:rwx
group:165536:rwx
group:166536:rwx
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重新映射的整个目的是使容器的帐户不映射到操作系统上的任何有效帐户,因此上述方法可能会适得其反并增加暴露风险。
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