$ tail /var/log/auth.log
Aug 1 20:23:41 zugzug sshd: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=22.214.171.124
Aug 1 20:23:44 zugzug sshd: Failed password for invalid user edward from 126.96.36.199 port 43812 ssh2
Aug 1 20:23:46 zugzug sshd: Invalid user erik from 188.8.131.52
Aug 1 20:23:46 zugzug sshd: (pam_unix) check pass; user unknown
Aug 1 20:23:46 zugzug sshd: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=184.108.40.206
Aug 1 20:23:49 zugzug sshd: Failed password for invalid user erik from 220.127.116.11 port 44088 ssh2
Aug 1 20:23:51 zugzug sshd: Invalid user eduardo from 18.104.22.168
Aug 1 20:23:51 zugzug sshd: (pam_unix) check pass; user unknown
Aug 1 20:23:51 zugzug sshd: (pam_unix) authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=22.214.171.124
Aug 1 20:23:53 zugzug sshd: Failed password for invalid user eduardo from 126.96.36.199 port 44346 ssh2
The warnings are clear: someone is trying to log in via sshd, the secure shell daemon, trying random username and password combinations. And, as great as high-bandwidth is, the downside is that malicious forces can attempt thousands of login attempts in a very short time.
Now, the right term for a person who attempts to exploit security is a cracker, but “hacker” – rightfully an inquisitive and enthusiastic person – has been popularised as a malicious meddler. Nevertheless, at this point in time semantics are the last thing on your mind: whether you're being hacked or cracked you’ve got to secure your system and keep out the bad guys.
In fact, someone successfully logging in is only one of your worries. Firstly, each attempt adds several lines to your log files. In time, a lot of disk space is wasted and your logs become almost meaningless with other information you might be seeking being well hidden amongst the tens of thousands of repetitive entries.
And, more importantly, a high-speed multi-threaded attack runs the risk of denying your service by consuming all your bandwidth – let alone system resources. And if your system is too busy handling these connections, how will you log in, yourself?
So then - what do you do?
Lock down sshd
The very first step is to beef up the security on SSH itself. Firstly, you don’t want anyone logging in to your system as root. This gives unfettered power. This doesn’t stop you remoting in to your system using an ordinary user account and su’ing to root. Edit the /etc/ssh/sshd_config file and ensure it has an entry “PermitRootLogin no”. This means ssh will just prevent root logins; any attempt will fail even if the password is right. Best of all, the failure message gives no hint the connection failed because of this rule; it looks like any other bad username/password combo. This means your cracker has to guess a genuine username and password so their work is made much harder.
Depending on how flexible your network is, you might also consider changing the port number that SSH listens on. By default, it uses port 22 and this is the port crackers will be attempting to use. You could change this to some other arbitrary port. You will still be able to SSH in, specifying the appropriate port number, but others will have no success unless they realise firstly there’s another port open, and secondly that it is used for SSH (the fact a non-standard port is open gives no immediate information as to the protocol it uses.) Once again, edit /etc/ssh/sshd_config and set a customised port number on the ListenAddress line.
This is a good start. Yet, there’s still more to do. The biggest problem is there’s no built-in mechanism to stop repeated login attempts from the same IP address, even if it is failing continually.
iptables – plan A
Fortunately, all modern Linux distros come standard with a built-in firewall package known as netfilter/iptables. Netfilter is the framework that interacts with the kernel to inspect network packets; iptables is a firewall component which uses netfilter to filter packets based on specified rules. (Previously, the predominant package for this purpose was ipchains.)
Well and good, but unfortunately, iptables has no built-in rules to protect against brute force attacks. It’s installed, but it’s just sitting there idle.
Recently, Kevin van Zonneveld faced this very problem. He solved his issue with two dead simple iptables rules that everyone can use. These are:
sudo iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
sudo iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 8 --rttl --name SSH -j DROP
This pair of commands rate-limits all incoming SSH connections to eight per minute. A valid user who knows their username and password won’t be inconvenienced at all, but brute-force crackers will have their connection attempts – each being a try at a username and password – dropped right down to eight from the lofty number otherwise offered by high-speed data networks.
You can use iptables –L at any time to list your firewall rules and you can use iptables –F at any time to flush the rules. It’s definitely worthwhile checking Kevin’s blog for more detail on his rules, including how to make them take effect upon system boot.
iptables – Plan B
The rules above are clear and concise. The problem is they don’t really send a strong message to the cracker to go away. 2020Code strive for more protection with stricter iptables rules:
-A SSH -m recent --name SSH_ABL --update --seconds 3600 -j REJECT
-A SSH -m recent --name SSH --rcheck --seconds 60 --hitcount 5 -j SSH_ABL
-A SSH_ABL -m recent --name SSH_ABL --set -j LOG --log-level warn --log-prefix "ABL: +SSH: "
-A SSH_ABL -j REJECT
-A SSH -m recent --name SSH --rcheck --seconds 2 -j LOG --log-level warn --log-prefix "RATE: "
-A SSH -m recent --name SSH --update --seconds 2 -j REJECT
-A SSH -m recent --name SSH_ABL --remove -j LOG --log-level warn --log-prefix "ABL: -SSH: "
-A SSH -m recent --name SSH --set -j ACCEPT
-A INPUT -m state --state NEW -p tcp -m tcp --dport 22 -j SSH
The net effect of these rules is threefold. Firstly, a two-second pause is forced between successive SSH connections from the same host, slowing down login attempts. Secondly, if the same host attempts (and fails) connection five times within the span of one minute, the host is automatically blacklisted. Thirdly, this block lapses after a full hour passes with no connection attempts from the host.
This is much more industrial strength. Now the cracker only gets five chances every sixty-one minutes. They’ll soon move on to other grounds. The downside is fat-fingered legitimate users can potentially lock themselves out, and this is especially problematic if you have possibly many users from behind a NAT’d network who legitimately need to connect to your system and some of the users are slightly forgetful or password-challenged.
You might prefer not to edit the iptables rules yourself. Or, you may want rules which exercise more intelligence so that genuine users, albeit absent-minded, aren’t locked out.
In this case, sshblack is a freely-downloaded Perl script that eases the burden. It actively monitors log files for any concerning activity and reacts to aggressive attacks by adding their IP address to a blacklist. Any host attempting a connection which is found in the blacklist is prevented from making an SSH connection. After a period of time, the host is removed from the blacklist. The script can send e-mail notices keeping you aware of any possible concerns.
The downsides to sshblack are that it hasn’t been updated since 2006, and also that it works by parsing log files. This means it doesn’t necessarily react instantaneously to cracking attempts depending on the processor’s load.
We've saved the best to last. Now you know the problem and approaches to solving it, there’s another terrific option, the SourceForge project called DenyHosts, written in Python. Like all the above, the fundamental purpose is to thwart SSH attacks by blocking IP addresses which appear to be engaging in malicious behaviour. Where DenyHosts differs is that it isn’t running in isolation. The above solutions will reject hosts after they’ve already made failed attempts on your computer; by contrast DenyHosts uses a communal database to proactively protect your system. You need not even suffer one connection attempt by a host which has been detected as hostile by others.
DenyHosts is a snap to set up. Just install the RPM or unpack the tarball – both available for free download from SourceForge. Out of all the methods surveyed here, DenyHosts is the slickest and most elegant. Its one catch is that it is tied to the version of Python installed. This means you need to exercise caution whenever upgrading Python. Alternatively, you can install multiple versions of Python in different directories.
It’s not uncommon to have crackers survey your system. Sadly, it’s also not uncommon for people to overlook security options. The above techniques can make the difference on your computer and bolster your security and peace of mind.