I have a docker with a php application on it
I have a share volume, for example
/home/me/dev/site <=> /var/www/site
I can write something in my host, it will be sync with the container
if I launch
sudo docker exec test touch /var/www/site/test.txt
It works
But if my server is trying to create a file as www-data this is not working because of the rights.
Is there a way to give access to my shared volumes to www-data ?
I am using boot2docker
(Bind-mounted) volumes in Docker will maintain the permissions that are set on
the Docker host itself. You can use this to set the permissions on those
files and directories before using them in the container.
Some background;
Permissions in Linux are based on user and group ids ('uid' / 'gid'). Even
though you see a user- and group name as owner, those names aren't actually
important in Linux, they are only there to make it easier for you to see who's the owner of a file (they are looked up from the /etc/passwd file).
You can set any uid/gid on a file; a user doesn't have to exist when setting those permissions. For example;
touch foobar && sudo chown 1234:5678 foobar && ls -la foobar
# UID and GID are set to 1234 / 5678, even though there's no such user
-rw-rw-r-- 1 1234 5678 0 Mar 25 19:14 foobar
Checking permissions (inside and outside a container)
As mentioned above, Docker maintains ownership of the host when using
a volume. This example shows that permissions and ownership in the volume are the
same outside and inside a container;
# (First create a dummy site)
mkdir -p volume-example/site && cd volume-example
echo "<html><body>hello world</body></html>" > site/index.html
# Permissions on the Docker host;
ls -n site
# total 4
# -rw-rw-r-- 1 1002 1002 38 Mar 25 19:15 index.html
# And, permissions inside a nginx container, using it as volume;
sudo docker run --rm -v $(pwd)/site:/var/www nginx ls -n /var/www
# total 4
# -rw-rw-r-- 1 1002 1002 38 Mar 25 19:15 index.html
Setting the permissions
As explained, a user doesn't have to exist in order to use them, so even if
we don't have a www-data user on the Docker host, we can still set the correct
permissions if we know the "uid" and "gid" of that user inside the container;
Let's see what the uid and gid of the www-data user is inside the container;
sudo docker run --rm nginx id www-data
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
First check the state before changing the permissions. This time we
run the nginx container as user www-data;
sudo docker run \
--rm \
--volume $(pwd)/site:/var/www \
--user www-data nginx touch /var/www/can-i-write.txt
# touch: cannot touch `/var/www/can-i-write.txt': Permission denied
Next, set the permissions on the local directory, and see if we are able to write;
sudo chown -R 33:33 site
sudo docker run \
--rm \
--volume $(pwd)/site:/var/www \
--user www-data nginx touch /var/www/can-i-write.txt
Success!
Add the following lines to your dockerfile and rebuild your image
RUN usermod -u 1000 www-data
RUN usermod -G staff www-data
Related
I want to provide write access to a non-root user in a docker container for a volume mounted on the host. I have the same non-root user and group inside container and host as well.
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.
Also, I this mounted folder permission is got changed inside the container automatically.
I have given assigned below user and group as an owner to this folder on the host:
“nonrootuser1:nonrootgroup1”
For 'rootful' docker it works fine, but if it's rootless inside the container it is showing below user and group as an owner to this folder:
“root:nobody”
Steps to reproduce this:
Add user and group on the host
groupadd -g 1015 nonrootgroup1
useradd -u 1099 nonrootuser1
Create dir which will be assigned to osquery groupadd on the host:
sudo su
mkdir -p /var/osquery/sock
echo "hello world" > /var/osquery/sock/file.txt
chown root:nonrootgroup1 /var/osquery/sock
chmod g+s /var/osquery/sock/
Create Dockerfile in the home directory :
FROM alpine:3.14
RUN addgroup -g 1015 -S nonrootgroup1
RUN adduser -D nonrootuser1 -u 1099
USER nonrootuser1:nonrootgroup1
CMD ["tail", "-f", "/dev/null"]
Run docker container :
docker run --rm -d --name sample-app -v /var/osquery/sock:/var/osquery/sock $(docker build . -q)
Check the user id of the container :
dmytro#dmytro:~/dev/sample-image$ docker exec sample-app id
uid=1099(nonrootuser1) gid=1015(nonrootgroup1) groups=1015(nonrootgroup1)
check the permissions to the /var/osquery/sock dir inside the container :
Directory /var/osquery/sock is assigned to group 'nobody' instead of 'nonrootgroup1' group
dmytro#dmytro:~/dev/sample-image$ docker exec sample-app ls -lah /var/osquery
total 12K
drwxr-xr-x 3 root root 4.0K Dec 28 10:47 .
drwxr-xr-x 1 root root 4.0K Dec 28 10:47 ..
drwxr-s--- 2 nobody nobody 4.0K Dec 28 09:34 sock
Host details are mentioned below:
Kernel Version: 5.4.0-135-generic
Operating System: Ubuntu 20.04.5 LTS
Storage Driver: overlay2
Please suggest.
Thanks!
I tried to setup in the daemon.json "userns-remap" config, but docker daemon doesn't start with it, because it lacks permission to create dockremap user and group.
I have a website that I'm trying to run inside Docker container. To avoid file permission errors, I change the ID of user www-data to match my user id on host. This setup worked for me, but after some time it stopped working, and I cannot find reason why.
FROM php:7.4-fpm
ARG HOST_UID
# Not relevant lines skipped
RUN usermod -u $HOST_UID www-data
RUN groupmod -g $HOST_UID www-data
Then I build container with:
docker compose build --build-arg HOST_UID=$(id -u)
All files on host belong to my user:
After starting container, I can see that ID is changed:
However, inside container they belong to root:
Could this be because docker daemon runs as root and mounts the volume as such?
I should have noted that this Dockerfile installs Supervisord (to run my background scripts), so I cannot run whole container with my user – this fixes permissions but nothing can be installed inside container.
You should use the -u flag for docker compose run.
Here an example:
setup:
mkdir mihai
touch mihai/test.txt
docker-compose.yml
version: '3'
services:
test:
image: "alpine:latest"
volumes:
- "./mihai:/mihai"
Run normal
docker compose run test ls -l /mihai
gives output:
total 0
-rw-r--r-- 1 root root 0 Sep 18 11:39 test.txt
Run as user:
docker compose run -u "$(id -u):$(id -g)" test ls -l /mihai
gives output:
total 0
-rw-r--r-- 1 501 dialout 0 Sep 18 11:39 test.txt
If you use a custom image where you actually declare user and group then you will see a nicer output than 501 and dialout.
I'm trying to understand some weird behavior that I'm seeing on docker. I want to run a container which writes cached data to a mounted volume from the host which I can later reuse for future executions of the container and that can also be read from the host.
On the host, I see that my user has the user id 1000:
# This is on the host
$ id
uid=1000(juan-munoz) gid=1000(juan-munoz) groups=1000(juan-munoz)...
I'm running the container without any special flags for the user so it runs as root:
# This is on the container
$ id
uid=0(root) gid=0(root) groups=0(root)
Also, there is already a user with id=1000:
# The image is provided by AWS and apparently it includes a user with this ID
$ id -nu 1000
ec2-user
I have mounted a directory with -v /some/local/directory:/var/mounted. Locally, this directory is owned by my user (id=1000):
# On the host
ls -ld /some/local/directory
drwx------ 2 juan-munoz juan-munoz 4.0K Jun 21 16:21 /some/local/directory
In the container, if I go check the directory I see that it's currently owned by root. This is the first part that I don't understand.
# ls -ld /var/mounted
drwx------ 2 root root 4096 Jun 22 03:36 /var/mounted
Why root? I would have expected that with the mount the user id would be respected.
If I then try to change the user to 1000, this happens:
# Inside the container
$ chown -R 1000:1000 /var/mounted
ls -ld /var/mounted
drwx------ 2 ec2-user 1000 4096 Jun 22 03:36 /var/mounted
Which looks good to me, but when I do that, if I look at what happened on the host I see the following:
# On the host
ls -ld /some/local/directory
drwx------ 2 100999 100999 4.0K Jun 21 20:36 /some/local/directory
So either the host or the container has a messed up owner. If I chown 1000:1000 on the host, the container sees it as root, if I chown 1000:1000 on the container, the host sees it as 100999.
What am I doing wrong here?
Repro steps
$ mkdir $HOME/testing
$ docker run -it --name=ubuntu-test --entrypoint="/bin/bash" --rm -v $HOME/testing:$HOME/testing ubuntu:latest
# Inside the container
$ cd /home/myusername/testing
$ touch file.txt
root#b98b7a5445e3:/home/juan-munoz/testing# ls -l
total 0
-rw-r--r-- 1 root root 0 Jun 30 23:52 file.txt
# Outside the container
$ ls -l $HOME/testing
total 0
-rw-r--r-- 1 juan-munoz juan-munoz 0 Jun 30 16:52 file.txt
Observed behavior:
In some computers, from outside the container, the file is owned by the local user. In others, it's owned by root.
Expected behavior:
To be consistent across computers
I encountered the same issue under Ubuntu 22.04 with Docker Desktop installed. After the Docker Desktop is uninstalled and the Docker Engine is reinstalled, things just go the way I want them to: having consistent ownership of mounted directories between the host and the container.
Here is my use case: I want to start an Ubuntu 20.04 container and use it as the compiling environment for my application. The Dockerfile is:
FROM ubuntu:20.04
ARG USER=docker
ARG UID=1000
ARG GID=1000
# create a new user with same UID & PID but no password
RUN groupadd --gid ${GID} ${USER} && \
useradd --create-home ${USER} --uid=${UID} --gid=${GID} --groups root && \
passwd --delete ${USER}
# add user to the sudo group and set sudo group to no passoword
RUN apt update && \
apt install -y sudo && \
adduser ${USER} sudo && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# prevent tzdata to ask for configuration
RUN DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt -y install tzdata
RUN apt install -y build-essential git cmake
# setup default user when enter the container
USER ${UID}:${GID}
WORKDIR /home/${USER}
The building script:
#!/bin/bash
set -e
UID=$(id -u)
GID=$(id -g)
docker build --build-arg USER="$USER" \
--build-arg UID="$UID" \
--build-arg GID="$GID" \
--tag "ubuntu-env" \
--file ./Dockerfile \
--progress=plain \
.
Since I use it as the compiling environment, the whole home directory is mounted for convenience:
#!/bin/bash
set -e
docker run -it \
--name "build-env" \
--user "${USER}" \
--workdir "${PWD}" \
--env TERM=xterm-256color \
--volume="$HOME":"$HOME" \
--detach \
"ubuntu-env" \
/bin/bash
Somehow, with the Docker Desktop installed, the owner of the mounted home directory is always the root rather than the host user. After it is uninstalled, the mounted directory now gets the expected owner in the container, aka the host user.
so - I'm building a docker container using apache2 on the inside - but I'm having issues with permissions and I don't know how to solve it...
If I run the container with no --user specifications, it runs fine - but I want to externally be able to assign it to a user and limit that user to only reading and writing to a particular directory (the one I map in with -v).
However when I run the docker container with --user to that user, the external permissions seem all correct - but internally - apache2 then goes bang saying it can't bind to port 80 - and other things fail writing to code internally.
How do I map users - like mapping ports or volumes. What I want to achieve is that the container externally only has the permissions of user X in the outer system - but internally it's root, running as the root user id and so on.
One possible way to achieve externally only has the permissions of user X in the outer system - but internally it's root is the use of sudo.
Create an 'internal' user which the UID should be same as the 'external' user:
FROM alpine
RUN apk add --no-cache apache2 sudo \
&& adduser -S newuser -s /bin/ash -D -H -u 1000 \
&& echo "newuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/newuser \
&& chmod 0440 /etc/sudoers.d/newuser
USER newuser
EXPOSE 80
ENTRYPOINT ["bin/ash","-c","sudo httpd -DFOREGROUND"]
Example on the host an there's a directory dedicated to a user:
drwx------ 2 ec2-user ec2-user 24 Aug 29 11:53 html
The directory can be mount as usual:
docker built -t myapache2 .
docker run -it -v ${PWD}/html:/html -p 8080:80 myapache2
docker exec -t 31c6cc627813 ls -l /html
total 4
-rwx------ 1 ec2-user 1000 58 Aug 29 03:53 index.html
curl localhost:8080
<html><body><h1>It works!</h1></body></html>
Alpine is used here but you can of course use any distro that suits your need.
NOTE: httpd site & folder permission is another topic and is not cover here.
I'm starting containers from my docker image like this:
$ docker run -it --rm --user=999:998 my-image:latest bash
where the uid and gid are for a system user called sdp:
$ id sdp uid=999(sdp) gid=998(sdp) groups=998(sdp),999(docker)
but: container says "no"...
groups: cannot find name for group ID 998
I have no name!#75490c598f4c:/home/myfolder$ whoami
whoami: cannot find name for user ID 999
what am I doing wrong?
Note that I need to run containers based on this image on multiple systems and cannot guarantee that the uid:gid of the user will be the same across systems which is why I need to specify it on the command line rather than in the Dockerfile.
Thanks in advance.
This sort of error will happen when the uid/gid does not exist in the /etc/passwd or /etc/group file inside the container. There are various ways to work around that. One is to directly map these files from your host into the container with something like:
$ docker run -it --rm --user=999:998 \
-v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro \
my-image:latest bash
I'm not a fan of that solution since files inside the container filesystem may now have the wrong ownership, leading to potential security holes and errors.
Typically, the reason people want to change the uid/gid inside the container is because they are mounting files from the host into the container as a host volume and want permissions to be seamless across the two. In that case, my solution is to start the container as root and use an entrypoint that calls a script like:
if [ -n "$opt_u" ]; then
OLD_UID=$(getent passwd "${opt_u}" | cut -f3 -d:)
NEW_UID=$(stat -c "%u" "$1")
if [ "$OLD_UID" != "$NEW_UID" ]; then
echo "Changing UID of $opt_u from $OLD_UID to $NEW_UID"
usermod -u "$NEW_UID" -o "$opt_u"
if [ -n "$opt_r" ]; then
find / -xdev -user "$OLD_UID" -exec chown -h "$opt_u" {} \;
fi
fi
fi
The above is from a fix-perms script that I include in my base image. What's happening there is the uid of the user inside the container is compared to the uid of the file or directory that is mounted into the container (as a volume). When those id's do not match, the user inside the container is modified to have the same uid as the volume, and any files inside the container with the old uid are updated. The last step of my entrypoint is to call something like:
exec gosu app_user "$#"
Which is a bit like an su command to run the "CMD" value as the app_user, but with some exec logic that replaces pid 1 with the "CMD" process to better handle signals. I then run it with a command like:
$ docker run -it --rm --user=0:0 -v /host/vol:/container/vol \
-e RUN_AS app_user --entrypoint /entrypoint.sh \
my-image:latest bash
Have a look at the base image repo I've linked to, including the example with nginx that shows how these pieces fit together, and avoids the need to run containers in production as root (assuming production has known uid/gid's that can be baked into the image, or that you do not mount host volumes in production).
It's strange to me that there's no built-in command-line option to simply run a container with the "same" user as the host so that file permissions don't get messed up in the mounted directories. As mentioned by OP, the -u $(id -u):$(id -g) approach gives a "cannot find name for group ID" error.
I'm a docker newb, but here's the approach I've been using in case it helps others:
# See edit below before using this.
docker run --rm -it -v /foo:/bar ubuntu:20.04 sh -c "useradd -m -s /bin/bash $USER && usermod -a -G sudo $USER && su - $USER"
I.e. add a user (useradd) with a matching name, make it sudo (usermod), then open a terminal with that user (su -).
Edit: I've just found that this causes a E: List directory /var/lib/apt/lists/partial is missing. - Acquire (13: Permission denied) error when trying to use apt. Using sudo gives the error -su: sudo: command not found because sudo isn't install by default on the image I'm using. So the command becomes even more hacky and requires running an apt update and apt install sudo at launch:
docker run --rm -it -v /foo:/bar ubuntu:20.04 sh -c "useradd -m -s /bin/bash $USER && usermod -a -G sudo $USER && apt update && apt install sudo && passwd -d $USER && su - $USER"
Not ideal! I'd have hoped there was a much more simple way of doing this (using command-line options, not creating a new image), but I haven't found one.
1) Make sure that the user 999 has right privilege on the current directory, you need to try something like this in your docker file
FROM
RUN mkdir /home/999-user-dir && \
chown -R 999:998 /home/999-user-dir
WORKDIR /home/999-user-dir
USER 999
try to spin up the container using this image without the user argument and see if that works.
2) other reason could be permission issue on the below files, make sure your group 998 has read permission on these files
-rw-r--r-- 1 root root 690 Jan 2 06:27 /etc/passwd
-rw-r--r-- 1 root root 372 Jan 2 06:27 /etc/group
Thanks
So, on your host you probably see your user and group:
$ cat /etc/passwd
sdp:x:999:998::...
But inside the container, you will not see them in /etc/passwd.
This is the expected behavior, the host and the container are completely separated as long as you don't mount the /etc/passwd file inside the container (and you shouldn't do it from security perspective).
Now if you specified a default user inside your Dockerfile, the --user operator overrides the USER instruction, so you left without a username inside your container, but please notice that specifying the uid:gid option means that the container have the permissions of the user with the same uid value in the host.
Now for your request not to specify a user in the Dockerfile - that shouldn't be a problem. You can set it on runtime like you did as long as that uid matches an existing user uid on the host.
If you have to run some of the containers in privileged mode - please consider using user namespace.