Files
fail2ban-webui/README.md
T
2026-10-25 20:49:39 +01:00

385 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`
![fail2ban-webui – Übersicht mit Host-Kachel, Herkunftskarte, IP-Sperre und Sperrliste](docs/images/screenshot-de.png)
```
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`](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)
```bash
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](https://git.retronerd.at/schiwagoa/public-docker-installer-26.04-v1):
```bash
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:
```bash
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](docs/agent.md)).
Im Beispiel hat der Host die LAN-Adresse `192.168.1.10` – überall durch deine ersetzen.
### fail2ban vorbereiten
```bash
apt install -y fail2ban python3 sudo openssh-server
```
```bash
systemctl enable --now fail2ban
```
```bash
fail2ban-client ping # Server replied: pong
```
### Quellen holen
```bash
git clone https://git.retronerd.at/schiwagoa/fail2ban-webui.git /opt/fail2ban-webui-src
```
### Weboberfläche einrichten
```bash
mkdir -p /opt/fail2ban-webui/{config,data} && cd /opt/fail2ban-webui
```
```bash
curl -fsSLo docker-compose.yaml https://git.retronerd.at/schiwagoa/fail2ban-webui/raw/branch/main/examples/docker-compose.yaml
```
```bash
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:
```bash
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](#hinter-einem-vorhandenen-nginx-proxy-manager-docker):
```bash
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`:
```bash
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):
```bash
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:
```bash
nano .env
```
SSH-Schlüssel der Weboberfläche erzeugen:
```bash
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
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
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
```bash
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:
```bash
docker compose run --rm -it fail2ban-webui keyscan
```
Verbindung testen – erwartet: `OK srv200 agent 1.0.0, 2 jails, 0 bans`:
```bash
docker compose run --rm fail2ban-webui check
```
### Starten
```bash
docker compose up -d
```
```bash
docker compose ps # Up … (healthy) nach etwa 30 Sekunden
```
```bash
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
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:
```bash
mkdir -p /backups/config_backups && cp -a /etc/fail2ban/jail.d/manual-block.local /backups/config_backups/manual-block.local_$(date +%F_%H-%M)
```
```bash
nano /etc/fail2ban/jail.d/manual-block.local
```
```ini
action = iptables-allports[name=manual-block]
iptables-allports[name=manual-block-docker, chain=DOCKER-USER]
```
```bash
fail2ban-client reload && fail2ban-client status manual-block
```
### 2. Docker-Netz von NPM ermitteln
```bash
docker ps --format '{{.Names}} {{.Image}}' | grep -iE 'npm|proxy-manager'
```
```bash
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:
```bash
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:
```bash
NPM_NET=npm_default
```
```bash
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
```
```bash
docker compose config --quiet && echo "Konfiguration ok"
```
### 4. Adresse und Proxy-Einstellung
```bash
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
```bash
docker compose up -d
```
```bash
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
```bash
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](docs/operations.md#login-mit-fail2ban-absichern)
(Banaction dort ebenfalls mit `chain=DOCKER-USER`).
## Dokumentation
| | |
|---|---|
| [Installation](docs/installation.md) | Weboberfläche Schritt für Schritt, von `mkdir` bis zur ersten Anmeldung |
| [Agent](docs/agent.md) | `f2b-webctl` auf Bare Metal, LXC und Docker-Hosts installieren |
| [Konfiguration](docs/configuration.md) | alle Einstellungen in `.env` und `hosts.yaml` |
| [Anmeldung](docs/authentication.md) | lokaler Login, OpenID Connect (Authentik), Anmeldung über den Reverse Proxy |
| [Reverse Proxy](docs/reverse-proxy.md) | Nginx Proxy Manager, Caddy, Traefik, nginx |
| [Betrieb](docs/operations.md) | Logs, Updates, Backups, Fehlersuche |
| [Entwicklung](docs/development.md) | lokales Testbed, Tests, neue Sprache hinzufügen |
| [Sicherheit](SECURITY.md) | Sicherheitsmodell und Melden von Schwachstellen |
## Lizenz
[MIT](LICENSE). IP-Geolokalisierung durch [DB-IP Lite](https://db-ip.com) (CC BY 4.0), wird
nur bei `GEOIP_AUTO_DOWNLOAD=1` heruntergeladen. Weltkarte von [Natural Earth](https://www.naturalearthdata.com)
(Public Domain), in der App enthalten.
---
<p align="center">
🏰 <a href="https://retronerd.at">Website</a> ·
🐘 <a href="https://social.retronerd.at/@schiwagoa">Mastodon</a> ·
🔒 <a href="https://retronerd.at/datenschutzerklaerung/">Datenschutz</a> ·
💬 <a href="https://helpdesk.retronerd.at/open.php">Support</a>
</p>
<p align="center">
<img src="https://dl.retronerd.at/Bilder/cat-footer.png" alt="" width="100%">
</p>
<p align="center">© Thomas Werner Schwaighofer · retronerd.AT · <a href="LICENSE">MIT Lizenz</a></p>