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>
This commit is contained in:
schiwagoaandClaude Opus 5.5 committed 2026-10-25 15:38:17 +01:00
commit 2e3f79236c
44 files changed
+4280

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
"""Translations: one JSON file per language in webui/locales/ (e.g. en.json, de.json).
A new language only needs a new file; "_name" is the label shown in the dropdown.
Missing keys fall back to English.
"""
import json
import os
DIR = os.path.join(os.path.dirname(__file__), "locales")
FALLBACK = "en"
LANGS = {}
def load():
LANGS.clear()
for fname in sorted(os.listdir(DIR)):
if fname.endswith(".json"):
with open(os.path.join(DIR, fname), encoding="utf-8") as fh:
LANGS[fname[:-5]] = json.load(fh)
def pick(cookie_value, default):
if cookie_value in LANGS:
return cookie_value
return default if default in LANGS else FALLBACK
def catalog(lang):
"""Full dictionary for the browser, English as base."""
return {**LANGS.get(FALLBACK, {}), **LANGS.get(lang, {})}
def translate(lang, key, **params):
text = LANGS.get(lang, {}).get(key) or LANGS.get(FALLBACK, {}).get(key) or key
try:
return text.format(**params) if params else text
except (KeyError, IndexError, ValueError):
return text
def languages():
return [(code, data.get("_name", code)) for code, data in sorted(LANGS.items())]
load()