Task 1 — fix Stop/Reload Jail returning 404
Root cause: reload_jail and reload_all sent an empty config stream
(["reload", name, [], []]). In fail2ban's reload protocol the end-of-
reload phase deletes every jail still in reload_state — i.e. every jail
that received no configuration commands. An empty stream means *all*
affected jails are silently removed from the daemon's runtime, causing
everything touching those jails afterwards (including stop) to receive
UnknownJailException → HTTP 404.
Fixes:
- reload_jail: send ["start", name] in the config stream; startJail()
removes the jail from reload_state so the end phase commits instead of
deletes, and un-idles the jail.
- reload_all: fetch current jail list first, build a ["start", name]
entry for every active jail, then send reload --all with that stream.
- stop_jail: made idempotent — if the jail is already gone (not-found
error) the operation silently succeeds (200 OK) rather than returning
404, matching the user expectation that stop = ensure-stopped.
- Router: removed dead JailNotFoundError handler from stop endpoint.
391 tests pass (2 new), ruff clean, mypy clean (pre-existing
config.py error unchanged).
Task 2 — access list simulator
- Docker/simulate_accesses.sh: writes fake HTTP-scan log lines in
custom format (bangui-access: http scan from <IP> ...) to
Docker/logs/access.log so the bangui-access jail detects them.
- fail2ban/filter.d/bangui-access.conf: failregex matching the above.
- fail2ban/jail.d/bangui-access.conf: polling jail on access.log,
same settings as bangui-sim (maxretry=3, bantime=60s).
- .gitignore: whitelist new bangui-access.conf files.
- Docker/fail2ban-dev-config/README.md: added "Testing the Access
List Feature" section with step-by-step instructions and updated
Configuration Reference + Troubleshooting.
BanGUI — Fail2ban Dev Test Environment
This directory contains the fail2ban configuration and supporting scripts for a
self-contained development test environment. A simulation script writes fake
authentication-failure log lines, fail2ban detects them via the bangui-sim
jail, and bans the offending IP — giving a fully reproducible ban/unban cycle
without a real service.
Prerequisites
- Docker or Podman installed and running.
docker compose(v2) orpodman-composeavailable on thePATH.- The repo checked out; all commands run from the repo root.
Quick Start
1 — Start the fail2ban container
docker compose -f Docker/compose.debug.yml up -d fail2ban
# or: make up (starts the full dev stack)
Wait ~15 s for the health-check to pass (docker ps shows healthy).
2 — Run the login-failure simulation
bash Docker/simulate_failed_logins.sh
Default: writes 5 failure lines for IP 192.168.100.99 to
Docker/logs/auth.log.
Optional overrides:
bash Docker/simulate_failed_logins.sh <COUNT> <SOURCE_IP> <LOG_FILE>
# e.g. bash Docker/simulate_failed_logins.sh 10 203.0.113.42
3 — Verify the IP was banned
bash Docker/check_ban_status.sh
The output shows the current jail counters and the list of banned IPs with their ban expiry timestamps.
4 — Unban and re-test
bash Docker/check_ban_status.sh --unban 192.168.100.99
One-command smoke test (Makefile shortcut)
make dev-ban-test
Chains steps 1–3 automatically with appropriate sleep intervals.
Testing the Access List Feature
The Access List tab in BanGUI displays each individual matched log line
stored in fail2ban's database. A second jail — bangui-access — monitors
Docker/logs/access.log for simulated HTTP bot-scan entries.
1 — Run the access-scan simulation
bash Docker/simulate_accesses.sh
Default: writes 5 HTTP-scan lines for IP 203.0.113.7 to
Docker/logs/access.log.
Optional overrides:
bash Docker/simulate_accesses.sh <COUNT> <SOURCE_IP> <LOG_FILE>
# e.g. bash Docker/simulate_accesses.sh 6 198.51.100.5
Log line format:
YYYY-MM-DD HH:MM:SS bangui-access: http scan from <IP> "GET /.env HTTP/1.1" 404
2 — Verify the IP was banned
bash Docker/check_ban_status.sh
The bangui-access jail should appear alongside bangui-sim, showing the
banned IP and matched line count.
3 — Unban and re-test
bash Docker/check_ban_status.sh --unban 203.0.113.7
Configuration Reference
| File | Purpose |
|---|---|
fail2ban/filter.d/bangui-sim.conf |
Defines the failregex that matches simulation log lines |
fail2ban/jail.d/bangui-sim.conf |
Jail settings: maxretry=3, bantime=60s, findtime=120s |
Docker/logs/auth.log |
Log file written by the simulation script (host path) |
fail2ban/filter.d/bangui-access.conf |
Defines the failregex that matches access-scan log lines |
fail2ban/jail.d/bangui-access.conf |
Access jail settings: maxretry=3, bantime=60s, findtime=120s |
Docker/logs/access.log |
Log file written by simulate_accesses.sh (host path) |
Inside the container the log file is mounted at /remotelogs/bangui/auth.log
(see fail2ban/paths-lsio.conf — remote_logs_path = /remotelogs).
To change sensitivity, edit fail2ban/jail.d/bangui-sim.conf:
maxretry = 3 # failures before a ban
findtime = 120 # look-back window in seconds
bantime = 60 # ban duration in seconds
Troubleshooting
Log file not detected
The jail uses backend = polling for reliability inside Docker containers.
If fail2ban still does not pick up new lines, verify the volume mount in
Docker/compose.debug.yml:
- ./logs:/remotelogs/bangui
and confirm Docker/logs/auth.log exists after running the simulation script.
Filter regex mismatch
Test the regex manually:
docker exec bangui-fail2ban-dev \
fail2ban-regex /remotelogs/bangui/auth.log bangui-sim
docker exec bangui-fail2ban-dev \
fail2ban-regex /remotelogs/bangui/access.log bangui-access
The output should show matched lines. If nothing matches, check that the log
lines match the corresponding failregex pattern:
# bangui-sim (auth log):
YYYY-MM-DD HH:MM:SS bangui-auth: authentication failure from <IP>
# bangui-access (access log):
YYYY-MM-DD HH:MM:SS bangui-access: http scan from <IP> "<METHOD> <path> HTTP/1.1" <STATUS>
iptables / permission errors
The fail2ban container requires NET_ADMIN and NET_RAW capabilities and
network_mode: host. Both are already set in Docker/compose.debug.yml. If
you see iptables errors, check that the host kernel has iptables loaded:
sudo modprobe ip_tables
IP not banned despite enough failures
Check whether the source IP falls inside the ignoreip range defined in
fail2ban/jail.d/bangui-sim.conf:
ignoreip = 127.0.0.0/8 ::1 172.16.0.0/12
The default simulation IP 192.168.100.99 is outside these ranges and will be
banned normally.