Brute-force password-guessing attacks on SSH services are common on the Internet today. They are a threat for two reasons:
- A large number of SSH password-guessing attempts can result in a denial of service — by saturating network connections, consuming large amounts of CPU resources (and therefore power), and/or by filling log partitions with all of the failed attempts.
- An attacker might get lucky and successfully guess a username/password combination.
Both of these scenarios are bad. We can substantially reduce, or even eliminate these threats by rate-limiting incoming SSH connections — not globally, but on a per-source-IP basis. On Linux, we can use the RECENT
match facility available in modern versions of iptables
to achieve this.
Here is prettified version of the configuration that is currently running on Kalimdor; see the comments in-line for an explanation of what each rules does.
RECENT
match implementation included in kernels prior to 2.6.18 as it is faulty; see CAN-2005-2872 and CAN-2005-2873 for details.
# INPUT:
# First, we add any inbound SSH connection attempts to an
# 'ssh-clients' list.
iptables -A INPUT -i eth0 -p tcp -m tcp -m state --state NEW --dport 22 \
-m recent --set --name ssh-clients --rsource
# Then, we check to see if the source of the current packet has
# attempted to connect more than 5 times in the last 60 seconds. If
# it has, then we treat it as a brute-force attack and send it to the
# SSHATTACK chain, which does not return.
iptables -A INPUT -i eth0 -p tcp -m tcp -m state --state NEW --dport 22 \
-m recent --update --seconds 60 --hitcount 5 --name ssh-clients \
--rsource -j SSHATTACK
# SSHATTACK ruleset:
# First, we log the incoming SSH attack. However, because many
# logged attacks could DoS our logging filesystems (and, because the
# kernel echos everything to our slow serial terminal, the kernel as a
# whole) we rate-limit the log messages, too.
iptables -A SSHATTACK -m limit --limit 1/minute --limit-burst 5 -j LOG \
--log-prefix "SSH ATTACK: "
# Finally, we reject the connection attempt. Currently, we simply
# return a 'port-unreachable' error packet, as if there were no
# service listening -- but other options, such as
# "icmp-admin-prohibited" might be more net-friendly.
iptables -A SSHATTACK -j REJECT --reject-with icmp-port-unreachable
This scheme will likely become wildly impractical as IPv6 takes off; with 128-bits of address-space and a liberal allocation scheme, any intelligent attacker would jump between available addresses within the netblock that they control. When this happens, more sophisticated mechanisms for identifying and dropping packets from whole netblocks will become necessary.