Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fail2ban-webui
Eine schlanke, selbst gehostete Weboberfläche, um fail2ban-Sperren auf mehreren Hosts anzuzeigen, zu setzen und aufzuheben – Bare Metal, VMs, LXC-Container oder Docker-Hosts.
- Eine Seite für alle Hosts: aktuelle Sperren pro Jail, Herkunft (GeoIP) mit Weltkarte, Suche und Filter
- IP dauerhaft auf beliebigen Hosts sperren, pro Jail oder überall entsperren
- Protokoll jeder Aktion (wer, wann, von wo)
- Optional: Log-Verlauf pro IP aus Loki, Links nach Grafana
- Anmeldung mit lokalem Benutzer, OpenID Connect (z. B. Authentik) oder über den Reverse Proxy
- Oberfläche auf Englisch und Deutsch, weitere Sprachen über eine JSON-Datei
- Läuft als gehärteter Container (schreibgeschützt, ohne Capabilities, nicht als root),
amd64undarm64
Browser ──HTTPS──▶ Reverse Proxy ──▶ fail2ban-webui (Docker)
│ SSH, ein eingeschränkter Schlüssel
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
Host A (Bare Metal) LXC-Container Docker-Host
f2b-webctl f2b-webctl f2b-webctl
fail2ban fail2ban fail2ban
Die Weboberfläche bekommt nie eine Shell auf deinen Hosts. Auf jedem Host läuft ein kleiner
Agent, f2b-webctl, als SSH-Forced-Command. Er kennt nur
list, ban <ip>, unban <jail> <ip>, unban-all <ip> und version, sperrt niemals
private Netze, die eigenen Adressen des Hosts oder Einträge seiner Schutzliste und
protokolliert jede Aktion im Syslog. Zugriff auf den fail2ban-Socket wäre gleichbedeutend
mit root – die Sicherheitsgrenze ist der Agent, nicht die Weboberfläche.
Voraussetzungen
Getestet auf Ubuntu 24.04/26.04 und Debian 12/13 – Bare Metal, VM oder LXC. Alle Befehle als root.
Grundpakete (alle Hosts)
apt update
apt install -y curl git
apt install -y mc # optional: Midnight Commander als Dateimanager
Docker-Host (Weboberfläche)
Gebraucht wird eine vollständige Docker-Umgebung – Docker Engine und das Compose-Plugin
(Befehl docker compose mit Leerzeichen), amd64 oder arm64.
Auf Ubuntu 24.04/26.04 z. B. mit dem Docker-Installer:
git clone https://git.retronerd.at/schiwagoa/public-docker-installer-26.04-v1.git
cd public-docker-installer-26.04-v1
chmod +x *.sh
./docker-installer-2604-v1.sh
Diese Anleitung verwendet docker compose (Plugin). Der Installer richtet zusätzlich das
eigenständige docker-compose ein; das Plugin kommt normalerweise mit docker-ce mit.
Prüfen – beide Befehle müssen eine Version ausgeben:
docker version --format '{{.Server.Version}}'
docker compose version
Meldet docker compose version den Fehler 'compose' is not a docker command, das Plugin
nachinstallieren: apt install -y docker-compose-plugin.
In einem LXC-Container braucht Docker auf dem Proxmox-Host die Features
nesting=1,keyctl=1 (unprivilegierter Container: pct set <id> --features nesting=1,keyctl=1,
danach den Container neu starten).
fail2ban-Hosts (Agent)
- fail2ban ≥ 0.11 (Start-/Endzeiten der Sperren brauchen
--with-time) sudofür den Standardbenutzerf2bweb; mit einem root-Schlüssel nicht nötigbash,ssh-keygen(Teil von OpenSSH) für den Installer
Installation auf einem Host
Weboberfläche und Agent auf demselben Docker-Host – so getestet auf Ubuntu 26.04 (LXC).
Weitere fail2ban-Hosts kommen später nur mit dem Agenten dazu (docs/agent.md).
Im Beispiel hat der Host die LAN-Adresse 192.168.1.10 – überall durch deine ersetzen.
fail2ban vorbereiten
apt install -y fail2ban python3 sudo openssh-server
systemctl enable --now fail2ban
fail2ban-client ping # Server replied: pong
Quellen holen
git clone https://git.retronerd.at/schiwagoa/fail2ban-webui.git /opt/fail2ban-webui-src
Weboberfläche einrichten
mkdir -p /opt/fail2ban-webui/{config,data} && cd /opt/fail2ban-webui
curl -fsSLo docker-compose.yaml https://git.retronerd.at/schiwagoa/fail2ban-webui/raw/branch/main/examples/docker-compose.yaml
curl -fsSLo .env https://git.retronerd.at/schiwagoa/fail2ban-webui/raw/branch/main/examples/.env.example
Der Container läuft als UID 10050 und muss in data/ schreiben:
chown -R 10050:10050 data && chmod 700 data
Für den Zugriff direkt im LAN (ohne Reverse Proxy) den Port an die LAN-Adresse binden – hinter einem Nginx Proxy Manager diesen Schritt weglassen:
sed -i 's/"127.0.0.1:8080:8080"/"192.168.1.10:8080:8080"/' docker-compose.yaml
Adresse, Sprache und Proxy-Einstellung setzen. Ohne TLS muss PUBLIC_URL mit http://
beginnen, sonst schickt der Browser das Sitzungs-Cookie nicht mit; ohne Proxy gehört
TRUSTED_PROXY_HOPS auf 0:
sed -i -e 's|^PUBLIC_URL=.*|PUBLIC_URL=http://192.168.1.10:8080|' -e 's|^DEFAULT_LANG=.*|DEFAULT_LANG=de|' -e 's|^TRUSTED_PROXY_HOPS=.*|TRUSTED_PROXY_HOPS=0|' .env
Passwort-Hash erzeugen (mindestens 12 Zeichen, zweimal eingeben):
docker compose run --rm -it fail2ban-webui hash-password
Die ausgegebene Zeile AUTH_PASSWORD_HASH='scrypt:…' kopieren und in der .env die Zeile
AUTH_PASSWORD_HASH='scrypt:...' damit ersetzen – die einfachen Anführungszeichen bleiben:
nano .env
SSH-Schlüssel der Weboberfläche erzeugen:
docker compose run --rm fail2ban-webui keygen
Agent installieren
Probelauf – zeigt nur, was passieren würde. 172.23.51.10 ist die feste Adresse der
Weboberfläche in ihrem Docker-Netz:
bash /opt/fail2ban-webui-src/agent/install.sh --key-file /opt/fail2ban-webui/data/ssh/id_ed25519.pub --from 172.23.51.10 --dry-run
Installieren und mit y bestätigen – am Ende meldet der Selbsttest
2 jails, 0 bans: manual-block, sshd. Laufen auf dem Host weitere Docker-Dienste mit
veröffentlichten Ports (z. B. ein Nginx Proxy Manager), zusätzlich --docker anhängen,
damit manuelle Sperren auch dort greifen:
bash /opt/fail2ban-webui-src/agent/install.sh --key-file /opt/fail2ban-webui/data/ssh/id_ed25519.pub --from 172.23.51.10
Host eintragen und testen
cat > config/hosts.yaml <<'EOF'
hosts:
- name: srv200
address: host.docker.internal
EOF
Host-Schlüssel holen – die Fingerprints werden angezeigt, mit y bestätigen:
docker compose run --rm -it fail2ban-webui keyscan
Verbindung testen – erwartet: OK srv200 agent 1.0.0, 2 jails, 0 bans:
docker compose run --rm fail2ban-webui check
Starten
docker compose up -d
docker compose ps # Up … (healthy) nach etwa 30 Sekunden
docker compose logs fail2ban-webui # hosts: srv200 – auth: local – geoip: on, danach "GeoIP database updated"
Im Browser http://192.168.1.10:8080 öffnen und mit admin und dem Passwort von oben anmelden.
docker compose runfunktioniert nur, solange der Dienst nicht läuft – die feste IP-Adresse wäre sonst doppelt vergeben (Address already in use). Im laufenden Betrieb die Hilfsbefehle mitexecaufrufen, z. B.docker compose exec fail2ban-webui python -m webui check.
Hinter einem vorhandenen Nginx Proxy Manager (Docker)
Ausgangslage: Nginx Proxy Manager (NPM) läuft auf demselben Host als Docker-Container, fail2ban ist auf dem Host installiert. Die Weboberfläche bekommt dann keinen veröffentlichten Port, sondern hängt zusätzlich im Docker-Netz von NPM; erreichbar ist sie nur über NPM mit TLS.
Voraussetzung: Docker Engine ≥ 28 und Compose ≥ 2.33 (für gw_priority, siehe Schritt 3).
1. Manuelle Sperren auch für Docker-Ports
Verkehr zu veröffentlichten Docker-Ports – also auch zu NPM – läuft nicht über die Kette
INPUT, sondern über DOCKER-USER. Den Agenten deshalb mit --docker installieren:
bash /opt/fail2ban-webui-src/agent/install.sh --key-file /opt/fail2ban-webui/data/ssh/id_ed25519.pub --from 172.23.51.10 --docker
War der Agent schon ohne --docker installiert, lässt der Installer das vorhandene Jail
unverändert. Dann die Datei sichern, die zwei action-Zeilen ergänzen und fail2ban neu laden:
mkdir -p /backups/config_backups && cp -a /etc/fail2ban/jail.d/manual-block.local /backups/config_backups/manual-block.local_$(date +%F_%H-%M)
nano /etc/fail2ban/jail.d/manual-block.local
action = iptables-allports[name=manual-block]
iptables-allports[name=manual-block-docker, chain=DOCKER-USER]
fail2ban-client reload && fail2ban-client status manual-block
2. Docker-Netz von NPM ermitteln
docker ps --format '{{.Names}} {{.Image}}' | grep -iE 'npm|proxy-manager'
docker inspect <NPM-Container> --format '{{range $n, $c := .NetworkSettings.Networks}}{{$n}}{{"\n"}}{{end}}'
3. Weboberfläche ins NPM-Netz hängen
Den veröffentlichten Port entfernen – erreichbar ist die Oberfläche danach nur noch über NPM:
cd /opt/fail2ban-webui && sed -i '/^ ports:$/,/8080:8080"$/d' docker-compose.yaml
Das NPM-Netz kommt in eine eigene Datei docker-compose.override.yaml, die Docker Compose
automatisch dazulädt. Den Netznamen aus Schritt 2 in NPM_NET setzen:
NPM_NET=npm_default
cat > docker-compose.override.yaml <<EOF
# fail2ban-webui hinter Nginx Proxy Manager: zusätzlich im Netz von NPM.
# gw_priority hält die Standardroute im eigenen Netz – sonst kommt die
# SSH-Verbindung zum Agenten mit der Adresse aus dem NPM-Netz an und
# --from 172.23.51.10 lehnt sie ab.
services:
fail2ban-webui:
networks:
fail2ban-webui.dockernetwork.local:
gw_priority: 1
npm: {}
networks:
npm:
name: ${NPM_NET}
external: true
EOF
docker compose config --quiet && echo "Konfiguration ok"
4. Adresse und Proxy-Einstellung
sed -i -e 's|^PUBLIC_URL=.*|PUBLIC_URL=https://fail2ban.example.org|' -e 's|^TRUSTED_PROXY_HOPS=.*|TRUSTED_PROXY_HOPS=1|' .env
5. Neu starten und prüfen
docker compose up -d
docker compose exec fail2ban-webui python -m webui check
6. DNS und Proxy-Host in NPM
- DNS:
fail2ban.example.orgauf die Adresse des Hosts zeigen lassen. - NPM → Hosts → Proxy Hosts → Add Proxy Host:
| Feld | Wert |
|---|---|
| Domain Names | fail2ban.example.org |
| Scheme / Forward Hostname / Port | http · fail2ban-webui · 8080 |
| Block Common Exploits | an |
| SSL | Zertifikat anfordern (für interne Namen wie *.int: eigenes Zertifikat oder DNS-Challenge), Force SSL, HTTP/2, HSTS |
| Access List | empfohlen: nur deine IP-Adressen |
7. Testen
curl -sI https://fail2ban.example.org/login | grep -iE '^(HTTP|set-cookie)'
Erwartet: HTTP/2 200 und ein Cookie mit Secure; HttpOnly; SameSite=Lax. Im Protokoll der
Weboberfläche steht nach einer Aktion die echte Client-Adresse, nicht die von NPM.
Optional: fehlgeschlagene Anmeldungen von fail2ban sperren lassen – siehe
Login mit fail2ban absichern
(Banaction dort ebenfalls mit chain=DOCKER-USER).
Dokumentation
| Installation | Weboberfläche Schritt für Schritt, von mkdir bis zur ersten Anmeldung |
| Agent | f2b-webctl auf Bare Metal, LXC und Docker-Hosts installieren |
| Konfiguration | alle Einstellungen in .env und hosts.yaml |
| Anmeldung | lokaler Login, OpenID Connect (Authentik), Anmeldung über den Reverse Proxy |
| Reverse Proxy | Nginx Proxy Manager, Caddy, Traefik, nginx |
| Betrieb | Logs, Updates, Backups, Fehlersuche |
| Entwicklung | lokales Testbed, Tests, neue Sprache hinzufügen |
| Sicherheit | Sicherheitsmodell und Melden von Schwachstellen |
Lizenz
MIT. IP-Geolokalisierung durch DB-IP Lite (CC BY 4.0), wird
nur bei GEOIP_AUTO_DOWNLOAD=1 heruntergeladen. Weltkarte von Natural Earth
(Public Domain), in der App enthalten.
🏰 Website · 🐘 Mastodon · 🔒 Datenschutz · 💬 Support
© Thomas Werner Schwaighofer · retronerd.AT · MIT Lizenz

