Dueling fail2ban and ipset timeouts

By Paul Heinlein | Dec 10, 2014

Quick background: Fail2ban scans system logs looking for entries that indicate network connections with malicious intent. When it finds enough such entries from a given IP address, it adds a firewall rule that blocks connections from that address for a given period of time.

In CentOS and Debian, Fail2ban is normally configured with a ban time of 600 seconds (10 minutes). That’s a safe default if you’re worried about locking yourself out of your system, but I don’t think it’s long enough to ward off persistent or obnoxious attackers.

On CentOS 7 machines, I’ve been using the newer IP Set framework for storing the IP addresses of banned hosts. Rather than have one iptables rule per banned address, there’s a rule that points to hash table (an ipset) within the kernel. The ipset can be maintained independently of the iptables rules and, more to the point, it’s a much faster mechanism for doing lookups of IP addresses.

It took a while for it to sink into my head, however, that an ipset has a timeout independent of the Fail2ban ban time.

In other words, Fail2ban might be configured to ban a host for 1800 seconds (30 mintues), but if the ipset timeout is set at 600 seconds, the banned IP address may be unbanned long before Fail2ban wants it to be.

On one CentOS 7 host that’s sort of typical in this regard, I’ve got the following configuration in jail.local:

# "bantime" is the number of seconds that a host is banned.
bantime = 2400
  
# host uses firewallcmd and ipset
banaction = firewallcmd-ipset

The problem is that default firewallcmd-ipset action creates each ipset with a default ban time of 600 seconds. Banned hosts were falling out of the ipset a full 30 minutes before I wanted to unban them.

I ended up adding a local configuration to override that default:

# /etc/fail2ban/action.d/firewallcmd-ipset.local
  
[Init]
# the default bantime for this action has historically been 600
# seconds, which means that ipset will sometimes timeout a host
# before fail2ban is ready to unblock it. by setting an absurdly
# long bantime here, we ensure that it's fail2ban (and not ipset)
# that unblocks a host.
bantime = 7200

That did the trick. It’s now up to Fail2ban to remove IP addresses from the appropriate ipset, and so far, it’s done just that.