This post tends to give you some custom configuration files to work with Traefik and TLS.


Version Date Comment
1 05/2022 Post creation

Goal : Configure Traefik with custom TLS settings

Environment : Debian 11.2 (bullseye), Docker 20.10.x, docker-compose 2.4.x, Traefik 2.6

docker-compose configuration file

Forward, you can't use the two types of configuration (static and dynamic) at the same time, it is only once. I prefer dynamic configuration, here's a quick reminder.

Let's prepare the docker-compose.yml file:

---
services:
  traefik:
    image: traefik:v2.6
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./conf/traefik.yml:/etc/traefik/traefik.yml:ro
      - ./conf/traefikdynamic:/etc/traefik/dynamic:ro
      - ./acme.json:/etc/traefik/acme.json
      - ./logs/traefik.log:/etc/traefik/applog.log

Explanation :

  • I'm using Traefik v2.6
  • exposition of TPC ports 80 and 443
  • Mount several volumes : a configuration file “traefik.yml” for basic options and a folder for dynamic options. The folder is mounted with its files inside.
  • “acme.json” file need to be created before using it, with touch acme.json && chmod 0600 acme.json.

Traefik configuration

Every configuration files here are stored in the “conf” folder. You can change the path to where you want, but remember to update your docker-compose file for it.

Content of the file traefik.yml :

---
global:
  sendAnonymousUsage: false
  checkNewVersion: false

api:
  dashboard: true

pilot:
  dashboard: false

log:
  filePath: "/etc/traefik/applog.log"
  format: json
  level: "ERROR"

providers:
  docker:
    endpoint: unix:///var/run/docker.sock
    exposedByDefault: false
    watch: true
    swarmMode: false
  file:
    directory: "/dynamic"
    watch: true

api:
  dashboard: true
  debug: false

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      email: contact@domain.local
      caServer: https://acme-staging-v02.api.letsencrypt.org/directory
#      caServer: https://acme-v02.api.letsencrypt.org/directory
      storage: "/etc/traefik/acme.json"
      keyType: EC256
      httpChallenge:
        entryPoint: web

In this file, one important bloc is “provider” with the definition of a “dynamic” folder path. This folder is created locally to have the file “traefik_dynamic.yml”.

The line watch = true allows Traefik to watch changes in the configuration files and applies them when an update is done.

Certificates are generated with Let's Encrypt ; indeed, Traefik uses its internal tool “lego”. When you are in build mode, you should use “staging” server, which have a less aggressive limiter than the “acme-v02”. In production environment, use the “acme-v02” server from Let's Encrypt.

Dynamic configuration file is the same type as the main configuration file (YAML), difference is the settings. Here's my dynamic configuration file “traefik_dynamic.yml”, in the folder “conf/traefikdynamic” :

---
tls:
  options:
    default:
      minVersion: VersionTLS12
      sniStrict: true
      cipherSuites:
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
        - TLS_ECDHE_RSA_WITH_AES_128_CBC_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
      alpnProtocols:
        - h2
        - http/1.1
    mintls13:
      minVersion: VersionTLS13

http:
  middlewares:
    compression:
      compress:
        excludedContentTypes:
          - text/event-stream
    security:
      headers:
        accessControlAllowMethods:
          - GET
          - OPTIONS
          - PUT
        accessControlMaxAge: 100
        addVaryHeader: true
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
        stsPreload: true
        customFrameOptionsValue: SAMEORIGIN
        referrerPolicy: "origin-when-cross-origin"
        permissionsPolicy: "camera 'none'; microphone 'none'; geolocation 'none'; payment 'none';"
        stsSeconds: 315360000
        hostsProxyHeaders:
          - "X-Forwarded-Host"

Some explanations :

  • TLS block specify every needed options to have an optimal configuration. Minimal version of TLS is 1.2 with newer algorithms used by modern navigators ;
  • HTTP bloc have 2 “under-blocs” :
    - a middleware to compress some flux aside streaming
    - a middleware for every security about HTTPS (headers)

Configuration files are ready, you now have to set up your services in the docker-compose.yml file. Here's what you need for every services behind Traefik :

    labels:
      traefik.enable: "true"
      traefik.http.routers.service-https.entrypoints: "websecure"
      traefik.http.routers.service-https.middlewares: "security@file, compression@file"
      traefik.http.routers.service-https.tls: "true"
      traefik.http.routers.service-https.tls.certresolver: "letsencrypt"

Middlewares security@file and compression@file are drawn in the file traefik_dynamic.yml.

When you set the docker labels, don't forget to re-up your containers (docker-compose up -d).

If you don't use docker labels, you can add these blocs in the “traefikdynamic.yml” – information about “routers” and “services” next to  the TLS configuration, in the bloc http : (for example, I'm using Portainer)

  services:
    sc-portainer:
      loadBalancer:
        servers:
        - url: "http://portainer:9000"
 
   routers:
     rt-portainer:
      entryPoints:
      - websecure
      middlewares:
      - security
      - compression
      service: sc-portainer
      rule: Host(`portainer.domain.local`)
      tls:
        certResolver: letsencrypt

You don't have to restart containers when you're done updates in the file “traefikdynamic.yml”. Updates (dynamic configuration) are taken when running.

Partager l'article