Repository navigation
11 Fail2Ban Verification and Rollback
"It is configured" is not evidence. This page is the acceptance checklist: prove the filter matches, the jail bans, the action fires, the queue receives the event and AbuseIPDB accepts the report — then know how to undo everything.
| # | What to prove | Command | Expected |
|---|---|---|---|
| 1 | configuration parses | fail2ban-client -t |
OK: configuration test is successful |
| 2 | jail is enabled | fail2ban-client status |
jail name in Jail list
|
| 3 | action is attached | fail2ban-client get <jail> actions |
nftables, abuseipdb-async |
| 4 | filter matches real abuse | fail2ban-regex <log> <filter> |
matches > 0, only attack lines |
| 5 | filter ignores normal traffic | fail2ban-regex <normal-sample> <filter> |
0 matched |
| 6 | a ban is actually applied | fail2ban-client set <jail> banip <doc IP> |
firewall set contains the IP |
| 7 | the queue receives bans | abuseipdb-report --stats |
pending events grows |
| 8 | the payload is correct | abuseipdb-report --dry-run |
JSON with comment/categories |
| 9 | the API accepts it | abuseipdb-report |
-> 200 in the log |
| 10 | the report is visible | check API with -d verbose
|
your comment in reports[]
|
| 11 | the schedule works | systemctl list-timers abuseipdb-report.timer |
next run in ≤16 min |
| 12 | logs rotate | logrotate -d /etc/logrotate.d/abuseipdb-reporter |
no error |
fail2ban-client -t
fail2ban-client status
fail2ban-client status sshdCurrently failed / Total failed count filter matches, not bans. A rising Currently failed
with Currently banned: 0 means the threshold is not reached yet or ignoreip swallowed the
address.
fail2ban-client get sshd actions
# The jail sshd has the following actions: nftables, abuseipdb-asyncIf abuseipdb-async is missing, the installer was not applied to that jail or fail2ban was not
restarted after adding the action. Re-run:
cd /path/to/Fail2Ban_AbuseIPDB
sudo ./install.sh --jails "sshd <other jails>"# positive
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf | tail -5
# negative: normal pages must not match
grep -h '"GET / ' /var/www/httpd-logs/*.access.log | head -200 > /tmp/normal.log
fail2ban-regex /tmp/normal.log /etc/fail2ban/filter.d/nginx-attack-scan.confA positive test with zero matches means: wrong path, wrong format, date-stripping assumption or a typo. See Fundamentals.
⚠️ Never use your own IP.fail2ban-client set ... banipbypassesignoreip.
fail2ban-client set sshd banip 198.51.100.77
nft list set inet f2b-table addr-set-sshd
fail2ban-client status sshd
fail2ban-client set sshd unbanip 198.51.100.77For a web jail the set is addr-set-nginx-attack-scan; for the recidive jail it is
addr-set-recidive.
If the address appears in fail2ban-client status but not in nftables, the ban action is
broken (missing nftables, wrong chain, unsupported kernel). Check:
nft list table inet f2b-table
journalctl -u fail2ban --since "-10 min"/usr/local/sbin/abuseipdb-report --stats
/usr/local/sbin/abuseipdb-report --dry-run
/usr/local/sbin/abuseipdb-report
tail -n 20 /var/log/abuseipdb-reporter.logExpected:
report: run start, pending_ips=1 budget=25 today=0
reported ip=198.51.100.77 jail=sshd cats=18,22 size=402 -> 200 {"data":{"abuseConfidenceScore":100}}
report: run done sent=1 skipped=0 errors=0 pending=0
KEY_FILE=/etc/abuseipdb/.abuseipdb.key
curl -s -G "https://api.abuseipdb.com/api/v2/check" \
--data-urlencode "ipAddress=198.51.100.77" -d maxAgeInDays=1 -d verbose \
-H "Key: $(cat "$KEY_FILE")" -H "Accept: application/json" \
| python3 -c 'import json,sys; d=json.load(sys.stdin)["data"]; print(d["abuseConfidenceScore"]); [print(r["reportedAt"], r["comment"][:200]) for r in d.get("reports", [])]'Our report may not be the newest one (the timestamp is the ban time, and other reporters may
have newer entries). Search the returned reports[] for your server marker or attribution footer.
systemctl list-timers abuseipdb-report.timer
cat /etc/cron.d/abuseipdb-reporter # when --cron was used
logrotate -d /etc/logrotate.d/abuseipdb-reporter# top SSH sources (auth.log, if present)
grep "Failed password" /var/log/auth.log | grep -oE 'from [0-9.]+' | awk '{print $2}' \
| sort | uniq -c | sort -rn | head -20
# top web attack sources (combined logs)
grep -hE '".*(\.env|\.git|wp-login|phpmyadmin|\.\./)' /var/www/httpd-logs/*.access.log \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -20
# queue and report journal
/usr/local/sbin/abuseipdb-report --stats
# current firewall bans per set
for s in $(nft list sets inet f2b-table | grep -oE 'addr-set-[a-z0-9-]+'); do
echo "$s: $(nft list set inet f2b-table "$s" | grep -c 'elements')"
done| Symptom | Likely cause | Go to |
|---|---|---|
fail2ban-client -t fails |
syntax, missing filter, bad jail file | Troubleshooting |
| jail active, no bans | filter matches nothing / threshold too high / ignoreip
|
Step 4–5 |
| bans in status, not in firewall | ban action broken | Step 6 |
| bans in firewall, queue empty | action not attached / fail2ban not restarted | Step 3 |
| queue has events, no reports | key missing, API down, daily budget, throttle | Troubleshooting |
| reports 401 | key invalid | Key Management |
| reports 429 | per-IP or daily limit | Report Anatomy |
| timer not running | systemd/cron install failed | systemctl status abuseipdb-report.timer |
# the installer keeps a pristine copy of every file it replaced
ls /var/backups/fail2ban-abuseipdb/pristine-*/
# restore one file, e.g. the generated action/jail snippet
cp -a /var/backups/fail2ban-abuseipdb/pristine-<ts>/etc/fail2ban/jail.d/50-abuseipdb-reporter.local \
/etc/fail2ban/jail.d/
rm -f /etc/fail2ban/jail.d/50-abuseipdb-reporter.local # or remove the snippet entirely
fail2ban-client -t && systemctl restart fail2banFor your own jails, restore the backup you took before editing and restart.
sudo /path/to/Fail2Ban_AbuseIPDB/install.sh --uninstall # keeps config, queue, key
# or reinstall a known-good release archive with install.sh- the updater restores the key snapshot if the installer fails and aborts before extraction on a checksum mismatch — in both cases nothing new is installed;
- if a release proves bad after installation, restore files from
/var/backups/fail2ban-abuseipdb/pristine-<ts>/or reinstall the previous release archive; - key file,
reporter.confand the queue are independent of the code and survive a code rollback.
# inspect
/usr/local/sbin/abuseipdb-report --stats
# drop it (loses pending reports, not firewall state)
sudo systemctl stop abuseipdb-report.timer
sudo rm -f /var/lib/abuseipdb/abuseipdb.sqlite3*
sudo systemctl start abuseipdb-report.timerThe key file is only read, never rewritten by updates. Restore it from your own secret store:
install -m 600 /dev/stdin /etc/abuseipdb/.abuseipdb.key <<< "KEY"
/usr/local/sbin/abuseipdb-report --check-key-
fail2ban-client -tis OK - all target jails are in
fail2ban-client status - every target jail lists
nftables, abuseipdb-async - each filter matches real abuse and ignores normal traffic
- a documentation-IP test ban appears in nftables and can be unbanned
-
abuseipdb-report --statsshows the ban as pending -
--dry-runshows the expected comment/categories - a real report returns
200 - the report is visible through the AbuseIPDB check API
- the timer/cron is active
- backups exist (
/var/backups/fail2ban-abuseipdb/, your own fail2ban backup)
Fail2Ban_AbuseIPDB · repository · releases · issues · MIT