Skip to main contentSkip to footer

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.

You need an API key with the 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.

conf
# /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:

bash
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.localin addition to the normal ban action (%(action_)s), not instead of it:

conf
# /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

Adding a new action needs a full restart — fail2ban-client reload does not reliably attach it. Use systemctl restart fail2ban, then confirm both actions are loaded.
bash
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:

log
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 lineMeaning
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:

json
{"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):

JailcategoriesMeaning
sshd18,22Brute-Force, SSH
postfix-sasl, dovecot11,18Email Spam, Brute-Force
proftpd, vsftpd5FTP Brute-Force
nginx-http-auth, apache-auth18,21Brute-Force, Web App Attack
nginx-badbots, apache-badbots14,19Port Scan, Bad Web Bot
apache-sqli, web exploits16,21SQL Injection, Web App Attack
WordPress / wp-login18,21Brute-Force, Web App Attack
recidive (repeat offenders)15,18Hacking, Brute-Force

Reporting more than one jail

Attach the action to each jail and override the categories inline so each report is accurate:

conf
# /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 ignoreip set 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 of actionban means a network hiccup at ReportedIP won't stop fail2ban from banning locally.
  • Sensible thresholds. Very low maxretry on noisy jails can over-report transient clients; the defaults (3–5) are fine.

Troubleshooting

SymptomFix
get <jail> actions shows only the ban actionYou reloaded instead of restarting. Run systemctl restart fail2ban.
unknown smtpd restriction / jail won't startConfig 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 / quotaDaily report limit reached for your plan; see Authentication.
Nothing in the log on banThe report action is silent on success. Confirm with fail2ban-client get <jail> actions and a manual curl.
Want to also block using community data? Import the blacklist feed into nftables/ipset, or for mail servers query the DNS/RBL zone.
Security Focused
GDPR Compliant
Made in Germany
Back to Docs