# Entwicklung ## Aufbau ``` app/webui/ Flask-Anwendung (Paket "webui") app.py Routen, Sicherheits-Header, Protokoll auth.py Anmeldung lokal / OIDC / Proxy, Login-Sperre backend.py SSH zu den Agenten, GeoIP, Loki cli.py serve, hash-password, keygen, keyscan, check config.py Einstellungen aus der Umgebung, hosts.yaml i18n.py, locales/ Übersetzungen (eine JSON-Datei pro Sprache) templates/, static/ (static/world.svg: Weltkarte, Natural Earth) agent/ f2b-webctl, install.sh, uninstall.sh docker/Dockerfile Produktions-Image examples/ docker-compose.yaml, .env.example, hosts.yaml.example dev/testbed/ Weboberfläche + zwei fail2ban-Hosts in Docker tests/smoke_test.py Tests der Weboberfläche ohne Docker/SSH ``` ## Smoke-Test (ohne Docker) ```bash python3 -m venv .venv .venv/bin/pip install -r requirements.txt .venv/bin/python tests/smoke_test.py ``` Die Agenten werden simuliert. Abgedeckt: Anmeldung, Login-Sperre, CSRF-/Origin-Prüfung, Übersetzungen, Prüfung beim Sperren/Entsperren, Protokoll, Proxy-Modus, Prüfung der Konfiguration, SSH-Befehlszeile, Log-Injection. ## Testbed (Docker) `dev/testbed` startet die Weboberfläche und zwei Debian-Hosts mit sshd und fail2ban (Dummy-Banactions – es wird nichts gesperrt). Die Agenten werden beim Containerstart mit `agent/install.sh` installiert: | Host | Agent-Einrichtung | |---|---| | `agent1` | Benutzer `f2bweb` + sudo, `--from` auf das Testbed-Netz, zusätzliche geschützte IP | | `agent2` | Benutzer `root`, Schlüssel in `authorized_keys2`, manuelles Jail namens `blocklist` | ```bash cd dev/testbed printf 'DEFAULT_LANG=en\nAUTH_MODE=local\nAUTH_USER=admin\nCACHE_SECONDS=5\n' > .env docker compose build docker compose run --rm -it webui hash-password >> .env docker compose run --rm webui keygen docker compose run --rm -T --entrypoint sh webui -c 'cat > /data/hosts.yaml' < hosts.yaml docker compose up -d agent1 agent2 docker compose run --rm webui keyscan --yes docker compose run --rm webui check docker compose up -d webui # http://127.0.0.1:18080 docker compose down -v # alles entfernen ``` `hosts.yaml` wird ins Daten-Volume geschrieben, weil Docker Desktop Netzlaufwerke nicht einbinden kann. Nach dem Neuanlegen eines Agenten ändert sich sein Host-Schlüssel – `keyscan` erneut ausführen. Unter Windows mit Git Bash `docker compose exec … /pfad` mit `MSYS_NO_PATHCONV=1` voranstellen, sonst wird der Pfad in einen Windows-Pfad umgeschrieben. ## Neue Sprache hinzufügen 1. `app/webui/locales/en.json` nach `app/webui/locales/.json` kopieren (z. B. `fr.json`). 2. `"_name"` auf den Eigennamen der Sprache setzen (`"Français"`) – so erscheint sie im Dropdown. 3. Die Werte übersetzen; `{Platzhalter}` unverändert lassen. Fehlende Schlüssel fallen auf Englisch zurück. 4. Smoke-Test ausführen und die Oberfläche prüfen. ## Image bauen ```bash docker build -f docker/Dockerfile --build-arg VERSION=1.0.0 -t fail2ban-webui:1.0.0 . # Multi-Arch, wie in der CI docker buildx build -f docker/Dockerfile --platform linux/amd64,linux/arm64 \ --build-arg VERSION=1.0.0 -t git.retronerd.at/schiwagoa/fail2ban-webui:1.0.0 --push . ``` Vor dem Veröffentlichen scannen: ```bash docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:0.58.1 \ image --scanners vuln --ignore-unfixed --severity HIGH,CRITICAL fail2ban-webui:1.0.0 ``` ## Agent-Protokoll Die Weboberfläche führt `ssh … @ ""` aus; der Agent liest den Befehl aus `SSH_ORIGINAL_COMMAND` und gibt genau eine JSON-Zeile aus: ```json {"ok": true, "host": "web01", "agent_version": "1.0.0", "manual_jail": "manual-block", "jails": {"sshd": {"bans": [{"ip": "203.0.113.7", "start": "2026-10-25 10:00:00", "bantime": 600, "end": "2026-10-25 10:10:00"}], "total": 42}}} ``` Fehler: `{"ok": false, "error_key": "agent_protected", "error_args": {"ip": "…"}, "error": "English text"}`. Die Weboberfläche übersetzt `error_key` mit `locales/*.json` (Schlüssel mit Präfix `agent_`); unbekannte Schlüssel zeigen den englischen `error`-Text. ## Versionierung - Versionsnummern folgen SemVer und werden beim Bauen gesetzt (Build-Argument `VERSION` → `APP_VERSION`); der Agent hat eine eigene `VERSION` in `agent/f2b-webctl`. - Jede Änderung kommt in `CHANGELOG.md` unter *Unreleased*.