schiwagoaandClaude Opus 5.5 b36499bbf5 Docs: tested single-host install, NPM integration, installer --docker
- README: step-by-step installation of web UI and agent on one host,
  exactly as tested (agent after keygen, all "run" helpers before
  "up -d", http PUBLIC_URL and TRUSTED_PROXY_HOPS=0 without a proxy)
- README: attach to an existing Nginx Proxy Manager in Docker – shared
  network via docker-compose.override.yaml, no published port,
  gw_priority so SSH to the agent keeps the source address of the own
  network (otherwise from= rejects it), TLS settings, test
- install.sh --docker: the new manual jail bans in INPUT and DOCKER-USER
- Running container: helpers via "docker compose exec … python -m webui"
  (run fails with "Address already in use"); troubleshooting entries

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:50:33 +01:00
2026-10-25 15:38:17 +01:00
2026-10-25 15:38:17 +01:00
2026-10-25 15:38:17 +01:00
2026-10-25 15:38:17 +01:00
2026-10-25 15:38:17 +01:00

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), amd64 und arm64
 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)
  • sudo für den Standardbenutzer f2bweb; mit einem root-Schlüssel nicht nötig
  • bash, 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 run funktioniert 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 mit exec aufrufen, 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.org auf 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

S
Description
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.
Readme MIT
270 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 53.6%
Shell 18.1%
JavaScript 11.1%
CSS 8.2%
HTML 6.2%
Other 2.8%