Kicking out SSH script kiddies with autoblock
I noticed recently that random people have been trying to brute-force SSH on this website – this is a pretty common thing to notice among Linux users unfortunately. Things like this will often appear in your logs (On Debian/Ubuntu it’s in /var/log/auth.log):
Failed password for invalid user pete from 72.22.77.196 port 59102 ssh2
Invalid user peter from 72.22.77.196
(pam_unix) check pass; user unknown
(pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=72.22.77.196
Failed password for invalid user peter from 72.22.77.196 port 59251 ssh2
Invalid user peter from 72.22.77.196
(pam_unix) check pass; user unknown
(pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=72.22.77.196
Failed password for invalid user peter from 72.22.77.196 port 59494 ssh2
Invalid user phil from 72.22.77.196
(pam_unix) check pass; user unknown
(pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=72.22.77.196
Invalid user peter from 72.22.77.196
(pam_unix) check pass; user unknown
(pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=72.22.77.196
Failed password for invalid user peter from 72.22.77.196 port 59251 ssh2
Invalid user peter from 72.22.77.196
(pam_unix) check pass; user unknown
(pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=72.22.77.196
Failed password for invalid user peter from 72.22.77.196 port 59494 ssh2
Invalid user phil from 72.22.77.196
(pam_unix) check pass; user unknown
(pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=72.22.77.196
Thanks 72.22.77.196, you’re a giant tool. So to get rid of you and your ilk, I wrote this Ruby script:
#!/usr/bin/ruby
ip_count = {}
blacklisted = {}
tables = `iptables –list`
STDIN.readlines.each do |x|
next unless x =~ /Failed.*ssh2/
m = /(\d*\.\d*\.\d*\.\d*)/.match(x)
next unless m and m[0]; addr = m[0]
ip_count[addr] ||= 0; ip_count[addr] += 1
next unless ip_count[addr] > 10
next if blacklisted[addr] or tables.include? addr
`iptables -I INPUT -s #{addr} -j DROP`
blacklisted[addr] = true
end
ip_count = {}
blacklisted = {}
tables = `iptables –list`
STDIN.readlines.each do |x|
next unless x =~ /Failed.*ssh2/
m = /(\d*\.\d*\.\d*\.\d*)/.match(x)
next unless m and m[0]; addr = m[0]
ip_count[addr] ||= 0; ip_count[addr] += 1
next unless ip_count[addr] > 10
next if blacklisted[addr] or tables.include? addr
`iptables -I INPUT -s #{addr} -j DROP`
blacklisted[addr] = true
end
Since the auth log is called different things depending on your distro, I made the script run from standard input. To make it run every half-hour, run crontab -e and add the following line:
0,30 * * * * cat /var/log/auth.log | ruby /root/ssh_block.rb