Dynamically blacklisting hosts on a netfilter firewall

by Olivier Sessink

A netfilter firewall (the kernel-level firewall on Linux, sometimes referred to as iptables firewall) can blacklist hosts dynamically. This is very useful to stop hosts from performing brute force attacks on serices that require a password, such as the ssh service.

The firewall rules below create a blacklist for ssh connections. Using the recent module, we create two tables, called sshconn and BLACKLIST. Every new ssh packet is listed in sshconn. If the number of new packets in the last 30 seconds is more than 3, the packet is redirected to the blacklistdrop chain. This chain adds the packet to the BLACKLIST, and drops the packet.

Every subsequent packet that is from a host that has been seen in BLACKLIST the last 120 seconds, updates the BLACKLIST and is dropped.

Outgoing packets to a host that has been seen in BLACKLIST the last 30 seconds are rejected using a special reject that will notify the sending process to clean up this connection. This is done in the properREJECT chain.

These are the firewall rules:

# create properREJECT chain that does different rejects for tcp/udp iptables -N properREJECT iptables -A properREJECT -p tcp -j REJECT --reject-with tcp-reset iptables -A properREJECT -j REJECT --reject-with icmp-port-unreachable # iptables -N blacklistdrop iptables -A blacklistdrop -j LOG --log-prefix "adding to BLACKLIST: " iptables -A blacklistdrop -m recent --name BLACKLIST --set -j DROP # # # on external hosts, do rate limiting on incoming ssh packets, and keep a blacklist for 30 seconds # this rule drops *any* packet if the IP is in the blacklist # icmp 'destination-unreachable' packets should not update BLACKLIST, because # they are generated by our own REJECT rule in the extern_out chain iptables -A extern_in -m recent --name BLACKLIST --update --seconds 120 -j DROP # # all *established* ssh connections simply continue iptables -A extern_in -p tcp --dport 22 -m state --state ESTABLISHED,RELATED -j ACCEPT # # *new* ssh connections are all put into a list 'sshconn', and if there are 3 such packets in 30 seconds # we send the package to chain 'blacklistdrop' which puts the IP in the blacklist iptables -A extern_in -p tcp --dport 22 -m state --state NEW -m recent --name sshconn --rcheck --seconds 30 --hitcount 3 -j blacklistdrop # # if we have seen less then 3 such packets in the last 30 seconds we accept iptables -A extern_in -p tcp --dport 22 -m state --state NEW -m recent --name sshconn --set -j ACCEPT # # if the destination address is in the blacklist, we REJECT *any* packet iptables -A extern_out -m recent --name BLACKLIST --rdest --rcheck --seconds 30 -j properREJECT # # outgoing we accept all ssh traffic, with connection tracking iptables -A extern_out -p tcp --sport 22 -m state --state ESTABLISHED,NEW,RELATED -j ACCEPT

The blacklist is used on several servers, and with a regular ssh brute force attack, attackers get the chance to try 2 or three passwords before they are blocked by the blacklist.