Skip to content

Add ability to mount volume as user other than root #2259

@mingfang

Description

@mingfang

Use case: mount a volume from host to container for use by apache as www user.
The problem is currently all mounts are mounted as root inside the container.
For example, this command
docker run -v /tmp:/var/www ubuntu stat -c "%U %G" /var/www
will print "root root"

I need to mount it as user www inside the container.

Activity

jpetazzo

jpetazzo commented on Oct 18, 2013

@jpetazzo
Contributor

If you chown the volume (on the host side) before bind-mounting it, it will work.
In that case, you could do:

mkdir /tmp/www
chown 101:101 /tmp/www
docker run -v /tmp/www:/var/www ubuntu stat -c "%U %G" /var/www

(Assuming that 101:101 is the UID:GID of the www-data user in your container.)

Another possibility is to do the bind-mount, then chown inside the container.

crosbymichael

crosbymichael commented on Nov 28, 2013

@crosbymichael
Contributor

@mingfang Will chown not work for you ?

bfirsh

bfirsh commented on Jan 17, 2014

@bfirsh
Contributor

It would be useful to have a shortcut for this. I often find myself writing run scripts that just set the permissions on a volume:

https://github.com/orchardup/docker-redis/blob/07b65befbd69d9118e6c089e8616d48fe76232fd/run

aldanor

aldanor commented on Feb 18, 2014

@aldanor

What if you don't have the rights to chown it?

iwinux

iwinux commented on Jun 26, 2014

@iwinux

Would a helper script that chown the volume solve this problem? This scirpt can be the ENTRYPOINT of your Dockerfile.

SvenDowideit

SvenDowideit commented on Jul 8, 2014

@SvenDowideit
Contributor

Can I say no - forcing users to add a helper script that does

#!/bin/sh
chown -R redis:redis /var/lib/redis
exec sudo -u redis /usr/bin/redis-server

(thanks @bfirsh for your eg)

is pretty terrible.

It means that the container has to be started as root, rather than running as the intended redis user. (as @aldanor alluded to )

and it means a user can't do something like:

docker run -v /home/user/.app_cfg/ -u user application_container application :(

SvenDowideit

SvenDowideit commented on Jul 8, 2014

@SvenDowideit
Contributor

There is one way to make it work, but you need to prepare ahead of time inside your Dockrfile.

RUN mkdir -p /var/lib/redis ; chown -R redis:redis /var/lib/redis
VOLUME ["/var/lib/redis"]
ENTRYPOINT ["usr/bin/redis-server"]
USER redis

(I didn't test this example, I'm working on a chromium container that then displays on a separate X11 container that .... )

tianon

tianon commented on Jul 10, 2014

@tianon
Member

And of course that method only works for direct new volumes, not bind
mounted or volumes-from volumes. ;)

thaJeztah

thaJeztah commented on Jul 14, 2014

@thaJeztah
Member

Additionally, multiple containers using volumes-from will have different uid/gid for the same user, which complicates stuff as well.

frankamp

frankamp commented on Aug 7, 2014

@frankamp

@SvenDowideit @tianon that method doesn't work either. Full example:

FROM ubuntu
RUN groupadd -r redis    -g 433 && \
useradd -u 431 -r -g redis -d /app -s /sbin/nologin -c "Docker image user" redis 
RUN mkdir -p /var/lib/redis
RUN echo "thing" > /var/lib/redis/thing.txt
RUN chown -R redis:redis /var/lib/redis
VOLUME ["/var/lib/redis"]
USER redis
CMD /bin/ls -lah /var/lib/redis

Two runs, with and without a -v volume:

bash-3.2$ docker run -v `pwd`:/var/lib/redis voltest 
total 8.0K
drwxr-xr-x  1 root root  102 Aug  7 21:30 .
drwxr-xr-x 28 root root 4.0K Aug  7 21:26 ..
-rw-r--r--  1 root root  312 Aug  7 21:30 Dockerfile
bash-3.2$ docker run  voltest 
total 12K
drwxr-xr-x  2 redis redis 4.0K Aug  7 21:30 .
drwxr-xr-x 28 root  root  4.0K Aug  7 21:26 ..
-rw-r--r--  1 redis redis    6 Aug  7 21:26 thing.txt
bash-3.2$ 
andrewmichaelsmith

andrewmichaelsmith commented on Nov 13, 2014

@andrewmichaelsmith

We're hitting an issue that would be solved by this (I think). We have an NFS share for our developer's home directories. Developers want to mount /home/dev/git/project in to Docker but cannot because we have Root Squash enabled.

This forbids root from accessing /home/dev/git/project so when I try and run docker mounting /home/dev/git/project I get an lstat permission denied error.

cpuguy83

cpuguy83 commented on Nov 13, 2014

@cpuguy83
Member

@frankamp This is because docker's current preference is to not modify host things which are not within Docker's own control.

Your "VOLUME" definition is being overwritten by your -v pwd`:/var/lib/reds`.
But in your 2nd run, it is using a docker controlled volume, which is created in /var/lib/docker. When the container starts, docker is copying the data from the image into the volume, then chowning the volume with the uid:gid of the dir the volume was specified for.

I'm not sure there is much that can be done here, and unfortunately bind mounts do not support (as far as I can tell) mounting as a different uid/gid.

389 remaining items

snowdream

snowdream commented on Jan 21, 2025

@snowdream

I need ability to mount volume as user other than root too.

I need to run docker container as non-root user,but volumes are always mouted as root.

so, I have no permission to write to the volumes.

mfhepp

mfhepp commented on Jan 21, 2025

@mfhepp

Again, my analysis of the problem and a solution is described here:

Solution: mamba-org/micromamba-docker#407 (comment)

Full Analysis: mamba-org/micromamba-docker#407

It is in the context of micromamba-docker but the underlying issue is generic and the solutions could be adjusted to other scenarios.

polarathene

polarathene commented on Jan 21, 2025

@polarathene
Contributor

Usually people who want to have a container with a non-root user are doing so as a "best practice" but I don't know if they give it much thought.

As @mfhepp points to, you might as well use Docker or Podman in rootless mode instead of rootful. Which will have the internal container user/groups mapped to something else on the host. There's some drawbacks to doing so, but that shouldn't be a surprise for those wanting non-root in a container.

Usually your concern is with someone compromising the container and being able to escape out of it to the host as the user running in the container. That requires capabilities that often aren't available by default to the containers root user. Using a non-root user will drop all capabilities and you'd then rely on granting a binary with any capabilities you'd need (via setcap) which can be insecure to do if those capabilities are the dangerous ones you are worried about a container root user having.

If the container user (root or non-root) has access to the Docker socket, and Docker is running rootful (default), then it doesn't matter if non-root when that socket can be used. If the socket isn't proxied and allows full control over the Docker daemon, the user can start a new container and become root through that, mount the host filesystem, etc. Now as the root user (even if that's only in a container for now, but access to the host filesystem where they can manipulate any file as root they can do the very thing you were trying to prevent.

I have seen projects create non-root users and advise users to mount the docker socket with the permissions for that non-root container user to leverage the socket. So please think carefully what you do with a non-root user, for the most part it's convenience to dropping all caps on the default root container user, or an attempt to prevent a user from making a container vulnerable because they grant additional caps / use --privileged in an attempt to solve something and the internet told them that'd do the trick.


Do what @mfhepp suggested and push such users towards rootless containers (these still run as root within the container).

Otherwise you're often chasing after something in an effort to be secure but introduce new problems which tend to get workarounds that can make the container less secure (setcap when enforcing a capability that should otherwise be optional for example prevents someone who knows what their doing from securing their container properly).

Liken it to complicated password rules which leads users to make decisions that are less secure defeating the purpose entirely. A password for example can be very secure with just lowercase text, no numbers or special characters or other rules needed, just entropy. Depending on how the password is stored by a service detailed snail summons slim lab coat (which provides 48 bits of entropy) is an example of what a secure password can look like, which on the surface goes against best practice advice users would be more familiar with seeing.

Clockwork-Muse

Clockwork-Muse commented on Jan 21, 2025

@Clockwork-Muse

Docker rootless currently (or last I checked) causes problems because in contrast to Podman with its --userns keep-id, there isn't a way to have the same uid on the host and in the container. This is a problem if you need any host access - often the case for mounting devices or displays, and completely breaks any devcontainers where the source is on the host and bind-mounted.

devnoname120

devnoname120 commented on Jan 22, 2025

@devnoname120

@polarathene What if I want/need to mount the Docker socket inside a container without risking to pwn my entire system in case of a misconfiguration or an exploit in the socket proxy? Without being an expert about CAP_ stuff

polarathene

polarathene commented on Jan 22, 2025

@polarathene
Contributor

This is a problem if you need any host access - often the case for mounting devices or displays, and completely breaks any devcontainers where the source is on the host and bind-mounted.

Ah that's unfortunate. I've not used rootless much myself, but I did observe with Podman that I could have root in the container or any other container ID and a different one on the host and it'd be seamlessly mapped. This was with --uidmap not the other similar features.

For example --uidmap '+5000:@100:1' would map the container UID 5000 to the host UID 100 (IIRC with a bind-mount while the host ownership would be 100 on the host in the container it appears as 5000). This option uses @ to get the relative /etc/subuid offset in rootless which has an additional level of indirection (your host UID could be 1000 for example as the first host UID to map in /etc/subuid, so @1000 maps to 0), whereas rootful --uidmap can directly map container ID or range to one on the host.

I assume there is already a feature request for the equivalent in Docker, if not perhaps someone should raise that if they're not comfortable using Podman for that kind of requirement.

polarathene

polarathene commented on Jan 22, 2025

@polarathene
Contributor

@devnoname120

TL;DR:

  • Running a container as non-root with the intention to leverage the docker socket is not helping you secure access to it.
  • Using a socket proxy should be safer than direct socket access, especially if you need to provide socket access to more than one container.
  • Leveraging the docker socket is often for features that provide convenience, while understanding security well is typically not convenient. There's a tradeoff. Not much can be done if a user prefers convenience at the expense of risk without learning how to minimize that risk.

What if I want/need to mount the Docker socket inside a container without risking to pwn my entire system in case of a misconfiguration or an exploit in the socket proxy?

Rootless should not be mounting a rootful socket AFAIK, so it should be limited to the scope of rootless? I've only used rootless with Podman personally, so you might have to verify yourself. However if you use Docker Desktop on any platform this takes an alternative approach by isolating Docker into a VM, and IIRC official docs describe this as effectively rootless (escaping the container leaves you stuck in the VM), but for an improvement over that Docker Desktop offers ECI mode (Enhanced Container Isolation) as a more restrictive choice and not even the docker socket can be mounted without additional config to permit conditions for when that is allowed.

Thus for local usage, if you're using Docker Desktop that probably meets your security concerns when you configure that properly instead of hoping each image does something the way you want instead.

For production, you can leverage rootless if that brings more comfort, otherwise defer to whomever understands how to secure a server properly as Docker alone is not sufficient, and you should have a decent understanding of the images you trust and deploy. I understand many don't have the time or interest to learn that.

If you were implying that running a container as non-root user magically protects you from exploits with the socket you would be mistaken:

# UID 1337 could be whatever UID you like
# GID 1001 here matches the GID on the host which is needed to interact with the socket if not root
# Notice that `:ro` is irrelevant here, it prevents modifications to the socket file itself,
# not traffic sent through it.
# `jq` is installed on the host to filter the JSON response for convenience of this example

$ docker run --rm  \
  --user 1337:1001 \
  --volume /var/run/docker.sock:/var/run/docker.sock:ro \
  fedora curl --silent --unix-socket /var/run/docker.sock http://localhost/version \
  | jq -r .Platform.Name

Docker Desktop

As you can see I can use the socket still. docker CLI uses the socket to interact with the Docker daemon too, so you can pull whatever image you like, volume mount what you like from the host system and run as whatever user in another container.

Providing access to the socket just requires the ability to send a request through it, so if there's Python, JS or whatever else on the system that an attacker could exploit to run some script or command of their own they can perform that operation even if there's no shell or package manager on the container.


As for the socket proxy, I don't know what your question is for that. Using a proxy should be far safer than complete access to the socket, especially if you provide that to multiple containers of varying trust.

You instead have one container that you trust to access the socket and it locks down what can actually be used by the other containers proxying through it. Like anything a vulnerability could appear and be exploited yes, but you can minimize that by choosing an image for this functionality that is purely focused on this functionality, no redundant shell, package manager, or other commands, with a binary that is compiled rather than an interpreter like python or nodejs

I recently heard about https://github.com/wollomatic/socket-proxy (see their related locked down config example](https://github.com/wollomatic/socket-proxy#example-for-proxying-the-docker-socket-to-traefik)) which is probably a good example of that. I've not had time to give it a proper look yet, I have my own with a Caddyfile which functions similarly.

Using a unix socket as a listener you can share access via a volume to only the containers that should have the right to access the socket proxy, or you can use a private subnet. My approach supports multiple unix sockets for different access rights, something that is extra work for TCP listeners to support restricting client access correctly.

Without being an expert about CAP_ stuff

If you're just on the user end, you drop all caps like the linked project compose.yaml shows. Should the image service actually require some caps to run (like it would if running as non-root), instead of the workaround the image maintainer would do to support non-root, they'd document the actual capabilities required to keep, which you as a user configure with --cap-add / cap_add should you really care about the security aspect to the level.

The non-root practice is mostly convenience in that sense so you don't have to think about this (but then you get issues like this one with volumes, or worse). The other worry it helps defend against tends to require you to do non-standard things you often shouldn't be doing.


If anyone wants to correct me on this I'd appreciate it, but AFAIK preferring a non-root user often pragmatically makes little real difference to security these days for the users it's intended to protect, if not actually reducing it when they run into caveats and resort to workarounds that reduce security.

dgutson

dgutson commented on Jan 22, 2025

@dgutson

Happily moved to podman, unsubscribing from this issue. I recommend others to do likewise.

dwt

dwt commented on Jan 22, 2025

@dwt

Just came here to say the same thing: Docker is ok to use on personal machines, but for deployments on servers Podman is far superior.

With Podman I can bind mount my files into the container and specify exactly as what UID I want them to be mounted. This completely solves all permission problems.

And of course you also get rid of the daemon running as root that all container users need to talk to. Happy Days.

devnoname120

devnoname120 commented on Jan 22, 2025

@devnoname120

@dgutson Will do too

@polarathene Thank you for your detailed explanations, I'm not sure how to say this politely but I'm not a noob and I think you misunderstood my use case. Hopefully it will help others though

oerp-odoo

oerp-odoo commented on Jan 22, 2025

@oerp-odoo

@polarathene there is also a case to run as non root, not just for security. Its very annoying, when mounted data becomes owned by root, when you initiate container from some other user. For example all data is mounted from user1 home directory, but some data still becomes owned by root, which causes access errors.

Clockwork-Muse

Clockwork-Muse commented on Jan 22, 2025

@Clockwork-Muse

This is a problem if you need any host access - often the case for mounting devices or displays, and completely breaks any devcontainers where the source is on the host and bind-mounted.

Ah that's unfortunate. I've not used rootless much myself, but I did observe with Podman that I could have root in the container or any other container ID and a different one on the host and it'd be seamlessly mapped. This was with --uidmap not the other similar features.

For example --uidmap '+5000:@100:1' would map the container UID 5000 to the host UID 100 (IIRC with a bind-mount while the host ownership would be 100 on the host in the container it appears as 5000). This option uses @ to get the relative /etc/subuid offset in rootless which has an additional level of indirection (your host UID could be 1000 for example as the first host UID to map in /etc/subuid, so @1000 maps to 0), whereas rootful --uidmap can directly map container ID or range to one on the host.

I assume there is already a feature request for the equivalent in Docker, if not perhaps someone should raise that if they're not comfortable using Podman for that kind of requirement.

There are at least some applications where, the last time I checked, the uids actually had to match internally/externally, because of how it tried to open them or something. This was mostly around x11 sockets, and I haven't touched it in a while... So it didn't matter if you mapped the uid this way, because the application went "I need to do this thing with the current uid, and pass that in a request", which of course didn't exist on the host (or represented a completely wrong one or similar).

jdmarshall

jdmarshall commented on Apr 9, 2025

@jdmarshall

@dgutson i don’t think podman has a solution for this for created volumes, do they?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @MDCore@djmaze@SvenDowideit@bfirsh@dwt

        Issue actions

          Add ability to mount volume as user other than root · Issue #2259 · moby/moby