How to Run Docker Containers as Non-Root User: Create User in Dockerfile

Run Docker Containers as Non-Root User

In this blog, I have explained detailed steps to run Docker containers as non-root user by creating a custom user in the Dockerfile.

What is a Non-Root User?

A non-root user is a standard user with limited permissions on system resources. They have no privileges other than the permissions given to them.

For example, unlike a root user, a non-root user cannot create, modify, or delete files and folders in another user’s home directory once they have permission on their home directory.

By default, the non-root users cannot use sudo , but can be configured to use sudo for specific commands.

Running Applications with Non-Root User

Before we look at the hands-on example, let’s understand the requirements and configurations for running applications inside the container using a non-root user.

When we say applications, it could be Java, Node.js, Ruby, Python, etc

One key thing to remember is the non-root user should have access to the application code and related files. It is similar to running an application as a non-root user in a Linux system.

For example, let’s say you want to run a Java application in a Docker container and the Java application code and related configurations will be placed in the /app directory.

In this case, the non-root user you create should have the necessary read/write permissions to the app directly.

The non-root user will not have any other privileged access to system-related files unless you specifically add permissions.

Now that we understand the basics, let’s look at a practical hands-on example of creating a Docker image with a non-root user.

Create a Docker Image with a Non-Root User

To demonstrate non-root users, we will create a Nginx Docker image from scratch.

We will create a Dockerfile with a custom non-root user and add the necessary permissions for that user to run the Nginx service in the container.

Create a Dockerfile and copy the below Dockerfile content

FROM nginx:alpine

RUN addgroup -g 1001 -S nginxgroup && adduser -u 1001 -S -G nginxgroup nginxuser

RUN mkdir -p /var/run/nginx /var/cache/nginx/client_temp /var/cache/nginx/proxy_temp /var/cache/nginx/fastcgi_temp /var/cache/nginx/scgi_temp /var/cache/nginx/uwsgi_temp \
    && chown -R nginxuser:nginxgroup /var/cache/nginx /var/run/nginx /var/log/nginx /etc/nginx /usr/share/nginx/html

RUN sed -i 's/listen       80;/listen       8080;/g' /etc/nginx/conf.d/default.conf
RUN sed -i 's/\/var\/run\/nginx.pid/\/var\/run\/nginx\/nginx.pid/g' /etc/nginx/nginx.conf

USER nginxuser

EXPOSE 8080

CMD ["nginx", "-g", "daemon off;"]

Let’s understand this Dockerfile and the key configurations involved in creating a non-root user.

  1. This Dockerfile uses nginx as the base image.
  2. It creates a group nginxgroup with GID 1001 and a user nginxuser with UID 1001 and give the non-root user ownership of Nginx directories. Creating user and group with IDs is particularly useful when you run this image on Kubernetes. By explicitly setting the UID and GID in the Docker image, you can align these IDs with the securityContext settings in Kubernetes.
  3. Then, it creates the required temporary directories for Nginx and assigns them to the non-root user so that Nginx can write its data on the required directories.
  4. For example, the /var/cache/nginx/client_temp directory stores temporary HTTP request data sent and received by Nginx. /var/run/nginx will hold the PID created by Nignx during its startup (explained in step 6) .
  5. The SED command changes Nginx to listen on port 8080 because the ports below 1024 are privileged ports, which means only users with root permission can use them by default.
  6. During startup, Nginx writes its PID to /var/run directory by default. The second SED command changes the PID file location to /var/run/nginx because the non-root user has no permission on the/var/run directory. By creating and changing /var/run/nginx directory permission to a non-root user, Nginx can save the PID file in it.
  7. Then, it switches the user to a non-root user nginxuser because if we don’t switch to the non-user, it will run as a root user by default.
  8. And it exposes port 8080 for external access.
  9. Finally, the CMD command tells Docker to start Nginx in the foreground. It daemon off keeps Nginx running and makes sure it doesn’t go into the background, which would cause the container to stop.

Now, use the following command to build the Docker image.

docker build -t nginx-non-root:1.0.0 .

Note: Assigning a specific ID to user and group is typically helpful if you want to run this container in Kubernetes as non root user using securityContext.

Also, if you don’t specify an ID, it will assign a random ID to user and group which will cause error when deploying in Kubernets if the correct ID is not specified.

Also, there are ways like setcap to make use of privileged ports by non-root users by assigning the CAP_NET_BIND_SERVICE capability to an executable, but it is not recomended due to security concerns.

Run Docker Container as non-root User

Now that the image is built, we will run the container and test it to see if it is running as a non-user user.

Use the below command to run the Docker image in a container.

docker run -d -p 8080:8080 nginx-non-root:1.0.0

Run the following command to check if the container is running

docker ps

If your container is running, check the current user using the docker inspect command

docker inspect -f '{{.Config.User}}' f5510bccfd39

Replace f5510bccfd39 with your Docker container ID.

This command will show you the current user of the container

checking the current user using docker inspect command

Or you can also use the exec command to access the shell of the container

docker exec -it f5510bccfd39

Then use the whoami command to check the current user as shown below

checking the current user using whoami command

You can see the container is running as a non-root user nginxuser

Now, If you want to run the non-root container as a root user, run the following command

For this, we will run the Docker image in a container in interactive mode

docker run -u 0 -it nginx-non-root:1.0.0 /bin/sh

The -u 0 option tells the container to run as the root user, 0 is the UID of the root user.

checking the current user

UID 0 (User ID 0) is reserved for the root user, who is the superuser or administrator with full access to all commands and files on the system

Conclusion

In this blog, you have learned about non-root user and how to create a non-root user for an application.

Also, you have learned about its importance and how it lowers security risk.

In a different blog we will look at how we can run a Kubernetes pod and containers using non-non users using securityContext.

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like