Skip to content

Traefik

Traefik is a reverse proxy encrypting traffic with Let's Encrypt certificates and is used to dispatch incoming requests to the right services in a secure and SSL-encrypted way.

Installation

The relevant installation steps comprise setting environment variables in a .env file. Thus, you need to create a hidden file with the name .env (including the '.') containing the following environment variables:

ACME_EMAIL=your@mail.com
TRAEFIK_DOMAIN=your.dashboard.domain
BASIC_AUTH=your_dashboard_user:your_htpasswd_password

Additionally, we need the ACME configuration in the acme.json file The JSON file in which Traefik stores all relevant certificate information needs to be secured with exactly the following user rights:

cd config
chmod 600 acme.json

Configuration

The docker-compose file defines the Traefik service including all relevant labels for proper configuration of our setup and connects it to the internet-facing traefik_proxy network (no backend connectivity required).

Application

The first part of the Traefik configuration is pretty straightforward:

  • defines the docker image to be downloaded from docker hub
  • provides a name for our service (for easier recognition of the container and navigation e.g. in Portainer)

Then it gets a little more interesting:

  • First, two entry points are defined for unencrypted http requests on port 80 (web) and for encrypted https requests on port 443 (web-secure).
  • The web entrypoint is always re-directed to web-secure which is in the labels section below. Here, all requests matching all hosts coming in on port 80 are caught and redirected to the redirect-to-https middleware. This middleware then redirects all traffic to https.
  • The we enable to provide the services via Docker at all and shares the Docker sock on the local file system with the container. The Docker sock is mapped into the container as a volume (see volume section).
  • Then we state that none of the services are proxied by default, but only those that we explicitly want to make visible on the internet (for example internal monitoring containers or databases, we do not want to expose to the internet by default) and define the network to be used for the proxied services.

Most important for the encryption is the configuration of the certificate resolvers. We are using Let's Encrypt which is configured using the config/acme.json (see above). Additionally, we only need the e-mail address for Let's Encrypt which is read from the .env file and set the TLS challenge to true.

All the rest of the configuration deals with enabling system logs and access logs and make them readable and persistent in the local file system of the host (realized via volumes).

---
version: '3'

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.web-secure.address=:443
      - --providers.docker=true
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=traefik_proxy
      - --providers.docker.endpoint=unix:///var/run/docker.sock
      - --api.dashboard=true
      # for test purposes this is the let's encrypt staging server
      # - --certificatesresolvers.default.acme.caserver=
      # https://acme-staging-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.default.acme.email=${ACME_EMAIL}
      - --certificatesresolvers.default.acme.storage=/config/acme.json
      - --certificatesresolvers.default.acme.tlschallenge=true
      - --log.filePath=/srv/docker/traefik/traefik.log
      - --log.format=json
      - --accesslog=true
      - --accesslog.filePath=/srv/docker/traefik/access.log
      - --accesslog.format=json

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /etc/localtime:/etc/localtime
      - ./config:/config
      - /srv/docker/traefik/access.log:/srv/docker/traefik/access.log
      - /srv/docker/traefik/traefik.log:/srv/docker/traefik/traefik.log

    labels:
      - "traefik.enable=true"

      # global redirect to https
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"

      # middleware redirect
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

      # loadbalancer service
      - "traefik.http.services.traefik.loadbalancer.server.port=80"

    networks:
      - traefik_proxy

    ports:
      - "80:80"
      - "443:443"

    restart: always

Dashboard

If you want to access the Traefik dashboard, then this can be configured as follows:

  • We define the web-secure entrypoint for encrypted traffic (there is no need to do that for the unencrypted web entrypoint on port 80, since our Traefik configuration catches and redirects all unencrypted traffic to web-secure on port 443... see above).
  • The router configuration tells Traefiki which domains shall be routed to the container to display the dashboard content: here all requests directed to the domain as configured in the environment variable TRAEFIK_DOMAIN (e.g. dashboard.example.org).
  • The encryption via Let's Encrypt is enabled via the corresponding labels with the default resolver. This is being handled via Traefik as defined in the certificate resolver configuration above (via ACME).
  • The service needs to be enabled with api@internal for the dashboard.
  • Last, but not least, I would not want to publicly display the dashboard data. Thus, I configure a basic HTTP authentication requiring to log in with the user credentials as outlined in the BASIC_AUTH environment variable (this can be created on the command line with htpasswd).
      # enable dashboard
      #   - "traefik.http.routers.api.entrypoints=web-secure"
      #   - "traefik.http.routers.api.rule=Host(`${TRAEFIK_DOMAIN}`)"
      #   - "traefik.http.routers.api.tls=true"
      #   - "traefik.http.routers.api.tls.certresolver=default"
      #   - "traefik.http.routers.api.service=api@internal"
      #   - "traefik.http.routers.api.middlewares=api-auth"
      #   - "traefik.http.middlewares.api-auth.basicauth.users=${BASIC_AUTH}"

Network

Last, but not least, we refer to the existing external networks... they need to exist as outlined here.

networks:
  traefik_proxy:
    external:
      name: traefik_proxy