83

I am running my application in a Docker container as a non-root user. I did this since it is one of the best practices. However, while running the container I mount a host volume to it -v /some/folder:/some/folder . I am doing this because my application running inside the docker container needs to write files to the mounted host folder. But since I am running my application as a non-root user, it doesn't have permission to write to that folder

Question

Is it possible to give a nonroot user in a docker container access to the hosted volume?

If not, is my only option to run the process in docker container as root?

Anthony
  • 33,838
  • 42
  • 169
  • 278

4 Answers4

53

There's no magic solution here: permissions inside docker are managed the same as permissions without docker. You need to run the appropriate chown and chmod commands to change the permissions of the directory.

One solution is to have your container run as root and use an ENTRYPOINT script to make the appropriate permission changes, and then your CMD as an unprivileged user. For example, put the following in entrypoint.sh:

#!/bin/sh

chown -R appuser:appgroup /path/to/volume
exec runuser -u appuser "$@"

This assumes you have the runuser command available. You can accomplish pretty much the same thing using sudo instead.

Use the above script by including an ENTRYPOINT directive in your Dockerfile:

FROM baseimage

COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/bin/sh", "entrypoint.sh"]
CMD ["/usr/bin/myapp"]

This will start the container with:

/bin/sh entrypoint.sh /usr/bin/myapp

The entrypoint script will make the required permissions changes, then run /usr/bin/myapp as appuser.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • I think this would work. To make this more general, is there a way to replace `/path/to/volume` with the parameters given to `-v` command when running the container? For example if container is run with `-v /some/path:/some/path` then the `chown` command in entrypoint.sh becomes `chown -R appeaser:appuser /some/path`. Rather than hardcoding a volume name in entrypoint.sh – Anthony Sep 08 '16 at 20:03
  • 2
    There's no way within the container to introspect available volume paths, but you could pass in an environment variable (`docker run -v /some/path:/some/path -e MYVOLUME=/some/path ...`), and then use `$MYVOLUME` in your `ENTRYPOINT` script. – larsks Sep 08 '16 at 20:05
  • ugh. I am getting an error `runuser` not found. How can I get that command? I am using ubuntu image.. – Anthony Sep 08 '16 at 20:19
  • Like I said, you can instead use `sudo`. As in `sudo -u appuser somecommand`. Or with a little work you could just use `su`, although I find the syntax less useful. – larsks Sep 08 '16 at 20:58
  • I realized that my app requires certain environment variables that exist on the root user. Without this environment variables my app won't start as expected. I asked a separate question for this if you have any ideas: http://stackoverflow.com/questions/39402396/how-to-set-copy-all-environment-variables-from-root-user-to-another-specific-use Thanks – Anthony Sep 09 '16 at 01:17
  • 2
    @larsks "have your container run as root" defies the purpose of a non-root container. – 13013SwagR Aug 30 '19 at 14:58
  • 2
    @13013SwagR I disagree. I think starting your container as `root` and then switching to a non-`root` user in your `ENTRYPOINT` script gets you *exactly* the same level of security. In either case, your service is not running as the `root` user. – larsks Aug 31 '19 at 18:22
  • This is great, but can you still pass in argument to your app in run-time? – somenickname Nov 08 '19 at 01:17
  • If you design your entrypoint script appropriately, sure. Nothing here changes the way Docker works. – larsks Nov 08 '19 at 03:06
  • 2
    @larsks - I must (politely) disagree with your disagreement. I'm no expert here but a quick Google turned up (https://pythonspeed.com/articles/root-capabilities-docker-security/ for details) which explains why starting as root and then de-escalating inside the container _does not_ give the same level of security as you'd get by specifying a non-root user via the `USER` keyword in a Dockerfile. – nickform Aug 13 '20 at 12:43
  • FYI: I turned this security discussion to another question: ( https://stackoverflow.com/questions/65574334/docker-is-it-safe-to-switch-to-non-root-user-in-entrypoint ). So far the answers seem to suggest that while it isn't the same level of security, it is an accepted approach. – Voy Jan 06 '21 at 09:59
23

There will throw error if host env don't have appuser or appgroup, so better to use a User ID instead of user name:

inside your container, run

appuser$ id

This will show:

uid=1000(appuser) gid=1000(appuser) groups=1000(appuser)

From host env, run:

mkdir -p /some/folder
chown -R 1000:1000 /some/folder
docker run -v /some/folder:/some/folder [your_container]

inside your container, check

ls -lh

to see the user and group name, if it's not root, then it's should worked.

James Yang
  • 1,306
  • 4
  • 15
  • 25
  • Is there something we can do if `appuser` is not installed? – bananabrann Mar 17 '20 at 15:20
  • 2
    @bananabrann `appuser` is not a tool, but some username, like `admin` etc., decided by your container's `USER admin` instruction. – James Yang Apr 05 '20 at 09:19
  • Oh I’m dumb —I didn’t make the distinction it was app” “user” and not the new word “appuser” – bananabrann Apr 05 '20 at 18:11
  • @bananabrann it's alright, I've made the same mistake – deFreitas Dec 30 '22 at 00:06
  • 1
    Is this really possible even if on the host there is no user / group with this ID? Sounds strange – MDickten Jun 12 '23 at 09:52
  • This doesn't work for me using "Docker version 20.10.21, build 20.10.21-0ubuntu1~20.04.2". I can see that the mount point folder gest created and gets the correct user and permissions, if I start the image without "-v". But as soon as I run the image and mount a volume on the folder, it gets the uid:gid combination of the folder on the host machine. – mhvelplund Aug 16 '23 at 13:39
12

In the specific situation of using an image built from a custom Dockerfile, you can do the following (using example commands for a debian image):

    FROM baseimage
    ...
    RUN useradd --create-home appuser
    USER appuser
    RUN mkdir /home/appuser/my_volume
    ...

Then mount the volume using

-v /some/folder:/home/appuser/my_volume

Now appuser has write permissions to the volume as it's in their home directory. If the volume has to be mounted outside of their home directory, you can create it and assign appuser write permissions as an extra step within the Dockerfile.

Melipone
  • 511
  • 5
  • 7
0

I found it easiest to recursively apply Linux ACL (Access Control Lists) permissions on the host directory so the non root host user can access volume contents.

sudo setfacl -m u:$(id -u):rwx -R /some/folder

To check who has access to the folder:

getfacl /some/folder

Writing to the volume will create files and directories with host user id which might not be desirable for host -> container transfer. Writing can be disabled with just giving :rx permission instead of :rwx.

To enable writing, add a mirror ACL policy in a container allowing container user id full access to volume parent path.

too
  • 3,009
  • 4
  • 37
  • 51