Is there a good and secure way to allow non-root user to start a docker image? - docker

I have a scenario where I want to let non-root users start a docker image and run it. It's a very simple image - we have a stupid proprietary piece of software that insists on blocking a certain port, making concurrent runs of that software impossible. I was thinking to fix that with docker.
Problem is that normal users (it's a part of a compile process) should be able to spin this up. How do I go about that in a sane and secure fashion?

If the desired docker command is static, create a simple start script, store in in /usr/local/bin and make it executeable. Make an entry in /etc/sudoers to allow desired users to run this command with sudo without a password.
E.g create file /usr/local/bin/alpine.docker:
#! /bin/sh
docker run --rm -it alpine sh
Make the script secure (non root user should not be able to edit it):
sudo chown root:root /usr/local/bin/alpine.docker
Set reasonable permissions and make it executeable:
sudo chmod 554 /usr/local/bin/alpine.docker
Create an entry in /etc/sudoers with visudo:
username ALL = (root) NOPASSWD: /usr/local/bin/alpine.docker
Now the user username can run sudo alpine.docker without a password.
Warning:
Don't add users to group docker if they should not have root privileges.
Note:
For this solution, you need to install sudo. But the user username does not need to be member of group sudo.
Note 2:
A similar setup is possible with policykit / pkexec. But I am not familar with it.

I prefer https://stackoverflow.com/a/50876910/348975 solution, but an alternative is to use something like docker machine https://stackoverflow.com/a/50876910/348975 or dind https://hub.docker.com/_/docker/ to create a brand new throwaway docker.
Then you set the environment variable export DOCKER_HOST=tcp://${IP_ADDRESS}:2376 and can use that docker without root.
This is probably not necessary for OPs case, but where it would come in handy is if the image had to be run with arbitrary privileges:
docker container run --privileged ...
Can you escalate from --privileged to root? I don't know you can not. I would rather assume you can and isolate the docker.
Since OP has one simple static predetermined docker command that OP is confident can not be escalated, I feel https://stackoverflow.com/a/50876910/348975 is the preferred solution.
If you are paranoid, you can use both https://stackoverflow.com/a/50876910/348975 and my solution together.

Create the docker group and add your user to the docker group.
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
Log out and log back in so that your group membership is re-evaluated.
You can follow docker documentation for more details manage-docker-as-a-non-root-user

Related

Should I run things inside a docker container as non root for safety?

I already run my docker build and docker run without sudo. However, when I launch a process inside a docker container, it appears as a root process on top on the host (not inside the container).
While it cannot access the host filesystem because of namespacing and cgroups from docker, is it still more dangerous than running as a simple user?
If so, how is the right way of running things inside docker as non root?
Should I just do USER nonroot at the end of the Dockerfile?
UPDATE:
root it also needed for building some things. Should I put USER on the very top of the Dockerfile and then install sudo together with other dependencies, and then use sudo only when needed in the build?
Can someone give a simple Dockerfile example with USER in the beggining and installing and using sudo?
Running the container as root brings a lot of risks. Although being root inside the container is not the same as root on the host machine (some more details here) and you're able to deny a lot of capabilities during container startup, it is still the recommended approach to avoid being root.
Usually it is a good idea to use the USER directive in your Dockerfile after you install some general packages/libraries. In other words - after the operations that require root privileges. Installing sudo in a production service image is a mistake, unless you have a really good reason for it. In most cases - you don't need it and it is more of a security issue. If you need permissions to access some particular files or directories in the image, then make sure that the user you specified in the Dockerfile can really access them (setting proper uid, gid and other options, depending on where you deploy your container). Usually you don't need to create the user beforehand, but if you need something custom, you can always do that.
Here's an example Dockerfile for a Java application that runs under user my-service:
FROM alpine:latest
RUN apk add openjdk8-jre
COPY ./some.jar /app/
ENV SERVICE_NAME="my-service"
RUN addgroup --gid 1001 -S $SERVICE_NAME && \
adduser -G $SERVICE_NAME --shell /bin/false --disabled-password -H --uid 1001 $SERVICE_NAME && \
mkdir -p /var/log/$SERVICE_NAME && \
chown $SERVICE_NAME:$SERVICE_NAME /var/log/$SERVICE_NAME
EXPOSE 8080
USER $SERVICE_NAME
CMD ["java", "-jar", "/app/some.jar"]
As you can see, I create the user beforehand and set its gid, disable its shell and password login, as it is going to be a 'service' user. The user also becomes owner of /var/log/$SERVICE_NAME, assuming it will write to some files there. Now we have a lot smaller attack surface.
Why you shouldn't run as root
While other people have pointed out that you shouldn't run images as root, there isn't much information here, or in the docs about why that is.
While it's true that there is a difference between having root access to a container and root access on the host, root access on a container is still very powerful.
Here is a really good article that goes in depth on the difference between the two, and this issue in general:
https://www.redhat.com/en/blog/understanding-root-inside-and-outside-container
The general point is that if there is a malicious process in your container, it can do whatever it wants in the container, from installing packages, uploading data, hijacking resources, you name it, it can do it.
This also makes it easier for a process to break out of the container and gain privileges on the host since there are no safeguards within the container itself.
How and when to run as non-root
What you want to do is run all your installation and file download/copy steps as root (a lot of things need to be installed as root, and in general it's just a better practice for the reasons I outline below). Then, explicitly create a user and grant that user the minimum level of access that they need to run the application. This is done through the use of chmod and chown commands.
Immediately before your ENTRYPOINT or CMD directive, you then add a USER directive to switch to the newly created user. This will ensure that your application runs as a non-root user, and that user will only have access to what you explicitly gave it access to in previous steps.
The general idea is that the user that runs the container should have an absolute minimum of permissions (most of the time the user doesn't need read, write, and execute access to a file). That way, if there is a malicious process in your container, its behavior will be as restricted as possible. This means that you should avoid creating or copying in any files, or installing any packages as that user too, since they would have complete control over any resources they create by default. I've seen comments suggesting otherwise. Ignore them. If you want to be in line with security best practices, you would then have to go back and revoke the user's excess permissions, and that would just be awful and error prone.
You can check out the CIS benchmark for Docker and they recommend to use non-root and this is one of the "Compliance" checks. Adding USER non-root at the bottom should suffice or you can use '-u' with your RUN command to specify user as well.
https://www.cisecurity.org/benchmark/docker/
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
Running your containers as non-root gives you an extra layer of security. By default, Docker containers are run as root, but this allows for unrestricted container activities.

How to securely add an entry into a docker container's /etc/passwd for the uid set with docker's --user option

Problem
For a docker image (alpine based) that is supposed to run as non-root I have two requirements:
I have to mount a FUSE filesystem inside the docker container
The users of the docker image are able to set the UID/GID of the docker
user with docker run --user {uid}:{gid}
FUSE's fusermount command requires a valid entry for the user in /etc/passwd, otherwise it won't mount the filesystem. Given that I don't know the the UID/GID of the user at build time I can't call adduser at build time. And I can't do it at runtime either, as the user then doesn't have the appropriate privileges.
Solutions found
So far I have found two solutions that both feel not appropriate/secure
1. Make /etc/passwd writable
When adding chmod 555 /etc/passwd to the Dockerfile I can then do at runtime
echo "someuser:x:${my_uid}:$(id -g)::/tmp:/sbin/nologin" >> /etc/passwd
This does the job for fusermount. Unfortunately I did not find a way to make change the passwd file back to read-only at runtime and without that I have security concerns that someone might be able to misuse this to gain root rights back. While I could not find a simple way to use the open passwd file for some exploit (while I was able to add/modify password & configurations directly in /etc/passwd for all users and then change users via login, alpine did not allow this for user root (neither via login nor via su). But I guess there are folk out there more clever than me, and somehow the whole solution feels like a quite dirty hack. Does anyone have specific ideas how a writeable passwd file inside a container could be used for getting inappropriate rights inside the container?
2. Replace requirement #2 with two additional environment variables
By introducing DUID and DGID as environment variables and set USER to some newly added non-root user inside the Dockerfile I found a solution with the help of sudo & /etc/sudoers: In a launch script that I use as entrypoint I can call sudo adduser/addgroup for the given DUID/DGID and then launch the actual program with the user specified via sudo -u someuser someprog.
Except for the fact that the whole setup became quite ugly, I disliked the fact the user's of my docker image could no longer use the regular docker run --user option, as this would break the sudo configuration.

Dockerfile hide variables (user creation)

I am trying to generate a docker image from Ubuntu 18.04.
To administrate the container I am creating a default user :
# set default user
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo
USER docker
My problem is I would like to set a secured password on it, and my dockerfile is intended to be versioned with Git.
So my question is : is there a way to load variables in dockerfile from a .env file or anything else ?
I have seen an option on the docker run command, but not for the docker build, am I wrong ?
Anything you write in the Dockerfile can be trivially retrieved in plain text with docker history. Any file in the image can be very easily retrieved by anyone who can run any docker command. There is no way around either limitation.
Do NOT try to set user passwords for your Docker images like this. In most cases it shouldn't be necessary to formally "log in" to a container at all. Let the container run the single application process it needs to run, and don't try to set up an ssh daemon, sudo, or other things you'd have in a more complete server environment.
(You shouldn't usually need a shell inside a container; you don't for other kinds of processes like your Nginx server, for example. If you do, you can get one with docker exec, and if your main process runs as a non-root user, you can add a -u root option to be root in that shell. Again, you can't prevent an end user from being able to do this.)
If you are using a standalone container, then you can use a script with the variables and run docker RUN, or ENTRYPOINT to run the script. This would contain your password information, and then you can carry on with the build of your image.
If you are using Docker Swarm, you can use secrets, more information on the following link, and differences if you are using Windows or Linux are explained as well.
https://docs.docker.com/engine/swarm/secrets/

Docker: How to disable root user in container?

Delivering images to customers they usually make
$ docker-compose up -d
to deploy those in production. It is easy to get root and to see / modify all file quite easy:
$ docker-compose exec <service> /bin/sh
/bin/sh(root)# ...
How can I avoid for customers to get full access rights to all files as root when running the container. Maybe this is not possible at all in Docker but then it should at least be more complicated for users to get full access to anything inside the container.
Is there a best practice to intrdoce non root accounts in containers?
You can’t. You can always run
docker exec -u 0 (container ID) sh
to get a root shell. (Assuming the image has a shell, but almost all do.)
Also remember that anyone who can run any docker command can edit any file on the host, and from there can trivially become root, and can prod around in /var/lib/docker to their heart’s content.
It’s generally considered good practice to set containers to run as non-root by RUN adduser to create a user using the base distribution’s tools and then a Dockerfile USER directive, but an operator can override this at runtime if they really want to.

How to allow users to run (but not manage) docker containers?

I would like to allow users of my docker containers (on a shared Linux server) to do
docker run
But not any of the other commands: build, inspect, ...
My use case is that of wrapped applications inside containers.
I was wondering if there is a best practice for this?
Typically, you could use a sudoers configuration in order to allow to execute docker command only for docker run.
See "How can I use docker without sudo?" for the theory.
Make sure your user is not from the docker group, and use sudo to execute only docker run as root.
See as an example "sudo / su to user in a specific group"

Resources