fail2ban integration
Turn every IP that fail2ban bans on your server into a report to the ReportedIP community blacklist. A small custom action calls the report API on each ban, so your firewall and the community protect each other — the same data also feeds the blacklist feed and the DNS/RBL zone.
report permission. Create one for free in your
dashboard; see Authentication for limits. Report only IPs your own server actually caught.How fail2ban works (in 30 seconds)
fail2ban watches log files (or the systemd journal). A filter matches attack
patterns, a jail counts matches per IP within findtime, and once an IP
hits maxretry the jail runs its actions (ban + whatever else you add).
We add a second action that reports the banned IP to ReportedIP.
1. Create the report action
Save this as /etc/fail2ban/action.d/reportedip.conf. It POSTs the banned IP, the
threat categories and a short comment to the report endpoint on every ban.
# /etc/fail2ban/action.d/reportedip.conf
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = curl -sS -m 10 -X POST "<report_url>" \
-H "X-Key: <api_key>" \
--data-urlencode "ip=<ip>" \
--data-urlencode "categories=<categories>" \
--data-urlencode "comment=fail2ban <name>: <failures> failed attempts" \
-o /dev/null || :
actionunban =
[Init]
report_url = https://reportedip.de/wp-json/reportedip/v2/report
api_key = <your-api-key>
categories = 18,22
Tighten the permissions so the key is not world-readable:
chmod 600 /etc/fail2ban/action.d/reportedip.conf
The tags <ip>, <name> (jail name) and <failures>
are filled in by fail2ban; <api_key>, <categories> and
<report_url> come from the [Init] block.
2. Enable it on a jail
Add the action to a jail in /etc/fail2ban/jail.local — in addition to the
normal ban action (%(action_)s), not instead of it:
# /etc/fail2ban/jail.local
[sshd]
enabled = true
action = %(action_)s
reportedip
You can override the categories per jail by passing them inline, e.g.
reportedip[categories="16,21"] on a web-application jail.
3. Apply and verify
fail2ban-client reload
does not reliably attach it. Use systemctl restart fail2ban, then confirm both
actions are loaded.systemctl restart fail2ban
fail2ban-client get sshd actions
# -> The jail sshd has the following actions:
# nftables, reportedip
Reading the fail2ban log
fail2ban logs to /var/log/fail2ban.log (or the journal: journalctl -u fail2ban).
The lines you care about:
2026-05-30 21:56:18 fail2ban.filter [38760]: INFO [sshd] Found 203.0.113.45 - 2026-05-30 21:56:18
2026-05-30 21:56:26 fail2ban.actions [38760]: NOTICE [sshd] Ban 203.0.113.45
2026-05-30 22:06:26 fail2ban.actions [38760]: NOTICE [sshd] Unban 203.0.113.45
| Log line | Meaning |
|---|---|
Found <ip> | The filter matched one attack attempt. Not yet banned. |
Ban <ip> | Threshold reached — the jail ran its actions, including the report to ReportedIP. |
Unban <ip> | bantime elapsed; the firewall rule was removed (no report). |
Restore Ban <ip> | fail2ban restarted and re-applied a still-active ban. |
The report action itself is silent on success (-o /dev/null). To watch reports go out,
tail the log while testing, or run the action's curl by hand for one IP — a success
returns the new report:
{"data":{"ipAddress":"203.0.113.45","abuseConfidencePercentage":39,"reportId":"4316026","aggregated":true}}
Live counters: check your key with
curl -H "X-Key: <key>" https://reportedip.de/wp-json/reportedip/v2/verify-key and read
limits.dailyReportUsage, or open the dashboard.
Choosing threat categories per jail
Pass the category IDs that match what the jail detects. Common mappings (full list in Threat Categories):
| Jail | categories | Meaning |
|---|---|---|
sshd | 18,22 | Brute-Force, SSH |
postfix-sasl, dovecot | 11,18 | Email Spam, Brute-Force |
proftpd, vsftpd | 5 | FTP Brute-Force |
nginx-http-auth, apache-auth | 18,21 | Brute-Force, Web App Attack |
nginx-badbots, apache-badbots | 14,19 | Port Scan, Bad Web Bot |
apache-sqli, web exploits | 16,21 | SQL Injection, Web App Attack |
WordPress / wp-login | 18,21 | Brute-Force, Web App Attack |
recidive (repeat offenders) | 15,18 | Hacking, Brute-Force |
Reporting more than one jail
Attach the action to each jail and override the categories inline so each report is accurate:
# /etc/fail2ban/jail.local
[sshd]
enabled = true
action = %(action_)s
reportedip[categories="18,22"]
[postfix-sasl]
enabled = true
action = %(action_)s
reportedip[categories="11,18"]
[recidive]
enabled = true
action = %(action_)s
reportedip[categories="15,18"]
Good practice & safety
- Only report your own bans. Never report IPs you have not observed attacking your own systems — false reports hurt the dataset (and may get your key suspended).
- Whitelist your own ranges. Keep
ignoreipset for your office/monitoring IPs so you never report yourself. - Keep the key private (
chmod 600). It is not exposed to attackers, but anyone with the file can submit reports as you. Rotate it from the dashboard if leaked. - A failed report never blocks a ban. The
|| :at the end ofactionbanmeans a network hiccup at ReportedIP won't stop fail2ban from banning locally. - Sensible thresholds. Very low
maxretryon noisy jails can over-report transient clients; the defaults (3–5) are fine.
Troubleshooting
| Symptom | Fix |
|---|---|
get <jail> actions shows only the ban action | You reloaded instead of restarting. Run systemctl restart fail2ban. |
unknown smtpd restriction / jail won't start | Config typo in jail.local; check indentation of the continuation line under action. |
| Reports rejected (HTTP 401/403) | Key missing the report permission, or wrong header — it must be X-Key. |
| HTTP 429 / quota | Daily report limit reached for your plan; see Authentication. |
| Nothing in the log on ban | The report action is silent on success. Confirm with fail2ban-client get <jail> actions and a manual curl. |