If you are like me you probably have a few servers with publicly accessible SSH daemons. We try to avoid it if at all possible, and firewall access to the service anywere we can, but at some point it is inevitable, you will have sshd open to the internet. Until a few years ago this didn't bother me too much. A good password policy, or even disallowing passwords for sshd, and diligence in monitoring the logs was all it took to let you sleep at night. Then it started creeping up on us, slowly at first, but steadily gaining in quantity, the SSH brute force attacks now innundate the net.
If you have a publicly accessible SSH daemon, check your auth.log. I almost guarantee you will see something like this:
Feb 12 03:20:20 mybox sshd[61877]: Invalid user oracle from 70.25.122.34 Feb 12 03:29:21 mybox sshd[61879]: Invalid user www-data from 70.25.122.34 Feb 12 03:32:23 mybox sshd[61881]: Invalid user httpd from 70.25.122.34 Feb 12 03:46:25 mybox sshd[61884]: Invalid user netdump from 70.25.122.34You may even run into a few lines like these:
Feb 15 19:46:58 myhost sshd[68300]: reverse mapping checking getaddrinfo for client-ip-89-42-143-249.2brothers.ro [89.42.143.249] failed - POSSIBLE BREAK-IN ATTEMPT! Feb 15 19:47:06 myhost sshd[68300]: reverse mapping checking getaddrinfo for client-ip-89-42-143-249.2brothers.ro [89.42.143.249] failed - POSSIBLE BREAK-IN ATTEMPT!The longer your service is running, the more your logs will fill up. Annoying isn't it? The worst part is, if they keep trying, eventually they may succeed.
Personally I would much prefer to block all access to my ssh services from the outside world. As the saying goes, the only truly secure box is the one not plugged in, but this isn't realistic. Aside from the best solution, one of the few I have found that works well is listed below.
Most of the solutions you see on the net are aimed at blocking fast brute force attempts. They watch for too many connections in too short of a time, or too many concurrent connections. The problem with these solutions is that for the past year or two most of the brute force attempts I have seen in my logs are of the slow variety. I still see some pounding away with each attempt only a few seconds apart, but many only make one attempt every five or so minutes, and sometimes there are even breaks of several hours between attempts.
I spent quite a bit of time over the past few years entering lists of IPs from the auth.log file to block lists on my firewalls. This works for a while, but eventually your table becomes so large it is difficult to deal with, and most of the old IPs are probably no longer assigned to the same machine as when you added them. This got me thinking that there is probably a better way, and it is probably pretty easy to implement.
After searching around a bit I came across an article on FreeBSDWiki.net that deals with this very problem. In short, the auth.log file is scanned for invalid login attempts and the IP addresses making these failed attempts are added to a block list in PF.
Since I use PF on my FreeBSD boxes and the example for PF was more geared towards OpenBSD, I modified it slightly to fit my needs. There are three parts to this solution, and you can modify them to suit your environment. First, you need to edit your pf.conf file.
table <ssh-violations> persist file "/etc/ssh-violations" ... block drop in log quick from <ssh-violations> to anyThis will create a table to hold the offending IPs in, and then block any incomming traffic from those IPs. You can modify the rule to fit your needs, for example if you only wanted to block access to the SSH service. Before you can apply this rule you need to create the persist file.
# touch /etc/ssh-violationsThe ssh-violations file can include a list of IP addresses, one per line, which will be added to the table on startup. If there are IPs you want blocked permanently you can add them there.
Next you need to create a script to add IPs to block into the table. Below is an example of my script.
#!/bin/sh
pfctl -t ssh-violations -T flush
for ips in `cat /var/log/auth.log | grep sshd | grep "Invalid" | awk '{print $10}' | uniq -d` ; do
pfctl -t ssh-violations -T add $ips
done
for ips in `cat /var/log/auth.log | grep sshd | grep "BREAK-IN" | awk '{print $12}' | \
tr -d "[]" | uniq -d` ; do
pfctl -t ssh-violations -T add $ips
done
This script scans the auth.log file looking for lines that include the strings "Invalid" or "BREAK-IN".
It then grabs the IP addresses from these lines and adds them to the ssh-violations table so PF can block them.
This is a one strike you are out system, but it can easily be changed to allow multiple failures before blocking.
cat /var/log/auth.log | grep sshd | grep "Invalid" | rev | cut -d\ -f 1 | rev | sort | uniq -c | \
( while read num ips; do if [ $num -gt 3 ]; then if ! pfctl -s rules | grep -q $ips ; then pfctl -t ssh-violations -T add $ips fi fi done )
All that is left now is to run our script on a regular basis, and cron is the perfect tool to do this. A five or ten minute delay between runs is probably sufficient, but you can experiment and determine what works best for you.
*/10 * * * * /root/sshd-fwscan.sh >/dev/null 2>&1
If you want to view the contents of the table and see what IP addresses are being blocked, you can do so with this command:
# pfctl -t ssh-violations -T show
I would encourage you to monitor your logs and watch for other types of failed logins that may need to be added to the script.
One side benefit of this method is that older IP addresses are dropped out eventually. The downside is that that the IPs are all removed as soon as the log is turned over by newsyslog. You could prevent this by changing /etc/newsyslog.conf to not bzip your previous auth.log files (remove "J" from the flags) and then "cat" both the current log and some number of previous ones (auth.log.X) in the script.
for ips in `cat /var/log/auth.log /var/log/auth.log.0 | grep sshd ...This would also be useful if your server gets a lot of traffic. For a really busy server you may also want to increase the size of the log files before they are turned over so you retain more records. IPs will still drop out in large chunks, but they will stick around longer overall.
This method of blocking SSH brute forces isn't anything revolutionary, but hopefully it will help stem the flow and make your log files more useful.