Ausgabe von cscli metrics: Acquisition aus dem Caddy-Access-Log und die aktiven Parser

CrowdSec für Caddy: Reverse Proxy im Homelab mit dem nftables-Bouncer absichern

Ich hatte gerade eine neue öffentliche Subdomain angelegt, und ein paar Minuten später standen die ersten fremden Anfragen im Log. Lauter 401er, also Scanner, die Login-Seiten abklappern. Mein Caddy-Reverse-Proxy beantwortet das zwar brav, aber ich wollte, dass solche IPs gar nicht erst bis zu den Diensten durchkommen. Deshalb läuft jetzt CrowdSec auf dem Proxy.

Caddy läuft bei mir an jedem Standort in einem eigenen LXC auf Proxmox. CrowdSec habe ich direkt in diesen Container installiert, zusammen mit dem Firewall-Bouncer für nftables. Der Bouncer sperrt IPs auf Firewall-Ebene, deshalb brauchst du keinen eigenen Caddy-Build mit Bouncer-Modul.

Schritt 1: CrowdSec und Bouncer installieren

Auf einem Debian-LXC fügst du zuerst das offizielle Repository hinzu und installierst dann beide Pakete. Bei mir ist es CrowdSec 1.8.1.

curl -s https://install.crowdsec.net | sh
apt install crowdsec crowdsec-firewall-bouncer-nftables

Wenn der Bouncer auf derselben Maschine wie CrowdSec landet, registriert er sich bei der Installation selbst an der lokalen API. Danach kommen die Collections:

cscli collections install crowdsecurity/caddy crowdsecurity/http-cve \
  crowdsecurity/base-http-scenarios crowdsecurity/whitelist-good-actors
systemctl reload crowdsec
  • crowdsecurity/caddy bringt den Parser für die Caddy-Logs mit.
  • crowdsecurity/http-cve erkennt Anfragen, die bekannte Sicherheitslücken ausnutzen wollen.
  • crowdsecurity/base-http-scenarios enthält die allgemeinen Szenarien wie Scans und Bruteforce auf HTTP.
  • crowdsecurity/whitelist-good-actors sorgt dafür, dass bekannte gute Bots nicht gesperrt werden.

Zusätzlich habe ich die Instanz in der CrowdSec Console angemeldet. Über die Central API (CAPI) bekommt sie dann die Community-Blocklist, also IPs, die andere CrowdSec-User bereits gemeldet haben.

Schritt 2: Caddy schreibt JSON-Logs

CrowdSec kann nur auswerten, was im Access-Log steht. Damit ich den Log-Block nicht in jede Site kopieren muss, steht er bei mir als Snippet im Caddyfile, und jede Site holt ihn sich per import:

(access_log) {
	log {
		output file /var/log/caddy/access.log
		format json
	}
}

app.example.de {
	import access_log
	reverse_proxy 192.168.1.20:8080
}

cloud.example.de {
	import access_log
	reverse_proxy 192.168.1.21:8443
}

Das Snippet muss im Caddyfile vor der ersten Site stehen, die es importiert. Steht es weiter unten, kennt Caddy es an der Stelle noch nicht.

Achte auch darauf, wem die Log-Datei gehört. Ich habe einmal caddy validate als root ausgeführt, bevor die Datei existierte. Dabei hat Caddy sie als root angelegt, und der eigentliche Caddy-Prozess, der als User caddy läuft, konnte danach nicht mehr reinschreiben. Logs kamen keine mehr an. Der Fix:

chown caddy:caddy /var/log/caddy/access.log

Schritt 3: Die Acquisition

Jetzt muss CrowdSec wissen, welche Datei es lesen soll und welcher Parser dazugehört. Das steht in /etc/crowdsec/acquis.d/caddy.yaml:

filenames:
  - /var/log/caddy/access.log
labels:
  type: caddy

Nach einem systemctl reload crowdsec siehst du mit cscli metrics, ob Zeilen aus dem Access-Log gelesen und geparst werden.

Schritt 4: Whitelists, damit du dich nicht selbst aussperrst

Bevor CrowdSec anfängt zu sperren, sollten deine eigenen Netze auf der Whitelist stehen. Bei mir liegt dafür eine Datei in /etc/crowdsec/parsers/s02-enrich/, zum Beispiel my-whitelist.yaml:

name: local/my-whitelist
description: "Eigene Netze nie sperren"
whitelist:
  reason: "LAN, Tailscale und eigene feste IP"
  ip:
    - "203.0.113.10"
  cidr:
    - "192.168.0.0/16"
    - "10.0.0.0/8"
    - "172.16.0.0/12"
    - "100.64.0.0/10"

Drin sind die privaten Adressbereiche, der Tailscale-Bereich 100.64.0.0/10 und die feste öffentliche IP meines Anschlusses zu Hause. Die 203.0.113.10 oben ist nur ein Platzhalter, da gehört deine eigene IP hin.

Der zweite Standort mit dynamischer IP

Mein zweiter Standort hat eine dynamische IP und ist über einen DDNS-Hostnamen erreichbar. Eine statische Whitelist hilft da wenig, weil die IP sich ändert. Deshalb gibt es eine zweite Whitelist-Datei, die ein kleines Script alle 15 Minuten neu schreibt:

#!/bin/sh
HOST="standort2.example.de"
FILE="/etc/crowdsec/parsers/s02-enrich/ddns-whitelist.yaml"

IP=$(getent ahostsv4 "$HOST" | awk 'NR==1 {print $1}')
[ -n "$IP" ] || exit 1

cat > "$FILE.tmp" <<EOF
name: local/ddns-whitelist
description: "Dynamische IP vom zweiten Standort"
whitelist:
  reason: "zweiter Standort per DDNS"
  ip:
    - "$IP"
EOF

if ! cmp -s "$FILE.tmp" "$FILE"; then
  mv "$FILE.tmp" "$FILE"
  systemctl reload crowdsec
else
  rm "$FILE.tmp"
fi

Leg das Script zum Beispiel unter /usr/local/bin/crowdsec-ddns-whitelist.sh ab. CrowdSec wird nur neu geladen, wenn sich die IP wirklich geändert hat. Gestartet wird es von einem systemd-Timer:

# /etc/systemd/system/crowdsec-ddns-whitelist.service
[Unit]
Description=CrowdSec Whitelist fuer DDNS-Standort aktualisieren

[Service]
Type=oneshot
ExecStart=/usr/local/bin/crowdsec-ddns-whitelist.sh

# /etc/systemd/system/crowdsec-ddns-whitelist.timer
[Unit]
Description=CrowdSec DDNS-Whitelist alle 15 Minuten

[Timer]
OnBootSec=2min
OnCalendar=*:0/15

[Install]
WantedBy=timers.target
chmod +x /usr/local/bin/crowdsec-ddns-whitelist.sh
systemctl daemon-reload
systemctl enable --now crowdsec-ddns-whitelist.timer

Die Pitfalls aus meinem Setup

1. Caddy hing in „reloading“

Nach den Änderungen am Caddyfile wollte ich mit systemctl reload caddy neu laden, und der Befehl lief in einen Timeout. Die systemd-Unit hing im Status „reloading“ fest. Funktioniert hat der Reload direkt über die Admin-API von Caddy:

caddy reload --config /etc/caddy/Caddyfile --force

Damit war die neue Config aktiv. Einen echten Restart des Dienstes habe ich später in einem ruhigen Moment nachgeholt, damit auch systemd wieder einen sauberen Status hat.

2. 512 MB RAM sind zu knapp

Mein Caddy-LXC hatte bis dahin 512 MB RAM, für Caddy allein war das genug. Mit CrowdSec dazu habe ich den Container auf 1 GB hochgesetzt.

Kontrollieren, was CrowdSec macht

Diese vier Befehle nutze ich am häufigsten:

BefehlZeigt
cscli decisions listAktuell gesperrte IPs mit Grund und Restlaufzeit
cscli alerts listWelche Szenarien wann ausgelöst haben
cscli metricsGelesene Logzeilen, Parser, Bouncer-Aktivität
cscli collections listInstallierte Collections und ob sie aktuell sind

Wenn du testen willst, ob die Whitelist greift, schau nach ein paar Fehlversuchen aus dem eigenen LAN in cscli decisions list. Deine eigene IP sollte dort nie auftauchen.

Fazit

CrowdSec liest jetzt die JSON-Logs von Caddy, der nftables-Bouncer sperrt erkannte Scanner direkt an der Firewall, und die Community-Blocklist hält bekannte IPs schon vorher draußen. Caddy selbst läuft dabei weiter als Standard-Build ohne zusätzliche Module.

Nutzt du CrowdSec schon, oder setzt du auf Fail2ban? Schreib es in die Kommentare.

Ähnliche Beiträge

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert