Files
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

281 lines
11 KiB
Markdown
Raw Permalink 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.
# Agent – f2b-webctl
Auf jedem fail2ban-Host läuft der Agent [`f2b-webctl`](../agent/f2b-webctl): ein kleines
Python-Skript (nur Standardbibliothek), das als SSH-**Forced-Command** für den Schlüssel der
Weboberfläche gestartet wird. Etwas anderes kann die Weboberfläche auf dem Host nicht ausführen.
| Befehl (von der Weboberfläche) | Wirkung |
|---|---|
| `list` | alle Jails mit ihren gesperrten IPs (JSON) |
| `ban <ip>` | dauerhaft im manuellen Jail (`manual-block`) sperren |
| `unban <jail> <ip>` | in einem Jail entsperren |
| `unban-all <ip>` | in allen Jails entsperren |
| `version` | Agent-Version |
Wird nie gesperrt, egal was die Weboberfläche schickt:
- private, Loopback-, Link-Local-, CGNAT-, Multicast- und reservierte Bereiche
(`10/8`, `172.16/12`, `192.168/16`, `100.64/10`, `127/8`, `169.254/16`, `fc00::/7`, `fe80::/10`, …)
- alle Adressen des Hosts selbst und die Adresse, von der die Weboberfläche zugreift
- alles, was unter `PROTECTED` in `/etc/f2b-webctl.conf` steht (z. B. deine Heim-IP)
Jede Aktion landet im Syslog (Facility `auth`, Tag `f2b-webctl`), auch abgelehnte.
## Voraussetzungen
| | |
|---|---|
| fail2ban | ≥ 0.11, laufend (`fail2ban-client ping`) – ältere Versionen ohne `--with-time` funktionieren, aber ohne Start-/Endzeiten |
| python3 | ≥ 3.8 |
| sshd | vom Webui-Host erreichbar |
| sudo | für den Standardbenutzer `f2bweb` (`apt install sudo`); mit `--user root` nicht nötig |
| bash, ssh-keygen | für den Installer |
Läuft auf Debian, Ubuntu, Proxmox VE und anderen Linux-Systemen mit oder ohne systemd –
Bare Metal, VMs, LXC-Container (privilegiert oder unprivilegiert) und Docker-Hosts.
## Installation
Gebraucht werden der öffentliche Schlüssel aus `fail2ban-webui keygen` und die Adresse, von
der die Weboberfläche zugreift (öffentliche IP des Docker-Hosts oder seine LAN-Adresse).
```bash
# als root auf dem fail2ban-Host
git clone https://git.retronerd.at/schiwagoa/fail2ban-webui.git /tmp/fail2ban-webui
cd /tmp/fail2ban-webui/agent
echo 'ssh-ed25519 AAAA… fail2ban-webui' > /tmp/webui.pub
# anzeigen, was passieren würde
bash install.sh --key-file /tmp/webui.pub --from 198.51.100.10 --dry-run
# installieren
bash install.sh --key-file /tmp/webui.pub --from 198.51.100.10
```
Ohne `git`: die drei Dateien `f2b-webctl`, `install.sh` und `uninstall.sh` aus dem Ordner
`agent/` in ein Verzeichnis laden und `install.sh` von dort starten.
Der Installer zeigt eine Zusammenfassung und fragt, bevor er etwas ändert:
```
fail2ban-webui agent 1.0.0 – fail2ban 1.1.0 – web01
script /usr/local/sbin/f2b-webctl
config /etc/f2b-webctl.conf
SSH user f2bweb (will be created)
sudo rule /etc/sudoers.d/f2b-webctl
key file /var/lib/f2bweb/.ssh/authorized_keys
key 256 SHA256:zfwh… (ED25519)
allowed from 198.51.100.10
manual jail manual-block (will be created)
backups /var/backups/fail2ban-webui-agent
```
Was er tut:
1. sichert jede Datei, die er ändert, nach `--backup-dir`
2. installiert `/usr/local/sbin/f2b-webctl` (`root:root 0750`)
3. schreibt `/etc/f2b-webctl.conf` (bleibt erhalten, wenn sie schon existiert)
4. legt den Systembenutzer `f2bweb` an (Home `/var/lib/f2bweb`, ohne Passwort) und die sudo-Regel
```
Defaults!/usr/local/sbin/f2b-webctl env_keep += "SSH_ORIGINAL_COMMAND SSH_CLIENT"
f2bweb ALL=(root) NOPASSWD: /usr/local/sbin/f2b-webctl
```
5. trägt den Schlüssel mit allen Einschränkungen ein:
```
restrict,from="198.51.100.10",command="sudo -n /usr/local/sbin/f2b-webctl" ssh-ed25519 AAAA… fail2ban-webui
```
6. legt das Jail `manual-block` an, falls es noch nicht existiert (siehe unten)
7. führt einen Selbsttest über denselben sudo-Weg aus, den die Weboberfläche nutzt
Den Installer erneut auszuführen ist unbedenklich: Er aktualisiert das Skript, ersetzt den
Schlüsseleintrag und lässt deine Konfiguration unverändert.
### Optionen
| Option | Standard | Bedeutung |
|---|---|---|
| `--key-file DATEI` / `--key "SCHLÜSSEL"` | – | öffentlicher Schlüssel der Weboberfläche (Pflicht) |
| `--from ADRESSEN` | jede | Schlüssel nur von diesen IPs/Netzen akzeptieren (durch Komma getrennt). **Dringend empfohlen.** |
| `--user NAME` | `f2bweb` | SSH-Benutzer; `root` funktioniert ohne sudo |
| `--authorized-keys-file PFAD` | `~USER/.ssh/authorized_keys` | wohin der Schlüsseleintrag kommt – siehe [verwaltete Schlüssel](#hosts-mit-verwalteten-authorized_keys) |
| `--manual-jail NAME` | `manual-block` | Jail für manuelle Sperren |
| `--banaction AKTION` | aus `jail.local` | Banaction für ein neu angelegtes manuelles Jail |
| `--docker` | – | Docker-Host: das neue manuelle Jail sperrt in `INPUT` **und** `DOCKER-USER` (iptables) – nicht mit `--banaction` kombinierbar |
| `--protect NETZE` | – | zusätzliche geschützte Adressen (nur wenn die Konfigurationsdatei geschrieben wird) |
| `--no-jail` | – | kein manuelles Jail anlegen |
| `--force-config` | – | vorhandene `/etc/f2b-webctl.conf` überschreiben |
| `--backup-dir VERZEICHNIS` | `/var/backups/fail2ban-webui-agent` | Sicherungen geänderter Dateien |
| `--dry-run` | – | nur anzeigen, was passieren würde |
| `-y`, `--yes` | – | nicht nachfragen |
Der Installer warnt, wenn sshd die gewählte Schlüsseldatei gar nicht liest
(`AuthorizedKeysFile`), wenn `AllowUsers`/`AllowGroups` gesetzt sind oder wenn `--user root`
auf `PermitRootLogin no` trifft.
## Das manuelle Jail
Sperren aus der Weboberfläche landen in einem Jail, standardmäßig `manual-block`, mit
`bantime = -1` (dauerhaft). Gibt es das Jail noch nicht, legt der Installer an:
`/etc/fail2ban/filter.d/manual-block.conf`
```ini
# fail2ban-webui: never matches – the jail is only filled via
# "fail2ban-client set manual-block banip <ip>"
[Definition]
failregex = ^FAIL2BAN-WEBUI-NEVER-MATCHES <HOST>$
ignoreregex =
```
`/etc/fail2ban/jail.d/manual-block.local`
```ini
# fail2ban-webui: permanent manual bans from the web UI
[manual-block]
enabled = true
filter = manual-block
backend = polling
logpath = /var/log/fail2ban-manual-block.log
maxretry = 1
bantime = -1
```
Das Jail nutzt deine Standard-`banaction` aus `jail.local`. Sperren überstehen Neustarts
über die fail2ban-Datenbank.
Ein vorhandenes Jail mit diesem Namen wird nie angefasst – gibt es schon ein Jail für
manuelle Sperren, dessen Namen mit `--manual-jail` übergeben.
### Docker-Hosts
Die Standardaktionen von fail2ban sperren in der Kette `INPUT`. Verkehr zu **veröffentlichten
Docker-Ports** läuft aber nicht über `INPUT`, sondern über `FORWARD` → `DOCKER-USER`; solche
Sperren würden deine Container also nicht schützen. Auf einem Docker-Host den Agenten deshalb
mit `--docker` installieren – das manuelle Jail sperrt dann in beiden Ketten:
```bash
bash install.sh --key-file /tmp/webui.pub --from 198.51.100.10 --docker
```
Das erzeugt im Jail:
```ini
action = iptables-allports[name=manual-block]
iptables-allports[name=manual-block-docker, chain=DOCKER-USER]
```
Ein schon vorhandenes Jail ändert der Installer nicht; die zwei Zeilen dann von Hand in
`/etc/fail2ban/jail.d/manual-block.local` eintragen und `fail2ban-client reload` ausführen.
Nach der ersten Sperre prüfen:
```bash
iptables -S DOCKER-USER | grep f2b
```
Läuft die Weboberfläche auf demselben Docker-Host, in `hosts.yaml` `address: host.docker.internal`
verwenden und SSH aus dem Docker-Netz erlauben, falls eine Firewall (z. B. UFW) es blockiert:
```bash
ufw allow from 172.23.51.0/24 to any port 22 proto tcp comment 'fail2ban-webui'
```
und den Agenten mit `--from 172.23.51.10` installieren.
### LXC-Container
Der Agent läuft im Container, in dem fail2ban läuft – sonst ist nichts Besonderes nötig.
In **unprivilegierten** Containern muss fail2ban selbst die Firewall des Containers ändern
können (nftables/iptables im CT); mit einer manuellen Sperre testen:
```bash
fail2ban-client set manual-block banip 203.0.113.1 && nft list ruleset | grep 203.0.113.1
fail2ban-client set manual-block unbanip 203.0.113.1
```
### Hosts mit verwalteten authorized_keys
Manche Werkzeuge (Ansible, Keywarden, cloud-init, …) überschreiben `~/.ssh/authorized_keys`.
Den Schlüssel dann in eine Datei legen, die sie nicht verwalten, und sicherstellen, dass sshd sie liest:
```bash
grep -i '^AuthorizedKeysFile' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf
# z. B. AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
bash install.sh --key-file /tmp/webui.pub --from 198.51.100.10 \
--user root --authorized-keys-file /root/.ssh/authorized_keys2
```
## Konfiguration – /etc/f2b-webctl.conf
```ini
# fail2ban-webui agent (f2b-webctl) – settings
# Jail used for manual bans from the web UI
MANUAL_JAIL=manual-block
# Addresses/networks that must never be banned (comma separated), e.g. your home IP.
# Private, loopback, link-local and CGNAT ranges are always protected.
PROTECTED=203.0.113.25,2001:db8:1234::/48
# Also protect all addresses of this host and the address of the web UI (yes/no)
PROTECT_OWN_ADDRESSES=yes
PROTECT_CLIENT=yes
```
Änderungen wirken sofort – der Agent liest die Datei bei jedem Aufruf.
## Test auf dem Host
```bash
# direkt als root
f2b-webctl list | python3 -m json.tool | head -30
f2b-webctl ban 10.0.0.1 # → abgelehnt, geschützt
# derselbe Weg wie die Weboberfläche (Benutzer f2bweb über sudo)
runuser -u f2bweb -- env SSH_ORIGINAL_COMMAND=version sudo -n /usr/local/sbin/f2b-webctl
# was der Agent getan hat
journalctl -t f2b-webctl --since today # oder: grep f2b-webctl /var/log/auth.log
```
## Aktualisieren
Den Installer der neuen Version mit denselben Optionen erneut ausführen. Konfiguration und
Jail bleiben erhalten; Skript und Schlüsseleintrag werden ersetzt.
## Deinstallieren
```bash
bash uninstall.sh --dry-run
bash uninstall.sh
```
| Option | Bedeutung |
|---|---|
| `--user NAME` | Agent-Benutzer (Standard `f2bweb`) |
| `--authorized-keys-file PFAD` | Datei mit dem Schlüsseleintrag |
| `--remove-user` | auch den Benutzer samt Home löschen |
| `--remove-jail` | auch das manuelle Jail löschen – **hebt alle manuellen Sperren auf** (sie werden vorher aufgelistet) |
| `--backup-dir VERZEICHNIS`, `--dry-run`, `--yes` | wie beim Installer |
## Manuelle Installation
Wer es lieber von Hand macht – das sind alle Schritte des Installers:
```bash
install -o root -g root -m 0750 f2b-webctl /usr/local/sbin/f2b-webctl
nano /etc/f2b-webctl.conf # Inhalt siehe oben
useradd --system --create-home --home-dir /var/lib/f2bweb --shell /bin/sh f2bweb
usermod -p '*' f2bweb
visudo -f /etc/sudoers.d/f2b-webctl # Inhalt siehe "Was er tut", Schritt 4
install -d -o f2bweb -g f2bweb -m 700 /var/lib/f2bweb/.ssh
nano /var/lib/f2bweb/.ssh/authorized_keys # Zeile siehe Schritt 5
chown f2bweb: /var/lib/f2bweb/.ssh/authorized_keys
chmod 600 /var/lib/f2bweb/.ssh/authorized_keys
# manuelles Jail: die zwei Dateien aus "Das manuelle Jail", dann
touch /var/log/fail2ban-manual-block.log
fail2ban-client reload
```