Mit Docker lassen sich sehr leicht neue Container auf einem Host erstellen. Sollen aber mehrere Container über dem Port 80 (HTTP) nach außen kommunizieren, so kann es etwas problematisch werden, da dieser Port nur einmal auf dem Server zur Verfügung steht. Wir benutzen Traefik und erweitern es mit CrowdSec um diese Thematik anzugehen.

0. Versionierung

DatumÄnderung
12.08.2022traefik.yml:
– unötigen Code entfernt.
– Log angepasst

dynamic_conf.yml
– deprecated funktion auskommentiert
10.08.2022Erfolgskontrolle der Anleitung weiter nach hinten geschoben.
4.3.6. -> 5.1.
Korrektur der dynamic_conf.yml: secHeder zu secHeaders
09.08.2022Name des CertResolver von ‚letsEncrypt‘ auf ‚http‘ geändert
Anleitung ist nun vollständig auf ältere Anleitungen kompatibel
Abschnitt 6 hinzugefügt
Kompatibilität zu alten Anleitungen verbessert
Optimierung der docker-compose.yml von Traefik
Netzwerk ‚proxy‘ wird automatisch erstellt
08.08.2022Fehler in der docker-compose.yml von Traefik behoben
- /var/log/crowdsec/traefik/:/var/log/traefik/
Information bei Voraussetzungen ergänzt
Pfad unter „3.2. Konfiguration anpassen“ korrigiert
Befehl um htpasswd-Passwort zu erstellen geändert
Veröffentlichung

1. Was ist das Ziel dieser Anleitung?

Grundsätzlich erweitert diese Anleitung die ursprüngliche Anleitung von Christian. Es werden ein paar Dinge optimiert und es gilt weiterhin der Anspruch euch eine Anleitung zu schreiben, dass Ihr eure Applikationen möglichst sicher nutzen könnt! In dieser Anleitung werden wir:

  • Traefik v2 installieren und konfigurieren
  • Crowdsec installieren und konfigurieren
  • Traefik via Crowdsec überwachen
  • Wissen, wie ihr neue Anwendungen zu Traefik hinzufügt.

Achtung: Nach dieser Anleitung ist das Hinzufügen von Anwendungen in Traefik nicht mehr vollständig kompatibel zu Christians Anleitung. Dafür etwas kürzer und einfacher.

2. Vorraussetzung

  • Docker mit Docker Compose installiert (Anleitung für Ubuntu / Debian) (Wichtig!)
  • htpasswd installiert (sudo apt update && sudo apt install apache2-utils)

3. Traefik

3.1. Verzeichnisse / Dateien anlegen

Als erstes legen wir uns ein Verzeichnis für Traefik an und wechsel in das Verzeichnis:

mkdir -p /opt/containers/traefik
cd /opt/containers/traefik

Anschließend erzeugen wir uns noch Datein und Ordner, welche wir später noch benötigen:

mkdir -p /opt/containers/traefik/data
touch /opt/containers/traefik/data/acme_letsencrypt.json
chmod 600 /opt/containers/traefik/data/acme_letsencrypt.json
touch /opt/containers/traefik/data/traefik.yml

3.2. Konfiguration anpassen

Nun bearbeiten wir die Datei traefik.yml:
Dabei ist wichtig, dass in der .yml nur Leerzeichen und keine Tabs benutzen werden dürfen!

nano /opt/containers/traefik/data/traefik.yml
api:
  dashboard: true

certificatesResolvers:
  http:
    acme:
      email: "meine@email.de"
      storage: "acme_letsencrypt.json"
      httpChallenge:
        entryPoint: http

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

global:
  checknewversion: true
  sendanonymoususage: false

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: "proxy"
  file:
    filename: "./dynamic_conf.yml"
    watch: true
  providersThrottleDuration: 10
# Statische Traefik-Konfigurationsdatei
# https://doc.traefik.io/traefik/getting-started/configuration-overview/#the-static-configuration
# https://doc.traefik.io/traefik/reference/static-configuration/cli/

api:
  dashboard: true                             # Aktivieren des Dashboard

# Certificate Resolver
# Diese sind für den Abruf von Zertifikaten von einem ACME-Server zuständig
# https://doc.traefik.io/traefik/https/acme/#certificate-resolvers
certificatesResolvers:
  http:
    acme:
      email: "meine@email.de"                 # E-Mail-Adresse für die Registrierung 
      storage: "acme_letsencrypt.json"        # Datei für die Speicherung von Zertifikate (Ich weiche hier bewusst von dem "Standard": acme.json ab) 
      httpChallenge:
        entryPoint: http

# EntryPoints
# EntryPoints sind die Netzwerk-Eingangspunkte in Traefik. Sie definieren den Port, der die Pakete empfängt.
# https://doc.traefik.io/traefik/routing/entrypoints/
entryPoints:
  http:
    address: ":80"                            # Erstellen des Einstiegspunkt für HTTP (Port 80)
    http:
      redirections:                           # Weiterleitung von HTTP auf HTTPS (Port 80 zu Port 443). 
        entryPoint:
          to: "https"                         # Das Ziel
          scheme: "https"                     # Umleitungszielschema
  https:
    address: ":443"                           # Erstellen des Einstiegspunkt für HTTPS (Port 443)


global:
  checknewversion: true                       # In regelmäßigen Abständen prüfen, ob eine neue Version veröffentlicht wurde.
  sendanonymoususage: false                   # Regelmäßige Übermittlung anonymer Nutzungsstatistiken.

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"   # Den UNIX Docker socket beobachten
    exposedByDefault: false                   # Nur Container ausstellen, die explizit aktiviert sind (mit dem Label traefik.enabled)
    network: "proxy"                          # Standardnetzwerk, das für Verbindungen zu allen Containern verwendet wird.
  file:
    filename: "./dynamic_conf.yml"            # Link zur dynamischen Konfiguration
    watch: true                               # Achten auf Änderungen
  providersThrottleDuration: 10               # Frequenz in welchen Abständen die Konfiguration nachgeladen wird

Notwendige Anpassungen (unkommentierte Version)

  • Zeile 7: Anpassen an die eigene Email-Adresse.

Informationen

  1. Ich verwende in dieser Variante bewusst nicht acme.json als Dateiname. Diese Variante soll am Ende nicht nur Zertifikate via HTTP-Challange abrufen können sondern auch vorbereitet sein dies via DNS-Challange (damit sind Wildcard-Zertifikate möglich) zu machen. So das wir uns für jeden Container seperat entscheiden können welche Challange wir nutzen bzw. benötigen.
  2. Mit ein Grund für Traefik ist, dass am Ende die Anfrage via HTTPS gehen soll. Um nun nicht jedem einzelnen Container händisch die Weiterleitung auf HTTPS mitgeben zu müssen machen wir das hier einfach als allgemeine Regel.

3.3. Anlegen der dynamischen Konfiguration

Jetzt legen wir noch die dynamische Konfiguration an:

nano /opt/containers/traefik/data/dynamic_conf.yml
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:
    traefikAuth:
      basicAuth:
        users:
          - "benutzer:passworthash"
    default:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    secHeaders:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    default-security-headers:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
#       Deprecated
#       sslRedirect: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: "SAMEORIGIN"
    gzip:
      compress: {}
# TLS
# Hier werden alle notwendigen Einstellungen für das Zertifikat getroffen.
# In Kombination mit den Einstellungen unter http.middlewares.default-security-headers bekommen  wir ein A+ Zertifikat.
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

# Middlewares
# Optionale Optimierungen, die bei jeder Anfrage vorgenommen werden sollen bevor diese an den Zielcontainer geleitet wird.
http:
  middlewares:
    # Eine grundlegende Authentifizierungs-Middleware, um das Traefik-Dashboard via htpasswd zu schützen
    # Um die Authentifizierung für einen Container zu nutzen können wir "traefik.http.routers.definierteRoute.middlewares=traefikAuth@file" nutzen
    traefikAuth:
      basicAuth:
        users:
          - "benutzer:passworthash"

    # Empfohlene Standard-Middleware für die meisten Dienste
    # Hinzufügbar via "traefik.http.routers.definierteRoute.middlewares=default@file"
    # Equivalent mit "traefik.http.routers.definierteRoute.middlewares=default-security-headers@file,gzip@file"
    # Die Liste kann hier auch beliebig erweitert werden
    default:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    # Kompatibilität zu alten Anleitungen. Damit kann auch wieder "traefik.http.routers.definierteRoute.middlewares=secHeader@file" 
    secHeaders:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    # Standard Header
    default-security-headers:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
#       Deprecated
#       sslRedirect: true
        #HSTS Configuration
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: "SAMEORIGIN"
    # Gzip Kompression
    gzip:
      compress: {}

3.4. Dashboard mit Passwort schützen

Wenn das Dashboard aktiviert wurde ist dies erst mal ungeschützt. Das Dashboard zeigt „nur“ Informationen über die laufenden Dienste an. Das sind Informationen, welche wir nicht unbedingt mit der gesamten Maße teilen möchten. Deswegen schützen wir das Dashboard via htpasswd. Die notwendige Middleware dazu haben wir bereits in der dynamischen Konfiguration unter traefikAuth schon getroffen. Wir müssen uns nur noch einen Benutzernamen überlegen und ein Wunschpasswort dazu hashen:

echo $(htpasswd -nb <user> '<password>')

# Zum Beispiel:
# echo $(htpasswd -nb jonathan 'supersicher')
# Ausgabe: jonathan:$$apr1$$J8vBlNIm$$lSwLv9iGa8KcCct3EyLD41
# Sollte es an dieser Stelle zu Problemen kommen kann man sich auch das Passwort auf Webseiten
# wie zum Beispiel: https://www.redim.de/blog/passwortschutz-mit-htaccess-einrichten generieren lassen.

Nun ergänzen wir die dynamische Konfiguration um den eben generierte Benutzername/Passwort-Information:

nano /opt/containers/traefik/data/dynamic_conf.yml
# [...]
http:
  middlewares:
    traefikAuth:
      basicAuth:
        users:
          - "benutzer:passworthash"
# [...]
# [...]
http:
  middlewares:
    traefikAuth:
      basicAuth:
        users:
          - "jonathan:$$apr1$$J8vBlNIm$$lSwLv9iGa8KcCct3EyLD41"
# [...]

3.5. Docker-Compose.yml anlegen

Nun legen wir die docker-compose.yml an:

nano /opt/containers/traefik/docker-compose.yml
version: '3.9'
services:
  traefik:
    container_name: traefik
    image: traefik:latest
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme_letsencrypt.json:/acme_letsencrypt.json
      - ./data/dynamic_conf.yml:/dynamic_conf.yml                                       
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=https"
      - "traefik.http.routers.traefik.rule=Host(`traefik.meinedomain.de`)"
      - "traefik.http.routers.traefik.middlewares=traefikAuth@file,default@file"
      - "traefik.http.routers.traefik.tls=true"
      - "traefik.http.routers.traefik.tls.certresolver=http"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.services.traefik.loadbalancer.sticky.cookie.httpOnly=true"
      - "traefik.http.services.traefik.loadbalancer.sticky.cookie.secure=true"
      - "traefik.docker.network=proxy"
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    hostname: traefik
    ports:
      - "80:80"
      - "443:443"

networks:
  proxy:
    name: proxy
    driver: bridge
    attachable: true
version: '3.9'
services:
  traefik:
    container_name: traefik
    image: traefik:latest
    volumes:
      # Traefik mit der Zeit vom Server syncronisieren
      - /etc/localtime:/etc/localtime:ro
      # Lesezugriff auf den UNIX Docker socket
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Traefik-Konfiguration bereitstellen
      - ./data/traefik.yml:/traefik.yml:ro
      # Datei mit Zertifikaten bereitstellen
      - ./data/acme_letsencrypt.json:/acme_letsencrypt.json
      # Datei mit dynamischer Konfiguration bereitstellen
      - ./data/dynamic_conf.yml:/dynamic_conf.yml                                       
    labels:
      # Traefik durch Watchtower aktualisieren lassen
      # https://goneuland.de/docker-images-automatisiert-aktualisieren-mit-watchtower/
      - "com.centurylinklabs.watchtower.enable=true"
      # Traefik für Traefik aktivieren
      - "traefik.enable=true"
      # Als Einstiegspunkt wählen wir direkt HTTPS
      - "traefik.http.routers.traefik.entrypoints=https"
      # Domain anpassen unter der Traefik erreichbar sein soll.
      - "traefik.http.routers.traefik.rule=Host(`traefik.meinedomain.de`)"
      # Middlewares definieren, welche verwendet werden sollen. Hier die Authentifizierung via htpasswd + alle die unter default definiert sind. 
      - "traefik.http.routers.traefik.middlewares=traefikAuth@file,default@file"
      # SSL Zertifikat abrufen
      - "traefik.http.routers.traefik.tls=true"
      # Definierten Zertifikat Resolver aus traefik.yml wählen
      - "traefik.http.routers.traefik.tls.certresolver=http"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.services.traefik.loadbalancer.server.port=80"
      - "traefik.http.services.traefik.loadbalancer.sticky.cookie.httpOnly=true"
      - "traefik.http.services.traefik.loadbalancer.sticky.cookie.secure=true"
     # Traefik dem Proxy-Netzwerk hinzufügen.
      - "traefik.docker.network=proxy"
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    hostname: traefik
    ports:
      # Ports definieren, welche durch Traefik gemanaget werden.
      - "80:80"
      - "443:443"

networks:
  proxy:
    name: proxy
    driver: bridge
    attachable: true

Notwendige Anpassungen in der unkommentierten Version

  • Zeile 16: Auf eigene Domain anpassen

3.6. Traefik starten

Nun können wir Traefik starten:

docker compose -f /opt/containers/traefik/docker-compose.yml up -d

Wenn ihr nun auf traefik.meinedomain.de geht, solltet ihr folgendes sehen:

Nach dem Login mit eurem oben festgelegten Passwort solltet ihr diese Übersicht sehen:

Hier sind wir jetzt fertig mit der Traefik Konfiguration und Installation.

3.7. Anwendungen hinzufügen

Im Allgemeinen ist es wichtig, dass ihr die Labels hinzufügen:

  wordpress:
    labels:
      # Aktiviert Traefik für diesen Container
      - "traefik.enable=true"
#     Hier wird der Einstiegspunkt definiert. Da wir eigentlich zu 99% HTTPS nutzen möchten können wir uns HTTP auch sparen.
#     Beispielhaft habe ich euch HTTP nochmal auskommentiert hinzugefügt. An dieser Stelle ist keine Weiterleitung zu HTTPS mehr notwenig.
#     Die Weiterleitung von HTTP zu HTTPS wurde bereits in der traefik.yml festgelegt
#     - "traefik.http.routers.wordpress.entrypoints=http"
#     - "traefik.http.routers.wordpress.rule=Host(`wordpress.euredomain.de`)"
      - "traefik.http.routers.wordpress-ssl.entrypoints=https"
      - "traefik.http.routers.wordpress-ssl.rule=Host(`wordpress.euredomain.de`)"
      - "traefik.http.routers.wordpress-ssl.tls=true"
      - "traefik.http.routers.wordpress-ssl.tls.certresolver=http"
      - "traefik.http.routers.wordpress-ssl.middlewares=default@file"
      - "traefik.http.routers.wordpress-ssl.service=wordpress-ssl"
      - "traefik.http.services.wordpress-ssl.loadbalancer.server.port=80"
      - "traefik.docker.network=proxy"

Wichtig ist hier auch, dass unser Service “wordpress-ssl” heißt und dementsprechend die Labels auch alle auf den Namen “wordpress-ssl” angepasst werden müssen. Dies kann aber auch jeder andere Name sein. Muss nur konstant sein.

Ihr müsst immer noch das Netzwerk hinzufügen, mit dem Traefik kommuniziert. Dieses Netzwerk ist im einfachsten Fall bei jeder Anwendung identisch. In diesem Fall also “proxy”.

  wordpress:
    networks:
      - proxy

networks:
  proxy:
    external: true

Der Name “proxy” wurde hier auch frei gewählt. Dort könnt ihr auch jeden anderen Namen verwenden. Das Netzwerk wird normalerweise durch die docker-compose.yml generiert. Sollte es das nicht der Fall sein:

docker network create proxy

4. CrowdSec

CrowdSec ist ein kostenloses, quelloffenes und kollaboratives IPS. Mit dem Verhaltensweisen analysiert werden, auf Angriffe reagiert wird und Hinweise in der Community geteilt werden. Es ist sozusagen das neue Fail2Ban.

4.1. Verzeichnisse / Dateien anlegen

Als erstes legen wir uns ein Verzeichnis für Crowdsec an und wechsel in das Verzeichnis:

mkdir -p /opt/containers/crowdsec
cd /opt/containers/crowdsec

Anschließend erzeugen wir uns noch Datein und Ordner, welche wir später noch benötigen:

mkdir -p /opt/containers/crowdsec/{config,data}
touch /opt/containers/crowdsec/.env

4.2. Docker-Compose.yml anlegen

Wir könnten CrowdSec nativ auf unserem Host installieren, allerdings machen wir hier alles mit Docker und auch CrowdSec funktioniert wunderbar mit Docker. In diesem Schritt legen wir uns für CrowdSec die docker-compose.yml an:

nano /opt/containers/crowdsec/docker-compose.yml
version: "3.9"
services:
  crowdsec:
    container_name: crowdsec
    image: crowdsecurity/crowdsec:latest
    environment:
      PGID: "1000"
      COLLECTIONS: "crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/whitelist-good-actors"
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./config:/etc/crowdsec
      - ./data:/var/lib/crowdsec/data
      - /var/log/auth.log:/var/log/auth.log:ro
      - /var/log/crowdsec:/var/log/crowdsec:ro
    restart: unless-stopped
    security_opt:
      - no-new-privileges=true
    networks:
      - proxy
    hostname: crowdsec

networks:
  proxy:
    external: true

Bis hier her ist das docker-compose.yml nichts besonderes. Wichtig ist, dass wir die GID Berechtigungen gesetzt haben und alle Logs lesen können. Dafür mounten wir:

      - /var/log/auth.log:/var/log/auth.log:ro
      - /var/log/crowdsec:/var/log/crowdsec:ro

Die Umgebungsvariable COLLECTIONS ermöglicht es uns die Vorinstallation von Collections aus dem CrowdSec Hub. Collections sind Bündel von Parsern und Szenarien, die es einfach machen, Dinge für beliebte Dienste einzurichten. Standardmäßig wird der Container mit der Linux-Sammlung ausgeliefert, welche Syslog- und SSH-Protokolle verarbeitet. Wir werden die traefik-Sammlung, die http-cve-Sammlung, die eine Sammlung derzeitiger aktiver CVEs abdeckt, und die whitelist-good-actors-Sammlung hinzufügen, die hauptsächlich CDNs wie Cloudflare abdeckt. Damit stellen wir sicher, dass wir sie nicht versehentlich blockieren und uns von großen Teilen des Internets abschneiden.

4.3. Konfiguration anpassen

Nun starten wir den Container um alle wichtigen Datein generieren zu lassen:

docker compose -f /opt/containers/crowdsec/docker-compose.yml up -d

4.3.1. Acquis.yaml bearbeiten

Nach dem wir den den Container gestartet haben finden wir die Datei config/acquis.yaml und passen diese für unsere Bedürfnisse an:

sudo nano /opt/containers/crowdsec/config/acquis.yaml
filenames:
 - /var/log/auth.log
 - /var/log/syslog
labels:
  type: syslog
---
filenames:
  - /var/log/crowdsec/traefik/*.log
labels:
  type: traefik
---

4.3.2. Traefik.yml anpassen

Jetzt müssen wir innerhalb Traefik die Ausgabe der Log-Dateien definieren um diese CrowdSec zur Verfügugn zu stellen. Dafür passen wir von Traefik die traefik.yml an. Folgenden Teil kopieren wir uns an das Ende der Datei:

nano /opt/containers/traefik/data/traefik.yml
# [...] Ende der Datei

log:
  level: "INFO"
  filePath: "/var/log/traefik/traefik.log"

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

4.3.3. Traefik docker-compose.yml anpassen

In diesem Schritt ergänzen wir die docker-compose.yml von Traefik um einen Mount für die Logs:

nano /opt/containers/traefik/docker-compose.yml
services:
  traefik:
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme_letsencrypt.json:/acme_letsencrypt.json
      - ./data/dynamic_conf.yml:/dynamic_conf.yml
      - /var/log/crowdsec/traefik/:/var/log/traefik/          # Diese Zeile muss hinzugefügt werden

4.3.4. Traefik neustarten

Um die geänderte Konfiguration zu laden starten wir Traefik erneut:

docker compose -f /opt/containers/traefik/docker-compose.yml up -d --force-recreate

4.3.5. CrowdSec Konfiguration neu laden

Jetzt muss noch die Konfiguration von CrowdSec neu geladen werden, dass er auch auf die von uns definierten Logs zugreifen kann. Dafür muss man den Container nicht neutstarten sondern kann einfach die Konfig neu laden:

docker compose exec -t crowdsec kill -SIGHUP 1

4.4. Traefik Bouncer

CrowdSec ist nun in der Lage die Log-Dateien zu lesen und verdächtige IP-Adressen auf die Ban-Liste zu setzten. Nice! Allerdings in dem aktuellen Zustand wird noch nichts unternommen um den potenziellen Angriff abzuwehren. In CrowdSec sind sogenante Bouncer dafür zuständig. Von diesen Bouncern gibt es sehr viele! Es gibt einen Firewall Bouncer für iptables/nftables, einen Cloudflare Bouncer um die Firewall von Cloudflare zu steueren und es gibt auch einen Traefik Bouncer, was für uns hier interessant ist.

4.4.1. CrowdSec: docker-compose.yml ergänzen

Wir ergänzen CrowdSec um den Traefik Bouncer:

nano /opt/containers/crowdsec/docker-compose.yml
  bouncer-traefik:
    container_name: crowdsec-bouncer-traefik
    image: fbonalair/traefik-crowdsec-bouncer:latest
    environment:
      CROWDSEC_BOUNCER_API_KEY: ${TRAEFIK_BOUNCER_KEY}
      CROWDSEC_AGENT_HOST: crowdsec:8080
    restart: unless-stopped
    depends_on:
      - crowdsec
    networks:
      - proxy
    hostname: crowdsec-bouncer-traefik

4.4.2. API-Schlüssel generieren

Nun müssen wir noch einen API-Schlüssel erzeugen in dem wir den Bouncer bei CrowdSec registieren:

docker compose exec -t crowdsec cscli bouncers add bouncer-traefik

Es wird uns der API-Key ausgegeben. Diesen müssen wir uns unbedingt notieren, da er nur dieses einemal angezeigt wird. Wir kopieren ihn dann in unsere .env-Datei:

nano /opt/containers/crowdsec/.env
TRAEFIK_BOUNCER_KEY=ef422bf99a6883bc9042x0xxx00x0xxx

4.4.3. Traefik erweitern

Nun können wir für jeden Container verdächtige IPs blocken aber wir werden diese Option global Konfigurieren. Damit kann keine unerwünschte IP Adresse auf unsere Services zugreifen.

Um dies zu aktivieren erweitern wir unsere dynamic_conf.yml von Traefik um eine weitere middleware:

nano /opt/containers/traefik/data/dynamic_conf.yml
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:
    traefikAuth:
      basicAuth:
        users:
          - "benutzer:passworthash"
    default:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    secHeaders:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    default-security-headers:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
#       Deprecated
#       sslRedirect: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: "SAMEORIGIN"
    gzip:
      compress: {}

    crowdsec-bouncer:
      forwardauth:
        address: http://crowdsec-bouncer-traefik:8080/api/v1/forwardAuth
        trustForwardHeader: true

Jetzt noch die statische Konfiguration von Traefik anpassen. Wir fügen zu unseren Einstiegspunkten (entryPoints) die gerade definierte Middleware standardgemäß hinzu:

nano /opt/containers/traefik/data/traefik.yml
api:
  dashboard: true

certificatesResolvers:
  http:
    acme:
      email: "meine@email.de"
      storage: "acme_letsencrypt.json"
      httpChallenge:
        entryPoint: http

entryPoints:
  http:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: "https"
          scheme: "https"
#      An dieser Stelle auskommentiert. Bevor die Anfrage an crowdsec geht wird sie erst an https weitergeleitet
#      middlewares:
#        - crowdsec-bouncer@file
  https:
    address: ":443"
    http:
      middlewares:
        - crowdsec-bouncer@file

global:
  checknewversion: true
  sendanonymoususage: false

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: "proxy"
  file:
    filename: "./dynamic_conf.yml"
    watch: true
  providersThrottleDuration: 10

log:
  level: "INFO"
  filePath: "/var/log/traefik/traefik.log"

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

5. Dienste neustarten

Nun starten wir nach der Reihe die Dienste einmal neu. Dannach wird jede eingehende Anfrage von Traefik an den Bouncer weitergeleitet, um zu prüfen, ob die IP auf der Banliste steht. Alle IP Adressen auf der Banliste bekommen dann 403 Forbidden:

docker compose -f /opt/containers/crowdsec/docker-compose.yml up -d --force-recreate
docker compose -f /opt/containers/traefik/docker-compose.yml up -d --force-recreate

5.1. Erfolgskontrolle

Nun können wir kontrollieren ob CrowdSec Log-Dateien von Traefik gefunden hat und diese auch analysiert:

docker exec crowdsec cscli metrics
Teil der Ausgabe

6. Ältere Anleitungen anpassen

Viele Anleitungen auf GoNeuland sind entstanden bevor es diese hier gab. Das bedeutet wir müssen zumindest kleine Anpassungen an den Traefik Labels des jeweiligen Container machen. So sehen Beispielhaft die typischen Labels einer Anleitung aus:

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.invidious.entrypoints=http"
      - "traefik.http.routers.invidious.rule=Host(`video.euredomain.de`)"
      - "traefik.http.middlewares.invidious-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.invidious.middlewares=invidious-https-redirect"
      - "traefik.http.routers.invidious-secure.entrypoints=https"
      - "traefik.http.routers.invidious-secure.rule=Host(`video.euredomain.de`)"
      - "traefik.http.routers.invidious-secure.tls=true"
      - "traefik.http.routers.invidious-secure.tls.certresolver=http"
      - "traefik.http.routers.invidious-secure.service=invidious"
      - "traefik.http.services.invidious.loadbalancer.server.port=3000"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.invidious-secure.middlewares=secHeaders@file"

Ich möchte diese jetzt Stück für Stück an die neue Anleitung anpassen euch dabei mitnehmen. Ich zeige euch die Stellen an denen wir was verändern.

Folgender Teil kann entfernt werden, da wir das schon in der traefik.yml definiert haben:

      - "traefik.http.middlewares.invidious-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.invidious.middlewares=invidious-https-redirect"

7. Quellen