So I decided today, since I seem to be getting a lot of ssh attempts to my firewall at home to set up some iptable rules.
It took me quite a while to figure it out, since I needed to set up some modprobe options.
First, I set that I can count up to 250 (I think the maximum is 256) recent ip hits.
cat /etc/modprobe.d/options
options ipt_recent ip_pkt_list_tot=250
Then I created a firewall script:
cat firewall.sh
#!/bin/sh
ipt=/sbin/iptables
set -x
if [ -z $1 ] ; then
echo "$0 <public device>"
exit
fi
# Clear rules
$ipt -D INPUT -i $1 -p TCP --dport ssh -m state --state NEW -j "$1"-SSH 2>/dev/null
# Set up an ssh and blacklist chain.
$ipt -F "$1"-SSH 2>/dev/null
$ipt -F "$1"-BLACKLIST 2>/dev/null
$ipt -X "$1"-SSH 2>/dev/null
$ipt -X "$1"-BLACKLIST 2>/dev/null
$ipt -N "$1"-SSH
$ipt -N "$1"-BLACKLIST
# Make sure that we update the recency of the packet, and then drop them. The timing is controlled by the ssh chain.
$ipt -A "$1"-BLACKLIST -m recent --name BLACKLIST --set
$ipt -A "$1"-BLACKLIST -j DROP
# In the ssh chain, incoming connections from BLACKLIST hosts are dropped. The timer is restarted everytime we get a packet within 600 s.
$ipt -A "$1"-SSH -m recent --update --name BLACKLIST --seconds 600 --hitcount 1 -j DROP
# Create several counting buckets.
$ipt -A "$1"-SSH -m recent --set --name "$1"-BUCKET1
$ipt -A "$1"-SSH -m recent --set --name "$1"-BUCKET2
$ipt -A "$1"-SSH -m recent --set --name "$1"-BUCKET3
$ipt -A "$1"-SSH -m recent --set --name "$1"-BUCKET4
# Blacklist if:
# More than 2 connections in 10 seconds
# More than 14 connections in 120 seconds
# More than 79 connections in 600 seconds
# More than 250 connections in 1800 seconds
$ipt -A "$1"-SSH -m recent --update --name "$1"-BUCKET1 --seconds 10 --hitcount 3 -j "$1"-BLACKLIST
$ipt -A "$1"-SSH -m recent --update --name "$1"-BUCKET2 --seconds 120 --hitcount 15 -j "$1"-BLACKLIST
$ipt -A "$1"-SSH -m recent --update --name "$1"-BUCKET3 --seconds 600 --hitcount 80 -j "$1"-BLACKLIST
$ipt -A "$1"-SSH -m recent --update --name "$1"-BUCKET4 --seconds 1800 --hitcount 250 -j "$1"-BLACKLIST
# All other ssh access is allowed.
$ipt -A "$1"-SSH -j ACCEPT
# Allow packets that belong to existing connections.
$ipt -D INPUT -i $1 -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null
$ipt -A INPUT -i $1 -m state --state RELATED,ESTABLISHED -j ACCEPT
# Allow all packets from loopback device.
$ipt -D INPUT -i lo -j ACCEPT 2>/dev/null
$ipt -A INPUT -i lo -j ACCEPT
# Redirect all incoming ssh connections to the chain of the same name.
$ipt -A INPUT -i $1 -p TCP --dport ssh -m state --state NEW -j "$1"-SSH
# What remains has no right to continue.
$ipt -D INPUT -i $1 -j DROP 2>/dev/null
$ipt -A INPUT -i $1 -j DROP
Finally, I set it up in my /etc/network/interfaces, that this should be called for my main interface (my public one):
auto eth0
iface eth0 inet dhcp
up firewall.sh eth0
I hope this helps anyone.










Hi,
I use denyhosts which in turn uses /etc/hosts.deny. Did you know it?
Hello Nicolas,
Yes I know about /etc/hosts.deny. Another option would’ve been to simply blackhole traffic from ip’s using iptable. However, the reason I went with the above approach is that I did not want static permanent blocks, and I wanted to learn a bit more about iptables.
Chris’ approach (1) doesn’t waste the SSH daemon’s time (2) is more secure since it drops packets as soon as possible and (3) is more dynamic and self-sufficient than simple throwing people in hosts.deny (which can get long, must be parsed on every connection, etc.)