Post

Understanding Traefik

Reverse Proxy Stack with Traefik+crowdsec+Nextcloud

Understanding Traefik

Traefik

Das wird möglicherweise der 100ste Artikel zum Thema Traefik - vielleicht mit ein paar Tips. Ich beschreibe zunächst, wie ich zu traefik gekommen bin und gehe dann auf meine aktuelle Konfiguration ein.

Wofür Traefik

Traefik ist ein Reverse Proxy. Einen Reverse Proxy benötigt man, wenn man eine IP-Adresse zur Verfügung hat, aber mehrere Dienste bzw. Domains betreiben möchte. Bei mir war Nextcloud der Auslöser für die Einführung eines Reverse Proxy.

Warum Traefik

Ich bin eigentlich zufällig auf traefik gestoßen und kam direkt gut zurecht mit der Lösung.

Folgende Vorteile von traefik schätze ich:

  • Konfiguration mit Text-Dateien
  • übersichtliches Dashboard
  • Container werden direkt über Labels vom Proxy erkannt
  • keine weitere Container Konfiguration im Proxy erfoderlich
  • letsencrypt Zertifikate werden vom Traefik bereitgestellt
  • crowdsec lässt sich gut einbinden
  • gute Dokumentation

Bei der Einrichtung habe ich mich bisher an zwei Quellen orientiert, die unterschiedliche Ansätze einer Traefik-Konfiguration aufzeigen:

  • Teqqy.de
  • goneuland.de

Traefik mit teqqy

teqqy (Christian Birkenbeul) hat auf https://teqqy.de/ eine sehr gute und verständliche Anleitung geschrieben bzw. auf Youtube veröffentlicht - meine erste Erfahrung mit traefik. Das müsste der Artikel Reverse Proxy mit Traefik unter Docker gewesen sein.

Traefik mit goneuland

goneuland hat einen traefik-Stack inklusive crowdsec veröffentlicht. Auf der Seite Nextcloud Server – mit Docker Compose und Traefik installieren findet man eine sehr ausführliche und verständliche Anleitung - mit etwas anderen Ansätzen, wie die Konfig von teqqy.

Traefik 3.0 und Crowdsec

Mit dem Übergang auf docker 29.0 hatte traefik eine kurze Auszeit, weil es die erforderliche API 1.52 nicht bedienen konnte.
(siehe https://forums.docker.com/t/docker-29-increased-minimum-api-version-breaks-traefik-reverse-proxy/150384 ).

Als Übergang habe ich Nginx Proxy Manager (NPM) (https://nginxproxymanager.com/) und auch NPM+ ( https://github.com/ZoeyVid/NPMplus ) eingesetzt. Beide Lösungen funktionieren, auch die Einbindung von Crowdsec in NPM+ war möglich. Dennoch haben mich die Lösungen nicht so geflasht, das traefik vom Tisch war. </br> Mit der aktuellen (3.6.12) Version vom Traefik habe ich meine bisher unterschiedlichen Implementierungnen auf einen einheitlichen Stand gebracht.

Mein Ziel:

  • Einbindung von Services die auf dem gleichen Docker-Host wie der Proxy laufen
  • Einbindung von Services von weiteren Quellen
  • Crowdsec soll eingebunden sein, aber als eigener Container laufen
  • Alle Container, die Traefik nutzen sind Teil des Traefik Netzes
  • Das Dashboard soll mit DNS-Namen nur intern erreichbar sein und automatisch ein SSL-Zertifikat erhalten

Aufbau der Konfiguration

Traefik besteht im Docker Umfeld aus 3 Komponenten:

  • docker-compose.yml für den Container
  • eine statische traefik.yml
  • eine dynamische dynamic.yml oder config.yml

Ich setze traefik zusammen mit Crowdsec ein. Bei Crowdsec handelt es sich um eine community-basierte Sicherheitslösung, die Log-Files auswertet und so verdächtige Aktivitäten erkennt und Angreifer blockiert. Sozusagen ein fail2ban für Fortgeschrittene.</br> Eine ausführliche Dokumention ist auf der Seite https://docs.crowdsec.net/ zu finden. Ich stelle in diesem Artikel nur die docker-compose des traefik teils vor.

traefik docker-compose.yml

Lasst mich mal das Compose File in ein paar Etappen vorstellen.

Part 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
services:
  traefik:
    image: traefik:v3
    container_name: traefik
    restart: unless-stopped
# No new privileges for security hardening
    security_opt:
      - no-new-privileges:true
# Fixed IP, um den Proxy sicher adressieren zu können
networks:
      traefik_proxy:
          ipv4_address: 192.168.0.253
     
    ports:
      - 80:80
      - 443:443
      # - 443:443/tcp # Uncomment if you want HTTP3
      # - 443:443/udp # Uncomment if you want HTTP3

Bis hierhin ist die Konfiguration Standard. Es wird der Service traefik definiert:

  • image: traefik:v3 sorgt für die letze aktuelle 3er Version, macht aber kein versehentliches Update auf Version 4 - wie beim Übergang von V2 auf V3.
  • container_name: traefik vergibt einen eindeutigen Namen, der dann im Docker referenziert werden kann.
  • restart: unless-stopped sorgt dafür, dass ein Container automatisch neu gestartet wird, wenn er unerwartet stoppt.
  • security_opt: - no-new-privileges:true ist eine Sicherheitsoption, die verhindert, dass Prozesse innerhalb des Containers neue Privilegien erhalten
  • networks: traefik_proxy: ipv4_address: 192.168.0.253 hängt den Container mit einer eindeutigen IP ins Netz traefik_proxy. Alle Container, die den Traefik nutzen, haben eine IP im traefik_proxy Netz. Das habe ich so eingerichtet, weil es aus meiner Sicht die übersichtlichste Lösung ist.
  • ports: - 80:80 - 443:443 definiert die exposed Ports. Absoluter Standard http und https zu exposen - dafür ist der Proxy ja da :smiley:. Auf https3 mit 443/udp habe ich verzichtet.

Part 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    environment:
      # MST No Token for Cloudflare, using Hetzner DNS Challenge
      #CF_DNS_API_TOKEN_FILE: /run/secrets/cf_api_token # note using _FILE for docker secrets
      # CF_DNS_API_TOKEN: ${CF_DNS_API_TOKEN} # if using .env
      #HTZ_DNS_API_TOKEN_FILE: /run/secrets/htz_api_token # note using _FILE for docker secrets
      HETZNER_TOKEN: ${HETZNER_TOKEN}
      HETZNER_API_TOKEN: ${HETZNER_TOKEN}
      TRAEFIK_DASHBOARD_CREDENTIALS: ${TRAEFIK_DASHBOARD_CREDENTIALS}

    #MST
    #secrets:
    #  - cf_api_token
    #  - htz_api_token
    env_file: .env # use .env

In diesem Bereich sorge ich dafür, das ich für eine interne Domain ein letsencrypt Zertifikat bekomme. Kurzer Exkurs: Es geht um dns-challenges für letsencrypt Zertifikate. Wenn man die Zone example.de bei Cloudflare oder Hetzner betreibt, kann man mit mit einer API auf den DNS Server zugreifen. Auf diesem Weg lassen sich Zertifikate für eine interne Zone intern.example.de erzeugen. Damit kann das traefik-dashboard auf einer internen IP mit einem gültigen Zertifikat laufen. Weiteres siehe unten.

  • HETZNER_TOKEN: ${HETZNER_TOKEN} liest den API-Token aus der .env Datei ein
  • HETZNER_API_TOKEN: ${HETZNER_TOKEN} liest den API-Token aus der .env Datei ein
  • TRAEFIK_DASHBOARD_CREDENTIALS: ${TRAEFIK_DASHBOARD_CREDENTIALS} liest das mit htpasswd erzeugte Passwort aus der .env ein
  • env_file: .envdefiniert das .env-file

Es wird hier eine Konfig adaptiert, die unter anderem von TechnoTim auf https://technotim.com/posts/traefik-3-docker-certificates/ vorgestellt wird. Das erklärt dann auch die Cloudflare Einträge.

Part 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    volumes:
      - /etc/localtime:/etc/localtime:ro
      #MST Toggling between host and user docker socket for better permissions handling. Use the one that works best for your setup.
      #- /run/user/1000/docker.sock:/var/run/docker.sock:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./config/traefik.yml:/etc/traefik/traefik.yml:ro
      - ./config/config.yml:/etc/traefik/config.yml:ro
      - ./config/ACME/acme.json:/etc/traefik/ACME/acme.json
      - ./config/ACME/acme-tls.json:/etc/traefik/ACME/acme-tls.json
      - /var/log/crowdsec/traefik:/var/log/traefik
     # - ./ca_mkcert:/certs
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=http"
      - "traefik.http.routers.traefik.rule=Host(`proxy.wido.sv-horstedt.de`)"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_DASHBOARD_CREDENTIALS}"
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`proxy.wido.sv-horstedt.de`)"
      - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
      #HTZ
      #Certresolver Hetzner DNS Challenge
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=hetzner"
      #- "traefik.http.routers.traefik-secure.tls.domains[0].main=wido.sv-horstedt.de"
      #- "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.wido.sv-horstedt.de"
      
      - "traefik.http.routers.traefik-secure.service=api@internal"

Im Bereich Volumes sind wieder einige Standardwerte gesetzt.</br> Es wird

  • /etc/localtimesorgt für die “richtige” Zeit im traefik
  • /var/run/docker.sock für den Zugriff auf den Docker Socket

Alle Konfig-Dateien liegen im Ordner .config. Man kann den kompletten Ordner mit einer Zeile in den Container mappen oder eben jedes File für sich.

  • ./config/traefik.yml:/etc/traefik/traefik.yml:ro enthält die statische Konfiguration
  • ./config/config.yml:/etc/traefik/config.yml:ro enthält die dynamisch Konfiguration
  • ./config/ACME/acme.json:/etc/traefik/ACME/acme.json enthält Zertifikate (http-challenge)
  • ./config/ACME/acme-tls.json:/etc/traefik/ACME/acme-tls.json enthält Zertifikate (tls-challenge)
  • /var/log/crowdsec/traefik:/var/log/traefik enthält die Log-Files von traefik. Die Speicherung im lokalen Log-Verzeichnis des Hosts ermlaubt dann auch den Zugriff von Crowdsec.
1
2
3
4
networks:
  traefik_proxy:
    external: true

Das Netzwerk traefik_proxy ist dauerhaft im Host angelegt und daher als external konfiguriert.

https://technotim.com/posts/traefik-3-docker-certificates/

dns-challenge acem erklären

Gerne! Die DNS-Challenge ist eine Methode, die vor allem bei der automatischen Ausstellung von SSL/TLS-Zertifikaten (z. B. mit Let’s Encrypt) verwendet wird, um zu verifizieren, dass du die Kontrolle über eine Domain besitzt. Was ist die DNS-Challenge?

1
2
3
Bei der DNS-Challenge musst du einen speziellen DNS-Eintrag (ein TXT-Record) für deine Domain setzen.
Die Zertifizierungsstelle (z. B. Let's Encrypt) prüft diesen Eintrag, um sicherzustellen, dass du tatsächlich Zugriff auf die DNS-Einstellungen der Domain hast.
Erst wenn die Challenge erfolgreich bestätigt wird, wird das Zertifikat ausgestellt.

Warum DNS-Challenge?

1
2
3
Domain-Kontrolle beweisen: Du zeigst, dass du die DNS-Zone verwalten kannst.
Geeignet für Wildcard-Zertifikate: Für Zertifikate wie *.example.com ist die DNS-Challenge notwendig, da HTTP-basierte Methoden hier nicht funktionieren.
Unabhängig von Webserver: Du kannst die Challenge lösen, auch wenn kein Webserver läuft oder Ports blockiert sind.

Wie funktioniert die DNS-Challenge technisch?

1
2
3
4
Das ACME-Protokoll (z. B. bei Let's Encrypt) fordert dich auf, einen TXT-Eintrag unter _acme-challenge.deinedomain.tld mit einem bestimmten Token zu setzen.
Du fügst diesen TXT-Eintrag im DNS deiner Domain hinzu.
Die Zertifizierungsstelle fragt den DNS-Eintrag ab und vergleicht den Wert mit dem erwarteten Token.
Wenn alles passt, gilt die Challenge als bestanden und das Zertifikat wird ausgestellt.
This post is licensed under CC BY 4.0 by the author.