CrowdSec: moderne, kollaborative Angriffsabwehr
CrowdSec mit dem Traefik-Bouncer einrichten: Angriffe aus den Zugriffs-Logs erkennen, Angreifer per 403 blocken und von der Community-Blocklist profitieren.
Inhaltsverzeichnis
Fail2ban blockt Brute-Force auf deinen SSH-Zugang – aber was ist mit den Scannern, die den ganzen Tag deine Web-Apps nach Lücken abklopfen? CrowdSec schließt genau diese Lücke: Es erkennt Angriffe in den Zugriffs-Logs von Traefik und sperrt die Angreifer, bevor sie deine Anwendungen überhaupt erreichen.
Was bauen wir?
Am Ende läuft ein CrowdSec-Agent neben deinem Traefik, der die Zugriffs-Logs
mitliest, verdächtiges Verhalten anhand von Szenarien erkennt (Port-Scans, Path-Probing,
bekannte CVE-Ausnutzung, Login-Brute-Force) und Angreifer-IPs auf eine Sperrliste setzt. Ein
Bouncer – hier als Traefik-Plugin – setzt diese Entscheidungen durch: Eine gesperrte IP
bekommt bei jeder deiner Apps nur noch 403 Forbidden. Getestet mit CrowdSec v1.7.8,
dem Traefik-Bouncer-Plugin v1.6.0 und Traefik v3.7.
So greifen die Teile ineinander:
- Agent + Local API (LAPI): liest die Logs, wertet sie gegen die Szenarien aus und verwaltet die aktiven Sperren („Decisions"). Das ist der Container, den wir aufsetzen.
- Bouncer: fragt die LAPI bei jeder Anfrage „ist diese IP gesperrt?" und blockt bei Ja. Der Traefik-Bouncer läuft als Plugin direkt im Reverse Proxy – kein zusätzlicher Container.
- Community-Blocklist: CrowdSec meldet (anonymisiert) erkannte Angriffe an das zentrale Netzwerk und bekommt dafür eine von allen Nutzern gespeiste Blocklist zurück. IPs, die andernorts schon als bösartig auffielen, sind bei dir dann von vornherein gesperrt. Genau das ist das „kollaborativ" im Titel.
CrowdSec statt fail2ban – oder zusätzlich?
Voraussetzungen
- Ein laufender Reverse Proxy mit Traefik (das
proxy-Netz und der Resolverlestammen von dort) mit mindestens einer App dahinter. - Fail2ban für den SSH-Schutz – CrowdSec kümmert sich hier um die HTTP-Ebene, nicht um SSH.
- Grundwissen zu Docker Compose und ein abgesicherter Server mit aktiver Firewall.
VPS 2000 G12
8 vCore · 16 GB RAM · 512 GB NVMe
ab 19,24 €/Monat
CrowdSec läuft mit wenigen hundert MB neben deinem bestehenden App-Stack – der VPS 2000 hat dafür genug Reserve.
💶 5 € Gutschein für netcup-Neukunden:
36nc17844976032
(nur Neukunden, keine Domains)
Schritt für Schritt
Schritt 1: Traefiks Zugriffs-Logs einschalten
CrowdSec kann nur erkennen, was es sieht – und es sieht Angriffe über Traefiks
Zugriffs-Logs. Die sind standardmäßig aus. Öffne die compose.yaml deines Traefik
(aus dem Traefik-Tutorial, Ordner ~/traefik) und
ergänze im command-Block diese drei Zeilen:
- "--accesslog=true"
- "--accesslog.filepath=/var/log/traefik/access.log"
- "--accesslog.format=json"Das JSON-Format ist wichtig – die CrowdSec-Parser erwarten es so. Damit sowohl Traefik
(schreibend) als auch CrowdSec (lesend) an dieselbe Datei kommen, legst du sie auf einen
festen Host-Pfad. Ergänze im volumes-Block von Traefik:
- /var/log/traefik:/var/log/traefikLege das Verzeichnis auf dem Host an und starte Traefik neu:
sudo mkdir -p /var/log/traefik
cd ~/traefik && docker compose up -dRuf einmal eine deiner Apps auf und prüfe, dass Zeilen ankommen:
tail -n 2 /var/log/traefik/access.logDu solltest JSON-Zeilen mit "ClientHost", "RequestPath" und "DownstreamStatus" sehen.
Bleibt die Datei leer, stimmt der Pfad im Volume nicht, oder es kam schlicht noch keine
Anfrage.
Schritt 2: Den CrowdSec-Container aufsetzen
CrowdSec bekommt einen eigenen Ordner. Erzeuge zuerst einen Bouncer-Schlüssel – mit ihm authentifiziert sich später der Traefik-Bouncer an der LAPI:
mkdir -p ~/crowdsec && cd ~/crowdsec
openssl rand -hex 32Kopiere die Ausgabe; sie kommt gleich an zwei Stellen zum Einsatz. Lege die Acquisition an – sie sagt CrowdSec, welche Datei mit welchem Log-Typ zu lesen ist:
# ~/crowdsec/acquis.yaml
filenames:
- /var/log/traefik/access.log
labels:
type: traefikJetzt die compose.yaml. Ersetze DEIN_BOUNCER_KEY durch den erzeugten Schlüssel:
services:
crowdsec:
image: crowdsecurity/crowdsec:v1.7.8
container_name: crowdsec
environment:
COLLECTIONS: "crowdsecurity/traefik"
BOUNCER_KEY_TRAEFIK: "DEIN_BOUNCER_KEY"
GID: "1000"
volumes:
- ./acquis.yaml:/etc/crowdsec/acquis.d/traefik.yaml:ro
- /var/log/traefik:/var/log/traefik:ro
- crowdsec-db:/var/lib/crowdsec/data/
- crowdsec-config:/etc/crowdsec/
networks: [proxy]
restart: unless-stopped
volumes:
crowdsec-db:
crowdsec-config:
networks:
proxy:
external: trueWas hier wichtig ist:
COLLECTIONS: crowdsecurity/traefikinstalliert beim ersten Start die passende Sammlung aus Parsern und Szenarien für Traefik-Logs (inklusive der HTTP-Basisszenarien).BOUNCER_KEY_TRAEFIKregistriert direkt beim Start einen Bouncer namensTRAEFIKmit deinem Schlüssel – das spart den Weg übercscli bouncers add.GID: "1000"lässt den CrowdSec-Prozess unter dieser Gruppe laufen.1000ist der übliche Default für den ersten Nutzer; läuft Traefik als root, ist dieaccess.logmeist world-readable und die GID ohnehin egal. Ist sie enger gesetzt, prüfe mitls -l /var/log/traefik/access.logund trag die GID der Gruppe der Datei ein.- Das Log ist nur lesend (
:ro) eingebunden. CrowdSec hängt improxy-Netz, damit der Traefik-Bouncer die LAPI später untercrowdsec:8080erreicht.
Starte den Container und prüfe, dass alles greift:
docker compose up -d
docker exec crowdsec cscli collections listDie Traefik-Collection und die HTTP-Szenarien sollten als enabled erscheinen:
crowdsecurity/base-http-scenarios ✔️ enabled
crowdsecurity/http-cve ✔️ enabled
crowdsecurity/traefik ✔️ enabledPrüfe, dass die Community-Blocklist aktiv ist – das kostet nichts und ist der halbe Nutzen von CrowdSec:
docker exec crowdsec cscli capi statusYou can successfully interact with Central API (CAPI)
Sharing signals is enabled
Pulling community blocklist is enabled
Pulling blocklists from the console is enabledPulling community blocklist is enabled bestätigt, dass du die kollektive Sperrliste der
CrowdSec-Community beziehst – Angreifer, die anderswo auffällig wurden, sind bei dir schon
gesperrt, bevor sie es überhaupt versuchen.
Schritt 3: Den Traefik-Bouncer einbauen
Jetzt der Teil, der die Sperren durchsetzt. Der Bouncer ist ein Traefik-Plugin und besteht aus zwei Ergänzungen an deinem Traefik: das Plugin laden und eine Middleware definieren, die auf alle Apps wirkt.
Lade das Plugin, indem du im command-Block von ~/traefik/compose.yaml ergänzt:
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--entrypoints.websecure.http.middlewares=crowdsec@file"Die letzte Zeile hängt die Bouncer-Middleware global an den websecure-Entrypoint –
damit läuft jede HTTPS-Anfrage an jede App durch den Bouncer, ohne dass du jede App einzeln
anfassen musst. Die Middleware selbst definierst du über den File-Provider.
Vorhandene Entrypoint-Middlewares nicht überschreiben
--entrypoints.websecure.http.middlewares ist einwertig: Hast du im
Traefik-Aufbau an diesem Entrypoint schon
Middlewares gesetzt (etwa Security-Header), überschreibt ein zweites Flag den vorhandenen
Wert – dann fallen deine bisherigen Middlewares still weg. Setz also kein zweites Flag, sondern
erweitere den bestehenden Wert komma-separiert, z. B.
--entrypoints.websecure.http.middlewares=header@file,crowdsec@file. Nur wenn an diesem
Entrypoint bisher keine Middleware hängt, fügst du das Flag wie oben neu hinzu.Kein zweites File-Provider-Flag
--providers.file.filename=… oder --providers.file.directory=…).
Traefik erlaubt nicht gleichzeitig filename und directory – ergänze also kein
zweites Flag blind. Lege die folgende Middleware-Datei entweder in dein bestehendes
dynamic-Verzeichnis, oder – falls dein Provider auf eine einzelne filename-Datei zeigt –
schreib den http.middlewares-Block einfach dort hinein. Die Pfade unten (~/traefik/dynamic
bzw. /etc/traefik/dynamic) gelten nur, falls du schon ein Verzeichnis nutzt.Lege die Middleware an (Pfad an deinen bestehenden File-Provider angepasst):
# ~/traefik/dynamic/crowdsec.yaml
http:
middlewares:
crowdsec:
plugin:
bouncer:
enabled: true
crowdsecMode: stream
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec:8080
crowdsecLapiKey: "DEIN_BOUNCER_KEY"crowdsecLapiKey ist derselbe Schlüssel wie in Schritt 2 – der Bouncer weist sich damit
bei der LAPI aus. Mit crowdsecMode: stream zieht der Bouncer die Sperrliste in einem festen
Intervall komplett vor und beantwortet jede Anfrage aus dem lokalen Cache – der vom Plugin
empfohlene Modus, weil er die LAPI nicht bei jedem Request belastet.
Sorg dafür, dass Traefik das Verzeichnis auch sieht. Nutzt dein bestehender File-Provider
schon ein gemountetes dynamic-Verzeichnis, liegt die neue Datei ohnehin darin – dann ist hier
nichts zu tun. Legst du das Verzeichnis neu an, ergänze es im volumes-Block von Traefik:
- ./dynamic:/etc/traefik/dynamic:roStarte Traefik neu, damit es das Plugin herunterlädt und die Middleware lädt:
cd ~/traefik && docker compose up -d
docker compose logs -f traefikIm Log darf kein ERR zum Plugin oder zur Middleware stehen (Traefik v3 loggt bei
Standard-Level nur Fehler – eine ruhige Ausgabe ist das gute Zeichen). Prüfe von der
CrowdSec-Seite, dass der Bouncer sich verbunden hat:
docker exec crowdsec cscli bouncers list Name IP Address Valid Last API pull Type
TRAEFIK 172.19.0.2 ✔️ 2026-07-23T10:49:58Z Crowdsec-Bouncer-Traefik-PluginEin gesetztes Last API pull beweist: Das Plugin spricht mit der LAPI.
Schritt 4: Scharf schalten und die Sperre testen
Ruf eine deiner Apps auf – sie sollte ganz normal antworten:
curl -s -o /dev/null -w "%{http_code}\n" https://DEINE_APP.DEINE_DOMAIN/200Jetzt sperrst du testweise eine IP von Hand und schaust, ob der Bouncer sie aussperrt. Nimm
deine eigene öffentliche IP (curl -s https://ifconfig.me):
docker exec crowdsec cscli decisions add --ip DEINE_TEST_IP --duration 5m --reason "test"Neue Sperren greifen nicht sofort
updateIntervalSeconds, Default 60 Sekunden), im live-Modus vom
Per-IP-Cache jeder Entscheidung (defaultDecisionSeconds, Default 60 Sekunden). Im echten
Betrieb ist das irrelevant; beim Testen musst du kurz warten. Wer die Reaktionszeit drücken
will, senkt den jeweils passenden Wert – also updateIntervalSeconds (stream) bzw.
defaultDecisionSeconds (live) – in der Middleware, statt den Modus zu wechseln.Nach der Wartezeit bekommt die gesperrte IP nur noch:
curl -s -o /dev/null -w "%{http_code}\n" https://DEINE_APP.DEINE_DOMAIN/403Heb die Test-Sperre wieder auf:
docker exec crowdsec cscli decisions delete --ip DEINE_TEST_IPSchritt 5: Echte Angriffe erkennen lassen
Der eigentliche Sinn: CrowdSec sperrt von selbst, ohne dass du Regeln schreibst. Sobald
ein Scanner deine Domain nach Pfaden abklopft (viele 404 in kurzer Zeit), greift das
Szenario crowdsecurity/http-probing. So sieht eine echte, automatisch erkannte Sperre aus:
docker exec crowdsec cscli alerts list ID value reason country as decisions
4 Ip:109.250.21.197 crowdsecurity/http-probing DE 8881 1&1 Versatel GmbH ban:1docker exec crowdsec cscli decisions list Source Scope:Value Reason Action Country AS
crowdsec Ip:109.250.21.197 crowdsecurity/http-probing ban DE 8881 1&1 Versatel GmbHCrowdSec reichert die Sperre automatisch um Land und Provider (AS) an. Neben den selbst
erkannten Angriffen tauchen hier mit der Zeit auch Sperren mit der Quelle
lists:crowdsecurity/... auf – das ist die Community-Blocklist: IPs, die weltweit schon
negativ aufgefallen sind, blockst du, bevor sie dich das erste Mal treffen.
Wer die Sperren lieber grafisch statt in cscli-Tabellen sieht, meldet die Instanz kostenlos
in der CrowdSec-Console an (der Enroll-Befehl steht in „Wartung &
Backups"). Dort erscheinen dieselben Entscheidungen aufbereitet:

Die mit der Traefik-Collection installierten HTTP-Szenarien decken das ab, was echte Scanner
den ganzen Tag versuchen: das Abklopfen nicht existierender Pfade (http-probing), das
Suchen nach sensiblen Dateien und Backdoors, Path-Traversal- und SQL-Injection-/
XSS-Sondierungen, verdächtige User-Agents bekannter Scanner sowie die Ausnutzung
bekannter CVEs (aus der Collection crowdsecurity/http-cve). Welche Szenarien aktiv sind,
zeigt docker exec crowdsec cscli scenarios list.
Noch eine Stufe schärfer: AppSec (WAF)
Ob CrowdSec deine Logs überhaupt liest, zeigt die Metrik-Übersicht:
docker exec crowdsec cscli metrics| Source | Lines read | Lines parsed |
| file:/var/log/traefik/access.log | 54 | 54 |Steht dort Lines read: 0, kommt kein Log an – dann zurück zu Schritt 1.
Schritt 6: Dich selbst nicht aussperren
Ein Fehlalarm gegen deine eigene Wartungs-IP ist ärgerlich. Trag deine feste IP als
vertrauenswürdig in die Middleware ein – der Bouncer prüft sie dann gar nicht erst.
Ergänze in ~/traefik/dynamic/crowdsec.yaml unter bouncer::
clientTrustedIPs:
- "DEINE_FESTE_IP/32"Hast du dich doch einmal selbst gesperrt, hebt ein Befehl die Sperre sofort auf – und im Notfall kommst du über die VNC-Konsole im SCP immer noch auf den Server:
docker exec crowdsec cscli decisions delete --ip DEINE_IP # eine IP
docker exec crowdsec cscli decisions delete --all # alle SperrenWenn es nicht funktioniert
Symptom: Eine frisch gesetzte Sperre wirkt nicht, die IP bekommt weiter 200.
Ursache & Lösung: Der Bouncer cached die Entscheidungen und zieht neue im
stream-Modus erst nach dem Pull-Intervall (bis zu ~60 Sekunden). Kurz warten. Wer die
Reaktionszeit drücken will, senkt updateIntervalSeconds in der Middleware (Schritt 3).
Symptom: cscli metrics zeigt für die Log-Datei Lines read: 0.
Ursache & Lösung: CrowdSec liest die Logs nicht. Prüfe, dass Traefik wirklich nach
/var/log/traefik/access.log schreibt (Schritt 1), dass beide Container denselben
Host-Pfad einbinden und dass die acquis.yaml genau diesen Pfad nennt.
Symptom: Nach dem Neustart von Traefik bekommen alle Apps einen Fehler oder laden nicht.
Ursache & Lösung: Im stream-Modus (oben gesetzt) kann der Bouncer nach mehreren
fehlgeschlagenen LAPI-Pulls dichtmachen – gesteuert über updateMaxFailure. Ist die
CrowdSec-LAPI nicht erreichbar, blockt der Bouncer dann im Zweifel, statt ungeschützt
durchzulassen. Das ist sicher, macht CrowdSec aber zu einer kritischen Komponente: Läuft
der crowdsec-Container nicht, ist deine Seite betroffen. Prüfe mit docker ps, dass er
Up ist; restart: unless-stopped (oben gesetzt) holt ihn nach einem Neustart automatisch
zurück.
Symptom: CrowdSec sperrt lauter harmlose oder gar keine echten Besucher – die erkannte IP ist immer die deines CDNs.
Ursache & Lösung: Sitzt ein CDN wie Cloudflare vor Traefik, sieht Traefik nur dessen
IP. Dann die echte Client-IP aus dem X-Forwarded-For-Header lesen lassen: In der Middleware
forwardedHeadersTrustedIPs mit den CDN-Netzen füllen – sonst sperrst du am Ende das CDN.
Direkt an netcup (ohne CDN) ist hier nichts zu tun.
Symptom: docker compose up -d bei Traefik meldet einen Plugin-Fehler.
Ursache & Lösung: Traefik lädt das Plugin beim Start aus dem Netz. Prüfe, dass der Server
raus darf (die netcup-Firewall sperrt ausgehend
nichts, UFW ebenso wenig) und dass modulename und version exakt stimmen.
Wartung & Backups
Szenarien aktuell halten. CrowdSecs Stärke sind gepflegte Erkennungsregeln – zieh sie regelmäßig nach:
docker exec crowdsec cscli hub update && docker exec crowdsec cscli hub upgrade. Das Image (crowdsecurity/crowdsec) und die Plugin-Version (v1.6.0) hebst du bewusst an; lies bei Hauptversionen die Release-Notes. CrowdSec entwickelt sich zügig – ein guter Kandidat für regelmäßige Versionsprüfung.Zugriffs-Log rotieren. Traefiks
/var/log/traefik/access.logwächst sonst unbegrenzt. Richte eine logrotate-Regel ein – zwingend mitcopytruncate: Diese Variante kopiert die Datei weg und leert das Original an Ort und Stelle, statt es umzubenennen und neu anzulegen. Nur so behält CrowdSecs Tail den Dateizugriff (bei rename/create verlöre es den Handle und läse nichts mehr). Lege/etc/logrotate.d/traefikan:Ausgabe/var/log/traefik/access.log { daily rotate 7 compress missingok notifempty copytruncate }CrowdSec liest nach dem Truncate an der wieder wachsenden Datei ganz normal weiter – ein Neustart des Containers ist nicht nötig.
fail2ban bleibt. Nimm fail2ban nicht heraus – es schützt weiter deinen SSH-Zugang auf Host-Ebene, während CrowdSec die Web-Apps abdeckt. Zwei Werkzeuge, zwei Zuständigkeiten.
Backups. Sichern musst du vor allem deine Konfiguration: den Ordner
~/crowdsec(compose.yaml,acquis.yaml) und~/traefik/dynamic/crowdsec.yamlmit dem Bouncer-Key. Nimm sie in dein Restic-Backup auf. Die Sperrlisten selbst sind flüchtig und bauen sich von allein wieder auf – kein Backup nötig.Optional: das CrowdSec-Dashboard. Wer die Angriffe grafisch sehen will, meldet die Instanz kostenlos in der CrowdSec Console an (
docker exec crowdsec cscli console enroll DEIN_ENROLL_KEY). Die Community-Blocklist läuft auch ohne Anmeldung.In dein Monitoring einbinden.
cscli metricsliefert dir Zahlen fürs Terminal; für Dauerbeobachtung stellt CrowdSec einen Prometheus-Endpunkt bereit (in der Config unterprometheus:aktivieren), den du an deinen Grafana-Stack hängen kannst. Dann siehst du Sperren nach Herkunft und Szenario auf einen Blick:
CrowdSec in Grafana: aktive Sperren nach Herkunft und Szenario, gespeist aus dem Prometheus-Endpunkt Ehrlich zum Aufwand: Nach dem Aufsetzen läuft CrowdSec weitgehend allein. Plane einmal im Monat den
hub upgradeund einen Blick aufcscli metricsund die Container-Version ein.
Feedback per E-Mail: feedback@serverkueche.de
Das könnte dir auch schmecken

Jellyfin selbst hosten: dein eigener Medienserver hinter Traefik
Jellyfin mit Docker hinter Traefik aufsetzen: deine Filme, Serien und Musik streamen – mit HTTPS und der Mediathek auf …

Monitoring mit Grafana & Prometheus: dein Server in Live-Dashboards
Ein vollständiger Monitoring-Stack aus Prometheus, node-exporter, cAdvisor und Grafana hinter Traefik – mit …

Immich selbst hosten: dein privates Foto-Backup hinter Traefik
Immich mit Docker hinter Traefik aufsetzen: die selbst gehostete Alternative zu Google Fotos – mit automatischem …