Crowdsec mit Nginx und Traefik integrieren — Layer-7-Schutz für eure Web-Apps

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.

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

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 crowdsec

Damit 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-bouncer

Bei der Installation wird automatisch ein API-Key generiert und in der Config eingetragen.

sudo cscli bouncers list

Sollte einen Eintrag crowdsec-nginx-bouncer mit valid: ✔️ zeigen.

Schritt 2: Bouncer-Konfiguration verstehen

sudo nano /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf

Wichtige 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=stream ist schneller, weil der Bouncer eine lokale Kopie der Decisions cached. MODE=live fragt 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 nginx

Schritt 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://localhost

Sollte 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.conf
CAPTCHA_PROVIDER=hcaptcha
CAPTCHA_SITE_KEY=eure-site-key
CAPTCHA_SECRET_KEY=euer-secret-key

Crowdsec-Decision auf „captcha“ statt „ban“ stellen

Per Default banned Crowdsec direkt. Wir wollen für bestimmte Szenarien stattdessen Captcha:

sudo nano /etc/crowdsec/profiles.yaml

Inhalt 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: break

Was 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.4

Schritt 2: API-Key generieren

sudo cscli bouncers add traefik-bouncer

Output:

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.yml

Ergä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 eventuell 172.17.0.1:8080 oder 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@file

Schritt 5: Traefik neu starten

cd /opt/traefik
sudo docker compose up -d
sudo docker compose logs -f traefik

Bei 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.yaml

Inhalt:

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 crowdsec

Was 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.yaml
name: 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 crowdsec

Logs 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 metrics

In der Cloud-Console seht ihr alles graphisch — empfehlenswert für Trends über Zeit.


Zusammenfassung & Checkliste

SchrittStatus
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 NAME zum 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 plugin
  • experimental.plugins in traefik.yml korrekt?
  • Version aktuell? GitHub-Releases prüfen
  • Internet-Verbindung beim ersten Start (Plugin wird gedownloadet)

Captcha funktioniert nicht

  • CAPTCHA_SITE_KEY und CAPTCHA_SECRET_KEY korrekt?
  • 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=stream aktiv? Sonst läuft jeder Request durch das LAPI
  • defaultDecisionSeconds zu 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!

Kommentar hinterlassen