Traefik letsencrypt Tutorial

Mit diesem Traefik letsencrypt Tutorial möchte ich dir helfen einfach und sauber Zertifikate via freier CA Let’s Encrypt zu beantragen und zu verwalten. In diesem Tutorial gehe ich auf die Punkte http-challenge, dns-challenge sowie weiteres Zertifikatsmanagement ein. Sollte Dir ein Punkt fehlen, schreibe mir dazu gerne in Kommentare.

Solltest Du auf dieses Tutorial gestoßen sein, weil du Docker nutzt, kann ich dir meine docker-compose Sammlung auf GitHub sehr ans Herz legen. Dort sind gerade für das Thema Homelab sehr viele Softwareprodukte hinterlegt die eine direkte Traefik Anbindung realisieren. Sicherlich ist auch meine Traefik Konfiguration ein guter Startpunkt für ein gutes Gelingen.

Traefik letsencrypt tutorial

Traefik Grundkonfiguration

Traefik lässt sich über mehrere Wege konfigurieren. Hierbei spielt es keine Rolle ob man sämtliche Befehle und Konfigurationen direkt in die docker-compose Datei oder in separate Dateien packt. Ich für meinen Teil verwende dedizierte um eine einfachere und übersichtliche Konfiguration zu haben. Solltest du einen anderen Weg bevorzugen ist dies für dieses Traefik letsencrypt Tutorial nicht wichtig. Du musst natürlich nur die Einstellungen adaptieren.

Das ist ist meine Docker Compose Beispielkonfiguration welche ich normalerweise einsetze. Je nach Umgebung kann diese aber natürlich ein wenig variieren. Ich gebe auch immer http3 mit aus, einfach weil ich es mag neue Standard direkt mit zu implentieren.

---
services:
  traefik:
    image: traefik:v3
    container_name: traefik
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/log/auth.log:/var/log/auth.log:ro
      - /var/log/traefik:/var/log/traefik
      - ./config:/etc/traefik
    networks:
      - traefik_proxy
      - default
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: udp
        mode: host
    restart: unless-stopped

networks:
  traefik_proxy:
    external: true
  default:
    driver: bridge

Anschließend brauchen wir natürlich noch die zwei Dateien die wir oben als Volume angegeben haben. Starten wir mit der traefik.yaml Diese beinhaltet alle notwenigen Eigenschaften um Traefik sauber ans laufen zu bekommen.

---
log:
  level: "INFO"
  filePath: "/var/log/traefik/traefik.log"
accessLog:
  filePath: "/var/log/traefik/access.log"
  bufferingSize: 100

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: "traefik_proxy"
  file:
    filename: "/etc/traefik/dynamic.yml"
    watch: true

api:
  dashboard: false

entryPoints:
  ping:
    address: ':88'
  web:
    address: ':80'
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ':443'
    http3:
    proxyProtocol:
      trustedIPs:
       - 10.0.0.0/8
       - 172.16.0.0/12
       - 192.168.0.0/16
    forwardedHeaders:
      trustedIPs:
       - 10.0.0.0/8
       - 172.16.0.0/12
       - 192.168.0.0/16

certificatesResolvers:
  default:
    acme:
      email: "deine@email-adresse.example" #Email Adresse hier anpassen
      storage: "/etc/traefik/ACME/acme.json"
      httpChallenge:
        entryPoint: web
  tls_resolver:
    acme:
      email: "deine@email-adresse.example" #Email Adresse hier anpassen
      storage: "/etc/traefik/ACME/acme.json"
      tlsChallenge: {}

Gehen wir die Punkt einmal durch:

[log]

Das Loglevel sollte für den produktiven Betrieb so gewählt werden, dass nach Möglichkeit nicht die Festplatte voll läuft. Für den Beginn ist es evtl. ratsam hier auf DEBUG umzustellen um mehr zu sehen und um Fehler leichter erkennen zu können.

[providers]

Wir nutzen für diese Beispielkonfiguration Docker. Wir möchten allerdings nicht, dass grundsätzlich alle Docker Container via Traefik veröffentlicht werden (exposedByDefault = false) und setzen die jeweiligen Anpassungen für jeden Container dediziert. Traefik lassen wir auf den Docker Socket hören um zu wissen was Traefik so machen muss. Gegebenenfalls musst du den Parameter „network“ anpassen. Ich nutze hierfür immer „traefik_proxy“ als Netzwerk. Möchtest du das ebenfalls nutzen musst du

docker network create traefik_proxy

ausführen und das Netzwerk anzulegen.

[api]

Das Dashboard brauchen wir hierfür nicht, wird also deaktiviert

[entryPoints]

Die entryPoints sind die Ports und Protokolle auf die Traefik hört. Stand jetzt kann Traefik nur HTTP/HTTPS Traffic. Weitere Protokolle sollen in Zukunft kommen. Wir lauschen natürlich auch auf Port 80 (HTTP) und machen eine direkte Weiterleitung auf Port 443 (HTTPS). Unverschlüsselte Kommunikation sollte nur in Ausnahmefällen noch erlaubt sein.

Wie du siehst binden wir auch eine dynamic.yml Datei ein. Diese hat folgenden Aufbau

---
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
http:
  middlewares:
    default:
      chain:
        middlewares:
          - default-security-headers
          - gzip
    default-security-headers:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: "SAMEORIGIN"
    gzip:
      compress: {}

Die dynamic.yml ist zur Konfiguration aller Sicherheitsmerkmale gedacht. Bitte passe diese nach den aktuellen Gegebenheiten an, da sich gerade die Cipher Suites häufig ändern können! Die restlichen Werte sind nach langjähriger Erfahrung von mir so zusammen getragen und müssen nicht dem aktuellen Sicherheitsstandards entsprechend. Für Tipps bin ich aber dankbar und passe die Datei dann gerne an.

Traefik letsencrypt tlsChallenge

Mit der tlsChallange ist es wahrscheinlich die einfachste Variante Zertifikate via Let’s encrypt zu beantragen. Hier kann Traefik direkt so konfiguriert, bzw. über die Docker Konfiguration so eingerichtet werden, dass nur Port 443, also HTTPS, veröffentlicht werden muss. Möchte man nur ein paar wenige Zertifikate beantragen ist das meiner Meinung die empfehlenswerte Version.

Die Konfiguration der tlsChallenge ist ziemlich einfach. Man fügt in der traefik.toml Datei folgende Zeilen am Ende ein:

certificatesResolvers:
  default:
    acme:
      email: "deine@email-adresse.example" #Email Adresse hier anpassen
      storage: "/etc/traefik/ACME/acme.json"

Email-Adresse ist für die Benachrichtigung von Let’s encrypt an dich gedacht. Entsprechend empfehle ich hier eine valide Email-Adresse anzugeben. Die Storage Angabe ist der Speicherort der Zertifikate. Entsprechend muss in der Docker Konfiguration auch das beachtet werden.

Traefik letsencrypt httpChallenge

Die httpChallenge ist quasi das gleich wie die tlsChallenge, allerdings via Port 80 anstatt 443. Hier muss als in der Docker Konfiguration auch Port 80 veröffentlicht werden. So ergibt sich daraus folgender Inhalt für die traefik.toml:

certificatesResolvers:
  default:
    acme:
      email: "deine@email-adresse.example" #Email Adresse hier anpassen
      storage: "/etc/traefik/ACME/acme.json"
      httpChallenge:
        entryPoint: web

Traefik letsencrypt dnsChallenge (am Beispiel Cloudflare)

Die dnsChallenge ist quasi die Königsdisziplin. Allerdings nur wirklich dann sinnvoll wenn man z. B. Wildcard Zertifikate benötig oder wenn man viele Subdomainzertifikate benötigt. Entsprechend muss man hier auch ein wenig mehr konfigurieren und vor allen Dingen benötigt man einen DNS Provider der von Let’s encrypt unterstützt wird. Ich empfehle hier Cloudflare.

certificatesResolvers:
  default:
    acme:
      email: "mail@teqqy.de"
      storage: "/etc/traefik/ACME/acme.json"
      dnsChallenge:
        provider: "cloudflare"
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"

Hinzu kommt, dass wir in der docker-compose.yaml noch den API Token für die Cloudflare API hinzufügen. Dazu einfach noch folgende Zeilen einfügen:

    environment:
      CF_DNS_API_TOKEN: "{STRENGGEHEIMERAPIKEY}"

Screenshot Cloudflare API Key Übersicht

Zertifikate löschen

Zertifikate werden in der acme.json Datei gespeichert und könnten dort heraus gelöscht werden. Zu jedem Zertifikat gibt es eine certificate und key Definition. Man kann also die Datei entsprechend editieren und die Zeichenketten entsprechend heraus löschen.

      {
        "domain": {
          "main": "teqqy.de",
          "sans": [
            "www.teqqy.de"
          ]
        },
        "certificate": "<Zeichenkette>",
        "key": "<Zeichenkette>",
        "Store": "default"
      },

Notwendig ist dies aber nur dann, wenn bei der Erstellung ein Fehler aufgetreten ist oder Let’s Encrypt Zertifikate wg. Fehlern zurückruft. Andernfalls laufen die Zertifikate automatisch nach 90 Tagen ab. Ein manuelles Aufräumen ist nicht notwendig.