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:
- Using multiple source ports (seen in the 122.225.109.219 attack)
- Distributing attempts across the time window
- 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
- Verify file permissions:
ls -l /var/log/secure
(must be readable by fail2ban) - Confirm time synchronization:
timedatectl status
- Check for journald interference if using rsyslog