10 KiB
Technical Architecture Specification
Dokumenten-ID: GNAI-006-SEC
Titel: Homelab / Infrastructure – Reverse Proxy & Containerized IPS Specification
Version: 1.0
Status: Aktiv
Vorgänger / Referenzen: GNAI-003-INFRA, GNAI-004-SEC
1. Zweck & Geltungsbereich
Dieses Dokument definiert die verbindliche System- und Sicherheitsarchitektur für den zentralen Reverse Proxy und das Intrusion Prevention System (IPS) der Ghostnet-Infrastruktur auf Proxmox LXC 103 (reverse-proxy).
Umfang dieses Dokuments:
- Betrieb von Nginx Proxy Manager (NPM) als zentraler Ingress-Controller
- Containerisiertes Fail2Ban als IPS mit nftables-Integration auf dem LXC
- Log-Parsing-Regeln und Filterdefinitionen für das NPM-spezifische Access-Log-Format
- Gehärtete Firewall-Baseline und Zugriffssteuerung
2. System- Baseline (LXC 103)
| Parameter | Wert |
|---|---|
| Container-ID | CT 103 |
| Hostname | reverse-proxy |
| Betriebssystem | Debian GNU/Linux 13 (Trixie) |
| Unprivileged | Ja |
| Cores / RAM / Swap | 2 Cores / 1024 MB RAM / 512 MB Swap |
| RootFS | 8 GB |
| Netzwerkkonfiguration | IP: 10.0.0.10/24 |
| LXC Features | nesting=1, keyctl=1 |
| Proxmox Host | Proxmox VE 8.4.21 (Kernel 6.8.12-23-pve) |
Hinweis zur Proxmox-Firewall: Proxmox Cluster- und Node-Firewalls sind deaktiviert (enable 0). Die Paketfilterung erfolgt exklusiv lokal über nftables innerhalb des LXC.
3. Netzwerk- & Firewall-Architektur (nftables)
Die lokale Paketfilterung basiert auf nftables mit einer strikten INPUT policy drop-Doktrin.
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
iifname "lo" accept
ct state invalid drop
ct state established,related accept
ip protocol icmp accept
ip saddr 10.0.0.0/24 tcp dport { 22, 81 } accept
tcp dport { 80, 443 } accept
}
chain forward {
type filter hook forward priority filter; policy accept;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
Port-Matrix
| Port | Protokoll | Zweck | Erreichbarkeit |
|---|---|---|---|
| 80 | TCP | HTTP Ingress (NPM) | Öffentlich |
| 443 | TCP | HTTPS Ingress (NPM) | Öffentlich |
| 81 | TCP | NPM Web Admin Interface | Nur LAN (10.0.0.0/24) |
| 22 | TCP | OpenSSH Management | Nur LAN (10.0.0.0/24) |
| 3000 | TCP | NPM Node.js Backend | Intern / Nicht exponiert |
4. Container-Infrastruktur (Podman Quadlets)
Nginx Proxy Manager und Fail2Ban werden als rootful Podman-Container via systemd Quadlets mit Network=host betrieben.
4.1 Nginx Proxy Manager Quadlet
- Datei:
/etc/containers/systemd/nginx-proxy-manager.container - Image:
docker.io/jc21/nginx-proxy-manager:2.11.1(ausdrücklich gepinnt)
[Unit]
Description=Nginx Proxy Manager
Wants=network-online.target
After=network-online.target
[Container]
Image=docker.io/jc21/nginx-proxy-manager:2.11.1
ContainerName=nginx-proxy-manager
Network=host
Volume=/opt/nginx-proxy-manager/data:/data:rw
Volume=/opt/nginx-proxy-manager/letsencrypt:/etc/letsencrypt:rw
Environment=DISABLE_IPV6=true
Environment=TZ=Europe/Berlin
[Service]
Restart=always
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target
4.2 Fail2Ban IPS Quadlet
- Datei:
/etc/containers/systemd/fail2ban.container - Image:
lscr.io/linuxserver/fail2ban:latest - Capabilities:
NET_ADMIN,NET_RAW(kein--privileged)
[Unit]
Description=Fail2Ban
Wants=network-online.target
After=network-online.target
[Container]
Image=lscr.io/linuxserver/fail2ban:latest
ContainerName=fail2ban
Network=host
AddCapability=NET_ADMIN
AddCapability=NET_RAW
Volume=/opt/fail2ban/config:/config:rw
Volume=/opt/nginx-proxy-manager/data/logs:/remotelogs/npm:ro
Environment=TZ=Europe/Berlin
[Service]
Restart=always
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target
5. NPM Logformat & Parsing-Regeln
NPM nutzt ein eigenes Access-Log-Format, bei dem die eigentliche Client-IP nicht am Zeilenanfang steht.
Beispielzeile:
[03/Sep/2026:18:35:02 +0200] - 204 204 - POST https jellyfin.ghostnet.selfhost.eu "/Sessions/Playing/Progress" [Client 93.228.100.74] [Length 0] [Gzip -] [Sent-to 10.0.0.11] "JellyfinDesktop/2.0.0" "-"
Verbindliche Parser-Regeln:
- Die Client-IP muss zwingend aus dem Feld
[Client <HOST>]extrahiert werden. - URL-Pfade oder Host-Header dürfen nicht fälschlicherweise als IP-Quelle interpretiert werden.
- Statuscodes oder Response-Zeiten sind für automatisches Banning ungeeignet, da legitim nicht erreichbare Backends (502) False Positives auslösen würden.
6. Fail2Ban-Konfiguration & Jails
LinuxServer.io nutzt /config/fail2ban/ als interne Konfigurationsstruktur. Sämtliche Konfigurationen liegen persistent auf dem Host unter /opt/fail2ban/config/fail2ban/.
6.1 Wichtige Erkenntnis zur Action-Syntax
Standardmäßig nutzt die Action nftables-multiport bei fehlenden Parametern Port 22 (SSH). Sperrports müssen zwingend direkt in der action-Zeile des Jails in eckigen Klammern übergeben werden, um Anfragen auf HTTP/HTTPS durchzusetzen:
action = nftables-multiport[port="80,443", protocol=tcp]
6.2 Jail 1: Exploit & Scanner Protection (npm-scanner)
Gezielte Blockade von Pfad-Traversal-Versuchen, Secret-Abfragen und automatisierten Framework-Scans.
-
Filter:
/opt/fail2ban/config/fail2ban/filter.d/npm-scanner.conf[Definition] failregex = ^\[.*\] - [0-9]{3} [0-9]{3} - (GET|POST|HEAD) [^ ]+ [^ ]+ "/(?:[^"]*/)?(?:\.env[^"/]*|\.git/[^" ]*|\.aws/[^" ]*|wp-config\.php[^" ]*|wp-login\.php(?:\?[^" ]*)?|wp-admin(?:/[^" ]*)?|actuator(?:/[^" ]*)?|_ignition(?:/[^" ]*)?|storage/logs(?:/[^" ]*))" \[Client <HOST>\] ignoreregex = -
Jail:
/opt/fail2ban/config/fail2ban/jail.d/npm-scanner.local[npm-scanner] enabled = true filter = npm-scanner backend = auto logpath = /remotelogs/npm/proxy-host-*_access.log findtime = 10m maxretry = 3 bantime = 1h ignoreip = 127.0.0.1/8 10.0.0.0/24 action = nftables-multiport[port="80,443", protocol=tcp]
6.3 Jail 2: Bad User-Agents (npm-badbots)
Blockade bekannter Angriffs-Scanner und Reconnaissance-Tools.
-
Filter:
/opt/fail2ban/config/fail2ban/filter.d/npm-badbots.conf
(Wichtig: Nutzt lokale Case-Insensitivity(?i:...), um Python-RegEx-Kompilierungsfehler zu vermeiden)[Definition] failregex = ^\[.*\] - [0-9]{3} [0-9]{3} - (?:GET|POST|HEAD|OPTIONS) [^ ]+ [^ ]+ "[^"]*" \[Client <HOST>\] .* "[^"]*(?i:sqlmap|nikto|nmap|masscan|zgrab|nessus|openvas|dirbuster|gobuster|wpscan|python-requests|go-http-client|censys|whatweb|acunetix)[^"]*" ignoreregex = -
Jail:
/opt/fail2ban/config/fail2ban/jail.d/npm-badbots.local[npm-badbots] enabled = true filter = npm-badbots backend = auto logpath = /remotelogs/npm/proxy-host-*_access.log findtime = 10m maxretry = 2 bantime = 24h ignoreip = 127.0.0.1/8 10.0.0.0/24 action = nftables-multiport[port="80,443", protocol=tcp]
7. Aktive Proxy Hosts
Aktuell im Nginx Proxy Manager konfigurierte und aktive Routen:
| ID | Hostname | Backend-Ziel | Dienst |
|---|---|---|---|
| 1 | jellyfin.ghostnet.selfhost.eu |
10.0.0.11:8096 |
Jellyfin Media Server |
| 3 | nextcloud.ghostnet.selfhost.eu |
10.0.0.12:8080 |
Nextcloud Cloud Storage |
| 4 | git.ghostnet.selfhost.eu |
10.0.0.17:3000 |
Gitea Code Hosting |
| 7 | auth.ghostnet.selfhost.eu |
10.0.0.4:8080 |
Keycloak Identity Provider |
| 10 | japan.ghostnet.selfhost.eu |
10.0.0.23:80 |
Japanese Web App |
| 12 | vault.ghostnet.selfhost.eu |
10.0.0.47:8080 |
Vaultwarden Password Manager |
8. Betriebs- & Diagnosekommandos
Fail2Ban-Status
# Gesamtstatus der aktiven Jails
podman exec fail2ban fail2ban-client status
# Spezifischen Jail prüfen
podman exec fail2ban fail2ban-client status npm-scanner
podman exec fail2ban fail2ban-client status npm-badbots
Filter mit echten Logs testen
podman exec fail2ban fail2ban-regex \
/remotelogs/npm/proxy-host-10_access.log \
/config/fail2ban/filter.d/npm-scanner.conf
nftables-Regeln & aktive Bans auf dem Host verifizieren
nft list ruleset | grep -A 10 "table inet f2b-table"
Manuelle Sperre / Freigabe
# IP manuell sperren
podman exec fail2ban fail2ban-client set npm-scanner banip <IP>
# IP manuell freigeben
podman exec fail2ban fail2ban-client set npm-scanner unbanip <IP>
9. Sicherheits- & Betriebsregeln für Administrierende
- Kein generisches Statuscode-Banning: Generische HTTP
4xx- oder5xx-Fehler dürfen nicht als pauschales Ban-Kriterium verwendet werden. - Keine verfrühten Upgrades: Die NPM-Version ist auf
2.11.1festgeschrieben. Upgrades müssen separat in einer Testumgebung evaluiert werden. - Privilegien-Minimierung: Der Fail2Ban-Container wird strikt ohne
--privilegedbetrieben (NET_ADMINundNET_RAWreichen aus). - Whitelist-Pflicht: Das Subnetz
10.0.0.0/24sowie127.0.0.1/8müssen in allen Jails inignoreiphinterlegt bleiben, um Aussperrungen zu verhindern. - Syntax-Check vor Reload: Jede Änderung an Fail2Ban-Konfigurationen ist vorab mit
podman exec fail2ban fail2ban-client -tauf Gültigkeit zu prüfen.
10. Backup & Disaster Recovery
Es existiert ein vollständiges Proxmox LXC Snapshot-Backup von CT 103:
- Backup-Datei:
/var/lib/vz/dump/vzdump-lxc-103-2026_08_31-19_11_10.tar.zst
Vor strukturellen Änderungen an nftables oder den Podman Quadlets ist stets ein neuer Proxmox LXC-Snapshot zu erstellen.
11. Änderungsverlauf
| Datum | Version | Geändert von | Beschreibung |
|---|---|---|---|
| 2026-08-28 | 0.1 | spektr | Erstellung der ersten Baseline-Drafts. |
| 2026-09-05 | 0.9 | spektr | Implementierung Fail2Ban Container & Test-Setup (F2B-01). |
| 2026-09-07 | 1.0 | spektr | Korrektur Action-Syntax (port="80,443"), Behebung RegEx-Fehler (npm-badbots), E2E-Validierung in nftables abgeschlossen (F2B-02). Finales SOP erstellt. |