blog.poucet.org Rotating Header Image

November, 2009:

Setting up iptables to throttle incoming ssh

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.