Skip to content

Traefik

Traefik Doc: https://doc.traefik.io/traefik/routing/providers/docker/

Config acme.json - Setup
  docker network create zabra # will be used by traefik and all containers behind it
  touch acme.json
  chmod 600 acme.json
  touch /var/log/traefik-access.log
  # Create user/password for traefik GUI basic authentification
  # And store the value in variable CREDS=myser:$$xyxxx in .env file
  # Sed command is used to double all $ for escaping
  echo $(htpasswd -nB $USER) | sed -e s/\\$/\\$\\$/g

Traefik V3

Create traefik.yaml

Create traefik.yaml file with Let's Encrypt as certificate resolver and TLS Cipher settings

traefik.yaml
api:
  dashboard: true

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

  websecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt

accessLog:
  filePath: "/var/log/access.log"
  format: json

certificatesResolvers:
  letsencrypt:
    acme:
      email: "email.to.kone@gmail.com"
      storage: "acme.json"
      httpChallenge:
        entryPoint: web

http:
  middlewares:
    # Basic Auth
    auth:
      basicAuth:
        users:
          - ${CREDS}

    # HSTS / Security headers
    servicests:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        stsPreload: true
        isDevelopment: false

tls:
  options:
    default:
      sniStrict: true
      curvePreferences:
        - CurveP521
        - CurveP384
      minVersion: TLS12
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305

    mintls13:
      minVersion: TLS13

providers:
  file:
    directory: /etc/traefik/dynamic
  docker:
    watch: true
    exposedByDefault: false
    network: zabra

Docker Compose FIle

docker-compose.yml
networks:
  zabra:
    external: true

services:
  traefik:
    image: "traefik:v3.5.3"
    container_name: "traefik"
    hostname: "traefik.enoks.fr"
    restart: always
    networks:
      - zabra
    labels:
      - "traefik.enable=true"
      # Dashboard
      - "traefik.http.routers.api.rule=Host(`traefik.enoks.fr`)"
      - "traefik.http.routers.api.service=api@internal"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls=true"
      - "traefik.http.routers.api.tls.certresolver=letsencrypt"
      - "traefik.http.routers.api.middlewares=auth,servicests"
      # Basic Auth
      - "traefik.http.middlewares.auth.basicAuth.users=${CREDS}"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yaml:/etc/traefik/traefik.yaml
      - ./dynamic:/etc/traefik/dynamic:ro
      - ./acme.json:/acme.json
      - /var/log/traefik-access.log:/var/log/access.log
      # mount custom CA
      # -  ./ca.crt:/etc/traefik/ssl/ca.crt:ro

Then run docker compose up -d

Enabling mTLS

dynamic/mtls.yaml
tls:
  options:
    mtls-required:
      clientAuth:
        caFiles:
          - /etc/traefik/ssl/ca.crt   # mount this in Traefik container
        clientAuthType: RequireAndVerifyClientCert
      minVersion: TLS12
      sniStrict: true

Then the client app can add this addtionnal label - "traefik.http.routers.<ROUTER>.tls.options=mtls-required@file" to enforce Certificate Based Authentication.

Any user or device must present a certificate signed by the ca.crt to get access

Traefik V2 - OLD

Create traefik.toml file with Let's Encrypt as certificate resolver.

traefik.toml
  [api]
    dashboard = true
   # insecure = true   ## using https

  [entryPoints]
    [entryPoints.web]
      address = ":80"
      [entryPoints.web.http]
        [entryPoints.web.http.redirections]
          [entryPoints.web.http.redirections.entryPoint]
            to = "websecure"
            scheme = "https"
            permanent = true

    [entryPoints.websecure]
      address = ":443"
        [entryPoints.websecure.http.tls]
          certResolver = "default"

  [accessLog]
    filePath = "/var/log/access.log"
    format = "json"

  [http.middlewares]
    [http.middlewares.test-auth.basicAuth]
      usersFile = "/usersfile"

  [providers]
    [providers.docker]
      watch = true
      exposedByDefault = false
      network = "zabra"
    [providers.file]
      filename =  "/etc/traefik/tls_config.toml"  # For TLS Hardening

  [certificatesResolvers.letsencrypt.acme]
    email = "user@email"
    storage = "acme.json"
    [certificatesResolvers.letsencrypt.acme.httpChallenge]
      # used during the challenge
      entryPoint = "web"

TLS Hardening

Traefik TLS Hardening to not support TLS 1.0, TLS 1.1 and enforce some ciphers.

tls_config.toml
  # https://doc.traefik.io/traefik/https/tls/
  # check results : nmap -Pn --script ssl-enum-ciphers -p 443 doc.enoks.fr  or https://www.ssllabs.com/ssltest/analyze.html
  [tls.options]

    [tls.options.default]
      sniStrict = true
      curvePreferences = ["CurveP521", "CurveP384"]
      minVersion = "VersionTLS12"
      cipherSuites = [
          "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
          "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
          "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
          "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
          "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
          "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
      ]

    [tls.options.mintls13]
      minVersion = "VersionTLS13"
docker-compose.yml with the tls config
  # Adapt traefik_url

  version: "3.3"

  networks:
    zabra:
      external: true

  services:
    traefik:
      image: "traefik:v2.9"
      container_name: "traefik"
      hostname: "traefik"
      restart: always
      networks:
        - zabra
      labels:
        # dashboard access without port 8080
        - "traefik.enable=true"
        - "traefik.http.routers.api.rule=Host(`traefik_url`)"
        - "traefik.http.routers.api.service=api@internal"
        # https
        - "traefik.http.routers.api.entrypoints=websecure"
        - "traefik.http.routers.api.tls=true"
        - "traefik.http.routers.api.tls.certresolver=letsencrypt"
        # auth
        - "traefik.http.routers.api.middlewares=auth"
        - "traefik.http.middlewares.auth.basicauth.users=${CREDS}"
        # Enforce HSTS (HTTP Strict Transport Security) & STS Headers for the  UI.
        - "traefik.http.middlewares.servicests.headers.stsseconds=31536000"
        - "traefik.http.middlewares.servicests.headers.stspreload=true"
        - "traefik.http.middlewares.servicests.headers.stsincludesubdomains=true"
        - "traefik.http.middlewares.servicests.headers.isdevelopment=false"
        # Disable STS service for traefik dashboard as it prevents basic HTTP authentification to work
        # Need to check if there is an option to enable the both to work as the same time
        # - "traefik.http.routers.api.middlewares=servicests"
      ports:
        - "80:80"
        - "443:443"
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock:ro
        - ./traefik.toml:/etc/traefik/traefik.toml
        # Mount TLS config
        - ./tls_config.toml:/etc/traefik/tls_config.toml
        - ./acme.json:/acme.json
        - /var/log/traefik-access.log:/var/log/access.log