Or, you could use a simple iptables module called "ipt_recent".  I
added this function to my /etc/shorewall/start script.  Whitelisted IP
addresses are found in /etc/shorewall/ssh-bruteforce-whitelist.  

FWDSSH_PORT=2345
SSH_PORT=22

ssh_bruteforce() {
    # Prime the whitelist
    iptables -N SSH_WHITELIST
    echo "   SSH Bruteforce whitelist chain created." >&2
    sed -e '/^#/ D' /etc/shorewall/ssh-bruteforce-whitelist | \
    while read CIDR; do
        iptables -A SSH_WHITELIST -s $CIDR -m recent --remove --name SSH \
                -j ACCEPT
        echo "      Added $CIDR to whitelist.">&2
    done
    
    # Start following state
    iptables -N SSH_BRUTEFORCE
    echo "   Creating SSH_BRUTEFORCE chain" >&2
    iptables -A SSH_BRUTEFORCE -m recent --set --name SSH
    iptables -A SSH_BRUTEFORCE -j SSH_WHITELIST
    iptables -A SSH_BRUTEFORCE -m recent --update --seconds 60 --hitcount 4 \
                --rttl --name SSH \
                -j LOG --log-prefix "SSH_BRUTE_FORCE "
    iptables -A SSH_BRUTEFORCE -m recent --update --seconds 60 --hitcount 4 \
                --rttl --name SSH \
                -j DROP
    echo "   Added rules to SSH_BRUTEFORCE chain" >&2
    iptables -I net2fw 2 -p tcp \
                -m multiport --destination-ports $SSH_PORT,$FWDSSH_PORT \
                -m state --state NEW \
                -j SSH_BRUTEFORCE
    echo "   Added SSH_BRUTEFORCE chain to net2fw" >&2
}
ssh_bruteforce

Logs look like this:

Jan 25 15:50:01 skuld kernel: SSH_BRUTE_FORCE IN=eth0 OUT=
    MAC=00:11:2f:9e:c9:1e:00:14:bf:18:80:c0:08:00 SRC=163.21.251.8
    DST=192.168.1.10 LEN=60 TOS=0x00 PREC=0x00 TTL=47 ID=22053 DF
    PROTO=TCP SPT=59345 DPT=22 WINDOW=5840 RES=0x00 SYN URGP=0

After four hits to the same port by the same IP address, it's added to
the black list for 60 seconds.  I could extend that if I wanted, but I
find that 60 seconds is sufficiently long to discourage most
worm/bots.

-- 
Chad Walstrom <chewie at wookimus.net>           http://www.wookimus.net/
           assert(expired(knowledge)); /* core dump */