Wer einen öffentlich erreichbaren Server betreibt, weiß: Es dauert keine Stunden, bis die ersten automatisierten Scanner anklopfen. In diesem Post zeige ich, wie ich meinen Server mit Caddy, CrowdSec und einer VPN-IP absichere — alles als Docker Compose Setup — und was die Live-Daten bereits zeigen.
Die Architektur
┌─────────────────┐
│ 🌐 Besucher │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 🔒 VPN-IP │
└────────┬────────┘
│
▼
┌─────────────────────────────────┐
│ 🛡️ CrowdSec Bouncer │
│ │
│ • Credential Scan / Brute Force│
│ • Bad User Agents │
│ • Bekannte Bad IPs (Community) │
│ • Path Scanning (/wp-admin…) │
└────────┬──────────┬─────────────┘
│ │
✅ OK │ │ 🚫 Bot / Bad UA
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Caddy │ │403 Forbidden │
│ Rev. Proxy │ └──────────────┘
└──────┬───────┘
│
▼
┌─────────────────┐
│ 🔧 Service │
│ Backend │
└─────────────────┘
Das Docker Compose Setup
Alles läuft in Containern. Caddy bringt den CrowdSec Bouncer direkt als Plugin mit — kein separater Prozess, kein Sidecar. CrowdSec selbst läuft als eigener Container und liest die Caddy-Logs über ein gemeinsames Volume.
services:
caddy:
image: caddy:crowdsec # custom build mit bouncer plugin
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_logs:/var/log/caddy # logs werden von crowdsec gelesen
depends_on:
- crowdsec
crowdsec:
image: crowdsecurity/crowdsec:latest
restart: unless-stopped
environment:
COLLECTIONS: >
crowdsecurity/caddy
crowdsecurity/http-cve
crowdsecurity/wordpress
volumes:
- ./crowdsec/config:/etc/crowdsec
- ./crowdsec/data:/var/lib/crowdsec/data
- caddy_logs:/var/log/caddy:ro # read-only zugriff auf caddy logs
volumes:
caddy_data:
caddy_logs:
Der entscheidende Punkt: Caddy schreibt Logs in ein Volume, CrowdSec liest genau dieses Volume. Der Bouncer im Caddy-Container fragt CrowdSec per lokaler API bei jedem Request an.
Wie ein Block konkret aussieht
Ein reales Beispiel aus den Logs, IP anonymisiert:
Ein Scanner aus einem Hyperscaler Netz startet morgens um 08:28 Uhr und feuert in weniger als 3 Minuten mehrere Angriffsmuster ab:
08:28:21 local/http-php-scan → Suche nach PHP-Shells und Backdoors
08:28:55 crowdsecurity/http-probing → Allgemeines Path-Scanning
08:29:03 crowdsecurity/http-wordpress-scan → /wp-login, /xmlrpc.php etc.
08:29:04 → BAN ausgelöst, 30 Tage gesperrt
Ab diesem Zeitpunkt antwortet Caddy auf jeden weiteren Request dieser IP sofort mit:
HTTP/1.1 403 Forbidden
Kein Log-Eintrag im Backend, kein Traffic zum Service — der Bouncer blockt direkt im Caddy-Layer, bevor die Anfrage auch nur geroutet wird.
Was nach kurzer Laufzeit bereits geblockt wurde
Aktive Bans: 27 IPs (+ 73 weitere Duplikate in der Datenbank)
Die häufigsten Angriffsmuster:
| Regel | Was wird erkannt |
|---|---|
http-generic-bf | Brute Force auf Login-Endpunkte |
http-probing | Allgemeines Abscannen von Pfaden |
http-sensitive-files | Zugriff auf .env, Backups, Configs |
http-wordpress-scan | WordPress-spezifische Scanner |
http-php-scan | PHP-Shell und Backdoor-Suche |
http-git-probe | Versuch .git/ auszulesen |
jira_cve-2021-26086 | Gezielter CVE-Exploit-Versuch |
http-path-traversal-probing | Directory Traversal |
http-admin-probe | Suche nach Admin-Panels |
http-backup-probe | Suche nach Backup-Dateien |
Auffällig: Der Großteil der angreifenden IPs kommen aus den Hyperscaler — also gemietete Cloud-VMs, die als Scanner-Plattform missbraucht werden. Herkunftsländer: gemischt — quer durch alle Zeitzonen, rund um die Uhr.
Fazit
Das Setup ist ressourcenschonend, braucht keine manuelle Pflege von Firewall-Regeln und funktioniert out-of-the-box. Docker Compose macht es auf jedem VPS reproduzierbar deploybar.
Was mich am meisten überzeugt: Die Kombination aus VPN-IP und CrowdSec bedeutet, dass der echte Server nie direkt exponiert ist — und selbst wenn ein Scanner die VPN-IP trifft, fliegt er nach wenigen Requests automatisch raus.


0 Comments