How to solve permission denied when mounting volume during docker run command?

Viewed 3728

I'm trying to run a container binding a directory and I get an error about permission being denied, so the container doesn't start at all.

In the current directory there is a directory called main and in the container there is a directory /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 

If I run the container without the -v parameter it runs fine (But it's useless since I can't get the data in).

Anyone has any idea of what the problem could be?

I'm running with user namespaces.

3 Answers

The issue was that I am running with usernamespaces and did not have the correct mapping.

In my distribution the file /etc/subuid contains this:

david:100000:65536

But I had to make it look like this:

david:1000:1
david:100000:65536

So that my own uid would be mapped to root inside the container.

A nice guide seems to be this one https://www.jujens.eu/posts/en/2017/Jul/02/docker-userns-remap/

Given a container bind mound, i.e. -v <host bind>:<container bind> with the default docker userns remapping, I also had an access denied type of error with the key details being:

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

Clearly docker is attempting to stat <host bind> but lacked permission and the "unknown" account name part is because the docker remapped uid has not entry setup in /etc/passwd.

I resolved my issue by ensuring the parent dir of <host bind> allowed directory execute permission. E.g. in the parent directory of the dir intended for the bind mount:

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

Where . is the parent of `./.

See Container with mounts fails to start with --userns-remap=default option #28859.

An often overlooked issue is the meaning of the unix execute permission for a directory, which provides "search" permissions, better explained by as (wikipedia: File-system permissions):

the ability to access file contents and meta-information if its name is known

Most installs with a weaker umask of 0002 won't run into this problem because other has execution rights by default, but I ran a more secure setup where I don't let every account on the system have access to my home dir, and other did not have exec or read. Anyhow, the docker container bind mount code could not stat the host's bind dir source because it lacked permission to get the metadata due to the missing access on the parent folder. Arguably, docker could rather perform bind mounts and then only switch to the remapped uid later on during container init to avoid needing parent dir execute access.

As a larger example, suppose you want to bind mount a dir into a container try this. E.g. given docker-root as the host bind dir:

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

Note, the docker-root parent dir perm bits are 770:

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

And with a default setting of "userns-remap": "default" in /etc/docker/daemon.json. Then the following will make docker-root bind mountable and usable by container processes run either as root or a lesser privileged user (e.g. uid 1000 in the container):

# 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

Note, most people likely want the extended ACL set as a default ACL. This ensures that files created within the directory, either by a host process, or by a container process, will have the same default ACLs propagated.

Once I did something like the above, the following works without error, with the container seeing root access from it's own perspective:

$ 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)

But on the host, the directory was owned by the remapped docker root ID and still accessible under my normal user thanks to extended 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::---

Which makes sense, given, in my case:

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

And to have remapped users named on the host, create entries in /etc/passwd and /etc/group. E.g.:

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

However, the whole point of the docker userns remap is to make the container's accounts not map to any valid account on the OS, so the above could be counterproductive and increase exposure.

I was facing the similar issue , and what i did was i gave the exact same path for target and source , just to test and that worked.

Can you please try the following and see if its able to run, do not worry about creating the same path in container , it will get created on its own just run the following once, edit the path as your main folder , and let me know if it works out for you :

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

Related