Warum überhaupt wechseln?
Ich habe lange Zeit den bekannten Nginx Proxy Manager als Reverse Proxy für den Zugriff von extern verwendet. Auch wenn er stets zuverlässig funktioniert hat, hat mich immer etwas gestört, dass es eben “nur” ein Reverse Proxy ohne weitere Sicherheitsfeatures ist.
Irgendwann habe ich mir dann überlegt, ob ich nicht zumindest Fail2Ban integrieren möchte und ob das überhaupt möglich ist. Nachdem ich aber auch schon lange nichts mehr mit Fail2Ban gemacht hatte, habe ich erstmal recherchiert, ob das immer noch der way to go ist. Nach kurzer Zeit bin ich auf eine modernere Alternative gestoßen: CrowdSec
Von CrowdSec hatte ich bis dahin noch nichts gehört und fing an, mich einzulesen. Die Welle an Infos hat mich erst einmal erschlagen und alles klang im ersten Moment super kompliziert. Ist es aber eigentlich gar nicht. Ich möchte versuchen es einmal einfach zu erklären:
CrowdSec - kurz erklärt
CrowdSec ist in seinen Grundzügen ähnlich zu Fail2Ban: Es werden Log Dateien überwacht und bei verdächtigen Aktivitäten IP Adressen gesperrt. Aber im Gegensatz zu Fail2Ban ist CrowdSec dezentral aufgebaut. Es besteht aus mehreren Komponenten, mindestens aber immer aus diesen dreien:
- LAPI: Das ist die lokale API, der Koordinator.
- Log Prozessor: Dieser überwacht die Log Dateien.
- Remediation Component (Bouncer): Der wehrt die Angriffe ab bzw. blockt die entsprechenden IP-Adressen.
Der Vorteil: Die Dezentralität. Die drei Komponenten können auf einem System installiert sein, müssen sie aber nicht. LAPI gibt es in der Regel nur eine. Log Prozessoren und Bouncer können aber mehrere existieren. Die Log Prozessoren können auf verschiedenen Systemen installiert sein und überwachen die jeweiligen Logs. Bei verdächtigen Aktivitäten, melden sie Alerts an die LAPI. Die LAPI erstellt dann nach Bedarf Decisions und sendet diese an die Bouncer -> Entdeckt ein Log Processor eine verdächtige Aktivität von einer IP wird diese von allen Bouncern automatisch blockiert.
Der Log Prozessor weiß aber nicht von sich aus, wie er Logs einer bestimmten Applikation interpretieren soll. Dafür gibt es Collections: Pakete aus Parsern und Szenarien, die CrowdSec beibringen, wie Logs einer Anwendung aussehen und was darin verdächtig ist.
Es gibt verschiedene Bouncer: Sie können z.B. auf Layer 3 direkt in iptables oder auf Layer 7 in einem Reverse Proxy sitzen.
Eine besondere Form des Bouncers ist die AppSec Component: Sie fungiert als Web Application Firewall (WAF) direkt innerhalb von CrowdSec und analysiert den HTTP-Traffic auf Anwendungsebene - also nicht nur anhand von IP-Adressen, sondern auch anhand der Anfragen selbst.
Die Komponente, die das volle Potential von CrowdSec erst entfaltet, ist allerdings die Cloud Console: Wenn man diese mit seiner LAPI verbindet, meldet die LAPI alle Decisions auch an die Cloud Konsole. Mit dieser kommen nämlich auch Blocklisten hinzu. Hat man einen CrowdSec Account erstellt und die Konsole verbunden, wird automatisch die Community Blocklist abonniert: Wird eine IP Adresse von genügend Community Mitgliedern gemeldet, landet diese auf der Community Blocklist und wird automatisch von allen verbundenen CrowdSec Installation geblockt. Das heißt die meisten bekannten IP Adressen werden schon blockiert, bevor sie überhaupt einen Angriffsversuch auf dein System starten können.
Im kostenlosen Plan kann man außerdem bis zu 3 weitere spezialisiertere Blocklisten abonnieren. Es gibt verschiedene, die z.B. speziell auf Mailserver oder Webserver zugeschnitten sind.
NPMplus + CrowdSec aufsetzen
Aber genug Theorie und zurück zum eigentlichen Ziel: CrowdSec in den Nginx Proxy Manager integrieren.
Nach etwas Recherche bin ich auf einen Fork von NPM mit wesentlich mehr Funktionen gestoßen: NPMplus. Und eines der besten Features ist die direkte Integration von CrowdSec, inklusive einer eigens von Zoey erstellten Collection für NPMplus & AppSec Integration.
Da ich nur eine Handvoll Hosts in meinem NPM hatte, habe ich mich entschieden NPMplus einfach von Scratch neu aufzusetzen. NPM hatte ich bisher in einem LXC auf einem meiner Proxmox Nodes gehostet. NPMplus und die zusätzlichen Komponenten sind allerdings auf Docker zugeschnitten. Es gibt zwar ein Proxmox Community Script für NPMplus, welches wohl Docker in einem LXC installiert, das habe ich aber nicht ausprobiert. Docker in einem LXC kann zu einigen Problemen führen und Zoey schreibt auch explizit in der NPMplus, dass man das lieber nicht machen sollte. Also habe ich eine Debian VM aufgesetzt, Docker installiert, und mich an die Konfiguration gemacht.
Ich habe dazu die Anleitung verwendet, welche von Zoey im CrowdSec Blog veröffentlicht wurde: Enhancing Web Server Security with NPMplus and CrowdSec. Die erforderlichen Schritte aus der Anleitung beschreibe ich auch im folgenden.
Stack vorbereiten + starten
Als erstes lädt man sich die aktuelle compose.yaml vom NPMplus GitHub Repo herunter:
curl -L https://raw.githubusercontent.com/ZoeyVid/NPMplus/refs/heads/develop/compose.yaml -o compose.yamlDiese muss nun an die eigenen Bedürfnisse angepasst werden. Die Datei erschlägt einen im ersten Moment mit den vielen Konfigurationsmöglichkeiten, ich habe sie deshalb für meine Bedürfnisse drastisch reduziert. Du solltest dich aber trotzdem mit der originalen Datei und ihren Möglichkeiten auseinandersetzen. Meine aktuelle Compose Datei sieht jedenfalls so aus:
name: npmplus
services:
npmplus:
container_name: npmplus
image: docker.io/zoeyvid/npmplus:2026-04-12-r1
restart: always
network_mode: host
volumes:
- /opt/npmplus:/data
environment:
- TZ=Europe/Berlin
- ACME_EMAIL=<[email protected]>
- LOGROTATE=true
- GOA=false
- GOA_LISTEN_LOCALHOST=true
- NGINX_TRUST_SECPR1=true # needed for Alexa AWS skill -> HomeAssistant communication
crowdsec:
container_name: crowdsec
image: docker.io/crowdsecurity/crowdsec:latest
restart: always
network_mode: bridge
ports:
- 127.0.0.1:7422:7422
- 8080:8080
environment:
- TZ=Europe/Berlin
- USE_WAL=true
- COLLECTIONS=ZoeyVid/npmplus crowdsecurity/http-dos
volumes:
- /opt/crowdsec/conf:/etc/crowdsec
- /opt/crowdsec/data:/var/lib/crowdsec/data
- /opt/npmplus/nginx/logs:/opt/npmplus/nginx/logs:ro
networks: {}Port 8080 (CrowdSec) ist bei mir bewusst nicht an localhost gebunden, da weitere Log Prozessoren und Bouncer auf anderen Systemen mit der LAPI kommunizieren. Wer das nicht plant, sollte hier 127.0.0.1:8080:8080 verwenden.
Ich verwende außerdem bewusst nicht docker.io/zoeyvid/npmplus:latest, da sich bei den Releases doch oftmals einiges ändert und ich lieber bewusst update.
Als nächstes muss der Ordner /opt/crowdsec/conf/acquis.d angelegt werden und dort folgende Datei erstellt werden: npmplus.yaml
Die Datei bekommt folgenden Inhalt:
filenames:
- /opt/npmplus/nginx/logs/*.log
labels:
type: npmplus
---
listen_addr: 0.0.0.0:7422
appsec_config: crowdsecurity/appsec-default
name: appsec
source: appsec
labels:
type: appsec
# if you use openappsec you can enable this
#---
#source: file
#filenames:
# - /opt/openappsec/logs/cp-nano-http-transaction-handler.log*
#labels:
# type: openappsecDie aktuelle Datei findet man auch im NPMplus Repo: npmplus.yaml.
Anschließend kann man den Stack auch schon starten:
docker compose up -dNach dem ersten Start müssen die Logs gecheckt werden, um das initiale Passwort zu erhalten:
docker logs npmplusNPMplus als Bouncer einrichten
Nun muss NPMplus als Remediation Component (Bouncer) eingerichtet werden, damit Decisions auch direkt von NPMplus auf Layer 7 geblockt werden können.
Hierzu muss der folgende Befehl ausgeführt werden und der ausgegebene API Key kopiert werden:
docker exec crowdsec cscli bouncers add npmplus -o rawDer API Key muss nun in der Datei /opt/npmplus/crowdsec/crowdsec.conf hinterlegt werden & ENABLED auf true gesetzt werden:
ENABLED=true
API_URL=http://127.0.0.1:8080
# Authentication options, either using an API key or TLS client certificate.
API_KEY=<APIKEY>
USE_TLS_AUTH=false
TLS_CLIENT_CERT=/path/to/client.crt
TLS_CLIENT_KEY=/path/to/client.key
CACHE_EXPIRATION=1
# bounce for all type of remediation that the bouncer can receive from the local API
BOUNCING_ON_TYPE=all
FALLBACK_REMEDIATION=ban
REQUEST_TIMEOUT=3000
UPDATE_FREQUENCY=10
# By default internal requests are ignored, such as any path affected by rewrite rule.
# set ENABLE_INTERNAL=true to allow checking on these internal requests.
ENABLE_INTERNAL=false
# live or stream
MODE=live
# exclude the bouncing on those location
EXCLUDE_LOCATION=
#those apply for "ban" action
# /!\ REDIRECT_LOCATION and RET_CODE can't be used together. REDIRECT_LOCATION take priority over RET_CODE
BAN_TEMPLATE_PATH=/data/crowdsec/ban.html
REDIRECT_LOCATION=
RET_CODE=
#those apply for "captcha" action
#valid providers are recaptcha, hcaptcha, turnstile
CAPTCHA_PROVIDER=
# Captcha Secret Key
SECRET_KEY=
# Captcha Site key
SITE_KEY=
CAPTCHA_TEMPLATE_PATH=/data/crowdsec/captcha.html
CAPTCHA_EXPIRATION=3600
APPSEC_URL=http://127.0.0.1:7422
APPSEC_FAILURE_ACTION=deny
APPSEC_CONNECT_TIMEOUT=
APPSEC_SEND_TIMEOUT=1000
APPSEC_PROCESS_TIMEOUT=30000
ALWAYS_SEND_TO_APPSEC=false
SSL_VERIFY=trueNun muss der Container noch neugestartet werden.
docker restart npmplusFazit & Ausblick
NPMplus ist jetzt mit CrowdSec verbunden. Die von Nginx Proxy Manager bekannte Weboberfläche ist unter https://<HOSTNAME>:81 erreichbar. Als Zugangsdaten wird die im Compose File konfigurierte ACME_EMAIL und das zuvor kopierte initiale Passwort verwendet.
Damit ist das Grundsetup abgeschlossen - NPMplus läuft, CrowdSec überwacht die Logs und blockt verdächtige IPs, die AppSec Komponente analysiert den Traffic auf Anwendungsebene.
Für die wichtigsten Befehle der Crowdsec-CLI habe ich außerdem ein Cheatsheet zusammengestellt.
Ich habe bereits erwähnt, dass bei mir noch weitere Log Prozessoren und Bouncer auf anderen Systemen mit der LAPI kommunizieren. Wie ich diese in das Gesamtsetup integriert habe und wie man seine LAPI mit der Cloud Console verbindet, zeige ich in einem Folgepost.