Stopping brute-force ssh scans the easy way
I recently stumbled over this blog entry on a nice, easy way to stop brute force ssh scans, so it's time to spread this knowledge around.
If you've got an ssh daemon exposed to the Internet, you know about the brute force ssh scans and password guessing attacks. The real problem with them is the sheer volume, which creates log clutter (and system load) as they spew login failures and unknown users all over your logs.
(If brute force ssh attacks give you security ulcers, get passwords that follow even basic password security rules, because the scanners pretty much only try obvious simple passwords. Anyone who gets compromised by one should be hideously embarrassed.)
In theory the easy way to stop them is to block ssh access from all the networks that don't need it (even tcpwrappers will do; a few 'connection refused' and the scanners go away). Unfortunately, figuring what such places are (or aren't) can be a slog, and it can change over time, and your users can lynch you when you get it wrong.
Thus the easy way to stop brute force ssh scans is to rate-limit
incoming ssh connections; this keeps the log spam down to a couple of
entries. Linux iptables can do this using the recent
and state
match modules, like so:
# iptables -A INPUT -p tcp --dport 22 -s <good-IP> -j ACCEPT
[repeat for all of the netblocks and IPs you want know you want to accept ssh from all the time.]
# 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 -m recent --update --seconds 60 --hitcount 3 --rttl --name SSH -j DROP
(This blocks people after 3 new ssh connections within 60 seconds.
If you prefer different numbers, adjust --seconds
and --hitcount
to taste.)
Both lines are necessary because you can only give one of --set
or
--update
, and --update
does nothing if the source IP address is
not already being tracked by the recent
module.
You can see the state of this ruleset in
/proc/net/ipt_recent/SSH
(or whatever name you gave
it). Entries can be removed by writing '-IP.AD.DR.ES
' into the file,
and the entire thing can be purged by writing 'clear
'. The ruleset
only keeps stuff on the most recent 100 IP addresses, which shouldn't
normally be a problem (this is controlled by the kernel module
ipt_recent
's ip_list_tot
module parameter).
Documentation on the recent
matcher is
here.
It is not yet in the iptables manpage, at least in the versions in
Fedora Core 4 and Debian Sarge.
Naturally, the kernel side of this has to be built for your kernel, which is yet another reason to always enable NAT et al in your kernel configuration. (I failed to do this on the system where the brute force scanners most annoy me, thereby insuring they will be annoying me for a while more yet.)
(It turns out that I am late to this particular party; people have been talking about this since at least February of 2005.)
Updated December 16th: corrected the iptables
usage; my original
version had iptables -I
instead of iptables -A
, with entertaining
havoc ensuing.
|
|