Make an existing Docker container rootless - docker

I'm not asking how to create a rootless container from scratch. Rather, I've been given some software deployed as pre-built Docker container images that run as root by default. I need to modify these containers so they can be deployed on Kubernetes, which means I need to make these containers rootless. To be clear, I DO NOT have the source to these containers so I can't simply rebuild them from scratch.
I've found plenty of articles about building rootless containers in general, but they all assume you're building your containers from scratch. I've spent hours searching but can't find anything about modifying an existing container to be rootless.
I realize this might be a very open question, but I don't know all the things I need to take into consideration. Two things I've been able to gather is adding a line such as USER 1000 to Dockerfile, and adjusting ownership and permissions on certain files and folders. Beyond that, I'm not sure what I need to do.

Create users in the container and switch users;
Add a new user, named user;
Let this user have root privileges;
Set its password to password;
After the Container is started, log in as user and go directly to the user's home directory;
Put the following code snippet in the Dockerfile.
RUN useradd --create-home --no-log-init --shell /bin/bash user \
&& RUN adduser user sudo \
&& RUN echo 'user:password' | chpasswd
USER user
WORKDIR /home/user
Use fixuid to modify the uid and gid of non-root users in the container;
After creating a non-root user with the above code, the user's uid and gid are generally 1000:1000.
Docker and the host share a set of kernel, and there is still only one set of uid and gid controlled by the kernel. In other words, we execute the process as a newly created docker user (uid 1000) in the container, and the host will think that the process is executed by a user with a uid of 1000 on the host, and this user may not necessarily be our account, which is equivalent to us A user who has replaced someone else with an impostor makes it difficult to trace back to the real user.
To solve this problem, you can specify the uid as the user's uid when adding the user, such as 1002;
RUN addgroup --gid 1002 docker && \
adduser --uid 1002 --ingroup docker --home /home/docker --shell /bin/sh --gecos "" docker
A better solution is to use fixuid to switch the uid when the container starts:
RUN useradd --create-home --no-log-init --shell /bin/bash user\
&& adduser user sudo \
&& echo 'user:password' | chpasswd
RUN USER=user && \
GROUP=docker && \
curl -SsL https://github.com/boxboat/fixuid/releases/download/v0.4.1/fixuid-0.4.1-linux-amd64.tar.gz | tar -C /usr/local/bin -xzf - && \
chown root:root /usr/local/bin/fixuid && \
chmod 4755 /usr/local/bin/fixuid && \
mkdir -p /etc/fixuid && \
printf "user: $USER\ngroup: $GROUP\n" > /etc/fixuid/config.yml
USER user:docker
ENTRYPOINT ["fixuid"]
At this time, you need to specify the uid and gid when starting the container. The command is as follows:
docker run --rm -it -u $(id -u):$(id -g) image-name bash

Related

docker: bind mount host folder: how to manage dockerfiles for different host uid:gid

On my host my uid and gid are:
$ id ravi
uid=1000(ravi) gid=100(users)
i want to mount a folder host_test with uid:gid = 1000:100 into docker
I have the following docker and its id
$ docker run --rm -it python:3.7.9-buster /bin/bash
root#48d7d3122ffd:/# id root
uid=0(root) gid=0(root) groups=0(root)
root#48d7d3122ffd:/#
After going through various solutions, the best solution i see is to create a user and group same as host on docker image.
i.e
Dockerfile_mypc
FROM python:3.7.9-buster
RUN groupadd --gid 100 users \
&& useradd --uid 1000 --gid users --create-home ravi
WORKDIR /home/ravi
USER ravi
this now solves the problem of permissions
but when I want to use this Dockerfie on aws ubuntu instance, I see there the user ubuntu has 1000:1000 has uid and gid. So again i have to change the Dockerfile
Dockerfile_aws
FROM python:3.7.9-buster
RUN groupadd --gid 1000 users \
&& useradd --uid 1000 --gid users --create-home ubuntu
WORKDIR /home/ubuntu
USER ubuntu
I am just looking what is the best practise. Should i make Dockerfiles for each system where ever i am going to setup like
Dockerfile_mypc
Dockerfile_aws
or is there any way i can do this with single dockerfile
Because i have to mount the host folder from my pc to docker in development and also mount host folder from aws ubuntu instance to docker in production

Using current user when running container in docker-compose

Is there a way to execute or login as current user to a bash of specific container . I tried running docker-compose exec -u $USER phoenix bash but it says unable to find user raz: no matching entries in passwd file
I tried another way by adding a useradd command in a dockerfile.
FROM elixir:latest
ARG USER_ID
ARG GROUP_ID
RUN addgroup --gid $GROUP_ID raz
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID raz
USER raz
RUN apt-get update && \
apt-get install -y postgresql-client && \
apt-get install -y inotify-tools && \
apt-get install -y nodejs && \
curl -L https://npmjs.org/install.sh | sh && \
mix local.hex --force && \
mix archive.install hex phx_new 1.5.3 --force && \
mix local.rebar --force
COPY . /app
WORKDIR /app
COPY ./entrypoint.sh /entrypoint.sh
RUN ["chmod", "+x", "/entrypoint.sh"]
ENTRYPOINT ["/entrypoint.sh"]
but when I run docker-compose build I get a permission denied error when running the apt-get commands.
I also look for gosu as a step down root user but it seems complicated.
Is it possible for added user in Dockerfile command to have same permission as my current user?
I'm running WSL2 btw.
This question is pretty interesting. Let me begin with a short explanation:
Understanding the problem
In fact the user that exists inside container will be valid only inside the container itself. What you're trying to do is to use a user that exists outside a container, i.e. your docker host, inside a container. Unfortunately this movement can't be done in a normal way.
For instance, let me try to change to my user in order to get this container:
$ docker run -it --rm --user jon ubuntu whoami
docker: Error response from daemon: unable to find user jon: no matching entries in passwd file.
I tried to run a classic ubuntu container inside my docker host; Although the user exists on my local machine, the Docker image says that didn't find the user.
$ id -a
uid=1000(jon) gid=1001(jon) groups=1001(jon),3(sys),90(network),98(power),108(vboxusers),962(docker),991(lp),998(wheel),1000(autologin)
The command above was executed on my computer, proving that "jon" username exists.
Making my username available inside a container: a docker trick
I suppose that you didn't create a user inside your container. For demonstration I'm going to use the ubuntu docker image.
The trick is to mount both files responsible for handling your user and group definition inside the container, enabling the container to see you inside of it.
$ docker run -it --rm --volume /etc/passwd:/etc/passwd:ro --volume /etc/group:/etc/group:ro --user $(id -u) ubuntu whoami
jon
For a more complete example:
$ docker run -it --rm --volume /etc/passwd:/etc/passwd:ro --volume /etc/group:/etc/group:ro --user $(id -u):$(id -g) ubuntu "id"
uid=1000(jon) gid=1001(jon) groups=1001(jon)
Notice that I used two volumes pointing to two files? /etc/password and /etc/group?
Both I mounted read only (appending ":ro") just for safety.
Also notice that I used the id -u, which brings me the user id (1000 on my case), forcing the user id for being the same of mine defined on my /etc/password file.
Caveat
If you try to set the username to jon rather than the UID, you're going to run into an issue:
$ docker run -it --rm --volume /etc/passwd:/etc/passwd:ro --volume /etc/group:/etc/group:ro --user jon ubuntu whoami
docker: Error response from daemon: unable to find user jon: no matching entries in passwd file.
This happens because the docker engine would try to change the username before mouting the volumes and this should exists before running the container. If you provide a numeric representation of the user, this one doesn't needs to exist within the container, causing the trick to work;
https://docs.docker.com/engine/reference/run/#user
I hope being helpful. Be safe!
Building on top of the answer by Joepreludian, focusing on docker-compose:
You can use the user: and volumes: options in the compose file. For example:
my-service:
image: ubuntu:latest
user: ${MY_UID}:${MY_GID}
volumes:
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
and define these variables where you are starting your compose:
MY_UID="$(id -u)" MY_GID="$(id -g)" docker-compose up

start a docker container in user mode with read-write access permission

I try to start a new container from ubuntu 18.04 docker image. I do as follows:
pull the docker image
docer pull ubuntu:18.04
create a new container
docker run -ti -v $(pwd):/home/shared --name ubuntu_test ubuntu:18.04
and then log out.
start the created container
docker start ubuntu_test
log in as root user, update OS and install vim
docker exec -ti ubuntu_test /bin/bash and apt update, apt install -y vim
then log out.
log in as non-root user
docker exec -ti -u daemon ubuntu_test /bin/bash
Then I found that I have no permission to create new files or new folders.
I do not want to log in as root user since there could be some problems with mpirun.
Is there any solution for this problem ? Thank you for any help.
It is not like you (non-root user) don't have permissions to write or read. It is that everything on the system (files/folders) belong to the root user and no other user can modify anything by default.
You can create a new user as well as home folder for that user when you are building the image and then the user will be able to modify stuff in its home (standard linux stuff).
Example Dockerfile
FROM ubuntu
RUN groupadd --gid 1000 someuser \
&& useradd --uid 1000 --gid someuser --shell /bin/bash --create-home someuser
test with
docker build -t utest .
docker container run -it -u someuser utest /bin/bash
cd /home/someuser
touch myfile
If you need to add some other folders under that user's administration other than its home, you can use chown -R someuser:someuser <folder> which will recursively change ownership of the specified folder and everything in it to that of the new user.
Example: changing ownership of /etc folder
FROM ubuntu
RUN groupadd --gid 1000 someuser \
&& useradd --uid 1000 --gid someuser --shell /bin/bash --create-home someuser
RUN chown -R someuser:someuser /etc

how to correctly use system user in docker container

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.

How to create and run docker container with new user other than root?

I met a problem while using docker.
Now I have a ubuntu based docker container. And in the container ,the user id is root by default which is not my expectation, I suppose the user id is like abc which is another user account on the HOST OS running docker.
I have tried the following ways but all fail:
su abc;
Then running docker run xxx to bring up a container, but login the container, the user in container is still root.
Then by adding the -u flag for docker run: like :
docker run -t -i -u abc ubuntu /bin/bash
the the docker show errors unable to find user abc
Can some one tell me how to fix it?
Or does docker support run a container in which the user is a specific one than the default root?
You can create User inside Docker images. But for that you will have to extend base image. For example you can create user abc in Ubuntu as below,
FROM ubuntu:14.04
RUN apt-get update
# Replace 1000 with your user / group id
RUN export uid=1000 gid=1000 && \
mkdir -p /home/abc && \
echo "abc:x:${uid}:${gid}:Abc,,,:/home/abc:/bin/bash" >> /etc/passwd && \
echo "abc:x:${uid}:" >> /etc/group && \
echo "abc ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/abc && \
chmod 0440 /etc/sudoers.d/abc && \
chown ${uid}:${gid} -R /home/abc
USER abc
ENV HOME /home/abc
WORKDIR $HOME
CMD /bin/bash
Then you build and run it,
docker build -t abc .
docker run -it abc bash
you should see bash prompt with user abc like below,
abc#<container-hostname>:~$
According to https://medium.com/redbubble/running-a-docker-container-as-a-non-root-user-7d2e00f8ee15, this is possibly, but you can only refer to HOST users by their numerical ids. Read that helpful article for more details and a fuller explanation.

Resources