BLOG

Technical

Run a Docker container as a non-root (unprivileged) user

Running a docker container as a non-root user is an important security feature that many example sites and references don't seem to document or at least recommend.

It is very important to restrict the container process in your production environments. Following this type of configuration will allow you to execute your production container as an unprivileged user.

To accomplish the task you need to make sure:

  • Ensure a user account and group exists on the host machine
  • Match the account and group id on the inside of your container
  • Beware of the ports your container is going open in your container
Set user and group locally

I recommend setting up a generic system account on your host machine. For example running the command:

adduser --system [--uid XXX] [--no-create-home] docker-runner

will add a system user with the optional user id and optionally you can specify not to create a home directory. The main objective is to have a regular user account that is only assigned to running the container, without root access to the host system.

As far as the group access, you can limit the group further or add the system account to a read only access to the directory(ies) that the container volumes will be accessing.

Match the ID's in your Dockerfile to the IDs of your host

When you are creating your custom image you want to add a local account and group and have their ID's match the IDs of the host machine.

Consider the following Dockerfile example:

FROM httpd:latest
EXPOSE 8080
RUN addgroup --gid 1199 itgroup &&\
  useradd -r -u 199 -g itgroup apacheuser &&\
  usermod -a -G www-data apacheuser &&\
  chown -R apacheuser:itgroup /usr/local/apache2
RUN ls -lah /usr/local/apache2
USER apacheuser
                    

These commands will create a custom image from the latest apache container, create a group (itgroup), an internal apacheuser with ID 199, and we will assign ownership of the main apache files to our new internal user.

Finally any execution of this specific container, will be executed as apacheuser, internally and user 199 in your host. You will confirm this on the host, when you run ps -fu or using your monitoring tool.

Ensure the ports opened inside the container can be opened by a non-root user

It may seem obvious, but when you are dealing with permissions and ports, sometimes you forget the simplest things.

Ports lower than 1024 are "priviledged" ports and by convention only root accounts are allowed to open them. In our case example, apache will open port 80 and potentially port 443 (https). To accomplish the goal or running the container as a standard user, you must configure apache to run on another port and when running your container based on your image, you can bind the expected port to your configured internal port. Note, if you attempt to execute the container to the default port, the container will not start.

For example, I recommend changing the configuration of apache to listen on a different port

#Inside the apache httpd.conf file
Listen 8080
# You can also apply this change in a virtual host entry
# You may also specify a port for https
#
Listen 8443

Finally, in your container build instruction, ie: docker-compose.yml file, you can expose the expected port to the container's internal port.

version: '3'
services:
   app:
    image: httpd:custom
    ports:
      - "80:8080"
      - "443:8443"
    volumes:
      - /path/to/your/updated.conf:/usr/local/apache2/conf/httpd.conf
      - /path/to/your/ssl-updated.conf:/usr/local/apache2/conf/extra/httpd-ssl.conf

After completing these overall steps, you will have a container that is executed with a regular account, not as root.

Written by Alberto Ochoa
November 20, 2019
@ajozz13