Home Posts Traefik Setup with Docker Compose

Traefik Setup with Docker Compose

Post Featured Image
Calendar Icon (date post was published) published on  05 Jan 2022

Traefik

Traefik is an edge router, reverse proxy, and load balancer that allows you — among other things — to expose docker containers (web services) to the Web in a very easy way.
Once set up, you never have to adjust its configuration again. Whenever you have a new service, you simply set some labels on that docker container and Traefik will forward requests as required/ configured. Automatic Let's Encrypt certificate generation and renewal included :)

This post briefly explains you how to setup Traefik with Docker Compose and make some basic configuration.
More details can be found in the official Traefik Docs. So look there, if you need more information about any topic or want to know what else is possible (there's a lot possible besides the things we configure in this post)!

The following diagram (taken from the Traefik docs) gives a very clear overview of what Traefik actually does: Traefik Overview

We'll start with some static configuration, followed by the simple docker compose setup, dynamic configuration and, at the end, we'll setup SSL encryption.

Static Configuration

There are three ways how to provide configuration for Traefik. Either via a configuration file, command line options, or via environment variables. We go with the first option, as it is the clearest in my opinion.

Additionally, you can provide dynamic configuration (which may, as the name suggests, dynamically change during runtime). We'll come back to that later.

For the basic configuration, create a file called traefik.yml. Important parts are explained by comments:

# Api and, additionally, a monitoring dashboard (checkout docs for more,
# disable in production if not required!).
api:
  dashboard: true
  insecure: true

# Entrypoints (ports) Traefik should listen to; here we define two: "http"
# for unencrypted traffic, and "https" for SSL-encrypted traffic (port 443).
entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"

# We define two providers
providers:
  # First, a docker provider, which allows us to enable routing to any Docker
  # container by setting some specific labels on the container. Example will
  # follow below ;).
  docker:
    endpoint: "unix:///var/run/docker.sock"
    # This ensures, that we manually have to request containers to be "added"
    # to Traefik.
    exposedByDefault: false
  # Second, a dynamic configuration file - we'll come back to that file below.
  file:
    filename: "/dynamic_conf.yml"

# For automatic Let's Encrypt certificate generation, we define a "letsencrypt"
# resolver. It may store the certificates in the defined file/storage and should
# use the http endpoint (defined above) for the http challenge (i.e., for
# generating/ requesting the certificates).
certificatesResolvers:
  letsencrypt:
    acme:
      email: yourname@yourmail.com
      storage: "/letsencrypt/acme.json"
      httpChallenge:
        entryPoint: http

Docker Compose File

Now, we need a very simple docker-compose.yml (same directory as traefik.yml) to create a Traefik instance:

version: '3'

services:
  # one service: Traefik
  traefik:
    # Checkout Docker Hub or the Traefik docs for the latest version (or use :latest)!
    image: "traefik:v2.5"
    restart: unless-stopped
    # Port mapping: required for http and https traffic, as wells as for the
    # Traefik Dashboard (8080), but this is optional (disable in production, if
    # not required)!
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"  # optional!
    # A Docker network is used to connect Traefik with other Docker containers.
    networks:
      - traefik_proxy
    volumes:
      # Storage for SSL certificates:
      - "./letsencrypt:/letsencrypt"
      # Required for the Docker provider:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      # Our configuration file:
      - "./traefik.yml:/traefik.yml:ro"
      # Our dynamic configuration, which can be adjusted during runtime:
      - "./dynamic_conf.yml:/dynamic_conf.yml"

networks:
  traefik_proxy:
    external: true

You can now already start Traefik (create an empty dynamic_conf.yml file):

docker compose up -d
# check logs
docker compose logs -f traefik

Next, we want to finally use our proxy and let it forward traffic to some docker containers.

Using Traefik with Docker Containers

Configuration takes place via labels on Docker Containers. These labels tell Traefik to route traffic to this specific container and they also tell traffic how to do that (e.g., which domain, which entrypoint, etc.).

A simple whoami service (you can put this, for testing, in the same file as the Traefik service):

version: '3'

services:
  whoami:
    image: "traefik/whoami"
    labels:
      # We want to use Traefik!
      - "traefik.enable=true"
      # This container receives requests, send to the specified domain. For that, we create a
      # router called "whoami" with a rule specifying this host.
      - "traefik.http.routers.whoami.rule=Host(`whoami.your-domain.com`)"
      # The "whoami" router should listen on the http entrypoint
      - "traefik.http.routers.whoami.entrypoints=http"

For this to work, you obviously need to control the whoami.your-domain.com domain and make it point to the IP Traefik is running on.
After starting the service, Traefik will automatically detect the new container and route any requests to the specified domain to this container. Well done :)

Dynamic Configuration

As mentioned before, the dynamic configuration allows you to configure stuff on demand during runtime. It is applied without any restart required. We'll do two things: specify the TLS version to use and setup a "secureHeaders" middleware.
Create the dynamic_conf.yml file, if you didn't already.

Specify SSL version

To set TLS 1.2 as a minimal version used (among other stuff), add the following:

tls:
  options:
    default:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
        - TLS_AES_128_GCM_SHA256
        - TLS_AES_256_GCM_SHA384
        - TLS_CHACHA20_POLY1305_SHA256
      curvePreferences:
        - CurveP521
        - CurveP384
      sniStrict: true

These options are taken from the Mozilla SSL Configuration Generator.

Secure Headers

Add the following to the dynamic_conf.yml:

http:
  middlewares:
    secureHeaders:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        frameDeny: true
        sslRedirect: true
        # HSTS Configuration
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: "SAMEORIGIN"

This basically defines a middleware, that — when used by a Traefik router — sets a secure set of headers by default on responses. Checkout MDN Docs for more information about each header.

You can apply this middleware by adding the following label (last in the example) to a Docker service:

version: '3'

services:
  whoami:
    image: "traefik/whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.your-domain.com`)"
      - "traefik.http.routers.whoami.entrypoints=http"
      - "traefik.http.routers.whoami.middlewares=secureHeaders@file"

We tell the whoami router to use the secureHeaders middleware, which is provided by the file provider (we set this name in our static configuration).

File/Directory Structure

At the end, your file structure should look like this:

marvinweber@server:/app/traefik$ ll
total 24
drwxrwxr-x  3 marvinweber marvinweber 4096 Jan  5 16:16 ./
drwxr-xr-x 10 marvinweber marvinweber 4096 Apr  2  2021 ../
-rw-rw-r--  1 marvinweber marvinweber  458 Nov 29  2020 docker-compose.yml
-rw-rw-r--  1 marvinweber marvinweber  743 Nov 29  2020 dynamic_conf.yml
drwxr-xr-x  2 root        root        4096 Jan 15  2021 letsencrypt/
-rw-rw-r--  1 marvinweber marvinweber  396 Nov 29  2020 traefik.yml

Advanced

HTTPS / SSL Encryption

Listening on SSL (404) Traffic is easy. We just need a router listening on the https entrypoint:

version: '3'

services:
  whoami:
    image: "traefik/whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.your-domain.com`)"
      - "traefik.http.routers.whoami.entrypoints=http"
      - "traefik.http.routers.whoami.middlewares=secureHeaders@file"
      
      # New router for the SSL traffic (note, that is has a different name: "whoami-https")!
      - "traefik.http.routers.whoami-https.entrypoints=https"  # it listens on the https entrypoint
      # use the same domain...
      - "traefik.http.routers.whoami-https.rule=Host(`whoami.your-domain.com`)"
      - "traefik.http.routers.whoami-https.tls=true"
      # We use the certresolver defined in our static configuration
      - "traefik.http.routers.whoami-https.tls.certresolver=letsencrypt"

Traefik will now automatically request a Let's encrypt certificate for the specified domain. You will then be able to open: https://whoami.your-domain.com

HTTPS Redirect (SSL only traffic)

For this, we dynamically (via labels) create a new middleware whoami-https-redirect which uses the https redirect scheme (i.e., it redirects to https://, if http:// is used). Then, we tell the whoami router (the one accepting http requests (non-ssl)) to use this middleware.

version: '3'

services:
  whoami:
    image: "traefik/whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.your-domain.com`)"
      - "traefik.http.routers.whoami.entrypoints=http"
      - "traefik.http.routers.whoami.middlewares=secureHeaders@file"

      - "traefik.http.routers.whoami-https.entrypoints=https"  # it listens on the https entrypoint
      - "traefik.http.routers.whoami-https.rule=Host(`whoami.your-domain.com`)"
      - "traefik.http.routers.whoami-https.tls=true"
      - "traefik.http.routers.whoami-https.tls.certresolver=letsencrypt"

      
      # Middleware for HTTPS forwarding:
      - "traefik.http.middlewares.whoami-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.whoami.middlewares=whoami-https-redirect"

That's it!

Categories: