A Textual-based terminal UI (TUI) that reads Fail2Ban logs and shows:
- Top banned IPs (count + country + ASN/Org)
- Current active jails
- Active bans per jail
- Last bans from the selected log period
- WHOIS / RDNS for the currently selected IP (via system tools)
Designed for Linux servers running Fail2Ban (e.g. Ubuntu).
- Python 3.10+
- Fail2Ban installed and logging to:
/var/log/fail2ban.log(plus rotated logs)
- Ability to run
fail2ban-client(the app usessudo fail2ban-client ...)
This project depends on:
textual- (optional)
geoip2— only if you want Python-based MMDB lookups; otherwise the app falls back tommdblookupif present.
Install from requirements.txt:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtOptional:
pip install geoip2These are used for enrichment and convenience actions:
mmdblookup(optional) — used whengeoip2isn't installedwhois(optional) — enables the WHOIS popup (w)dig(optional) — enables better RDNS output (d), otherwise the app falls back togetent hosts
On Ubuntu:
sudo apt-get update
sudo apt-get install -y mmdb-bin whois dnsutilsThis app does not download GeoIP/ASN data. It expects pre-downloaded MaxMind-style MMDB files on the host.
By default, the script reads these stable filenames:
GEO_COUNTRY_DB = "/usr/share/GeoIP/dbip-country-lite.mmdb"
GEO_ASN_DB = "/usr/share/GeoIP/dbip-asn-lite.mmdb"If the files are missing, the UI still runs, but the country/ASN fields will show fallback values like "Unknown" / "No ASN".
This repo includes a helper script to download and refresh the free DB-IP Lite databases monthly and maintain stable symlinks:
update_geoip_db.py(Python)
What it does:
- downloads the monthly
.mmdb.gzfiles (Country + ASN) from DB-IP - decompresses them
- installs them into
/usr/share/GeoIP - updates these symlinks so the app can always read consistent paths:
/usr/share/GeoIP/dbip-country-lite.mmdb/usr/share/GeoIP/dbip-asn-lite.mmdb
- prunes old MMDB files by default, keeping only the latest 2 per type (current + previous rollback)
Prereqs:
- Python 3.x (tested with system Python on Ubuntu; e.g. 3.12)
- root privileges to write into
/usr/share/GeoIP
Copy it somewhere in PATH (optional but convenient):
sudo cp update_geoip_db.py /usr/local/bin/update_geoip_db.py
sudo chmod +x /usr/local/bin/update_geoip_db.pyRun it:
sudo /usr/bin/python3 /usr/local/bin/update_geoip_db.pySanity check:
ls -lh /usr/share/GeoIP/dbip-*-lite*.mmdb
readlink -f /usr/share/GeoIP/dbip-country-lite.mmdb
readlink -f /usr/share/GeoIP/dbip-asn-lite.mmdbInstead of writing log files and dealing with logrotate, you can pipe output into logger and filter by tag via journalctl.
Edit root’s crontab:
sudo crontab -eAdd:
@monthly /usr/bin/python3 /usr/local/bin/update_geoip_db.py 2>&1 | /usr/bin/logger -t dbip-geoip-updaterView logs:
journalctl -t dbip-geoip-updater --since "90 days ago"By default, the updater keeps the latest 2 MMDB files per type (Country/ASN) and deletes older ones. This keeps a “previous month” rollback without accumulating files.
To disable pruning:
sudo /usr/bin/python3 /usr/local/bin/update_geoip_db.py --no-pruneTo keep more history (e.g. last 6):
sudo /usr/bin/python3 /usr/local/bin/update_geoip_db.py --keep-last 6DB-IP Lite downloads page: https://db-ip.com/db/lite.php
Because the app calls sudo fail2ban-client ..., you’ll typically want to allow passwordless access for fail2ban-client via sudoers.
Edit safely with visudo and add something like:
stef ALL=(ALL) NOPASSWD: /usr/bin/fail2ban-client
Adjust the username and path to fail2ban-client as needed:
which fail2ban-clientpython3 offenders.pyGlobal:
q— quitr— refresh nowt— toggle table cursor mode (row/cell)corx— copy selection- in row mode: copies the entire row (tab-separated)
- in cell mode: copies the current cell
Network tools (on selected IP):
w— WHOIS (requireswhois)d— reverse DNS- uses
dig -xif available, otherwise falls back togetent hosts
- uses
Modal popup (WHOIS/RDNS output):
escorq— closec— copy output (prints to stdout if clipboard isn't available)
Edit these constants in the script:
TOP_COUNT— number of offenders to showLOOKBACK_DAYS— how many days of bans to include (0= all logs)IGNORE_PRIVATE— skip private/loopback/link-local IPsCHECK_INTERVAL_SECONDS— refresh interval
MIT — see LICENSE.
