Jonathan Starck

Notfallsanitäter

Praxisanleiter

Systemadministrator

Jonathan Starck

Notfallsanitäter

Praxisanleiter

Systemadministrator

Blog Post

Traefik v2 + 3 – Reverse-Proxy mit CrowdSec im Stack einrichten

10. Juni 2023 Allgemein


Wenn mehrere Dienste auf demselben Server über Port 80 (für HTTP) oder 443 (für HTTPS) kommunizieren sollen, kann dies zu Konflikten führen, da normalerweise nur ein Dienst einen bestimmten Port zu einem bestimmten Zeitpunkt belegen kann. Hier kommt Traefik ins Spiel und hilft, dieses Problem zu lösen.

Traefik agiert als Reverse-Proxy und Load-Balancer und ermöglicht es, dass alle eingehenden Anfragen auf Port 80 oder 443 an die richtigen Dienste weitergeleitet werden, basierend auf den von Ihnen konfigurierten Regeln. Dies wird manchmal als “Routing” bezeichnet.

Inhaltsverzeichnis

0. Versionierung

DatumÄnderung
04.06.2023sleep 30 aus Punkt 5.2. entfernt
25.05.2023Migration ans Ende der Einleitung verschoben
Initialer Release
Zeitlicher Änderungsverlauf des Artikels

1. Zielsetzung & Vorwort

  • Traefik v2 installieren und konfigurieren
  • Crowdsec installieren und konfigurieren
  • Traefik Services via CrowdSec sichern
  • Einzelner Stack
  • Wie wir unsere Services mit Traefik verfügbar machen

Hinweis: Bitte beachtet, dass ich nicht jede Änderung im Vergleich zur alten Anleitung ausführlich darlegen werde. Allerdings werde ich besondere Sorgfalt darauf verwenden, an Stellen, an denen die neue Anleitung nicht mehr mit der alten kompatibel ist, einen entsprechenden Hinweis einzufügen. Mein Ziel ist es, euch durch diesen Prozess so reibungslos und verständlich wie möglich zu führen.

Wenn du ein neues Setup, also einen neuen Server aufsetzt, dann kannst du diese Anleitung einfach abarbeiten.
Solltest du aber die alte Anleitung befolgt haben und möchtest nun auf diese FullStack-Anleitung setzten musst du vorher ALLE Container und ALLE Netzwerke außer host,bridge und none herunterfahren und dann nach der Anleitung anpassen und Schritt für Schritt wieder hochfahren!

ACHTUNG! Die gesamte Anleitung wurde als root-User durchgeführt!

2. Voraussetzung

  • Docker mit Docker Compose installiert (Anleitung für Ubuntu / Debian) (Wichtig!)

3. Traefik + CrowdSec im Stack vorbereiten

In dieser aktualisierten Anleitung konzentrieren wir uns darauf, Traefik, CrowdSec und alle damit verbundenen Komponenten in einem einzigen Stack zu definieren. Warum? Ganz einfach, die Erfahrung hat gezeigt, dass die Startreihenfolge der Container durchaus relevant sein kann. Mit einem Full-Stack-Ansatz kann ich diesen Aspekt besser steuern und somit eine optimale Performance und Funktionalität gewährleisten.

Wichtiger Hinweis!
Diese Anleitung ist nicht vollständig kompatibel zu meiner alten und wohl bekanntesten Anleitung! Ich werde am Ende ein extra Abschnitt nur der Migration widmen.

3.1. Verzeichnisse / Dateien anlegen

Beginnen wir mit der Erstellung der benötigten Verzeichnisse für den Full-Stack. Im Folgenden wird das Hauptverzeichnis traefik-crowdsec-stack erstellt, und dann darin Unterordner für traefik, crowdsec, config und zwei zusätzliche Unterordner für config und data in crowdsec.

# Hauptverzeichnis erstellen und zusätzliche Unterordner in 'crowdsec' erstellen
mkdir -p /opt/containers/traefik-crowdsec-stack/{traefik,crowdsec/{config,data},config}

# Ins Hauptverzeichnis wechseln
cd /opt/containers/traefik-crowdsec-stack

Im nächsten Schritt erzeugen wir die notwendigen Dateien und setzen die korrekten Zugriffsrechte.

# .env Datei im Hauptverzeichnis erstellen
touch /opt/containers/traefik-crowdsec-stack/.env

# Umgebungsspezifische .env Dateien in 'config' erstellen
touch /opt/containers/traefik-crowdsec-stack/config/{crowdsec.env,traefik.env,traefik-crowdsec-bouncer.env}

# Zusätzliche Dateien in 'traefik' erstellen und Zugriffsrechte für bestimmte Dateien festlegen
touch /opt/containers/traefik-crowdsec-stack/traefik/{acme_letsencrypt.json,traefik.yml,dynamic_conf.yml,tls_letsencrypt.json}
chmod 600 /opt/containers/traefik-crowdsec-stack/traefik/{acme_letsencrypt.json,tls_letsencrypt.json}

Als nächstes möchten wir sicherstellen, dass alles korrekt angelegt wurde. Hierfür verwenden wir den tree Befehl. Falls das tree Programm noch nicht installiert ist, können wir es wie folgt installieren:

Für Ubuntu/Debian:

apt install tree

Jetzt können wir den Befehl tree Verzeichnis ausführen

tree -L 2 -a /opt/containers/traefik-crowdsec-stack/

um die Struktur zu überprüfen. Die Ausgabe sollte folgendermaßen aussehen:

.
├── config
│   ├── crowdsec.env
│   ├── traefik.env
│   └── traefik-crowdsec-bouncer.env
├── crowdsec
│   ├── config
│   └── data
├── .env
└── traefik
    ├── acme_letsencrypt.json
    ├── dynamic_conf.yml
    ├── tls_letsencrypt.json
    └── traefik.yml

Wenn eure Ausgabe genau so aussieht, habt ihr alles richtig gemacht und wir können weitermachen. Wenn nicht, überprüft bitte die vorherigen Schritte.

Bevor wir diesen Abschnitt abschließen, sollten wir einige wichtige Unterschiede zu meiner alten Anleitung hervorheben:

  • Datenstruktur: Die Art und Weise, wie wir unsere Daten strukturieren, hat sich erheblich verändert. Wir nutzen jetzt eine ordentlichere und effizientere Struktur, die es uns ermöglicht, die Dinge besser zu organisieren und zu verwalten.
  • Verwendung von .env Dateien: In dieser Anleitung verwenden wir .env Dateien, um Umgebungsvariablen zu speichern. Dies ist eine übliche Praxis, die uns hilft, sensitive Informationen sicher und organisiert zu halten, und es uns ermöglicht, verschiedene Einstellungen für verschiedene Umgebungen zu haben.
  • TLS Resolver: Ein zusätzliches Feature dieser Anleitung ist die Verfügbarkeit eines TLS Resolvers. Dies ist ein mächtiges Werkzeug, das wir im weiteren Verlauf der Anleitung detailliert behandeln werden.

3.2. docker-compose.yml anlegen

Wir beginnen nun mit der Erstellung unserer docker-compose.yml Datei. Hierfür verwenden wir den Texteditor nano. Um die Lesbarkeit zu verbessern und die Verwaltung zu erleichtern, teilen wir die Datei in mehrere Abschnitte auf. Jeder Abschnitt befasst sich mit einem bestimmten Aspekt unserer Stack-Konfiguration.

nano docker-compose.yml 

Mit dem obigen Befehl öffnen wir nano und erstellen gleichzeitig die Datei docker-compose.yml, falls sie noch nicht existiert. Nun können wir mit der Konfiguration unserer Full-Stack-Anwendung beginnen.

# Die Version der Docker Compose-Datei. Hier verwenden wir Version 3.9.
version: "3.9"
# Der Beginn des 'services' Abschnitts.
services:
  # Name des Services.
  crowdsec:
    # Der Name des Containers, der aus diesem Service erzeugt wird
    container_name: ${SERVICES_CROWDSEC_CONTAINER_NAME:-crowdsec}
    # Umgebungsvariablen für den Container.
    env_file: ./config/crowdsec.env
    # Hostname des CrowdSec-Containers, kann über eine Umgebungsvariable angepasst werden.
    hostname: ${SERVICES_CROWDSEC_HOSTNAME:-crowdsec}
    # Gesundheitsüberprüfung für den CrowdSec-Service
    healthcheck:
      test: ["CMD", "cscli", "version"]
      interval: 20s
      timeout: 2s
      retries: 5
      start_period: 10s
    # Docker-Image, das für den Container verwendet wird.
    image: ${SERVICES_CROWDSEC_IMAGE:-crowdsecurity/crowdsec}:${SERVICES_CROWDSEC_IMAGE_VERSION:-latest}
    # Netzwerke, zu denen der Container gehört.
    networks:
      crowdsec:
        # Feste IPv4-Adresse vergeben.
        ipv4_address: ${SERVICES_CROWDSEC_NETWORKS_CROWDSEC_IPV4:-172.31.254.254}
    # Restart-Strategie für den Container.
    restart: unless-stopped
    # Sicherheitsoptionen für den Container.
    security_opt:
      - no-new-privileges=true
    # Volumes, die vom Container verwendet werden.
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/log/auth.log:/var/log/auth.log:ro
      - /var/log/traefik:/var/log/traefik:ro
      - ./crowdsec/config:/etc/crowdsec
      - ./crowdsec/data:/var/lib/crowdsec/data
  # Der Name des Service
  traefik:
    # Der Name des Containers, der aus diesem Service erzeugt wird
    container_name: ${SERVICES_TRAEFIK_CONTAINER_NAME:-traefik}
    # Diese Option sorgt dafür, dass der Traefik-Service erst gestartet wird,
    # nachdem der Crowdsec-Service healthy ist
    depends_on:
      crowdsec:
        condition: service_healthy
    # Umgebungsvariablen für den Container.
    env_file: ./config/traefik.env
    # Hostname des Traefik-Containers, kann über eine Umgebungsvariable angepasst werden.
    hostname: ${SERVICES_TRAEFIK_HOSTNAME:-traefik}
    # Gesundheitsüberprüfung für den Traefik-Service
    healthcheck:
      test: ["CMD", "traefik", "healthcheck", "--ping"]
      interval: 10s
      timeout: 1s
      retries: 3
      start_period: 10s
    # Das Docker-Image, das für diesen Service verwendet wird
    # Version kann über eine Umgebungsvariable angepasst werden. Standard: 2.10
    image: ${SERVICES_TRAEFIK_IMAGE:-traefik}:${SERVICES_TRAEFIK_IMAGE_VERSION:-2.10}
    # Docker Labels für den Traefik-Service. Diese werden für die Traefik-Konfiguration verwendet
    labels:
      traefik.docker.network: proxy
      traefik.enable: "true"
      traefik.http.routers.traefik.entrypoints: websecure
      traefik.http.routers.traefik.middlewares: default@file
      traefik.http.routers.traefik.rule: Host(${SERVICES_TRAEFIK_LABELS_TRAEFIK_HOST})
      traefik.http.routers.traefik.service: api@internal
      traefik.http.routers.traefik.tls: "true"
      traefik.http.routers.traefik.tls.certresolver: http_resolver
      traefik.http.services.traefik.loadbalancer.sticky.cookie.httpOnly: "true"
      traefik.http.services.traefik.loadbalancer.sticky.cookie.secure: "true"
      traefik.http.routers.pingweb.rule: PathPrefix(`/ping`)
      traefik.http.routers.pingweb.service: ping@internal
      traefik.http.routers.pingweb.entrypoints: websecure
    # Die Netzwerke, zu denen dieser Service gehört
    networks:
      crowdsec:
        # IPv4-Adresse des CrowdSec Containers im Traefik-Netwerk: crowdsec
        ipv4_address: ${SERVICES_TRAEFIK_NETWORKS_CROWDSEC_IPV4:-172.31.254.253}
      proxy:
        # IPv4-Adresse des Traefik Containers im Traefik-Netwerk: proxy
        ipv4_address: ${SERVICES_TRAEFIK_NETWORKS_PROXY_IPV4:-172.16.255.254}
    # Die Ports, die diesem Service zugeordnet sind
    ports:
      - "80:80"  # HTTP
      - "443:443"  # HTTPS
    # Der Restart-Policy dieses Service
    restart: unless-stopped
    # Sicherheitsoptionen für diesen Service
    security_opt:
      - no-new-privileges:true
    # Die Volumes, die diesem Service zugeordnet sind
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/log/traefik/:/var/log/traefik/
      - ./traefik/traefik.yml:/traefik.yml:ro
      - ./traefik/acme_letsencrypt.json:/acme_letsencrypt.json
      - ./traefik/tls_letsencrypt.json:/tls_letsencrypt.json
      - ./traefik/dynamic_conf.yml:/dynamic_conf.yml
  # Definition des Traefik CrowdSec Bouncer-Dienstes
  traefik_crowdsec_bouncer:
    # Der Name des Containers, der aus diesem Service erzeugt wird
    container_name: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_CONTAINER_NAME:-traefik_crowdsec_bouncer}
    # Abhängigkeitsdefinition: Dieser Service wird erst gestartet, wenn der Crowdsec-Service als "healthy" gekennzeichnet ist
    depends_on:
      crowdsec:
        condition: service_healthy
    # Pfad zur .env-Datei für den Traefik CrowdSec Bouncer-Dienst
    env_file: ./config/traefik-crowdsec-bouncer.env
    # Der Hostname des Containers, kann über eine Umgebungsvariable angepasst werden
    hostname: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_HOSTNAME:-traefik-crowdsec-bouncer}
    # Docker-Image, das für den Container verwendet wird. Die Version kann über eine Umgebungsvariable angepasst werden
    image: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE:-fbonalair/traefik-crowdsec-bouncer}:${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE_VERSION:-latest}
    # Netzwerke, zu denen der Container gehört
    networks:
      crowdsec:
        # Feste IPv4-Adresse für den Container in diesem Netzwerk
        ipv4_address: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_NETWORKS_CROWDSEC_IPV4:-172.31.254.252}
    # Restart-Strategie für den Container. 'unless-stopped' bedeutet, dass der Container immer neu gestartet wird, es sei denn, er wird manuell gestoppt
    restart: unless-stopped
# Definition der Netzwerke, die von den Services verwendet werden
networks:
  # Definition des 'proxy' Netzwerks
  proxy:
    # Der Name des Netzwerks, kann über eine Umgebungsvariable angepasst werden
    name: ${NETWORKS_PROXY_NAME:-proxy}
    # Der Treiber, der für das Netzwerk verwendet wird, hier ist es 'bridge'
    driver: bridge
    # IP-Adress-Management-Konfiguration (IPAM)
    ipam:
      # Konfiguration des IP-Adress-Subnetzes für das Netzwerk
      config:
      - subnet: ${NETWORKS_PROXY_SUBNET_IPV4:-172.30.0.0/16}
    # Wenn 'attachable' auf 'true' gesetzt ist, können Standalone-Container an dieses Netzwerk angehängt werden
    attachable: true
  # Definition des 'crowdsec' Netzwerks
  crowdsec:
    # Der Name des Netzwerks, kann über eine Umgebungsvariable angepasst werden
    name: ${NETWORKS_CROWDSEC_NAME:-crowdsec}
    # Der Treiber, der für das Netzwerk verwendet wird, hier ist es 'bridge'
    driver: bridge
    # IP-Adress-Management-Konfiguration (IPAM)
    ipam:
      # Konfiguration des IP-Adress-Subnetzes für das Netzwerk
      config:
      - subnet: ${NETWORKS_CROWDSEC_SUBNET_IPV4:-172.31.0.0/16}
    # Wenn 'attachable' auf 'true' gesetzt ist, können Standalone-Container an dieses Netzwerk angehängt werden
    attachable: true
version: "3.9"
services:
  crowdsec:
    container_name: ${SERVICES_CROWDSEC_CONTAINER_NAME:-crowdsec}
    env_file: ./config/crowdsec.env
    hostname: ${SERVICES_CROWDSEC_HOSTNAME:-crowdsec}
    healthcheck:
      test: ["CMD", "cscli", "version"]
      interval: 20s
      timeout: 2s
      retries: 5
      start_period: 10s
    image: ${SERVICES_CROWDSEC_IMAGE:-crowdsecurity/crowdsec}:${SERVICES_CROWDSEC_IMAGE_VERSION:-latest}
    networks:
      crowdsec:
        ipv4_address: ${SERVICES_CROWDSEC_NETWORKS_CROWDSEC_IPV4:-172.31.254.254}
    restart: unless-stopped
    security_opt:
      - no-new-privileges=true
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/log/auth.log:/var/log/auth.log:ro
      - /var/log/traefik:/var/log/traefik:ro
      - ./crowdsec/config:/etc/crowdsec
      - ./crowdsec/data:/var/lib/crowdsec/data
  traefik:
    container_name: ${SERVICES_TRAEFIK_CONTAINER_NAME:-traefik}
    depends_on:
      crowdsec:
        condition: service_healthy
    env_file: ./config/traefik.env
    hostname: ${SERVICES_TRAEFIK_HOSTNAME:-traefik}
    healthcheck:
      test: ["CMD", "traefik", "healthcheck", "--ping"]
      interval: 10s
      timeout: 1s
      retries: 3
      start_period: 10s
    image: ${SERVICES_TRAEFIK_IMAGE:-traefik}:${SERVICES_TRAEFIK_IMAGE_VERSION:-2.10}
    labels:
      traefik.docker.network: proxy
      traefik.enable: "true"
      traefik.http.routers.traefik.entrypoints: websecure
      traefik.http.routers.traefik.middlewares: default@file
      traefik.http.routers.traefik.rule: Host(${SERVICES_TRAEFIK_LABELS_TRAEFIK_HOST})
      traefik.http.routers.traefik.service: api@internal
      traefik.http.routers.traefik.tls: "true"
      traefik.http.routers.traefik.tls.certresolver: http_resolver
      traefik.http.services.traefik.loadbalancer.sticky.cookie.httpOnly: "true"
      traefik.http.services.traefik.loadbalancer.sticky.cookie.secure: "true"
      traefik.http.routers.pingweb.rule: PathPrefix(`/ping`)
      traefik.http.routers.pingweb.service: ping@internal
      traefik.http.routers.pingweb.entrypoints: websecure
    networks:
      crowdsec:
        ipv4_address: ${SERVICES_TRAEFIK_NETWORKS_CROWDSEC_IPV4:-172.31.254.253}
      proxy:
        ipv4_address: ${SERVICES_TRAEFIK_NETWORKS_PROXY_IPV4:-172.30.255.254}
    ports:
      - "80:80"
      - "443:443"
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/log/traefik/:/var/log/traefik/
      - ./traefik/traefik.yml:/traefik.yml:ro
      - ./traefik/acme_letsencrypt.json:/acme_letsencrypt.json
      - ./traefik/tls_letsencrypt.json:/tls_letsencrypt.json
      - ./traefik/dynamic_conf.yml:/dynamic_conf.yml
  traefik_crowdsec_bouncer:
    container_name: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_CONTAINER_NAME:-traefik_crowdsec_bouncer}
    depends_on:
      crowdsec:
        condition: service_healthy
    env_file: ./config/traefik-crowdsec-bouncer.env
    hostname: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_HOSTNAME:-traefik-crowdsec-bouncer}
    image: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE:-fbonalair/traefik-crowdsec-bouncer}:${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE_VERSION:-latest}
    networks:
      crowdsec:
        ipv4_address: ${SERVICES_TRAEFIK_CROWDSEC_BOUNCER_NETWORKS_CROWDSEC_IPV4:-172.31.254.252}
    restart: unless-stopped
networks:
  proxy:
    name: ${NETWORKS_PROXY_NAME:-proxy}
    driver: bridge
    ipam:
      config:
      - subnet: ${NETWORKS_PROXY_SUBNET_IPV4:-172.30.0.0/16}
    attachable: true
  crowdsec:
    name: ${NETWORKS_CROWDSEC_NAME:-crowdsec}
    driver: bridge
    ipam:
      config:
      - subnet: ${NETWORKS_CROWDSEC_SUBNET_IPV4:-172.31.0.0/16}
    attachable: true

Es ist zu beachten, dass wir nun keine direkten Änderungen an der docker-compose.yml Datei mehr vornehmen müssen. Die meisten Einstellungen, einschließlich der Container-Namen, Hostnamen, Netzwerkeinstellungen und verwendeten Docker-Images, werden im nächsten Schritt über Umgebungsvariablen in einer .env Datei definiert.

3.3. DOTENV Konfiguration

In diesem Schritt bearbeiten wir die .env Datei, um die Dienste und Netzwerkeinstellungen anzupassen.

nano /opt/containers/traefik-crowdsec-stack/.env

Hier ist unser Setup:

# Service Crowdsec
SERVICES_CROWDSEC_CONTAINER_NAME=crowdsec
SERVICES_CROWDSEC_HOSTNAME=crowdsec
SERVICES_CROWDSEC_IMAGE=crowdsecurity/crowdsec
SERVICES_CROWDSEC_IMAGE_VERSION=latest
SERVICES_CROWDSEC_NETWORKS_CROWDSEC_IPV4=172.31.254.254

# Service Traefik
SERVICES_TRAEFIK_CONTAINER_NAME=traefik
SERVICES_TRAEFIK_HOSTNAME=traefik
SERVICES_TRAEFIK_IMAGE=traefik
SERVICES_TRAEFIK_IMAGE_VERSION=2.10
SERVICES_TRAEFIK_LABELS_TRAEFIK_HOST=`traefik.DeineDomainHier.de`
SERVICES_TRAEFIK_NETWORKS_CROWDSEC_IPV4=172.31.254.253
SERVICES_TRAEFIK_NETWORKS_PROXY_IPV4=172.30.255.254

# Service Traefik Crowdsec Bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_CONTAINER_NAME=traefik_crowdsec_bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_HOSTNAME=traefik-crowdsec-bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE=fbonalair/traefik-crowdsec-bouncer
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_IMAGE_VERSION=latest
SERVICES_TRAEFIK_CROWDSEC_BOUNCER_NETWORKS_CROWDSEC_IPV4=172.31.254.252

# Netzwerkeinstellungen
NETWORKS_PROXY_NAME=proxy
NETWORKS_PROXY_SUBNET_IPV4=172.30.0.0/16
NETWORKS_CROWDSEC_NAME=crowdsec
NETWORKS_CROWDSEC_SUBNET_IPV4=172.31.0.0/16

Bis auf eine Ausnahme ist dieses Beispielsetup bereits optimiert und erfordert keine weiteren Anpassungen, um richtig zu funktionieren. Die einzige Zeile, die du anpassen musst, ist SERVICES_TRAEFIK_LABELS_TRAEFIK_HOST. Hier definierst du die eigene Domain für das Traefik-Dashboard. Denke daran, `traefik.DeineDomainHier.de` durch die tatsächliche Domain zu ersetzen, welche auch via A oder CNAME auf den richtigen Server zeigt, die du für das Traefik-Dashboard verwenden möchtest.

Achtung! Die `` sind unabdingbar! Hier könnten aber auch mehrere Domains definiert werden:

SERVICES_TRAEFIK_LABELS_TRAEFIK_HOST=`traefik.DeineDomainHier.de`,`www.traefik.DeineDomainHier.de`

3.4. Erläuterung zur Konfiguration

3.4.1. Healthchecks

Unsere Konfiguration nutzt Healthchecks für Traefik und CrowdSec. Healthchecks sind praktische Funktionen, die uns ermöglichen zu überprüfen, ob ein Service ordnungsgemäß läuft. Wenn ein Healthcheck fehlschlägt, weiß Docker, dass mit diesem Service etwas nicht stimmt, und kann entsprechend reagieren.

Die Healthchecks sind in diesem Kontext besonders wichtig, weil wir mit ihnen und der depends_on-Funktion die Reihenfolge und Abhängigkeiten der einzelnen Services in unserem Stack besser kontrollieren können.

Die depends_on-Option erlaubt es uns, die Startreihenfolge unserer Container festzulegen. Ein Container, der von einem anderen Container abhängt, wird erst gestartet, wenn der abhängige Container vollständig hochgefahren und betriebsbereit ist. Mit Healthchecks können wir sicherstellen, dass ein Service nicht nur gestartet wurde, sondern tatsächlich ordnungsgemäß funktioniert, bevor wir einen abhängigen Service starten.

In unserem Fall sorgt dies dafür, dass der Traefik und CrowdSec vollständig betriebsbereit sind, bevor der Traefik CrowdSec Bouncer gestartet wird, was uns dabei hilft, sicherzustellen, dass unser Stack stabil und zuverlässig läuft.

3.4.2. Netzwerkkonfiguration

In unserer Konfiguration vergeben wir für die beiden Netzwerke proxy und crowdsec spezifische IP-Adressbereiche. Zudem weisen wir den drei Services jeweils feste IP-Adressen zu, die sich am Ende des jeweils verfügbaren IP-Bereichs befinden. Aber warum machen wir das eigentlich?

Der IP-Adressbereich jedes Netzwerks wird festgelegt, um eine gewisse Konsistenz in unserer Setup-Konfiguration zu gewährleisten. Wir weisen jeder Service-Instanz im jeweiligen Netzwerk eine spezifische IP-Adresse zu, die sich am Ende des verfügbaren IP-Bereichs befindet. Dadurch stellen wir sicher, dass die IP-Adressen der Services konstant bleiben und es unwahrscheinlich ist, dass sie versehentlich von Docker’s DHCP-Dienst einem anderen Container zugewiesen werden. Dies könnte zu Adresskonflikten führen.

Zudem benötigen viele Systeme, wie zum Beispiel WordPress, die als Docker-Container laufen, die genaue IP-Adresse des Proxy-Servers (in unserem Fall Traefik). Sollte diese IP-Adresse nicht festgelegt sein, können Fehlfunktionen auftreten. Das liegt daran, dass sich die IP-Adresse bei einem neuen DHCP-Lease ändern könnte, was dazu führen würde, dass die Einstellungen von WordPress oder ähnlichen Systemen nicht mehr korrekt wären.

Durch die Festlegung von spezifischen IP-Adressbereichen und die Zuweisung von IP-Adressen am Ende dieser Bereiche an unsere Services sorgen wir für eine stabile und konsistente Netzwerkkonfiguration. Dies ermöglicht es uns, später weitere Container sicher hinzuzufügen, die dann vom Docker-Netzwerk-DHCP eine Adresse aus dem Anfangsbereich des Netzwerks zugewiesen bekommen können.

3.4.3. Neustart und Abhängigkeiten

Unsere Verwendung eines Docker Stacks bietet weitere Vorteile in Bezug auf die Wartung und Zuverlässigkeit unseres Systems. Innerhalb eines Docker Stacks werden alle definierten Netzwerke und Dienste als zusammenhängende Einheit behandelt. Das bedeutet, dass bei einem Neustart des Stacks alle Dienste in der korrekten Reihenfolge und mit den notwendigen Abhängigkeiten hochgefahren werden.

Durch diese Organisation wird sichergestellt, dass, falls das System neu gestartet werden muss oder aus irgendeinem Grund ausfällt, alle Dienste und Netzwerke ordnungsgemäß und in der korrekten Reihenfolge wieder hergestellt werden.

Das trägt nicht nur zu einem stabileren System bei, sondern erleichtert auch die Wartung, da wir nicht manuell jeden Dienst in einer bestimmten Reihenfolge starten müssen. Der Docker Stack kümmert sich automatisch darum, was letztlich Zeit spart und das Risiko von Fehlern reduziert.

4. Traefik-Konfiguration

4.1. Konfiguration der traefik.yml

Jetzt widmen wir uns der Konfigurationsdatei traefik.yml. In dieser Datei setzen wir statische Konfigurationsoptionen fest.

nano /opt/containers/traefik-crowdsec-stack/traefik/traefik.yml

Hier ein unsere Konfiguration:

api:
  dashboard: true

metrics:
  prometheus:
    addrouterslabels: true

certificatesResolvers:
  http_resolver:
    acme:
      email: "deine@email.de"
      storage: "acme_letsencrypt.json"
      httpChallenge:
        entryPoint: web
  tls_resolver:
    acme:
      tlsChallenge: true
      email: "deine@email.de"
      storage: "tls_letsencrypt.json"
entryPoints:
  ping:
    address: ":88"
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: "websecure"
          scheme: "https"
      middlewares:
        - traefik-crowdsec-bouncer@file
  websecure:
    address: ":443"
    http:
      middlewares:
        - traefik-crowdsec-bouncer@file
    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

ping:
  entryPoint: "ping"

global:
  checknewversion: true
  sendanonymoususage: false

experimental:
  hub: true
  plugins:
    real-ip:
      moduleName: github.com/Paxxs/traefik-get-real-ip
      version: "v1.0.2"

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

Beachte bitte, dass du die E-Mail-Adressen unter certificatesResolvers zwei mal anpasst. Sie dienen als Kontaktinformationen für Let’s Encrypt und sollten daher auf eine gültige E-Mail-Adresse gesetzt sein, die du kontrollierst.

Hinweis! An dieser Stelle darf nur eine E-Mail-Adresse gesetzt werden. Zwei unterschiedliche führen zu einem Fehler:

traefik.go:81: command traefik error: unable to initialize certificates resolver "tls_resolver", all the acme resolvers must use the same email

4.2. Konfiguration der dynamic_conf.yml

Jetzt widmen wir uns der Konfigurationsdatei dynamic_conf.yml. In dieser Datei setzen wir statische Konfigurationsoptionen fest.

nano /opt/containers/traefik-crowdsec-stack/traefik/dynamic_conf.yml

Hier ein unsere Konfiguration:

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: {}

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

    real-ip-cf:
      plugin:
        real-ip:
          Proxy:
            - proxyHeadername: "*"
              realIP: Cf-Connecting-Ip
              OverwriteXFF: true

4.3. Erläuterung zu Traefik-Konfiguration

4.3.1. Änderung im ACME-Bereich

Unter dem Abschnitt certificatesResolvers haben wir den Bezeichner für den HTTP-Resolver von http in http_resolver geändert.

certificatesResolvers:
  http_resolver:
    acme:
      email: "deine@email.de"
      storage: "acme_letsencrypt.json"
      httpChallenge:
        entryPoint: web

Der Grund für diese Änderung ist, die Klarheit und Verständlichkeit der Konfigurationsdatei zu verbessern. Da der Begriff http an mehreren Stellen in der traefik.yml Konfiguration verwendet wird, kann dies zu Verwirrung führen. Durch die Umbenennung in http_resolver verdeutlichen wir, dass es sich hier speziell um den Mechanismus zur Erstellung des Zertifikats handelt, nicht um den allgemeinen HTTP-Eintrag unter entryPoints. Dies trägt zur Verbesserung der Lesbarkeit und des Verständnisses der Konfiguration bei.

4.3.2. Hinzufügen des tls_resolver

Unter dem Abschnitt certificatesResolvers haben wir nun auch einen TLS-Resolver: tls_resolver

tls_resolver:
  acme:
    tlsChallenge: true
    email: "deine@email.de"
    storage: "tls_letsencrypt.json"

Die TLS-Challenge (oder TLS-ALPN-01 Challenge) ist eine moderne und effektive Methode zur Überprüfung der Domainkontrolle für die Ausstellung von Let’s Encrypt-Zertifikaten. Sie nutzt eine spezielle TLS-Erweiterung namens “Application-Layer Protocol Negotiation” (ALPN) zur Kommunikation mit dem Let’s Encrypt-Validierungsserver während des TLS-Handshakes.

Für die Entwicklung von Python-Webanwendungen, insbesondere solchen, die auf dem Flask-Framework basieren, kann die Verwendung eines TLS-Resolvers besonders vorteilhaft sein. Viele solcher Anwendungen laufen auf Port 443 und profitieren daher von der Verwendung der TLS-Challenge zur Zertifikatserzeugung.

Wichtig zu beachten ist, dass die Verwendung der TLS-Challenge spezifische Anforderungen an die Server- und Netzwerkkonfiguration stellt. Unter anderem muss Ihr Server den ALPN-Erweiterungsstandard unterstützen und Port 443 muss in Ihrer Firewall offen sein. Daher ist es empfehlenswert, die Eignung Ihrer Server- und Netzwerkkonfiguration zu überprüfen, bevor Sie sich für die Verwendung der TLS-Challenge entscheiden.

4.3.3. Überarbeitung der EntryPoints

In unserem aktualisierten Setup haben wir einige Anpassungen an den entryPoints vorgenommen. Insbesondere haben wir den Namen des http entryPoint zu web und den https entryPoint zu websecure geändert.

entryPoints:
  ping:
    address: ":88"
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: "websecure"
          scheme: "https"
      middlewares:
        - traefik-crowdsec-bouncer@file
  websecure:
    address: ":443"
    http:
      middlewares:
        - traefik-crowdsec-bouncer@file
    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

Diese Änderungen sind hauptsächlich aus Kompatibilitäts- und Klarheitsgründen durchgeführt worden. Die offizielle Dokumentation von Traefik verwendet in der Regel web und websecure als entryPoint-Namen, und durch die Einhaltung dieser Benennungskonvention werden unsere Konfigurationen leichter verständlich für jeden, der mit Traefik vertraut ist. Darüber hinaus erleichtert die Einhaltung der offiziellen Benennungskonventionen das Verständnis und die Anwendung der offiziellen Traefik-Dokumentation.

Es ist wichtig zu beachten, dass trotz der Namensänderungen die Funktionalität der entryPoints gleich bleibt – web leitet den unverschlüsselten HTTP-Verkehr auf Port 80 und websecure handhabt den verschlüsselten HTTPS-Verkehr auf Port 443.

4.3.4. Anpassung der Middleware-Namen

In der neuesten Version der Konfiguration haben wir die Middleware für beide Einträge web und websecure aktualisiert. Die Middleware wurde von crowdsec-bouncer@file auf traefik-crowdsec-bouncer@file geändert.

entryPoints:
  ...
  web:
    ...
    http:
      middlewares:
        - traefik-crowdsec-bouncer@file
  websecure:
    ...
    http:
      middlewares:
        - traefik-crowdsec-bouncer@file

Der Grund für diese Änderung liegt in der klaren Namensführung und Übersichtlichkeit. Der Name traefik-crowdsec-bouncer verdeutlicht, dass dieser Bouncer speziell für Traefik konzipiert ist. Dies ist besonders wichtig, da es verschiedene Bouncer für unterschiedliche Anwendungen und Kontexte gibt. Ein klares und explizites Benennen erleichtert das Verständnis und die Wartung des Setups.

5. CrowdSec-Konfiguration

5.1. Konfiguration der crowdsec.env

Zur Konfiguration von CrowdSec bearbeiten wir die Datei config/crowdsec.env. Diese Datei ist essentiell, um die Umgebungsvariablen übersichtlich und zentral zu definieren. Durch die Auslagerung dieser Definitionen in eine separate Datei, bleibt unser Setup klar strukturiert und wartungsfreundlich.

nano /opt/containers/traefik-crowdsec-stack/config/crowdsec.env

Dort definieren wir ein paar Environments welche wir in CrowdSec benötigen:

PGID="1000"
COLLECTIONS="crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/whitelist-good-actors crowdsecurity/postfix crowdsecurity/dovecot crowdsecurity/nginx"
  • PGID: Dies steht für “Group ID” und bestimmt, unter welcher Gruppen-ID der CrowdSec-Prozess ausgeführt wird. Im vorliegenden Fall ist das die Gruppen-ID “1000”. Die Gruppen-ID sollte der ID einer existierenden Gruppe in deinem System entsprechen, die die benötigten Zugriffsrechte hat.
  • COLLECTIONS: Dies ist eine Liste von sogenannten “Collection”-Namen. Eine Collection in CrowdSec ist eine Gruppe von Szenarien, Parsern und Post-Overflows, die einen bestimmten Zweck erfüllen. Im vorliegenden Fall werden die folgenden Collections verwendet:
  • crowdsecurity/traefik: Diese Collection ist speziell für die Überwachung und den Schutz von Traefik, einem modernen Reverse-Proxy und Load-Balancer.
  • crowdsecurity/http-cve: Diese Collection enthält Szenarien zur Erkennung von bekannten Schwachstellen und Angriffen (CVEs) auf HTTP-Server.
  • crowdsecurity/whitelist-good-actors: Eine Collection, die dafür sorgt, dass bekannte “gute” Akteure nicht fälschlicherweise als bösartig erkannt werden.
  • crowdsecurity/postfix und crowdsecurity/dovecot: Diese beiden Collections sind speziell für die Überwachung und den Schutz von Postfix- und Dovecot-Mailservern.
  • crowdsecurity/nginx: Eine Collection, die speziell für die Überwachung und den Schutz von Nginx-Webservern entwickelt wurde.

Diese Umgebungsvariablen sind entscheidend für die Konfiguration von CrowdSec. Sie ermöglichen es, das Verhalten von CrowdSec fein abzustimmen und an die spezifischen Anforderungen deines Systems anzupassen. Es ist wichtig, diese Werte sorgfältig zu überprüfen und zu aktualisieren, um die bestmögliche Sicherheit zu gewährleisten.

5.2. Konfiguration der acquis.yaml

Zu diesem Zeitpunkt könntest du bemerkt haben, dass wir bisher keine acquis.yaml-Datei angelegt haben. Der Grund dafür ist, dass diese Datei Teil der Ressourcen ist, die das Docker-Image von CrowdSec bereitstellt. Um die Konfigurationsdateien von CrowdSec zu generieren, müssen wir den CrowdSec-Dienst kurz starten. Dazu wechseln wir zunächst in das Verzeichnis unserer Docker-Compose-Datei für den Traefik-CrowdSec-Stack und starten dann den Dienst:

cd /opt/containers/traefik-crowdsec-stack
docker compose up crowdsec -d && docker compose down

Mit dem Befehl docker compose up crowdsec -d starten wir den CrowdSec-Dienst im Hintergrundmodus. Schließlich beenden wir den Dienst wieder mit docker compose down.

Jetzt sollten alle Konfigurationsdateien von CrowdSec vorhanden sein und wir können nun die acquis.yaml-Datei nach anpassen. In der acquis.yaml-Datei werden die Log-Dateien definiert welche eingelesen und beobachtet werden sollen. Hier ist es wichtig, dass die Logdateien und Services auch in CrowdSec eingebunden sind. Dies habe ich in dieser Anleitung bereits in der docker-compose.yml angelegt:

version: "3.9"
services:
  crowdsec:
    ...
    volumes:
      ...
      # Hier wird die auth.log vom System in CrowdSec eingebracht
      - /var/log/auth.log:/var/log/auth.log:ro
      # Hier wird die der Ordner in den Traefik seine Logs schreibt in CrowdSec eingebracht
      - /var/log/traefik:/var/log/traefik:ro
      ...
  traefik:
    ...
    volumes:
      ...
      # Hier wird der Ordner in den Traefik seine Logs schreibt auf den Host gemounted
      - /var/log/traefik/:/var/log/traefik/
      ...
  ...
...

Nun zur acquis.yaml-Datei. Die haben wir nun generiert. Nach aktuellem Stand sieht diese so aus:

filenames:
  - /var/log/nginx/*.log
  - ./tests/nginx/nginx.log
#this is not a syslog log, indicate which kind of logs it is
labels:
  type: nginx
---
filenames:
 - /var/log/auth.log
 - /var/log/syslog
labels:
  type: syslog
---
filename: /var/log/apache2/*.log
labels:
  type: apache2

Wir passen diese nun an unsere Bedürfnisse an und dafür öffnen wir sie mit nano.

nano /opt/containers/traefik-crowdsec-stack/crowdsec/config/acquis.yaml

Dort fügen wir folgendes ein bzw. ersetzen den vorhandenen Inhalt:

filenames:
 - /var/log/auth.log
 - /var/log/syslog
labels:
  type: syslog
---
filenames:
  - /var/log/traefik/*.log
labels:
  type: traefik
---

In ersten Abschnitt der Konfigurationsdatei werden die auth.log und syslog Dateien zur Analyse hinzugefügt. Das Label “syslog” wird diesen Dateien zugeordnet. Dies ermöglicht es CrowdSec, bestimmte Analysemethoden auf diese Dateien anzuwenden, die für syslog-Logs geeignet sind.

Im zweiten Abschnitt der Konfigurationsdatei fügen wir alle Logdateien hinzu, die im Verzeichnis /var/log/traefik/ liegen und deren Dateinamen mit .log enden. Diesen Dateien weisen wir das Label “traefik” zu. So kann CrowdSec spezifische Analysemethoden anwenden, die für Traefik-Logs geeignet sind.

6. Traefik und Crowdsec verheiraten – der Bouncer

CrowdSec hat nun die Fähigkeit, Logdateien zu analysieren und verdächtige IP-Adressen in eine Sperrliste aufzunehmen – das ist ein großer Schritt! Doch in der aktuellen Konfiguration haben wir noch keine Maßnahmen ergriffen, um potenzielle Angriffe tatsächlich abzuwehren. Das ist die Aufgabe sogenannter “Bouncer” in CrowdSec.

Es gibt eine Vielzahl an verschiedenen Bouncern für diverse Einsatzgebiete: Es gibt zum Beispiel Bouncer für Firewalls wie iptables oder nftables, und es gibt auch Bouncer zur Steuerung der Firewall von Cloudflare. Für unseren konkreten Anwendungsfall ist jedoch der Traefik Bouncer besonders interessant, da er die Fähigkeit hat, speziell mit unserem Traefik Load Balancer zu interagieren.

6.1. Traefik CrowdSec Bouncer

Wie du vielleicht bemerkt hast, haben wir in der zu Beginn definierten Docker-Compose-Datei bereits alle notwendigen Komponenten für den Traefik CrowdSec Bouncer unter dem Abschnitt services -> traefik_crowdsec_bouncer eingefügt.

An dieser Stelle brauchen wir nur noch die zugehörige .env-Datei, die wir zuvor erstellt haben, um zwei weitere Werte zu erweitern in dem wir die Datei mit nano öffnen:

nano /opt/containers/traefik-crowdsec-stack/config/traefik-crowdsec-bouncer.env

Jetzt fügen wir folgende Variablen ein:

# Access-Token damit Bouncer und CrowdSec kommunizieren können
CROWDSEC_BOUNCER_API_KEY=
# Hostname mit richtigem Port von CrowdSec
CROWDSEC_AGENT_HOST=${SERVICES_CROWDSEC_HOSTNAME}:8080

6.1.1. Access Token anlegen

In der traefik-crowdsec-bouncer.env haben wir die Variable CROWDSEC_BOUNCER_API_KEY angelegt und um diese mit einem Access-Token bzw. API-Key auch nutzen zu können müssen wir diesen wie folgt generieren:

cd /opt/containers/traefik-crowdsec-stack
docker compose up crowdsec -d
docker compose exec -t crowdsec cscli bouncers add traefik-crowdsec-bouncer
docker compose down

So, oder so ähnlich sollte die Ausgabe aussehen:

[+] Running 3/3
 ✔ Network crowdsec    Created                                                                                                                                                                                0.1s 
 ✔ Network proxy       Created                                                                                                                                                                                0.1s 
 ✔ Container crowdsec  Started                                                                                                                                                                                0.4s 
Api key for 'traefik-crowdsec-bouncer':

   ee21c448d67e04550dec5b07b42ad6ee

Please keep this key since you will not be able to retrieve it!
[+] Running 3/3
 ✔ Container crowdsec  Removed                                                                                                                                                                               10.2s 
 ✔ Network crowdsec    Removed                                                                                                                                                                                0.2s 
 ✔ Network proxy       Removed

Nun speichern wir uns den generierten Schlüssel (in diesem Beispiel: ee21c448d67e04550dec5b07b42ad6ee) in die Zwischenablage und fügen via nano in unsere config/traefik-crowdsec-bouncer.env.

# Access-Token damit Bouncer und CrowdSec kommunizieren können
CROWDSEC_BOUNCER_API_KEY=ee21c448d67e04550dec5b07b42ad6ee
# Hostname mit richtigem Port von CrowdSec
CROWDSEC_AGENT_HOST=${SERVICES_CROWDSEC_HOSTNAME}:8080

6.1.2. Traefik kontrollieren

Wie wir bereits in einem früheren Abschnitt dieser Anleitung festgelegt haben, wird in der dynamic_conf.yml-Datei die traefik-crowdsec-bouncer-Konfiguration definiert:

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

Dieser Abschnitt ermöglicht es Traefik, mit dem CrowdSec Bouncer zu kommunizieren. Hierbei müssen wir sicherstellen, dass der unter address angegebene Hostname dem entspricht, den wir in der .env-Datei unter folgendem Punkt definiert haben:

SERVICES_TRAEFIK_CROWDSEC_BOUNCER_HOSTNAME=traefik-crowdsec-bouncer

Leider ist es in dieser Konfiguration nicht ohne Weiteres möglich, die Variable direkt zu setzen. Alternativ kann hier jedoch auch die IP-Adresse, die unter:

SERVICES_TRAEFIK_CROWDSEC_BOUNCER_NETWORKS_CROWDSEC_IPV4=172.31.254.252

definiert ist, verwendet werden.

In der traefik.yml-Datei haben wir unter entryPoints Traefik angewiesen, bei jeder Anfrage, die über den jeweiligen Point web bzw. websecure kommt, diese direkt über die Middleware an den Bouncer zu senden:

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: "websecure"
          scheme: "https"
      middlewares:
        - traefik-crowdsec-bouncer@file
  websecure:
    address: ":443"
    http:
      middlewares:
        - traefik-crowdsec-bouncer@file

Diese Konfiguration gewährleistet letztendlich das erfolgreiche Zusammenspiel zwischen allen Komponenten. Traefik kommuniziert mit dem Bouncer, der wiederum mit CrowdSec kommuniziert. Diese strukturierte Kommunikation zwischen den Komponenten sorgt dafür, dass potenziell schädliche Anfragen effektiv blockiert werden.

7. Dienst starten

Uff! Das war wirklich eine lange und gründliche Anleitung bis jetzt. Ich habe mein Bestes gegeben, um viele Aspekte direkt zu erläutern und das Verständnis der Funktionsweise sowie des Aufbaus des Stacks zu erleichtern.

Jetzt, bevor wir mit dem Starten des Dienstes fortfahren, möchte ich dich bitten: Nimm dir einen Moment Zeit, geh nochmal alle Schritte durch und prüfe, ob alle Dateien richtig erstellt und mit dem passenden Inhalt gefüllt wurden.

Also, gönn dir eine kleine Pause, streck dich mal und gönn deinen Augen eine kurze Verschnaufpause vom Bildschirm. Dann, mit frischem Blick und voller Energie, können wir uns dem nächsten Schritt zuwenden. Und keine Sorge, wir sind schon fast am Ziel!

☕️☕️☕️

Bereit?

cd /opt/containers/traefik-crowdsec-stack
docker compose up -d

🚀🚀🚀 Fertig, oder?

So, wir sind fast durch, aber es gibt noch ein paar Dinge, die wir erledigen müssen. Was fehlt? Die Überprüfung und die bereits angekündigte Migration der alten Anleitung zu diesem Setup.

Außerdem werde ich noch einige optionale Dinge hinzufügen. Es handelt sich dabei um Extras, die nicht unbedingt notwendig sind, aber je nach deinen spezifischen Anforderungen nützlich sein könnten. Bleib also dran und lass uns diese letzten Schritte gemeinsam erledigen!

8. Überprüfung

Natürlich sollten wir zwischendurch und gemäß dieser Anleitung immer wieder überprüfen, ob alles funktioniert, wie es sollte. Das tun wir jetzt nach all diesen Schritten.

Wir haben den Container ja bereits gestartet. Als erstes überprüfen wir, ob alle Container als “healthy” (gesund) markiert sind:

cd /opt/containers/traefik-crowdsec-stack
docker compose ps
root@mail:/opt/containers/traefik-crowdsec-stack# docker compose ps
NAME                       IMAGE                                       COMMAND                  SERVICE                    CREATED             STATUS                  PORTS
crowdsec                   crowdsecurity/crowdsec:latest               "/bin/sh -c '/bin/ba…"   crowdsec                   3 days ago          Up 19 hours (healthy)   
traefik                    traefik:2.10                                "/entrypoint.sh trae…"   traefik                    3 days ago          Up 22 hours (healthy)   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp
traefik_crowdsec_bouncer   fbonalair/traefik-crowdsec-bouncer:latest   "/app"                   traefik_crowdsec_bouncer   3 days ago          Up 3 days (healthy)     
root@mail:/opt/containers/traefik-crowdsec-stack# 

Wenn wir alles richtig gemacht haben, sind alle Container als “healthy” markiert.

Dann rufen wir das Traefik-Dashboard mit der zuvor festgelegten URL auf. Das Dashboard sollte sich öffnen und auch ein valides LE-Zertifikat haben.

Beim Öffnen des Dashboards werden auch Logdateien erstellt, und diese überprüfen wir als nächstes:

cd /var/log/traefik
ls -all
cat traefik.log
cat access.log

Hier sollten wir idealerweise die Dateien traefik.log und access.log finden, und in beide sollte etwas geschrieben sein. Wenn die Datei access.log nicht vorhanden ist, ist das kein Beinbruch, sollte aber beobachtet werden.

Wenn das alles passt, betrachten wir mit dem Befehl:

docker exec crowdsec cscli metrics

die ganz oben stehenden Acquisition Metrics. Dort sollte nun die traefik.log aufgeführt sein. Wenn die Datei access.log im System vorhanden ist, sollte sie ebenfalls dort erscheinen.

Sollte etwas nicht funktionieren schaue als erstes “Hinweis zu Kommentaren” an ☕️

9. Optional

Es gibt noch einige optionale Punkte. Diesen Bereich werde ich nach und nach erweitern bzw. auf vorhandene Anleitungen verweisen!

9.1. CrowdSec aktuell halten

Die benutzten COLLECTIONS werden überwiegend durch die Community gepflegt und natürlich auch auf neue Crowdsec-Versionen angepasst. Damit wir nicht in einen Fehler laufen oder eine veraltet COLLECTION verwenden ist es Sinnvoll diese regelmäßig zu aktualisieren. Dazu legen wir uns einen Cronjob an:

crontab -e

Dieser Cronjob wird jeden Tag um 03:00 Uhr aufgerufen, aktualisiert die Pakete aus dem CrowdSec Hub und läd die Konfiguration neu.

0 3 * * * docker exec crowdsec cscli hub update && docker exec crowdsec cscli hub upgrade && docker exec -t crowdsec kill -SIGHUP 1 >/dev/null 2>&1

9.2. Traefik Dashboard schützen

Um das Traefik Dashboard abzusichern und unbefugten Zugriff zu verhindern, wird empfohlen, eine zusätzliche Authentifizierung einzurichten. Dadurch wird eine Benutzername-Passwort-Überprüfung erforderlich, um auf das Dashboard zugreifen zu können.

Traefik bietet eine einfache Methode zur Implementierung der Authentifizierung. Um dies zu erreichen, müssen wir zunächst die folgenden Pakete auf unserem System installieren:

apt update && apt install apache2-utils

Nachdem wir diese Schritte durchgeführt haben, müssen wir innerhalb von Traefik eine Middleware konfigurieren. Bevor wir dies tun, erstellen wir ein Passwort mit dem folgenden Befehl:

echo $(htpasswd -nb DeinUsername 'DeinSuperSicheresPasswort')

# Ausgabe
DeinUsername:$apr1$xSRxT4UY$wk42WRgVzBW5Pf69sS5aT.

Es wird empfohlen, die Ausgabe zu speichern, zum Beispiel durch Kopieren und Einfügen in eine Textdatei.

9.2.1. dynamic_conf.yml anpassen

Jetzt ist es an der Zeit, die Middleware zu konfigurieren. Dazu öffnen wir die Datei dynamic_conf.yml mit Texteditor:

nano /opt/containers/traefik-crowdsec-stack/traefik/dynamic_conf.yml

Wir befinden uns im Abschnitt “http” -> “middlewares”. Bisher sieht dieser Teil folgendermaßen aus:

...
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: {}

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

    real-ip-cf:
      plugin:
        real-ip:
          Proxy:
            - proxyHeadername: "*"
              realIP: Cf-Connecting-Ip
              OverwriteXFF: true
...

Nun fügen wir die Konfiguration für unsere Schutzmaßnahmen hinzu:

    traefikAuth:
      basicAuth:
        users:
          - "DeinUsername:$apr1$xSRxT4UY$wk42WRgVzBW5Pf69sS5aT."

Hierbei ersetzen wir natürlich “DeinUsername” und “$apr1$xSRxT4UY$wk42WRgVzBW5Pf69sS5aT.” durch die zuvor generierten Werte.

Dieses Snippet fügen wir an beliebiger Stelle in die middlewares ein:

...
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: {}

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

    real-ip-cf:
      plugin:
        real-ip:
          Proxy:
            - proxyHeadername: "*"
              realIP: Cf-Connecting-Ip
              OverwriteXFF: true

    traefikAuth:
      basicAuth:
        users:
          - "DeinUsername:$apr1$xSRxT4UY$wk42WRgVzBW5Pf69sS5aT."
...

9.2.2. docker-compose.yml anpassen

Im letzten Schritt müssen wir Anpassungen in der Datei docker-compose.yml vornehmen. Öffnen Sie die Datei mit einem Texteditor:

nano /opt/containers/traefik-crowdsec-stack/docker-compose.yml

Suchen Sie den Abschnitt für Traefik:

  traefik:
    container_name: ${SERVICES_TRAEFIK_CONTAINER_NAME:-traefik}
    depends_on:
      crowdsec:
        condition: service_healthy
    env_file: ./config/traefik.env
    hostname: ${SERVICES_TRAEFIK_HOSTNAME:-traefik}
    healthcheck:
      test: ["CMD", "traefik", "healthcheck", "--ping"]
      interval: 10s
      timeout: 1s
      retries: 3
      start_period: 10s
    image: ${SERVICES_TRAEFIK_IMAGE:-traefik}:${SERVICES_TRAEFIK_IMAGE_VERSION:-2.10}
    labels:
      traefik.docker.network: proxy
      traefik.enable: "true"
      traefik.http.routers.traefik.entrypoints: websecure
      traefik.http.routers.traefik.middlewares: default@file
      traefik.http.routers.traefik.rule: Host(${SERVICES_TRAEFIK_LABELS_TRAEFIK_HOST})
      traefik.http.routers.traefik.service: api@internal
      traefik.http.routers.traefik.tls: "true"
      traefik.http.routers.traefik.tls.certresolver: http_resolver
      traefik.http.services.traefik.loadbalancer.sticky.cookie.httpOnly: "true"
      traefik.http.services.traefik.loadbalancer.sticky.cookie.secure: "true"
      traefik.http.routers.pingweb.rule: PathPrefix(`/ping`)
      traefik.http.routers.pingweb.service: ping@internal
      traefik.http.routers.pingweb.entrypoints: websecure
    networks:
      crowdsec:
        ipv4_address: ${SERVICES_TRAEFIK_NETWORKS_CROWDSEC_IPV4:-172.31.254.253}
      proxy:
        ipv4_address: ${SERVICES_TRAEFIK_NETWORKS_PROXY_IPV4:-172.16.255.254}
    ports:
      - "80:80"
      - "443:443"
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/log/traefik/:/var/log/traefik/
      - ./traefik/traefik.yml:/traefik.yml:ro
      - ./traefik/acme_letsencrypt.json:/acme_letsencrypt.json
      - ./traefik/tls_letsencrypt.json:/tls_letsencrypt.json
      - ./traefik/dynamic_conf.yml:/dynamic_conf.yml

Innerhalb dieses Abschnitts finden Sie unter “labels” das Label:

traefik.http.routers.traefik.middlewares: default@file

Erweitern Sie diese Zeile nun um unsere gerade erstellte Middleware “traefikAuth”:

traefik.http.routers.traefik.middlewares: default@file,traefikAuth@file

9.2.3 Neustart des Setups

Abschließend führen wir einen Neustart des FullStacks durch und überprüfen anschließend, ob beim Aufruf des Traefik Dashboards nun ein Passwort abgefragt wird.

Wechseln Sie zum Verzeichnis des Traefik-Crowdsec-Stacks:

cd /opt/containers/traefik-crowdsec-stack

Starten Sie den FullStack neu und erzwingen Sie die Neuerstellung der Container:

docker compose up -d --force-recreate

Nachdem der Neustart abgeschlossen ist, überprüfen Sie, ob beim Aufruf des Traefik Dashboards nun ein Passwort abgefragt wird.

9.3. Erweiterung der Firewall (IP-Tables bzw. UFW) mit CrowdSec

Jetzt wollen wir CrowdSec auf unserem System installieren und die Firewall mithilfe des Bouncers um CrowdSec erweitern. In dieser Anleitung haben wir CrowdSec bereits über Docker in unser System integriert. Doch wir möchten den Firewall-Bouncer direkt auf unserem System installieren und ihn dann mit CrowdSec verknüpfen, das in einem Docker-Container läuft.

9.3.1. Repository für CrowdSec Firewall Bouncer

Um den Firewall Bouncer zu installieren, müssen wir das entsprechende Repository auf unserem System einbinden. Wir führen den folgenden Befehl aus, um die erforderlichen Repositorys hinzuzufügen:

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash

Dadurch werden die benötigten Repositorys zu unserem System hinzugefügt.

9.3.2. Installation des Firewall Bouncers

Die Pakete für den CrowdSec Firewall Bouncer sind in unseren Repositories verfügbar. Wir müssen das Paket entsprechend unserem Firewall-System auswählen: IPTables bzw. UFW

Verwenden wir den folgenden Befehl, um den CrowdSec Firewall Bouncer für IPTables bzw. UFW zu installieren:

apt install crowdsec-firewall-bouncer-iptables

9.3.3. Access Token anlegen

Ähnlich wie in Schritt 6.1.1. benötigen wir auch für den CrowdSec Firewall Bouncer für IPTables einen Access Token, um mit CrowdSec kommunizieren zu können. Um diesen Access Token zu erhalten, führen wir die folgenden Schritte aus:

cd /opt/containers/traefik-crowdsec-stack
docker compose exec -t crowdsec cscli bouncers add crowdsec-firewall-bouncer-iptables

So könnte die erwartete Ausgabe aussehen:

API-Schlüssel für 'crowdsec-firewall-bouncer-iptables':
   ee21c448d67e04550dec5b07b42ad6ee
Wir müssen beachten, dass wir diesen Schlüssel gut aufbewahren, da wir ihn nicht erneut abrufen können!

Nun speichern wir uns den generierten Schlüssel (in diesem Beispiel: ee21c448d67e04550dec5b07b42ad6ee) in die Zwischenablage.

9.3.4. Anpassung der Konfiguration

Um die Konfiguration anzupassen, geben wir einfach den folgenden Befehl ein:

nano /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml

Vorher:

api_url: http://127.0.0.1:8080/
api_key:

Nachher:

api_url: http://172.31.254.254:8080/
api_key: ee21c448d67e04550dec5b07b42ad6ee

Hierbei ersetzen wir die Werte für “api_url” und “api_key” entsprechend. Beachtet, dass die euer eigener Access-Token individuell ist und von den oben gezeigten Beispielen abweichen wird.

9.3.5. CrowdSec Firewall Bouncer starten

Jetzt starten wir den Bouncer mit dem folgenden Befehl:

systemctl start crowdsec-firewall-bouncer

Anschließend überprüfen wir den Status mit dem Befehl:

systemctl status crowdsec-firewall-bouncer

Die Ausgabe sollte in etwa wie folgt aussehen:

 crowdsec-firewall-bouncer.service - The firewall bouncer for CrowdSec
     Loaded: loaded (/etc/systemd/system/crowdsec-firewall-bouncer.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2022-10-24 23:51:09 CEST; 21s ago
    Process: 14877 ExecStartPre=/usr/sbin/crowdsec-firewall-bouncer -c /etc/crowdsec/bouncers/crowd
..
..

Dadurch stellen wir sicher, dass der CrowdSec Firewall Bouncer ordnungsgemäß gestartet ist.

9.3.6. Anzeigen der CrowdSec Bouncer-Liste

Jetzt können wir uns alle CrowdSec Bouncer anzeigen lassen, um sicherzustellen, dass unser Firewall Bouncer dort aufgeführt wird.

Führen wir den folgenden Befehl aus:

docker exec crowdsec cscli bouncers list

Das Ergebnis sollte in etwa wie folgt aussehen:

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Name                                 IP Address       Valid   Last API pull          Type                        Version                                                        Auth Type 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 traefik-crowdsec-bouncer             172.31.255.252   ✔️       2023-05-24T21:14:45Z   Go-http-client              1.1                                                            api-key   
 crowdsec-firewall-bouncer-iptables   172.31.240.1     ✔️       2023-05-24T21:15:27Z   crowdsec-firewall-bouncer   v0.0.27-debian-pragmatic-8d09f19d69e92a63e63888794af3a57c6a-   api-key   
                                                                                                                  de3489                                                                   
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Wir müssen beachten, dass die im obigen Beispiel aufgeführten IP-Adressen individuell für unser Setup sind und bei uns andere Werte haben werden.

Damit haben wir bestätigt, dass alles erfolgreich funktioniert hat und unser Firewall Bouncer in der Liste aufgeführt wird.

9.3.7. Auslesen des CrowdSec Firewall Bouncer Logs

Nun werfen wir einen Blick auf das Log des Firewall Bouncers. Dies kann besonders im Fehlerfall sehr hilfreich sein. Geben wir dazu den folgenden Befehl ein:

cat /var/log/crowdsec-firewall-bouncer.log

In den letzten Zeilen sollten wir Informationen sehen, dass unser Bouncer neue Entscheidungen von CrowdSec erhalten hat. Ein Beispiel könnte wie folgt aussehen:

time="24-10-2022 23:51:09" level=info msg="Using API key auth"
time="24-10-2022 23:51:09" level=info msg="Processing new and deleted decisions . . ."
time="24-10-2022 23:51:09" level=info msg="14 decisions added"
time="24-10-2022 23:52:59" level=info msg="1 decision added"
time="24-10-2022 23:54:29" level=info msg="1 decision added"

Wir müssen beachten, dass die genauen Informationen in unserem Log individuell sein können und von den oben gezeigten Beispielen abweichen können. Durch das Auslesen des Logs können wir jedoch überprüfen, ob unser Bouncer neue Entscheidungen von CrowdSec erhalten hat.

9.3.8. Automatisches Starten des CrowdSec Firewall Bouncers

Um sicherzustellen, dass unser Firewall Bouncer automatisch bei jedem Serverneustart gestartet wird, können wir einen Cron-Eintrag erstellen. Dadurch ersparen wir uns das manuelle Starten nach jedem Neustart. Credits an @ice-cue für diesen Tipp in der alten Anleitung!

Geben wir den folgenden Befehl ein, um den Cron-Editor zu öffnen:

crontab -e

Dort fügen wir nun folgende Zeile hinzu:

@reboot sleep 120 && systemctl start crowdsec-firewall-bouncer

Mit diesem Befehl wird der CrowdSec Firewall Bouncer 120 Sekunden nach dem Serverneustart gestartet. Dadurch ist gewährleistet, dass unser Firewall Bouncer automatisch beim Start des Servers gestartet wird.

9.3.9. Anzeigen der gebannten IP-Adressen

Wir könnn uns mit einem Befehl alle aktuellen Bans anschauen:

docker exec crowdsec cscli decisions list

9.3.10. Eigene IP-Adresse bannen

Um zu testen, ob alles funktioniert, wollen wir uns selbst bannen. Dadurch haben wir für EINE MINUTE keinen Zugriff mehr auf unseren Server. Zuerst ermitteln wir unsere eigene IP-Adresse mithilfe einer Webseite wie z. B. https://www.wieistmeineip.de/.

Anschließend geben wir den folgenden Befehl ein. Achtet darauf, dass ihr die “1.2.3.4” durch eure eigene IP-Adresse ersetzt:

docker exec crowdsec cscli decisions add --ip 1.2.3.4 --duration 1m

Wenn wir alles richtig gemacht haben, sollten unsere SSH-Verbindung sowie alle anderen Verbindungen zu unserem Server nun eine Minute lang blockiert bzw. gesperrt sein.

Bitte beachtet, dass dies nur zu Testzwecken empfohlen ist und wir sicherstellen sollten, dass wir wieder Zugriff auf unseren Server erhaltet, nachdem der Test abgeschlossen ist.

9.4. Logrotate für Traefik

Es ist bereits bekannt, dass die traefik.log-Datei sehr schnell wächst, insbesondere wenn das Debug-Level auf “info” gesetzt ist. Das kann schnell zu Speicherplatzproblemen führen, wenn sie nicht gehandhabt wird. Traefik selbst bietet keine eingebaute Log-Rotation-Funktion, aber zum Glück gibt es eine Standardkomponente namens Logrotate in vielen Linux-Distributionen wie Ubuntu, die uns dabei helfen kann.

[ihc-hide-content ihc_mb_type=”show” ihc_mb_who=”3,4″ ihc_mb_template=”1″ ]

Logrotate ist ein Systemdienst, der automatisch alte Logdateien löscht und neue erstellt. Es kann täglich, wöchentlich, monatlich oder wann immer der Logfile eine bestimmte Größe erreicht, ausgeführt werden.

Zunächst erstellen wir eine neue Konfigurationsdatei für Logrotate. Wir können nano verwenden, um die Datei zu öffnen:

nano /etc/logrotate.d/traefik

In dieser Datei fügen wir die folgende Konfiguration ein, passen den Pfad zu unserer traefik.log-Datei entsprechend an:

/var/log/traefik/traefik.log {
    daily
    missingok
    rotate 5 
    compress
    delaycompress
    notifempty
    create 0644 root root
    postrotate
       docker restart $(docker ps --no-trunc --filter name=^/traefik$ --quiet)
    endscript
}

Hier sind die einzelnen Zeilen erklärt:

  • /var/log/traefik/traefik.log {: Dieser Eintrag bestimmt, welche Logdatei rotiert wird. In diesem Fall ist es die Traefik-Logdatei.
  • daily: Diese Direktive gibt an, dass die Logdatei täglich rotiert werden soll.
  • missingok: Diese Direktive besagt, dass es in Ordnung ist, wenn die Logdatei fehlt und Logrotate beim Versuch, sie zu rotieren, keinen Fehler ausgeben soll.
  • rotate 5: Diese Direktive bestimmt, wie viele alte Logdateien aufbewahrt werden sollen. In diesem Fall werden fünf alte Logdateien aufbewahrt.
  • compress: Diese Direktive gibt an, dass alte Logdateien komprimiert werden sollen, um Speicherplatz zu sparen.
  • delaycompress: Diese Direktive bedeutet, dass die Kompression der Logdatei erst nach der zweiten Rotation erfolgt. Dies ist nützlich, wenn Programme noch auf die alte Logdatei zugreifen könnten.
  • notifempty: Diese Direktive besagt, dass die Logdatei nicht rotiert werden soll, wenn sie leer ist.
  • create 0644 root root: Diese Direktive erstellt eine neue Logdatei mit den angegebenen Berechtigungen und dem angegebenen Benutzer und Gruppenbesitzer nach der Rotation. In diesem Fall ist root sowohl der Benutzer als auch der Gruppenbesitzer, und die Berechtigungen sind auf 0644 gesetzt. Im Vorfeld habe ich mir mit ls -all /var/log/traefik angeschaut mit welcher Berechtigung die Datei geschrieben wird.
  • postrotate und endscript: Alles zwischen diesen beiden Direktiven wird nach jeder Rotation ausgeführt. In diesem Fall wird der Traefik-Docker-Container neu gestartet.
  • docker restart $(docker ps --no-trunc --filter name=^/traefik$ --quiet): Dieser Befehl wird nach der Rotation ausgeführt und startet den Traefik-Container neu. Der docker ps-Teil des Befehls filtert den Ausgabe der laufenden Docker-Container, um den Traefik-Container zu finden.

Die Klammer } am Ende markiert das Ende der Konfiguration für diese spezielle Logdatei.

Wir können die Log-Rotation auch basierend auf der maximalen Dateigröße anstelle des täglichen Zeitplans begrenzen. Hier ist die aktualisierte Konfiguration, die auf einer maximalen Dateigröße basiert:

/var/log/traefik/traefik.log {
    size 10M
    rotate 5 
    compress
    delaycompress
    missingok
    notifempty
    create 0644 root root
    postrotate
        docker restart $(docker ps --no-trunc --filter name=^/traefik$ --quiet)
    endscript
}

In dieser Konfiguration haben wir die Option size 10M hinzugefügt, um die maximale Dateigröße auf 10 Megabyte festzulegen. Sobald die Log-Datei diese Größe erreicht, wird sie rotiert.

[/ihc-hide-content]

10. Migration

Ich bin mir ziemlich sicher, dass dieser Beitrag meinen alten Beitrag ersetzen wird. Genau so ist es auch gedacht. Mein Plan ist, den alten Beitrag weiterhin zu erhalten, allerdings mit dem Hinweis, dass es diesen neuen gibt. Weder Christian, ich noch irgendeiner der Co-Autoren wird in der Lage sein, alle Anleitungen zeitnah anzupassen. Deshalb möchte ich hier eine allgemeine Migrationsanleitung beschreiben.

  • Für diejenigen, die ihre bisherige Konfiguration bereits individuell verändert haben, gehe ich davon aus, dass sie den Stack gut genug verstanden haben, um ihr Setup selbst zu übertragen.
  • Für diejenigen, die einfach nur dankbar sind, dass das alte Setup funktioniert hat, gibt es hier die Migrationsanleitung.

10.1. Neuen Container hinzufügen

Um einen neuen Container über Traefik verfügbar zu machen und gleichzeitig durch CrowdSec zu schützen, müssen wir in der zugehörigen Docker-compose.yml die Ports entfernen und dem entsprechenden Container Labels hinzufügen. Hier ein Beispiel mit WordPress:

Dies ist die docker-compose.yml aus der WordPress-Dokumentation auf hub.docker.com:

version: '3.1'
services:
  wordpress:
    image: wordpress
    restart: always
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wordpress:/var/www/html
  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - db:/var/lib/mysql
volumes:
  wordpress:
  db:

Dies wird dann zu folgender Konfiguration geändert:

version: '3.1'
services:
  wordpress:
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: exampledb
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_USER: exampleuser
    image: wordpress
    labels:
      traefik.docker.network: proxy
      traefik.enable: "true"
      traefik.http.routers.wordpress-secure.entrypoints: websecure
      traefik.http.routers.wordpress-secure.middlewares: default@file
      traefik.http.routers.wordpress-secure.rule: Host(`meinwordpress.de`)
      traefik.http.routers.wordpress-secure.service: wordpress
      traefik.http.routers.wordpress-secure.tls: "true"
      traefik.http.routers.wordpress-secure.tls.certresolver: http_resolver
      traefik.http.routers.wordpress.entrypoints: web
      traefik.http.routers.wordpress.rule: Host(`meinwordpress.de`)
      traefik.http.services.wordpress.loadbalancer.server.port: "80"
    networks:
      default: null
      proxy: null
    restart: always
    volumes:
    - wordpress:/var/www/html
  db:
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_PASSWORD: examplepass
      MYSQL_RANDOM_ROOT_PASSWORD: "1"
      MYSQL_USER: exampleuser
    image: mysql:5.7
    networks:
      default: null
    restart: always
    volumes:
    - db:/var/lib/mysql
networks:
  default:
  proxy:
    external: true
volumes:
  db:
  wordpress:

Eine kurze Erklärung dazu: Die Ports müssen nicht mehr explizit freigegeben (exposed) werden, da dies nun Traefik übernimmt. Wir teilen Traefik lediglich über Labels mit, welcher Resolver verwendet werden soll und über welchen Port Traefik auf WordPress zugreifen kann. Zusätzlich muss dem WordPress-Container noch das Traefik-Netzwerk namens “proxy” zugewiesen werden. Allerdings sollte dies nur dem WordPress-Container zugewiesen werden, da wir zwar die Datenbank gemeinsam mit WordPress im “default”-Netzwerk betreiben, die Datenbank jedoch nicht öffentlich zugänglich machen möchten.

10.2. Beispiel Mailcow anpassen

Eine sehr bekannte Anleitung hier im Blog ist die “Mailcow – E-Mail Komplettsytem mit Antivirus, Spam Filer, Webmail, Webfrontend installieren mittels Docker und Traefik” die von Christian und mir gepflegt wird. In der Anleitung von Mailcow ist an Punkt “4.2 docker-compose.override.yml erstellen“ der Teil an dem die Labels definiert werden.

10.2.1. Ursprüngliche Konfiguration

version: '3.9'
services:
    nginx-mailcow:
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.nginx-mailcow.entrypoints=http"
        - "traefik.http.routers.nginx-mailcow.rule=HostRegexp(`{host:(autodiscover|autoconfig|mail|mta-sts|imap|pop3|smtp).+}`)"
        - "traefik.http.routers.nginx-mailcow-secure.entrypoints=https"
        - "traefik.http.routers.nginx-mailcow-secure.rule=Host(`mail.euredomain.de`)"
        - "traefik.http.routers.nginx-mailcow-secure.rule=HostRegexp(`{host:(autodiscover|autoconfig|mail|mta-sts|imap|pop3|smtp).+}`)"
        - "traefik.http.routers.nginx-mailcow-secure.rule=Host(`mail.euredomain.de`, `autodiscover.euredomain.de`, `autoconfig.euredomain.de`, `mta-sts.euredomain.de`)"
        - "traefik.http.routers.nginx-mailcow-secure.middlewares=default@file"
        - "traefik.http.routers.nginx-mailcow-secure.tls=true"
        - "traefik.http.routers.nginx-mailcow-secure.tls.certresolver=http"
        - "traefik.http.routers.nginx-mailcow-secure.service=nginx-mailcow"
        - "traefik.http.services.nginx-mailcow.loadbalancer.server.port=80"
        - "traefik.docker.network=proxy"
      networks:
        - proxy
    certdumper:
        image: humenius/traefik-certs-dumper:latest
        restart: unless-stopped
        network_mode: none
        command: --restart-containers mailcowdockerized-postfix-mailcow-1,mailcowdockerized-dovecot-mailcow-1
        volumes:
          - /opt/containers/traefik/data:/traefik:ro
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - ./data/assets/ssl:/output:rw
        environment:
          DOMAIN: ${MAILCOW_HOSTNAME}
          ACME_FILE_PATH: "/traefik/acme_letsencrypt.json"

networks:
  proxy:
    external: true

10.2.2. Neue Konfiguration

version: '3.9'
services:
    nginx-mailcow:
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.nginx-mailcow.entrypoints=web"
        - "traefik.http.routers.nginx-mailcow.rule=HostRegexp(`{host:(autodiscover|autoconfig|mail|mta-sts|imap|pop3|smtp).+}`)"
        - "traefik.http.routers.nginx-mailcow-secure.entrypoints=websecure"
        - "traefik.http.routers.nginx-mailcow-secure.rule=Host(`mail.euredomain.de`)"
        - "traefik.http.routers.nginx-mailcow-secure.rule=HostRegexp(`{host:(autodiscover|autoconfig|mail|mta-sts|imap|pop3|smtp).+}`)"
        - "traefik.http.routers.nginx-mailcow-secure.rule=Host(`mail.euredomain.de`, `autodiscover.euredomain.de`, `autoconfig.euredomain.de`, `mta-sts.euredomain.de`)"
        - "traefik.http.routers.nginx-mailcow-secure.middlewares=default@file"
        - "traefik.http.routers.nginx-mailcow-secure.tls=true"
        - "traefik.http.routers.nginx-mailcow-secure.tls.certresolver=http_resolver"
        - "traefik.http.routers.nginx-mailcow-secure.service=nginx-mailcow"
        - "traefik.http.services.nginx-mailcow.loadbalancer.server.port=80"
        - "traefik.docker.network=proxy"
      networks:
        - proxy
    certdumper:
        image: humenius/traefik-certs-dumper:latest
        restart: unless-stopped
        network_mode: none
        command: --restart-containers mailcowdockerized-postfix-mailcow-1,mailcowdockerized-dovecot-mailcow-1
        volumes:
          - /opt/containers/traefik-crowdsec-stack/traefik:/traefik:ro
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - ./data/assets/ssl:/output:rw
        environment:
          DOMAIN: ${MAILCOW_HOSTNAME}
          ACME_FILE_PATH: "/traefik/acme_letsencrypt.json"

networks:
  proxy:
    external: true

10.2.3. Änderungen

  1. Änderung der Eintrittspunkte:
    • Von: “traefik.http.routers.nginx-mailcow.entrypoints=http”
    • Zu: “traefik.http.routers.nginx-mailcow.entrypoints=web”
    Und
    • Von: “traefik.http.routers.nginx-mailcow-secure.entrypoints=https”
    • Zu: “traefik.http.routers.nginx-mailcow-secure.entrypoints=websecure”
  2. Änderung des Zertifikat-Resolver:
    • Von: “traefik.http.routers.nginx-mailcow-secure.tls.certresolver=http”
    • Zu: “traefik.http.routers.nginx-mailcow-secure.tls.certresolver=http_resolver”
  3. Änderung des Volumes für den “certdumper” Dienst:
    • Von: “/opt/containers/traefik/data:/traefik:ro”
    • Zu: “/opt/containers/traefik-crowdsec-stack/traefik:/traefik:ro”

10.2.4. Konfiguration mit neuerer Notationen

Mit den steigenden Docker Versionen hat auch eine leicht angepasste Notation Einzug genommen, welche ich zumindest kurz zeigen möchte:

version: '3.9'
services:
    nginx-mailcow:
      labels:
        traefik.enable: true
        traefik.http.routers.nginx-mailcow.entrypoints: web
        traefik.http.routers.nginx-mailcow.rule: HostRegexp(`{host:(autodiscover|autoconfig|mail|mta-sts|imap|pop3|smtp).+}`)
        traefik.http.routers.nginx-mailcow-secure.entrypoints: websecure
        traefik.http.routers.nginx-mailcow-secure.rule: Host(`mail.euredomain.de`)
        traefik.http.routers.nginx-mailcow-secure.rule: HostRegexp(`{host:(autodiscover|autoconfig|mail|mta-sts|imap|pop3|smtp).+}`)
        traefik.http.routers.nginx-mailcow-secure.rule: Host(`mail.euredomain.de`, `autodiscover.euredomain.de`, `autoconfig.euredomain.de`, `mta-sts.euredomain.de`)
        traefik.http.routers.nginx-mailcow-secure.middlewares: default@file
        traefik.http.routers.nginx-mailcow-secure.tls: true
        traefik.http.routers.nginx-mailcow-secure.tls.certresolver: http_resolver
        traefik.http.routers.nginx-mailcow-secure.service: nginx-mailcow
        traefik.http.services.nginx-mailcow.loadbalancer.server.port: "80"
        traefik.docker.network: proxy
      networks:
        - proxy
    certdumper:
        image: humenius/traefik-certs-dumper:latest
        restart: unless-stopped
        network_mode: none
        command: --restart-containers mailcowdockerized-postfix-mailcow-1,mailcowdockerized-dovecot-mailcow-1
        volumes:
          - /opt/containers/traefik-crowdsec-stack/traefik:/traefik:ro
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - ./data/assets/ssl:/output:rw
        environment:
          DOMAIN: ${MAILCOW_HOSTNAME}
          ACME_FILE_PATH: "/traefik/acme_letsencrypt.json"

networks:
  proxy:
    external: true

10.3. Beispiel Nextcloud Server

Eine weitere sehr bekannte Anleitung hier im Blog ist die “Nextcloud Server – mit Docker Compose und Traefik installieren” die von Christian gepflegt wird. In der Anleitung von Nextcloud ist an Punkt “2. Docker Compose anlegen“ der Teil an dem die Labels definiert werden.

10.3.1. Alte Konfiguration

version: '3.3'
services:
  ...
  nextcloud-app:
    environment:
        TRUSTED_PROXIES: 172.18.0.2/16
        ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.nextcloud.entrypoints=https"
      - "traefik.http.routers.nextcloud.rule=(Host(`nextcloud.euredomain.de`))"
      - "traefik.http.routers.nextcloud.tls=true"
      - "traefik.http.routers.nextcloud.tls.certresolver=http"
      - "traefik.http.routers.nextcloud.service=nextcloud"
      - "traefik.http.services.nextcloud.loadbalancer.server.port=80"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.nextcloud.middlewares=nextcloud-dav,default@file"
      - "traefik.http.middlewares.nextcloud-dav.replacepathregex.regex=^/.well-known/ca(l|rd)dav"
      - "traefik.http.middlewares.nextcloud-dav.replacepathregex.replacement=/remote.php/dav/"
...

10.3.2. Neue Notation und Konfiguration

version: '3.3'
services:
  ...
  nextcloud-app:
    environment:
        TRUSTED_PROXIES: 172.16.255.254
        ...
    labels:
      traefik.docker.network: proxy
      traefik.enable: "true"
      traefik.http.middlewares.nextcloud-dav.replacepathregex.regex: ^/.well-known/ca(l|rd)dav
      traefik.http.middlewares.nextcloud-dav.replacepathregex.replacement: /remote.php/dav/
      traefik.http.routers.nextcloud.entrypoints: websecure
      traefik.http.routers.nextcloud.middlewares: nextcloud-dav,default@file
      traefik.http.routers.nextcloud.rule: (Host(`nextcloud.euredomain.de`))
      traefik.http.routers.nextcloud.service: nextcloud
      traefik.http.routers.nextcloud.tls: "true"
      traefik.http.routers.nextcloud.tls.certresolver: http_resolver
      traefik.http.services.nextcloud.loadbalancer.server.port: "80"
...

10.3.3. Hinweis zur Nextcloud Anleitung

An dieser Stelle ist nochmal ein gesonderter Hinweis zur Anleitung angebracht! Der Punkt “2.1 Trusted Proxy IP ermitteln” entfällt vollständig! In dem FullStack hat Traefik immer eine fest definierte IPv4-Adresse. So müssen wir keine Range mehr an Nextcloud weitergeben sondern können uns auf die spezifische Adresse von Traefik im proxy-Netzwerk verlassen!

10.4. Liste bereits migrierter Anleitungen

Hier ist eine Liste von Anleitungen, die bereits erfolgreich migriert wurden:

Diese Anleitungen wurden sorgfältig überarbeitet und sind nun auf die neue Umgebung und die aktuelle Anleitung angepasst.

Hinweis

Hinweis zu Traefik v3.0beta2

Das in diesem Artikel beschriebene Setup ist grundsätzlich auch mit Traefik v3.0beta2 kompatibel. Ich habe es erfolgreich getestet. Es ist jedoch wichtig zu beachten, dass in der aktuellen Beta-Version ein bekannter Fehler vorhanden ist, der dazu führt, dass das Dashboard trotz korrekter Konfiguration nicht erreichbar ist. Wie kann ich dann sicher sein, dass das Setup korrekt funktioniert? Zum Beispiel funktioniert die oben beschriebene WordPress-Anleitung perfekt und ist mit Traefik v3.0beta2 gut erreichbar. Daher ist es sicher zu schlussfolgern, dass das Setup korrekt funktioniert, trotz des Bugs mit dem Dashboard.

Hinweis zu Kommentaren

Wenn etwas nicht funktioniert, gehe noch einmal alle einzelnen Punkte in der Anleitung durch. Ich habe die Anleitung mehrfach getestet und Christian hat sie auch durchgesehen. Wenn du sicher bist, dass du alles richtig konfiguriert hast und das noch einmal überprüft hast, dann hinterlasse gerne einen Kommentar. Der Blog wird von Christian betrieben und er, sowie alle Co-Autoren, bemühen uns, alle Fragen und Probleme zu lösen – allerdings ist dies auch immer von unserer verfügbaren Zeit abhängig!

Bitte abonniert GoNeuland, damit dieser Blog uns auch weiterhin viele Jahre Freude bereiten kann 😉

Write a comment