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.
Diese Anleitung ist eine überarbeitete Variante von Christians Version und erweitert Traefik zusätzlich um das Sicherheitssuite CrowdSec.
Diese Anleitung wird nicht weiter gepflegt. Es gibt eine neue FullStack-Anleitung:
0. Versionierung
| Datum | Änderung |
|---|---|
| 25.05.2023 | Hinweis auf neue Anleitung |
| 29.12.2022 | Nützliches hinzugefügt als Community Inhalt – X Real IP hinzugefügt |
| 28.12.2022 | Crontab nach oben geschoben Verweis auf Authelia hinzugefügt |
| 23.12.2022 | Traefik um statische IP-Adresse im CrowdSec Netzwerk erweitert |
| 22.12.2022 | Überprüfen der Anleitung Traefik: – Docker Network überarbeitet Crowdsec: – Docker Network überarbeitet Statische IPs vergeben: crowdsec -> 172.31.0.2 crowdsec-bouncer-traefik -> 172.31.0.3 Es handelt sich hier um elementare Änderungen. Die Änderungen können auch bereits laufende Systeme adaptiert werden. Hierzu Traefik und crowdsec stoppen und den jeweiligen Netzwerkteil in allen docker-compose.yml anpassen. Crontab hinzugefügt (Danke an @ice-cue) Testen der Anleitung auf einer neuen Instanz |
| 12.08.2022 | traefik.yml: – unötigen Code entfernt. – Log angepasst dynamic_conf.yml – deprecated funktion auskommentiert |
| 10.08.2022 | Erfolgskontrolle der Anleitung weiter nach hinten geschoben. 4.3.6. -> 5.1. |
Korrektur der dynamic_conf.yml: secHeder zu secHeaders | |
| 09.08.2022 | Name 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.2022 | Fehler 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 |
Hinweis
Seit dem 25.05.2023 gibt es unter steht eine neue Anleitung zur Verfügung!
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. Voraussetzung
- 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: 10Code-Sprache: CSS (css)
# 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 wirdCode-Sprache: PHP (php)
Notwendige Anpassungen (unkommentierte Version)
- Zeile 7: Anpassen an die eigene Email-Adresse.
Informationen
- Ich verwende in dieser Variante bewusst nicht
acme.jsonals 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. - 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: {}Code-Sprache: PHP (php)
# 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: {}Code-Sprache: PHP (php)
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.Code-Sprache: PHP (php)
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"
# [...]Code-Sprache: CSS (css)
# [...]
http:
middlewares:
traefikAuth:
basicAuth:
users:
- "jonathan:$$apr1$$J8vBlNIm$$lSwLv9iGa8KcCct3EyLD41"
# [...]Code-Sprache: PHP (php)
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: trueCode-Sprache: PHP (php)
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://stage.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: trueCode-Sprache: PHP (php)
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"Code-Sprache: PHP (php)
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: trueCode-Sprache: JavaScript (javascript)
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:
crowdsec:
ipv4_address: 172.31.0.2
hostname: crowdsec
networks:
crowdsec:
name: crowdsec
driver: bridge
ipam:
config:
- subnet: 172.31.0.0/24
gateway: 172.31.0.1Code-Sprache: JavaScript (javascript)
# Dies entspricht der Anleitung vor dem 22.12.2022
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: trueCode-Sprache: PHP (php)
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:roCode-Sprache: JavaScript (javascript)
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
---Code-Sprache: JavaScript (javascript)
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: 100Code-Sprache: PHP (php)
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 und das Netzwerk von Crowdsec
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
- /var/log/crowdsec/traefik/:/var/log/traefik/ # Diese Zeile muss hinzugefügt werden
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:
crowdsec: # Diese Zeile muss hinzugefügt werden
ipv4_address: 172.31.0.10 # Diese Zeile muss hinzugefügt werden
hostname: traefik
ports:
- "80:80"
- "443:443"
networks:
proxy:
name: proxy
driver: bridge
attachable: true
crowdsec: # Diese Zeile muss hinzugefügt werden
external: true # Diese Zeile muss hinzugefügt werdenCode-Sprache: PHP (php)
# Dies entspricht der Anleitung vor dem 22.12.2022
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 werdenCode-Sprache: PHP (php)
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 # Hier könnte nun auch bei Bedarf die statische IP 172.31.0.2:8080 verwendet werden
restart: unless-stopped
depends_on:
- crowdsec
networks:
crowdsec:
ipv4_address: 172.31.0.3
hostname: crowdsec-bouncer-traefikCode-Sprache: PHP (php)
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:
crowdsec:
ipv4_address: 172.31.0.2
hostname: crowdsec
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 # Hier könnte nun auch bei Bedarf die statische IP 172.31.0.2:8080 verwendet werden
restart: unless-stopped
depends_on:
- crowdsec
networks:
crowdsec:
ipv4_address: 172.31.0.3
hostname: crowdsec-bouncer-traefik
networks:
crowdsec:
name: crowdsec
driver: bridge
ipam:
config:
- subnet: 172.31.0.0/24
gateway: 172.31.0.1Code-Sprache: PHP (php)
# Dies entspricht der Anleitung vor dem 22.12.2022
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-traefikCode-Sprache: PHP (php)
# Dies entspricht der Anleitung vor dem 22.12.2022
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
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
networks:
proxy:
external: trueCode-Sprache: PHP (php)
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 # Hier könnte nun auch bei Bedarf die statische IP 172.31.0.3:8080 verwendet werden
trustForwardHeader: trueCode-Sprache: PHP (php)
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: 100Code-Sprache: CSS (css)
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
Wenn es zu folgendem Fehler kommt:
[+] Running 1/2
⠿ Container crowdsec Started 0.8s
⠿ Container crowdsec-bouncer-traefik Starting 0.5s
Error response from daemon: Address already in useCode-Sprache: JavaScript (javascript)
Dann hat sich Traefik die statische IP Adresse von crowdsec-bouncer-traefik geangelt. Sollte das der Fall sein einfach:
docker compose -f /opt/containers/traefik/docker-compose.yml down
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

6. Crontab einrichten
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.
sudo crontab -e
0 * * * * docker exec crowdsec cscli hub update && docker exec crowdsec cscli hub upgrade
7. Authelia verwenden
Anstelle von der htaccess-Authentication kann selbstverständlich auch das noch sicherer Authelia eingesetzt werden. Dafür wurde die folgende Anleitung angepasst:
8. Ä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"Code-Sprache: JavaScript (javascript)
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"Code-Sprache: JavaScript (javascript)
Danke für den Hinweis @Ice-Cue
9. Nützliches
9.1. Remote IP an Container übertragen
Bei diesem Inhalt handelt es sich um exklusiven Content für Community Plus Mitglieder und Supporter.
Bitte logge dich mit deinem Account ein um den Inhalt zu sehen.
Quellen
- https://stage.goneuland.de/traefik-v2-reverse-proxy-fuer-docker-unter-debian-10-einrichten/
- https://docs.ibracorp.io/crowdsec/
- https://www.benjaminrancourt.ca/a-complete-traefik-configuration/
- https://docs.ibracorp.io/crowdsec/crowdsec/docker-compose/traefik-bouncer
- https://blog.vacum.se/securing-mailcow-with-crowdsec/
- https://spad.uk/integrating-crowdsec-with-traefik-discord/
- https://docs.technotim.live/posts/crowdsec-traefik/

Hallo,
danke für die super Anleitung. Soweit funktioniert auch alles. Ich habe nur ein Problem, und das habe ich seit der Installation auf Debian 11 jetzt ein paar Tage beobachtet, dass der Crowd-Sec Container einen Restart alle paar Sekunden macht. So kann ich mich leider auch nicht im Traefik anmelden. Bin für jeden Tipp dankbar!
Seit heute Morgen (Server-Neustart, sonst wüsste ich nicht was daran “Schuld” gewesen sein könnte) geht bei mir Crowdsec leider gar nicht mehr auf einem meiner Server. Folgendes steht in den Logs:
docker logs crowdsec
docker logs crowdsec-bouncer-traefik
{"level":"warn","status":403,"method":"GET","path":"/api/v1/forwardAuth","ip":"123.123.123.123","latency":2.062481,"user_agent":"Uptime-Kuma/1.21.3","time":"2023-05-12T06:08:43Z","message":"Request"} 2023-05-12T06:08:43Z WRN An error occurred while checking IP "" error="Get \"http://crowdsec:8080/v1/decisions?type=ban&ip=123.123.123.123\": dial tcp: lookup crowdsec: Temporary failure in name resolution"Ich werde leider gerade nicht schlau draus und werde Crowdsec auf dem Server erstmal deaktivieren. Wäre aber cool, wenn jemand ne Idee hat. 🙂
Macht ihr noch eine Variante für Wildcard-Zertifikate bzw. hat es jemand hinbekommen?
Ich hab es mal selbst versucht, aber ich bekomme immer den Fehler
Folgendes hab ich angepasst:
traefik.yml
docker-compose.yml
Hat jemand einen Tipp, woran es scheitert?
Der Entrypoint ist doch korrekt?
Hey!
Erst einmal vielen Dank für deine Anleitung.
Bis Traefik hat bei mir alles wunderbar geklappt. Dann habe ich mit crowdsec angefangen und seitdem bekomme ich keine Webseite mehr erreicht. Ich bekomme immer die Meldung “Forbidden”.
Daher habe ich mir mal wie in den Kommentaren schon geschrieben “docker compose up” ausgeführt und sehe folgendes, sobald ich eine Seite öffnen möchte:
crowdsec-bouncer-traefik | {"level":"warn","status":403,"method":"GET","path":"/api/v1/forwardAuth","ip":"213.41.21.146","latency":4.104505,"user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.48","time":"2023-04-20T11:10:52Z","message":"Request"} crowdsec | time="20-04-2023 13:10:52" level=error msg="while fetching bouncer info: select bouncer: ent: bouncer not found: unable to query" ip=172.31.0.3 crowdsec | time="20-04-2023 13:10:52" level=info msg="172.31.0.3 - [Thu, 20 Apr 2023 13:10:52 CEST] \"GET /v1/decisions?type=ban&ip=213.41.21.146 HTTP/1.1 403 4.097894ms \"Go-http-client/1.1\" \""Irgendwie kann er die IP-Adresse nicht abfragen, aber ich weiß nicht warum.
In der dynamic_conf.yml habe ich auch mal die IP-Adresse angegeben. Das Problem bleibt leider bestehen.
Hast du vielleicht noch eine Idee?
Hallo,
Ich möchte eine WordPress Seite über Traefik mit einem Passwortschutz ausstatten, ähnlich dem Dashboard aus der Anleitung.
Versuche das über die BasicAuth middleware, bekomme das aber irgendwie nicht hin. Ich habe in der dynamic_conf.yaml folgendes hinzugefügt:
middlewares:
testAuth:
basicAuth:
users:
– “user:passwort”
Aber wie rufe ich das ganze in der Docker-Compose.yaml auf?
– “traefik.http.routers.wordpress.middlewares=testAuth@file”
funktioniert nicht
Hallo 🙂
Ich hänge leider bei Punkt 3.6 / 3.7.
Ich habe alles so konfiguriert, auch meinen DNS mit *.meinedomain.com per TypA an meine statische IP verwiesen. Ping funktioniert!
Doch ich komme einfach nicht auf das Anmeldefenster. Von einem Rechner im Netzwerk als auch am Server selbst bekomme ich andauernd denn Fehlercode: SSL_ERROR_UNRECOGNIZED_NAME_ALERT im Firefox
Was kann hier falsch sein? Muss ich das Zertifikat meiner Domain irgendwo hinterlegen?
Beste Grüße
Launga
Hallo VeePay, hast Du in der docker-compose von traefik in Zeile 24 nicht -restart: unless-stopped- stehen. Ich kenne solche Verhalten nur wenn eine andere Instanz sich die IP von Traefik schnappt. Aber dann reicht es nicht einfach nur denn Container neu zu starten.
Hallo,
vielen Dank für diese super Anleitung! Ich bin gerade dabei meinen ersten Server einzurichten!
Leider hakt es bei mir ab der Installation von CrowdSec. Aktuell habe ich folgendes Fehlerbild:
sobald ich im Traefik Docker das Netzwerk CrowdSec hinzufüge, sind meine Dienste und das Dashboard von außen nicht mehr erreichbar (Google Chrome gibt: ERR_CONNECTION_RESET)
Die Erfolgskontrolle docker exec … gibt bei mir leider keine ausgabe!
Ich habe das ganze zwei mal versucht. Beim ersten mal, hätte eigentlich alles funktioniert, ein Fehler von mir hatte sich an anderer Stelle eingeschlichen, ich habe ihn aber nicht zuordnen können und deshalb versucht meinen Einbau von CrowdSec rückgängig zu machen. Das hat soweit auch funktioniert, aber beim erneuten Versuch CrowdSec aufzusetzten geht es nicht mehr. Ich vermute beim Versuch CrowdSec runter zu schmeißen, hab ich irgendwo irgendwie was liegen lassen.
Ich habe per docker -rm den Container entfernt und den Ordner /crowdsec gelöscht. Ich dachte das würde reichen.
Falls jemand eine Idee oder einen Tipp hat, wäre ich sehr dankbar!
Kann auch sein, dass mein Fehler sich wo anders befindet, das will ich wirklich nicht ausschließen.
Danke!
Ihr Lieben,
kurze Frage, ich wollte gern nach Anleitung auf der Slack-Website die Benachrichtigung per Slack für Crowdsec einrichten. Leider bekomme ich dann wieder das Problem, dass der Crowdsec-Container ständig restarted. Vielleicht hat jemand einen Tipp für mich? Den Crowdsec-Container habe ich nach der Änderung neu erstellt.
Änderungen habe ich laut Anleitung vorgenommen an der /opt/containers/crowdsec/config/profiles.yaml –> uncomment – slack_default
und
/opt/containers/crowdsec/config/notifications/ slack.yaml –> meinen Webhook eingetragen.
Hier die Anleitung Slack Plugin | CrowdSec
Auch von mir vielen Dank für die Anleitung. Ich habe jetzt allerdings ein Problem, welches ich nicht allein zu lösen weiß.
Ich kann jetzt wunderbar Subdomains auf andere Docker-Container umlegen. seafile.meinedomain.de – traefik.meinedomain.de usw. funktionieren tadellos.
Was ich jetzt nicht hinbekomme, ist die Verwendung der Adressen meinedomain.de und http://www.meinedomain.de
nginx läuft jetzt mal Testweise unter nginx.meinedomain.de – Wenn ich aber als Host die Subdomain weglasse, funktioniert es nicht.
Edit: Jetzt, warum auch immer, funktioniert es.
Aber eine Frage bleibt offen. Wie schaffe ich es, die Seite auch unter http://www.meinedomain.de erreichbar zu machen?
Hey danke nochmal für die Anleitungen und Hilfe.
Ich habe das Problem, dass ich die richtige IP im Docker Container bekomme. Leider bekomme ich nur die IP vom Traefik.
Ich habe hier auch die Anleitung genutzt mit real ip
Die Middleware wird auch geladen. Leider ohne Erfolg.
Bekomme in den Logs nur die IP von Traefik und nicht die Reale IP.
Gibt es vielleicht noch einen Tip?
Würde mich freuen was zu hören.
LG Dirk