Crowdsec mit Nginx und Traefik verbinden: Bouncer-Integration für Layer-7-Schutz vor Brute-Force, Bot-Crawlern und Web-Exploits. Mit Captcha-Challenges statt direktem Ban.
💡 Hinweis: Dieses Tutorial setzt voraus, dass Crowdsec auf Debian 13 bereits läuft. Wir erweitern das bestehende Setup um Web-Layer-Schutz für eure Reverse Proxies.
Einleitung — Layer-7 vs. Layer-3
Crowdsec mit dem Standard-Firewall-Bouncer schützt euch auf Layer 3/4 — IP-Adressen werden in iptables/nftables geblockt. Funktioniert gut, hat aber zwei Schwächen:
- Alles oder nichts — Eine IP ist gebannt oder nicht
- Keine Captcha-Option — Verdächtige IPs werden direkt geblockt, statt verifiziert
- Reverse-Proxy-Probleme — Bei Cloudflare/Traefik davor sieht der Firewall-Bouncer nur die Proxy-IP, nicht den echten Client
Layer-7-Bouncer lösen das. Sie sitzen direkt im Reverse Proxy (Nginx oder Traefik), kennen die echten Client-IPs aus den HTTP-Headern und können differenziert reagieren:
- Ban — Komplett blockieren
- Captcha — Vor Zugriff Mensch verifizieren
- Throttle — Rate-Limiting verschärfen
Wir richten beides ein — den Crowdsec-Bouncer für Nginx und den Crowdsec-Bouncer für Traefik. Beide Wege führen zum gleichen Ziel.
Was wir absichern
- Login-Pages (Brute-Force-Versuche)
- Admin-Panels (WordPress wp-admin, Login-Endpoints)
- API-Endpoints (Rate-Limit-Verstöße, Bot-Scraping)
- Bekannte Exploit-Pfade (z.B.
/.env,/wp-login.php)
Voraussetzungen
- Debian 13 mit Crowdsec aktiv — siehe Crowdsec-Hub-Tutorial
- Reverse Proxy am Start — entweder Nginx oder Traefik v3
- Crowdsec-Collections für Web installiert
Web-Collections nachinstallieren
sudo cscli collections install crowdsecurity/nginx
sudo cscli collections install crowdsecurity/http-cve
sudo cscli collections install crowdsecurity/base-http-scenarios
sudo systemctl reload crowdsecDamit hat Crowdsec ein vollständiges Set an Web-Erkennungsregeln — Brute-Force, Path-Traversal, SQL-Injection-Versuche, Bot-Crawler usw.
Variante A: Crowdsec mit Nginx
Schritt 1: Nginx-Bouncer installieren
sudo apt install -y crowdsec-nginx-bouncerBei der Installation wird automatisch ein API-Key generiert und in der Config eingetragen.
sudo cscli bouncers listSollte einen Eintrag crowdsec-nginx-bouncer mit valid: ✔️ zeigen.
Schritt 2: Bouncer-Konfiguration verstehen
sudo nano /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.confWichtige Werte:
# API-Key zum Crowdsec-LAPI
API_KEY=AUTOGEN_KEY
# LAPI-URL (lokal)
API_URL=http://127.0.0.1:8080
# Modi:
# - "live": Bouncer fragt LAPI bei jedem Request (sicher, etwas langsamer)
# - "stream": Bouncer holt Decisions periodisch (schneller, kleines Zeitfenster ohne Schutz)
MODE=stream
UPDATE_FREQUENCY=10
# Captcha-Konfiguration (kommt später)
CAPTCHA_PROVIDER=hcaptcha
CAPTCHA_SITE_KEY=
CAPTCHA_SECRET_KEY=
# Wie lange Captcha-Verifikation gültig ist (Sekunden)
CAPTCHA_GRACE_PERIOD=60💡 Hinweis:
MODE=streamist schneller, weil der Bouncer eine lokale Kopie der Decisions cached.MODE=livefragt bei jedem Request live nach. Für die meisten Setups reicht stream.
Schritt 3: Nginx neu laden
Der Bouncer hat automatisch ein Lua-Modul in Nginx eingebunden. Reload nötig:
sudo nginx -t
sudo systemctl reload nginxSchritt 4: Funktionalität testen
Auf eurem Server:
sudo cscli decisions add --ip 1.2.3.4 --duration 30m --reason "Test-Ban"Vom anderen Rechner mit IP 1.2.3.4 würde jetzt Nginx blocken. Da ihr das nicht testen könnt, simuliert mit Localhost:
sudo cscli decisions add --ip 127.0.0.1 --duration 1m --reason "Local Test"
curl -I http://localhostSollte 403 Forbidden zurückgeben.
# Test-Ban entfernen
sudo cscli decisions delete --ip 127.0.0.1✅ Geschafft! Crowdsec blockt jetzt direkt im Nginx, bevor Anfragen die Backend-Apps erreichen. Effizienter und mit besserer IP-Erkennung als der Firewall-Bouncer.
Schritt 5: Captcha-Challenges aktivieren
Statt verdächtige IPs hart zu bannen, könnt ihr ihnen ein Captcha vorschalten — Bots scheitern, echte Nutzer kommen durch.
hCaptcha Account anlegen
hcaptcha.com→ Account erstellen- Site Key + Secret Key kopieren
In Bouncer-Config eintragen
sudo nano /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.confCAPTCHA_PROVIDER=hcaptcha
CAPTCHA_SITE_KEY=eure-site-key
CAPTCHA_SECRET_KEY=euer-secret-keyCrowdsec-Decision auf „captcha“ statt „ban“ stellen
Per Default banned Crowdsec direkt. Wir wollen für bestimmte Szenarien stattdessen Captcha:
sudo nano /etc/crowdsec/profiles.yamlInhalt anpassen:
name: default_ip_remediation
debug: <strong>false</strong>
filters:
- Alert.Remediation == true <strong>&&</strong> Alert.GetScope() == "Ip" <strong>&&</strong> Alert.GetScenario() contains "http-crawl"
decisions:
- type: captcha
duration: 4h
on_success: break
---
name: default_ban_remediation
debug: <strong>false</strong>
filters:
- Alert.Remediation == true <strong>&&</strong> Alert.GetScope() == "Ip"
decisions:
- type: ban
duration: 4h
on_success: breakWas hier passiert:
- HTTP-Crawler-Erkennung → Captcha (Mensch kann durch)
- Alle anderen Angriffe → Ban (klar bösartig)
sudo systemctl reload crowdsec
sudo systemctl reload nginx💡 Tipp: Captcha ist gut für Web-Layer-Verdachtsfälle. Bei klaren Angriffen (SQL-Injection-Versuche, Login-Brute-Force) bleibt der harte Ban die richtige Wahl.
Variante B: Crowdsec mit Traefik
Schritt 1: Traefik-Bouncer installieren
Es gibt zwei Wege: Bouncer als externer Container oder als Traefik-Plugin.
Wir nehmen das Traefik-Plugin — eleganter und ohne extra Container.
In eurer traefik.yml:
experimental:
plugins:
bouncer:
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
version: v1.4.4Schritt 2: API-Key generieren
sudo cscli bouncers add traefik-bouncerOutput:
Api key for 'traefik-bouncer':
abcdefghijklmnopqrstuvwxyz1234567890
Please keep this key since you will not be able to retrieve it!Key kopieren — wird gleich gebraucht.
Schritt 3: Middleware in dynamic.yml definieren
sudo nano /opt/traefik/config/dynamic.ymlErgänzen:
http:
middlewares:
crowdsec:
plugin:
bouncer:
enabled: <strong>true</strong>
logLevel: INFO
updateIntervalSeconds: 60
defaultDecisionSeconds: 60
httpTimeoutSeconds: 10
crowdsecMode: stream
crowdsecLapiKey: "EUER_API_KEY_HIER"
crowdsecLapiHost: "host.docker.internal:8080"
crowdsecLapiScheme: http
forwardedHeadersTrustedIPs:
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
clientTrustedIPs:
- "EURE_HEIM_IP/32"⚠️ Wichtig:
crowdsecLapiHost: "host.docker.internal:8080"funktioniert auf Docker Desktop und Docker für Linux mit--add-host=host.docker.internal:host-gateway. Bei nativen Docker-Setups eventuell172.17.0.1:8080oder die echte Server-IP nehmen.
Schritt 4: Middleware auf Services anwenden
In den Labels eures Services:
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls=true"
- "traefik.http.routers.app.middlewares=crowdsec@file,secure-headers@file"Oder global in der traefik.yml:
entryPoints:
websecure:
address: ":443"
http:
middlewares:
- crowdsec@file
- secure-headers@fileSchritt 5: Traefik neu starten
cd /opt/traefik
sudo docker compose up -d
sudo docker compose logs -f traefikBei Plugin bouncer initialized läuft alles.
Custom-Scenarios — eigene Angriffsmuster
Manchmal habt ihr eigene Apps mit eigenen Login-Pfaden. Standard-Scenarios decken die nicht ab. Wir bauen ein eigenes Scenario für eine fiktive App.
sudo nano /etc/crowdsec/scenarios/my-app-bf.yamlInhalt:
type: leaky
name: my-app/login-bf
description: "Brute-Force gegen /api/login"
filter: |
evt.Meta.log_type == 'http_access-log' &&
evt.Meta.http_path == '/api/login' &&
evt.Meta.http_status in ['401', '403']
groupby: evt.Meta.source_ip
distinct: evt.Meta.source_ip
capacity: 5
leakspeed: 30s
blackhole: 5m
labels:
service: my-app
type: bruteforce
remediation: <strong>true</strong>Crowdsec neu laden:
sudo systemctl reload crowdsecWas das tut: 5 fehlgeschlagene Login-Versuche in 30 Sekunden → Decision (entweder Ban oder Captcha, je nach Profile).
💡 Tipp: Custom-Scenarios sind euer Hebel bei eigenen Apps. Schaut in eure Logs, identifiziert wiederkehrende Angriffsmuster, schreibt ein Scenario dafür. Mit der Zeit baut ihr eine eigene Defense-in-Depth.
Whitelist für eigene IPs
Bei intensivem Testen oder eigenen Tools triggert ihr eure eigenen Scenarios.
sudo nano /etc/crowdsec/parsers/s02-enrich/00-whitelist-web.yamlname: crowdsecurity/whitelists-web
description: "Web-Whitelist"
whitelist:
reason: "Eigene Heim-IP"
ip:
- "EURE_HEIM_IP"
- "EURE_VPN_IP"
cidr:
- "192.168.0.0/16"
- "10.0.0.0/8"sudo systemctl reload crowdsecLogs auswerten
Was hat Crowdsec heute geblockt?
# Alle Web-Alerts
sudo cscli alerts list --type http-bf --since 24h
# Aktuelle Bans
sudo cscli decisions list
# Statistiken
sudo cscli metricsIn der Cloud-Console seht ihr alles graphisch — empfehlenswert für Trends über Zeit.
Zusammenfassung & Checkliste
| Schritt | Status |
|---|---|
| Crowdsec aktiv und gehärtet | ☐ |
| Web-Collections installiert | ☐ |
| Nginx-Bouncer ODER Traefik-Plugin installiert | ☐ |
Bouncer in cscli bouncers list sichtbar | ☐ |
Test-Ban via cscli decisions getestet | ☐ |
| Captcha-Setup mit hCaptcha (optional) | ☐ |
| Profiles für ban-vs-captcha angepasst | ☐ |
| Eigenes Custom-Scenario erstellt (optional) | ☐ |
| Whitelist für eigene IPs gesetzt | ☐ |
| Cloud-Console verbunden für Übersicht | ☐ |
Troubleshooting
Bouncer nicht in cscli bouncers list
sudo systemctl status crowdsec-nginx-bouncer
# oder bei Traefik
sudo docker compose logs traefik | grep -i bouncer- API-Key korrekt in Config?
cscli bouncers add NAMEzum manuellen Anlegen
Nginx-Bouncer: Lua-Modul-Fehler beim Reload
- OpenResty oder Nginx mit Lua-Modul nötig
- Bei Standard-Debian-Nginx: Crowdsec liefert eigene Pakete mit Lua-Support, sicherstellen dass diese installiert sind
Traefik-Plugin lädt nicht
sudo docker compose logs traefik | grep -i pluginexperimental.pluginsintraefik.ymlkorrekt?- Version aktuell? GitHub-Releases prüfen
- Internet-Verbindung beim ersten Start (Plugin wird gedownloadet)
Captcha funktioniert nicht
CAPTCHA_SITE_KEYundCAPTCHA_SECRET_KEYkorrekt?- hCaptcha-Domain-Whitelist konfiguriert?
- Browser blockt Drittanbieter-Cookies? hCaptcha braucht die
Falscher Client-IP im Log (immer der Reverse-Proxy)
forwardedHeadersTrustedIPs korrekt konfiguriert? Bei Cloudflare zusätzlich Cloudflare-IP-Liste eintragen.
Performance-Probleme nach Bouncer-Aktivierung
MODE=streamaktiv? Sonst läuft jeder Request durch das LAPIdefaultDecisionSecondszu niedrig? Höher setzen reduziert LAPI-Last
Nächste Schritte
- Cloudflare-Bouncer — Crowdsec-Decisions an Cloudflare-Firewall-Regeln pushen
- Wazuh / Graylog — Crowdsec-Alerts in zentrales SIEM
- Discord-Notifications — Bei kritischen Bans direkt informiert werden
- Multi-Server-LAPI — Eine zentrale Crowdsec-Instanz für mehrere Server
Habt ihr Fragen oder Probleme? Schreibt es in die Kommentare — ich helfe gerne!