How to run Træfik as a non-root user ? Part 1

The goal of this tutorial is to explain an easy way to run Træfik, a modern HTTP reverse proxy and load balancer, as a non-privileged user.

If you are here, it is possibly because you want to use Træfik without running the main process (pid 1) as root or any other privileged user. The root user within a Docker container cannot in theory escalate to be root on the host machine; but many people believe that it is possible to do so.

If security is a big concern, you should stack up multiple safety mechanisms. Three best practices are:

  1. Running your process as non-privileged user within the containers. Docker lets you do this easily even though it carries constraints.
  2. Running a Docker engine with User namespaces: the userns-remap option in the Docker daemon. Just a little note about User namespaces: they were introduced as early as Linux 3.5 and are considered stable since Linux 4.3, so be sure to have an updated kernel.
  3. Stripping the container of all potentially dangerous system capabilities (Docker does this automatically to some degree) or running an hardened Linux with SELinux, AppArmor…

This article covers the first solution. The next articles will cover the others.


The technical solution will follow these four rules:

  1. Create a traefik user in the Dockerfile to run Træfik.
  2. Create an host user traefik with the same userid/groupid as the container, bearing in mind that this breaks portability.
  3. Configure Træfik to open ports > 1024 and use Docker to map the host ports 80/443 to container ports.
  4. Configure Træfik to use TLS certificates.

First create a local user on the host.

groupadd traefik
useradd -g traefik -m traefik
# Add traefik to Docker group
usermod -G docker traefik

To be able to create a traefik user in the Dockerfile, you need to use the image based on Alpine Linux distribution, and not the FROM SCRATCH one. Otherwise the commands we will add to the Dockerfile would not exist.

FROM traefik:raclette-alpine
RUN addgroup -S traefik && adduser -S -g traefik traefik
USER traefik
FYI, raclette is a popular wintertime dish in Switzerland and french Alps.
# Build your own image with
docker build -t zepouet/traefik .

Now we need to generate our certificate files and configure the Docker engine to use them. Please read Docker’s documentation about certificates https://docs.docker.com/engine/security/https/

You must copy the client certificates to a directory under the traefik home directory. These certificates are for this user. Don’t forget: having access to the Docker socket is equivalent to having root access (with a few exceptions if User namespaces are active)

DOCKER_OPTS=”--bip 172.17.42.1/16 \
--tlsverify lscacert=/root/.docker/ca.pem \
--tlscert=/root/.docker/server.pem \
--tlskey=/root/.docker/server-key.pem \
-H tcp://0.0.0.0:2376 \
-H unix:///var/run/docker.sock \
-s aufs"

Use the “ bip” option to specify the network bridge IP but you are free to adapt it with your value. We don’t remove the Unix socket because it is nice to have it when locally connected on the server.

To test the configuration, you can execute as root or traefik user.

docker --tlscacert=/home/traefik/.docker/ca.pem \
--tlscert=/home/traefik/.docker/cert.pem \
--tlskey=/home/traefik/.docker/key.pem \
-—tlsverify \
ps

Now you can add the Træfik configuration

defaultEntryPoints = [“http”]
[entryPoints]
[entryPoints.http]
address = “:2048”
##################################
# Docker configuration backend
##################################
[docker]
endpoint = “tcp://172.17.42.1:2376”
watch = true
exposedbydefault = true
# ca.pem server-key.pem server.pem
[docker.tls]
ca = “/docker-certs/ca.pem”
cert = “/docker-certs/cert.pem”
key = “/docker-certs/key.pem”
insecureskipverify = true

You just have to run Traefik on port 2048 in order to not worry about user privileges. Any port > 1024 would be fine :) And in the next step we will use the Docker magic to map host to container ports.

Concerning endpoint field configuration, you will notice that we use the IP gateway defined by “ — bip” option for Docker daemon.

Then the next step is to use the following docker-compose file to run all containers.

version: “2”
services:
  traefik:
container_name: traefik
image: zepouet/traefik
user: traefik
command: — web — logLevel=DEBUG
ports:
— “80:2048
— “9090:8080”
volumes:
— ./traefik.toml:/etc/traefik/
— /home/traefik/.docker:/docker-certs
labels:
— “traefik.enable=false”
  tomcat:
container_name: tomcat
image: tomcat
labels:
— “traefik.backend=tomcat”
— “traefik.port=8080”
— “traefik.frontend.rule=Host:tomcat.127.0.0.1.xip.io”

Be careful about user rights for the Docker volume. We put client certificates in the traefik home directory for this reason.

So now you can create and run containers :

docker-compose up -d

You could execute this command to be sure of the user account used to run Træfik.

docker exec -it traefik ps

So we see that Træfik is not root now but actually launched with the traefik user.

PID  USER    TIME COMMAND
1 traefik 0:00 traefik traefik — web — logLevel=DEBUG
33 traefik 0:00 ps

To access the Tomcat manager, you can open a browser on http://tomcat.127.0.0.1.xip.io

In the next article, we will use another Docker feature : User namespaces!