How to Permanently Ban Repeat Offender IPs After Multiple Fail2Ban Blocks


2 views

Many system administrators face this common security dilemma: While temporary bans (like 300-second blocks after 3 failed attempts) help prevent brute-force attacks without locking out legitimate users who mistype credentials, we often need to identify and permanently block persistent attackers.

Fail2Ban's standard configuration (typically in /etc/fail2ban/jail.local) might look like this:

[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 3
findtime = 600
bantime  = 300

This handles temporary blocking well, but lacks native functionality for permanent bans after repeated offenses.

We'll create a custom action that:

  1. Tracks how many times each IP gets banned
  2. Permanently blocks IPs reaching our threshold (5 offenses)

Step 1: Create the IP Tracking Script

Create /etc/fail2ban/action.d/ip_tracker.sh:

#!/bin/bash

TRACK_FILE="/etc/fail2ban/ip_offenders"
THRESHOLD=5
IP=$1

# Create tracking file if not exists
[ -f "$TRACK_FILE" ] || touch "$TRACK_FILE"

# Get current count or initialize
COUNT=$(grep -c "^$IP " "$TRACK_FILE" || echo 0)

# Increment count
((COUNT++))

# Update record
grep -v "^$IP " "$TRACK_FILE" > "$TRACK_FILE.tmp"
echo "$IP $COUNT" >> "$TRACK_FILE.tmp"
mv "$TRACK_FILE.tmp" "$TRACK_FILE"

# Check if threshold reached
if [ $COUNT -ge $THRESHOLD ]; then
    # Add to permanent block list
    iptables -A f2b-permanent -s $IP -j DROP
    # Remove from tracking
    grep -v "^$IP " "$TRACK_FILE" > "$TRACK_FILE.tmp"
    mv "$TRACK_FILE.tmp" "$TRACK_FILE"
fi

Step 2: Create Permanent IPTables Chain

Add this to your firewall rules (before the Fail2Ban chain):

iptables -N f2b-permanent
iptables -I INPUT -j f2b-permanent

Step 3: Configure Custom Action in Fail2Ban

Create /etc/fail2ban/action.d/permanent.conf:

[Definition]
actionstart = 
actionstop = 
actioncheck = 
actionban = /etc/fail2ban/action.d/ip_tracker.sh 
actionunban = 

Then update your jail configuration:

[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 3
findtime = 600
bantime  = 300
action   = %(action_)s
         permanent

The permanent block list will persist through reboots if you save your iptables rules. To manage blocked IPs:

  • View permanently blocked IPs: iptables -L f2b-permanent -n
  • Remove a permanent block: iptables -D f2b-permanent -s 1.2.3.4 -j DROP

Fail2Ban has a built-in recidive jail that can detect repeat offenders, though it doesn't support permanent bans directly:

[recidive]
enabled  = true
logpath  = /var/log/fail2ban.log
bantime  = 86400  # 1 day
findtime = 86400  # 24 hours
maxretry = 5

Fail2Ban's default behavior of temporary bans works well for most scenarios, but sophisticated attackers often exploit the temporary nature of these blocks. When dealing with persistent brute-force attacks, we need a more robust solution that can:

  • Maintain existing temporary ban functionality (3 attempts → 300s ban)
  • Track repeat offenders across multiple ban cycles
  • Escalate to permanent bans after specified recurrence (5 cycles in this case)

Fail2Ban actually includes built-in functionality for this exact scenario through its recidive jail. Here's how to implement it:

# /etc/fail2ban/jail.local
[recidive]
enabled  = true
logpath  = /var/log/fail2ban.log
bantime  = -1  # Permanent ban
findtime = 1d  # Look back period for repeat offenses
maxretry = 5   # Number of temporary bans before permanent

For a complete CentOS implementation with SSH protection:

# Main SSH jail configuration
[sshd]
enabled   = true
port      = ssh
filter    = sshd
logpath   = /var/log/secure
maxretry  = 3
bantime   = 300
findtime  = 3600

# Recidive jail for permanent bans
[recidive]
enabled   = true
logpath   = /var/log/fail2ban.log
banaction = iptables-allports
bantime   = -1
findtime  = 86400
maxretry  = 5

After implementing, verify the configuration:

fail2ban-client status sshd
fail2ban-client status recidive

To test the permanent banning:

  1. Generate 3 failed SSH attempts from a test IP
  2. Wait for temporary ban (verify with fail2ban-client status sshd)
  3. After ban expires, repeat the process 4 more times
  4. On the 5th cycle, check iptables -L for permanent ban

For more granular control, consider these additional parameters:

# Customizing recidive behavior
[recidive]
...
ignoreip = 127.0.0.1/8 ::1  # Whitelist localhost
banaction = %(banaction_allports)s
chain = FORWARD  # For specific network setups

If you need more complex logic than recidive provides, here's a Python script alternative:

#!/usr/bin/env python3
import re
from collections import defaultdict
from datetime import datetime, timedelta

# Config
LOG_FILE = '/var/log/fail2ban.log'
BAN_THRESHOLD = 5
BAN_FILE = '/etc/fail2ban/ip.permanentban'

def analyze_fail2ban_log():
    ip_counts = defaultdict(int)
    cutoff = datetime.now() - timedelta(days=1)
    
    with open(LOG_FILE) as f:
        for line in f:
            if 'Ban' in line:
                match = re.search(r'Ban (\d+\.\d+\.\d+\.\d+)', line)
                if match:
                    ip = match.group(1)
                    ip_counts[ip] += 1
    
    return [ip for ip, count in ip_counts.items() 
            if count >= BAN_THRESHOLD]

if __name__ == '__main__':
    repeat_offenders = analyze_fail2ban_log()
    with open(BAN_FILE, 'w') as f:
        for ip in repeat_offenders:
            f.write(f"{ip}\n")
    # Then configure fail2ban to read this file