Troubleshooting Fail2ban SSH Bruteforce Protection: Why Automatic IP Bans Fail Despite Matching Log Entries


2 views

The core issue manifests when Fail2ban appears properly configured yet fails to automatically ban IP addresses that clearly trigger the defined failure conditions in SSH logs. Manual banning via fail2ban-client works, indicating the banning mechanism itself is functional, but the automated detection pipeline is broken somewhere.

First, verify the log file permissions and SELinux context (if applicable):

ls -lZ /var/log/secure
# Expected output should show fail2ban has read access
-rw-------. 1 root root system_u:object_r:var_log_t:s0 /var/log/secure

Check the filter matching behavior with fail2ban-regex:

fail2ban-regex /var/log/secure /etc/fail2ban/filter.d/sshd.conf
# Sample success output should show matched patterns
Lines: 15 lines, 0 ignored, 15 matched, 0 missed

The current configuration shows:

findtime = 600  # 10 minute window
maxretry = 6    # Allowed attempts
bantime = 300   # 5 minute ban

Attack patterns may circumvent this by:

  1. Using multiple source ports (seen in the 122.225.109.219 attack)
  2. Distributing attempts across the time window
  3. Testing different usernames (admin/root in logs)

Create a custom filter in /etc/fail2ban/filter.d/sshd-custom.conf:

[INCLUDES]
before = sshd.conf

[Definition]
failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error) for .* from (?: port \d+)?\s*$
            ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error) for invalid user .* from (?: port \d+)?\s*$
            ^%(__prefix_line)sFailed (?:password|publickey) for (?:invalid user )?.* from (?: port \d+)?(?: ssh\d*)?$
ignoreregex =

Modify jail.local with aggressive parameters for SSH:

[ssh]
enabled = true
port = ssh
filter = sshd-custom
logpath = /var/log/secure
maxretry = 3
findtime = 300
bantime = 86400
ignoreip = 127.0.0.1/8 ::1
backend = auto
banaction = iptables-multiport
action = %(action_mwl)s

Check current bans and monitor live:

fail2ban-client status ssh
tail -f /var/log/fail2ban.log | grep --line-buffered "NOTICE.*Ban"

Ensure logrotate doesn't interfere by adding in /etc/logrotate.d/fail2ban:

/var/log/fail2ban.log {
    missingok
    notifempty
    delaycompress
    sharedscripts
    postrotate
        fail2ban-client flushlogs 1>/dev/null
    endscript
}

After configuration changes:

systemctl restart fail2ban
fail2ban-client reload ssh
# Generate test failures:
for i in {1..4}; do ssh root@localhost; done
# Verify ban appears:
fail2ban-client status ssh

The core issue manifests when examining the logs: While manual bans via fail2ban-client succeed, automated detection isn't triggering despite clear bruteforce patterns in both /var/log/secure and /var/log/messages. The attack pattern shows:

Nov 21 07:51:32 my_hostname sshd[51074]: Failed password for root from 122.225.109.219 port 1788 ssh2
Nov 21 07:51:34 my_hostname sshd[51072]: Failed password for root from 122.225.109.219 port 58285 ssh2
Nov 21 07:51:35 my_hostname sshd[51076]: Failed password for invalid user admin from 122.225.109.219 port 2221 ssh2

Your jail.local shows correct baseline settings:

[ssh]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/secure
maxretry = 6

1. Log Pattern Mismatch
The critical issue lies in PAM authentication messages not matching fail2ban's default regex patterns. Compare these log formats:

# Standard format (caught by default filter)
Failed password for root from 122.225.109.219 port 1788 ssh2

# PAM format (missed by default filter)
pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=103.41.124.41 user=root

2. Custom Filter Solution
Create /etc/fail2ban/filter.d/sshd-pam.conf:

[Definition]
failregex = ^%(__prefix_line)spam_unix$sshd:auth$: authentication failure; .* rhost=(?:\s+user=.*)?$
            ^%(__prefix_line)sPAM \d+ more authentication failures; .* rhost=(?:\s+user=.*)?$
ignoreregex =

Then update your jail configuration:

[ssh]
enabled = true
port = ssh
filter = sshd-pam
logpath = /var/log/secure
maxretry = 6
findtime = 600

After making changes:

# Test the new filter
fail2ban-regex /var/log/secure /etc/fail2ban/filter.d/sshd-pam.conf

# Reload configuration
fail2ban-client reload ssh

# Monitor real-time processing
tail -f /var/log/fail2ban.log | grep --line-buffered "ssh"

For better protection against distributed attacks:

[ssh]
bantime = 86400
maxretry = 3
findtime = 300
ignorecommand = /usr/local/bin/check_whitelist %s

Sample whitelist script (/usr/local/bin/check_whitelist):

#!/bin/bash
# Check against known good IP ranges
whitelist=("192.168.1.0/24" "10.0.0.0/8")

for ip_range in "${whitelist[@]}"; do
    if ipcalc -s -c "$1" "$ip_range" >/dev/null 2>&1; then
        exit 1
    fi
done
exit 0
  1. Verify file permissions: ls -l /var/log/secure (must be readable by fail2ban)
  2. Confirm time synchronization: timedatectl status
  3. Check for journald interference if using rsyslog