Mitigating Brute Force Attacks

Put a server up on the internet (which allows SSH connections from anywhere and you'll quickly notice that there lots of machines which are constantly attempting to login to server via SSH via brute force attacks. So long as your users have strong passwords the risk isn't too high but it certainly is annoying.

Reading Debian Administration I came across this idea for using iptables to restrict how many times a single IP can try and connect via SSH to your server.

Here is a very simple script which only allows a single IP address to SSH to your server 5 times each 60 seconds. Any IP address or subnet listed in TRUSTED_HOSTS will be able to connect as many times as it wants.

#!/bin/bash
# Requires the ipt_recent kernel modules

TRUSTED_HOSTS="127.0.0.1 192.168.4.192/26"

iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j SSH_WHITELIST
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 --rttl --name SSH -j ULOG --ulog-prefix SSH_brute_force
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 --rttl --name SSH -j DROP

for host in $TRUSTED_HOSTS; do
    iptables -A SSH_WHITELIST -s $host -m recent --remove --name SSH -j ACCEPT
done

If you have legitimate reasons to connect more then 5 times within a 60 second timeframe you can either whitelist your IP address or using the connection sharing feature in modern versions of OpenSSH.

Connection Sharing

Versions of OpenSSH greater then 4.0 have a new feature which allows you to reuse a single SSH connection for all subsequent connections to that same host. This has two primary advantages

It's easy to implement, add these lines lines to your ~/.ssh/config, check the ssh man page for details:

Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h:%p

Update (8 Apr 08): I've been using this for a while now and the only downside I've found is that Subversion, using the svn+ssh protocol, doesn't clean up properly after itself and leaves a stale socket file behind when it closes the connection. There are two ways around this, make a normal ssh connection into the server before using Subversion (so Subversion is using the socket but isn't the master), or delete the socket and retry. The error looks like this:

overkill(shand)$ svn ci -m ""
Control socket connect(/Users/shand/.ssh/adam@joe.spack.org:22): Connection refused