When analyzing the provided configuration, we can see fail2ban correctly identifies malicious traffic (XML-RPC attacks from 80.82.70.239) and adds the IP to iptables. However, the banned IP continues to appear in Apache logs, suggesting either:
1. Rule ordering issues in iptables
2. Chain traversal misconfiguration
3. Missing NAT table rules
4. Packet processing before filtering occurs
The key insight comes from examining the iptables chain structure:
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
244 39277 fail2ban-apache-xmlrpc tcp -- * * 0.0.0.0/0 0.0.0.0/0 multiport dports 80,443
101 7716 fail2ban-ssh tcp -- * * 0.0.0.0/0 0.0.0.0/0 multiport dports 22
0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
349 20900 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
The fundamental issue becomes clear: The fail2ban chain rules are being processed AFTER the blanket HTTP/HTTPS acceptance rules. This means packets get accepted before reaching the banning rules.
Here's the corrected firewall rules structure that works in production environments:
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:fail2ban - [0:0]
# Base protection rules
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Security before services
-A INPUT -j fail2ban
# Then service rules
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT
COMMIT
For the jail configuration, we should implement these optimizations:
[apache-xmlrpc]
enabled = true
port = http,https
filter = apache-xmlrpc
logpath = /var/log/apache2/other_vhosts_access.log
maxretry = 3
findtime = 300
bantime = 86400
action = iptables-allports[name=apache-xmlrpc, protocol=all]
To confirm the solution works:
# Check rule ordering
sudo iptables -vnL --line-numbers
# Test packet flow
sudo iptables -t raw -A PREROUTING -s 80.82.70.239 -j TRACE
sudo iptables -t raw -A OUTPUT -s 80.82.70.239 -j TRACE
# Monitor live traffic
sudo iptables -I INPUT 1 -p tcp --dport 80 -j LOG --log-prefix "HTTP-INPUT: "
Let me document a particularly stubborn case where iptables appeared to be ignoring Fail2ban rules completely. Despite seeing the banned IP (80.82.70.239) in both the fail2ban.log and iptables ruleset, Apache was still receiving requests from this address.
The key revelation came when examining the packet flow through iptables chains:
$ sudo iptables -nvL --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 244 39277 fail2ban-apache-xmlrpc tcp -- * * 0.0.0.0/0 0.0.0.0/0 multiport dports 80,443
2 101 7716 fail2ban-ssh tcp -- * * 0.0.0.0/0 0.0.0.0/0 multiport dports 22
3 0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
4 0 0 REJECT all -- * * 0.0.0.0/0 127.0.0.0/8 reject-with icmp-port-unreachable
5 3404 582K ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
6 349 20900 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
7 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
8 12 720 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22
9 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
10 2 80 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 5/min burst 5 LOG flags 0 level 7 prefix "iptables denied: "
11 2 80 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
The critical issue was rule #5 accepting all ESTABLISHED,RELATED connections. Since this rule appeared before the HTTP/HTTPS accept rules (#6-7), it was short-circuiting the blocking logic.
Here's the problematic sequence:
1. Attacker makes initial connection (gets through)
2. Connection becomes "ESTABLISHED"
3. Subsequent packets bypass all blocking rules because of rule #5
The fix required reordering the rules to evaluate blocking before allowing established connections:
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Fail2ban chains first
-A INPUT -p tcp -m multiport --dports 80,443 -j fail2ban-apache-xmlrpc
-A INPUT -p tcp --dport 22 -j fail2ban-ssh
# Then basic protections
-A INPUT -i lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -j REJECT
# Now service-specific rules (HTTP/HTTPS/SSH)
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
# Finally, allow established connections (AFTER service rules)
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Logging and default deny
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
-A INPUT -j DROP
COMMIT
After applying the new ruleset, we can verify proper blocking:
# Simulate attack from banned IP
$ sudo hping3 -S -p 80 -c 3 80.82.70.239
HPING 80.82.70.239 (eth0 80.82.70.239): S set, 40 headers + 0 data bytes
--- 80.82.70.239 hping statistic ---
3 packets transmitted, 0 packets received, 100% packet loss
For deeper inspection, these commands help:
1. Check packet counters for specific rules:
$ sudo iptables -L fail2ban-apache-xmlrpc -v -n
2. Monitor real-time traffic:
$ sudo tcpdump -ni eth0 host 80.82.70.239
3. Trace iptables decisions:
$ sudo iptables -t raw -A PREROUTING -j TRACE
$ dmesg -w