Sign In
Access your IPWhois.net account
No account? Create one
Fail2Ban Integration
Automatically report every IP that Fail2Ban bans to the IPWhois Blacklist. Contribute to community threat intelligence with zero manual effort.
10 min setup Linux Fully automated
Blacklist / Docs / Fail2Ban
What is Fail2Ban?

Fail2Ban is an intrusion prevention framework that monitors log files for suspicious activity and automatically bans offending IP addresses by updating firewall rules. It ships with filters for SSH, Apache, Nginx, Postfix, Dovecot, and many other services.

By adding a custom action, you can make Fail2Ban report every banned IP to the IPWhois Blacklist. This means your server actively contributes to community threat intelligence -- every brute-force attacker, spammer, or scanner that hits your server gets shared with thousands of other sysadmins who use the blacklist to protect their own infrastructure.

The integration works by calling the IPWhois Blacklist report API via curl each time Fail2Ban bans an IP. It runs alongside your existing ban actions (iptables, firewalld, etc.) so your server remains protected while also reporting the threat.

Requirements
  • Fail2Ban 0.10+ installed and running (0.11+ recommended)
  • curl installed on the server (used to call the API)
  • Root or sudo access to create action files and edit jail configuration
  • Outbound HTTPS access to bl.ipwhois.net on port 443
No API key required. The report endpoint is free for community use. If you run a large cluster reporting thousands of IPs per day, consider using an API key for higher rate limits.
Installing Fail2Ban

If Fail2Ban is already installed, skip to Step 1. Otherwise, install it for your distribution:

Ubuntu 20.04 / 22.04 / 24.04

sudo apt update sudo apt install -y fail2ban curl # Enable and start the service sudo systemctl enable fail2ban sudo systemctl start fail2ban # Verify it is running sudo systemctl status fail2ban
On Ubuntu 24.04, Fail2Ban uses the nftables backend by default. The IPWhois action works identically regardless of the firewall backend.

Debian 11 (Bullseye) / Debian 12 (Bookworm)

sudo apt update sudo apt install -y fail2ban curl sudo systemctl enable fail2ban sudo systemctl start fail2ban

On Debian 12 with the default nftables backend, ensure the nftables service is running:

sudo systemctl enable nftables sudo systemctl start nftables

CentOS / RHEL / Rocky Linux / AlmaLinux

# Enable EPEL repository (required for Fail2Ban) sudo dnf install -y epel-release # RHEL/CentOS/Rocky/Alma 8+ # or: sudo yum install -y epel-release # CentOS 7 sudo dnf install -y fail2ban fail2ban-firewalld curl # or: sudo yum install -y fail2ban fail2ban-firewalld curl sudo systemctl enable fail2ban sudo systemctl start fail2ban
On RHEL-based systems, the fail2ban-firewalld package configures Fail2Ban to use firewalld as the ban action backend. The IPWhois reporting action runs alongside this.

Verify your installation:

fail2ban-client version # Expected output: Fail2Ban v0.11.x or v1.x fail2ban-client status # Should show "Number of jail" and list active jails
Step 1: Create the Action File

Fail2Ban actions are defined in /etc/fail2ban/action.d/. Create a new file called ipwhois-report.conf:

Create the file
sudo nano /etc/fail2ban/action.d/ipwhois-report.conf

Paste the following content:

/etc/fail2ban/action.d/ipwhois-report.conf
# IPWhois Blacklist reporter for Fail2Ban # Reports banned IPs to https://bl.ipwhois.net/ # # Usage: add "ipwhois-report" to your jail's action list # Docs: https://bl.ipwhois.net/docs/fail2ban [Definition] # actionstart - called when the jail starts (optional logging) actionstart = # actionstop - called when the jail stops (optional logging) actionstop = # actioncheck - called before each ban to verify prerequisites actioncheck = # actionban - called each time an IP is banned # <ip> = the banned IP address (injected by Fail2Ban) # <name> = the jail name (e.g., sshd, postfix, nginx-http-auth) actionban = /usr/bin/curl -sSf -m 10 -X POST https://bl.ipwhois.net/api/report \ -d "ip=<ip>" \ -d "type=%(threat_type)s" \ -d "message=Fail2Ban+jail+<name>+on+%(hostname)s" \ 2>&1 | logger -t ipwhois-bl # actionunban - called when the ban expires (nothing to do) actionunban = [Init] # Default threat type - override per jail in jail.local threat_type = brute-force # Server hostname included in the report message hostname = %(hostname)s

Line-by-line explanation

Line / DirectivePurpose
actionbanThe command Fail2Ban executes every time it bans an IP. This is where the API call happens.
/usr/bin/curlFull path to curl. Using the absolute path avoids issues when Fail2Ban runs in a restricted environment.
-sSf-s silent, -S show errors, -f fail on HTTP errors. Prevents noisy output while still catching real failures.
-m 1010-second timeout. Prevents Fail2Ban from hanging if the API is slow.
-X POSTSends an HTTP POST request to the report endpoint.
-d "ip=<ip>"<ip> is a Fail2Ban variable that gets replaced with the actual banned IP address.
-d "type=%(threat_type)s"The threat category. Defaults to brute-force but can be overridden per jail.
-d "message=..."Human-readable context including the jail name and server hostname.
2>&1 | logger -t ipwhois-blRedirects output to syslog with tag ipwhois-bl for easy log filtering.
actionunbanLeft empty. There is no need to "un-report" an IP from the blacklist.
[Init] threat_typeDefault threat type. Override this in jail.local per jail section.
Important: Make sure the file is owned by root and has correct permissions: sudo chmod 644 /etc/fail2ban/action.d/ipwhois-report.conf
Step 2: Configure Jail (sshd)

Edit your jail.local file to add the IPWhois reporting action alongside your existing ban action. Never edit jail.conf directly as it gets overwritten on package upgrades.

Open jail.local
sudo nano /etc/fail2ban/jail.local

Add or modify the [sshd] section:

/etc/fail2ban/jail.local - sshd jail
[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 5 findtime = 600 bantime = 3600 # Ban with firewall AND report to IPWhois Blacklist action = iptables-multiport[name=sshd, port="ssh", protocol=tcp] ipwhois-report[threat_type=brute-force]

Key points

  • The action directive takes multiple actions separated by newlines (indented). The first action (iptables-multiport) blocks the IP in the firewall. The second action (ipwhois-report) reports it to the blacklist.
  • threat_type=brute-force tells the API this is a brute-force attack. See the threat types table below for other values.
  • On systems using firewalld (CentOS/RHEL), replace iptables-multiport with firewallcmd-ipset.
  • On systems using nftables (Debian 12, Ubuntu 24.04), replace with nftables-multiport.
CentOS / RHEL / Rocky users: The SSH log path is /var/log/secure instead of /var/log/auth.log. Also use firewallcmd-ipset as the firewall action.
CentOS / RHEL variant
[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/secure maxretry = 5 findtime = 600 bantime = 3600 action = firewallcmd-ipset[name=sshd, port="ssh", protocol=tcp] ipwhois-report[threat_type=brute-force]

After saving, restart Fail2Ban:

sudo systemctl restart fail2ban # Verify the jail is active sudo fail2ban-client status sshd

Expected output:

Status for the jail: sshd |- Filter | |- Currently failed: 2 | |- Total failed: 47 | `- File list: /var/log/auth.log `- Actions |- Currently banned: 1 |- Total banned: 12 `- Banned IP list: 203.0.113.42
Step 3: Configure for Other Services

The same ipwhois-report action works with any Fail2Ban jail. Here are configurations for common services:

Postfix (email spam)

/etc/fail2ban/jail.local
[postfix] enabled = true port = smtp,465,submission filter = postfix[mode=auth] logpath = /var/log/mail.log maxretry = 3 bantime = 7200 action = iptables-multiport[name=postfix, port="smtp,465,submission"] ipwhois-report[threat_type=spam]

Dovecot (IMAP/POP3 brute-force)

/etc/fail2ban/jail.local
[dovecot] enabled = true port = pop3,pop3s,imap,imaps filter = dovecot logpath = /var/log/mail.log maxretry = 5 bantime = 3600 action = iptables-multiport[name=dovecot, port="pop3,pop3s,imap,imaps"] ipwhois-report[threat_type=brute-force]

Apache (HTTP auth brute-force)

/etc/fail2ban/jail.local
[apache-auth] enabled = true port = http,https filter = apache-auth logpath = /var/log/apache2/error.log maxretry = 5 bantime = 3600 action = iptables-multiport[name=apache, port="http,https"] ipwhois-report[threat_type=brute-force] [apache-badbots] enabled = true port = http,https filter = apache-badbots logpath = /var/log/apache2/access.log maxretry = 2 bantime = 86400 action = iptables-multiport[name=apache-badbots, port="http,https"] ipwhois-report[threat_type=bot] [apache-noscript] enabled = true port = http,https filter = apache-noscript logpath = /var/log/apache2/error.log maxretry = 5 bantime = 3600 action = iptables-multiport[name=apache-noscript, port="http,https"] ipwhois-report[threat_type=scan]

Nginx (HTTP auth + bot detection)

/etc/fail2ban/jail.local
[nginx-http-auth] enabled = true port = http,https filter = nginx-http-auth logpath = /var/log/nginx/error.log maxretry = 5 bantime = 3600 action = iptables-multiport[name=nginx-auth, port="http,https"] ipwhois-report[threat_type=brute-force] [nginx-botsearch] enabled = true port = http,https filter = nginx-botsearch logpath = /var/log/nginx/access.log maxretry = 3 bantime = 86400 action = iptables-multiport[name=nginx-bot, port="http,https"] ipwhois-report[threat_type=scan]

WordPress (wp-login brute-force)

Requires a custom filter. Create /etc/fail2ban/filter.d/wordpress-login.conf:

/etc/fail2ban/filter.d/wordpress-login.conf
[Definition] failregex = ^<HOST> .* "POST /wp-login\.php ^<HOST> .* "POST /xmlrpc\.php ignoreregex =

Then add the jail:

/etc/fail2ban/jail.local
[wordpress-login] enabled = true port = http,https filter = wordpress-login logpath = /var/log/nginx/access.log maxretry = 5 findtime = 300 bantime = 3600 action = iptables-multiport[name=wordpress, port="http,https"] ipwhois-report[threat_type=brute-force]

After adding any new jails, always restart Fail2Ban:

sudo systemctl restart fail2ban sudo fail2ban-client status
Threat Types for Different Jails

Use the correct threat_type parameter for each jail so the blacklist categorizes reports accurately:

Jailthreat_typeDescription
sshdbrute-forceSSH login attempts
postfix, postfix-saslspamEmail relay / SMTP auth abuse
dovecotbrute-forceIMAP/POP3 login attempts
apache-auth, nginx-http-authbrute-forceHTTP authentication attempts
apache-badbotsbotMalicious crawler / scraper
apache-noscript, nginx-botsearchscanVulnerability scanning / path probing
wordpress-loginbrute-forceWordPress login brute-force
recidivebrute-forceRepeat offenders banned by multiple jails
DDoS-related jailsddosFlood / volumetric attacks
Custom phishing jailsphishingPhishing page hosting or credential theft
Malware C&C jailsmalwareKnown malware command-and-control
Testing & Verification

1. Test the API call manually

Before relying on Fail2Ban, verify the API is reachable from your server:

curl -sSf -X POST https://bl.ipwhois.net/api/report \ -d "ip=192.0.2.1" \ -d "type=brute-force" \ -d "message=Test+report+from+setup"

Expected response (JSON):

{"success":true,"message":"Report received"}

2. Trigger a test ban

Manually ban a test IP to see the full flow:

# Ban a test IP (use a documentation/test range IP) sudo fail2ban-client set sshd banip 192.0.2.99 # Check it was banned sudo fail2ban-client status sshd # Unban it when done testing sudo fail2ban-client set sshd unbanip 192.0.2.99

3. Check syslog for the report

The action pipes output to syslog with the tag ipwhois-bl:

# View recent reports sudo grep "ipwhois-bl" /var/log/syslog | tail -10 # Or on CentOS/RHEL: sudo grep "ipwhois-bl" /var/log/messages | tail -10 # Follow in real time sudo tail -f /var/log/syslog | grep "ipwhois-bl"

4. Verify on bl.ipwhois.net

After a report, you can check the IP on the blacklist:

# Check via API curl -s "https://bl.ipwhois.net/api/check?ip=192.0.2.99" | python3 -m json.tool # Or visit in your browser: # https://bl.ipwhois.net/check/192.0.2.99

5. Check Fail2Ban logs

# Fail2Ban's own log sudo tail -50 /var/log/fail2ban.log # Look for ban/unban entries sudo grep "Ban\|Unban" /var/log/fail2ban.log | tail -20
Troubleshooting

curl: command not found

The action uses /usr/bin/curl. If curl is not installed:

# Debian/Ubuntu sudo apt install -y curl # CentOS/RHEL sudo dnf install -y curl # Verify the path which curl # Should output: /usr/bin/curl

If curl is installed at a different path (e.g., /usr/local/bin/curl), update the path in the action file or create a symlink:

sudo ln -s $(which curl) /usr/bin/curl

Permission denied

If you see permission errors in syslog, check file ownership:

# Action file should be readable ls -la /etc/fail2ban/action.d/ipwhois-report.conf # Expected: -rw-r--r-- root root # Fix if needed sudo chown root:root /etc/fail2ban/action.d/ipwhois-report.conf sudo chmod 644 /etc/fail2ban/action.d/ipwhois-report.conf

API unreachable / connection timeout

If reports are not going through:

# Test connectivity curl -v https://bl.ipwhois.net/api/check?ip=8.8.8.8 # Check DNS resolution nslookup bl.ipwhois.net # Check if outbound HTTPS is blocked by firewall sudo iptables -L OUTPUT -n | grep 443 # If behind a proxy, set the environment variable in the action: # actionban = https_proxy=http://proxy:3128 /usr/bin/curl ...

Fail2Ban fails to start after editing jail.local

# Check for syntax errors sudo fail2ban-client -t # Common issue: indentation. The second action line must be # indented with spaces (not tabs) on the line after the first action. # Correct: # action = iptables-multiport[...] # ipwhois-report[...] # # Wrong (no indentation): # action = iptables-multiport[...] # ipwhois-report[...]

Reports not appearing on bl.ipwhois.net

  • Check that the IP is not a private/reserved address (10.x, 172.16-31.x, 192.168.x). The API only accepts public IPs.
  • Verify curl returns a success response by running the manual test above.
  • Check rate limits: the API returns 429 Too Many Requests if you exceed 500 reports/day. Check with: curl -sI -X POST https://bl.ipwhois.net/api/report -d "ip=test" and look for X-RateLimit-Remaining.
  • Reports for IPs in documentation ranges (192.0.2.x, 198.51.100.x, 203.0.113.x) may be filtered.

Action runs but Fail2Ban log shows errors

# Run Fail2Ban in debug mode temporarily sudo fail2ban-client -vvv set sshd banip 192.0.2.50 # Check the full action output sudo journalctl -t ipwhois-bl --since "5 minutes ago"
Advanced Configuration

Rate limiting reports

If your server bans hundreds of IPs per hour, you may want to rate-limit reports to stay within the API quota. Create a wrapper script:

/usr/local/bin/ipwhois-report.sh
#!/bin/bash # Rate-limited IPWhois reporter # Reports at most 1 IP per 10 seconds to avoid API throttling LOCKFILE="/tmp/ipwhois-report.lock" IP="$1" TYPE="${2:-brute-force}" MSG="${3:-Fail2Ban}" # Simple lock to prevent concurrent calls from flooding exec 200>"$LOCKFILE" flock -w 30 200 || exit 1 /usr/bin/curl -sSf -m 10 -X POST https://bl.ipwhois.net/api/report \ -d "ip=$IP" \ -d "type=$TYPE" \ -d "message=$MSG" \ 2>&1 | logger -t ipwhois-bl sleep 2 # Brief delay between reports exec 200>&-
sudo chmod +x /usr/local/bin/ipwhois-report.sh

Then update the action file to use the script:

actionban = /usr/local/bin/ipwhois-report.sh <ip> %(threat_type)s "Fail2Ban+<name>"

Filtering by severity (only report repeat offenders)

Use the recidive jail to only report IPs that have been banned multiple times:

/etc/fail2ban/jail.local
[recidive] enabled = true filter = recidive logpath = /var/log/fail2ban.log bantime = 604800 findtime = 86400 maxretry = 3 action = iptables-allports[name=recidive] ipwhois-report[threat_type=brute-force]

This only reports IPs that have been banned 3+ times within 24 hours, filtering out one-off attempts and reducing noise.

Custom message format with more context

Include the number of failures and the service port in the report:

/etc/fail2ban/action.d/ipwhois-report-verbose.conf
[Definition] actionban = /usr/bin/curl -sSf -m 10 -X POST https://bl.ipwhois.net/api/report \ -d "ip=<ip>" \ -d "type=%(threat_type)s" \ -d "message=Fail2Ban+jail=<name>+failures=<failures>+port=<port>+server=%(hostname)s" \ 2>&1 | logger -t ipwhois-bl actionunban = [Init] threat_type = brute-force

The <failures> tag is replaced by Fail2Ban with the number of failed attempts that triggered the ban. The <port> tag contains the monitored port(s).

Whitelisting IPs from being reported

To prevent reporting specific IPs (e.g., your own monitoring), use the Fail2Ban ignoreip directive:

[DEFAULT] # These IPs will never be banned or reported ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 192.168.0.0/16 YOUR.OFFICE.IP.HERE
Example Log Output

Here is what a complete flow looks like in your server logs when an SSH brute-force attack is detected, banned, and reported:

Sequence of events in logs
# 1. Attacker fails SSH login (auth.log) Mar 15 14:22:01 web1 sshd[28401]: Failed password for root from 185.220.101.34 port 44820 ssh2 Mar 15 14:22:03 web1 sshd[28401]: Failed password for root from 185.220.101.34 port 44820 ssh2 Mar 15 14:22:05 web1 sshd[28401]: Failed password for root from 185.220.101.34 port 44820 ssh2 Mar 15 14:22:07 web1 sshd[28401]: Failed password for root from 185.220.101.34 port 44820 ssh2 Mar 15 14:22:09 web1 sshd[28401]: Failed password for root from 185.220.101.34 port 44820 ssh2 # 2. Fail2Ban detects and bans (fail2ban.log) Mar 15 14:22:10 web1 fail2ban.actions[1205]: NOTICE [sshd] Ban 185.220.101.34 # 3. IPWhois report sent (syslog) Mar 15 14:22:10 web1 ipwhois-bl: {"success":true,"message":"Report received"} # 4. IP is now listed on bl.ipwhois.net # curl https://bl.ipwhois.net/api/check?ip=185.220.101.34 # {"listed":true,"ip":"185.220.101.34","threat_type":"brute-force", # "confidence":85,"total_reports":47,"last_seen":"2026-03-15T14:22:10Z"}
Success! Your server is now automatically reporting threats to the IPWhois Blacklist community. Every banned IP you report helps protect other servers around the world.
IPWhois Blacklist — Community-driven IP threat intelligence — ipwhois.net