给定一个容器绑定堆,即 -v <host bind>:<container bind> 与默认的 docker 用户重新映射,我也遇到了访问被拒绝类型的错误,关键细节是:
mounting "<host bind>" to rootfs at "<container bind>" caused: stat <host bind>: permission denied: unknown.
显然 docker 正在尝试统计 <host bind> 但缺少权限,并且“未知”帐户名部分是因为 docker 重新映射的 uid 没有在 /etc/passwd 中设置条目。
我通过确保<host bind> 的父目录允许目录执行权限解决了我的问题。例如。在用于绑定挂载的目录的父目录中:
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 执行权限的含义,它提供“搜索”权限,更好地解释为 (wikipedia: File-system permissions):
在文件名已知的情况下访问文件内容和元信息的能力
大多数使用较弱的 umask 0002 的安装不会遇到此问题,因为其他默认情况下具有执行权限,但我运行了一个更安全的设置,我不会让系统上的每个帐户都可以访问我的主目录,其他没有 exec 或 read。无论如何,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 传播。
一旦我做了上述类似的事情,以下工作没有错误,容器从它自己的角度看到 root 访问:
$ 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 root ID 所有,并且由于扩展 ACL,我的普通用户仍然可以访问:
$ 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 remap 的全部目的是使容器的帐户不映射到操作系统上的任何有效帐户,因此上述可能会适得其反并增加曝光率。