Files
fail2ban-webui/docs/development.md
T
schiwagoaandClaude Opus 5.5 2e3f79236c Initial version of fail2ban-webui
Web interface to view, ban and unban fail2ban IPs on several hosts via
SSH, generalised from the private f2b-web tool.

- Web UI (Flask/Waitress): hosts.yaml, local/OIDC/proxy authentication,
  English/German UI, origin map, optional Loki/Grafana/GeoIP, CLI helpers
- Agent f2b-webctl with install.sh/uninstall.sh (dedicated user + sudo,
  from= restriction, protected networks, manual jail)
- Hardened Docker image, examples, documentation, smoke test, testbed

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

4.0 KiB
Raw Blame History

Development

Layout

app/webui/           Flask application (package "webui")
  app.py             routes, security headers, audit log
  auth.py            local / OIDC / proxy authentication, login lockout
  backend.py         SSH to the agents, GeoIP, Loki
  cli.py             serve, hash-password, keygen, keyscan, check
  config.py          settings from the environment, hosts.yaml
  i18n.py, locales/  translations (one JSON file per language)
  templates/, static/
agent/               f2b-webctl, install.sh, uninstall.sh
docker/Dockerfile    production image
examples/            docker-compose.yaml, .env.example, hosts.yaml.example
dev/testbed/         web UI + two fail2ban hosts in Docker
tests/smoke_test.py  web UI tests without Docker/SSH

Smoke test (no Docker needed)

python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python tests/smoke_test.py

The agents are simulated. Covered: login, lockout, CSRF/Origin checks, translations, ban/unban validation, audit log, proxy mode, config validation, SSH command line, log injection.

Testbed (Docker)

dev/testbed starts the web UI and two Debian hosts with sshd and fail2ban (dummy ban actions – nothing is blocked). The agents are installed with agent/install.sh at container start:

Host Agent setup
agent1 user f2bweb + sudo, --from the testbed network, extra protected IP
agent2 user root, key in authorized_keys2, manual jail named blocklist
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                # remove everything

hosts.yaml is written into the data volume because Docker Desktop cannot bind-mount network drives. After recreating an agent its host key changes – run keyscan again.

On Windows with Git Bash, prefix docker compose exec … /path with MSYS_NO_PATHCONV=1, otherwise the path is rewritten to a Windows path.

Adding a language

  1. Copy app/webui/locales/en.json to app/webui/locales/<code>.json (e.g. fr.json).
  2. Set "_name" to the language's own name ("Français") – it appears in the dropdown.
  3. Translate the values; keep {placeholders} unchanged. Missing keys fall back to English.
  4. Run the smoke test and check the UI.

Building the image

docker build -f docker/Dockerfile --build-arg VERSION=1.0.0 -t fail2ban-webui:1.0.0 .
# multi-arch, as in 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 .

Scan before publishing:

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 protocol

The web UI runs ssh … <user>@<host> "<command>"; the agent reads the command from SSH_ORIGINAL_COMMAND and prints exactly one JSON line:

{"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}}}

Errors: {"ok": false, "error_key": "agent_protected", "error_args": {"ip": "…"}, "error": "English text"}. The web UI translates error_key with locales/*.json (keys starting with agent_); unknown keys show the English error.

Versioning

  • Version numbers follow SemVer and are set at build time (VERSION build arg → APP_VERSION); the agent has its own VERSION in agent/f2b-webctl.
  • Every change goes into CHANGELOG.md under Unreleased.