Table of Contents
SSH is most likely the most secure way to remotely connect to a LINUX-based server machine. However, the fact that the SSH daemon service needs to be reached from the Internet and is usually configured to listen to a well-known TCP port has always been a major security flaw: it allows attackers to relentlessly spam it with a huge number login attempts, hoping to find a hole in your UAC setup.
To better understand what we’re talking about, let’s take a look at the following screenshot:
Those 150 failed login attempts have been attempted on one of our CentOS7 servers in a fifteen-minute range: we’re easily talking about thousands of them every single day, which would eventually break any non-strong password, other than flooding our beloved port 22.
It would be a good thing if we could do something about this nasty problem, for example issuing some throttling rules that could force these login attempts to respect a time limit each time they issue a wrong password. Luckily enough, there’s more than something we can do about that.
UPDATE: if you have the same problem with the Windows Server RDP service, read here to fix it.
Changing SSH port
The first thing you should do, if you can afford that, is to change the SSH port from the default (22) to a different one: here’s a great guide explaining how to do it with CentOS (if you aren’t running CentOS, look here instead).
However, doing this could be not enough to fend off less-than-trivial brute-force scenarios, not to mention the fact that you would need to have the required permissions to the server machine. If you don’t, of if you want to take a different approach, here’s another way to mitigate the issue.
The service is called Fail2ban and features a great amount of useful and highly-configurable features that can help us to mitigate these brute-force attacks. In this post we’ll briefly describe how we can setup and configure it by summarizing a great post taken from DigitalOcean: for additional info on the project itself, we strongly suggest to visit the Fail2ban official page.
The first thing we have to do is to add a reference to the EPEL project, since the Fail2ban package is not available (yet) in the official CentOS package repo. EPEL is an acronym for Extra Packages for Enterprise Linux and can be installed via YUM with the following line:
$ sudo yum install epel-release
Answer y when prompted for confirmation and the package will be installed.
Once done, you’ll be able to install Fail2ban with the following command:
$ sudo yum install fail2ban
Again, answer y to confirm and wait for the package installation to complete.
Once installed, issue the following command to enable the Fail2ban service upon system start:
$ sudo systemctl enable fail2ban
The great thing about Fail2ban is that it comes with a default set of options that are already ok to cover all your basic needs. However, it can’t hurt to tune them a little: to do that, you have several options:
- Change the default settings by editing the /etc/fail2ban/jail.conf file directly.
- Create the /etc/fail2ban/jail.local configuration file and use it to override the default settings you want to change.
The former option is easier, as the default configuration file feature a lot of useful comments about the various available settings. However, it could also be overwritten whenever you update the package or perform other operations, so we suggest to go with the latter.
Here’s a good jail.local file that you could use to fine-tune the most useful settings:
# Ban hosts for 1 hour after they perform 3 failed login attempts within 10 minutes
bantime = 3600
findtime = 600
maxretry = 3
# Never ban the following space-separated IP addresses/masks
ignoreip = 127.0.0.1/8
# Override /etc/fail2ban/jail.d/00-firewalld.conf
# to ensure that iptables will be used for firewall configuration
banaction = iptables-multiport
# Choose what to do when issuing a ban:
# $(action_)s : [default]
# sets the OS firewall to reject all incoming calls
# from that IP address for the specified amount of time
# $(action_mw)s : same as above + send and alert e-mail
# $(action_mwl)s : same as above + adds relevant log lines to the e-mail
# action = $(action_)s
# Send fail2ban alerts & warnings to the following e-mail address
destemail = firstname.lastname@example.org
sendername = Fail2Ban
mta = sendmail
# Enables the sshd jail
enabled = true
We decorated each relevant setting with a comment explaining what it does, so you should have no problems understaing what each one of them will do.
It’s worth noting that the jail.conf file can also be overridden by any .conf file present in the /etc/fail2ban/jail.d/ folder: similarly, the jail.local file we just added can also be overridden by any .local file present in that same folder. Here’s the cascading order:
- /etc/fail2ban/jail.d/*.conf (from first to last, sorted alphabetically)
- /etc/fail2ban/jail.d/*.local (from first to last, sorted alphabetically)
Regardless how you choose to configure it, be sure to restart the Fail2ban services after you change any of these files:
$ sudo systemctl restart fail2ban
The following commands can be used to ensure that Fail2ban services is working properly and to check what it actually does.
This command gives some basic real-time info regarding the service status:
$ sudo systemctl status fail2ban
This command allows the admin to check the Fail2ban log file since the last startup:
$ sudo journalctl -b -u fail2ban
This command can be used to list the number of available jails:
$ sudo fail2ban-client status
This command can be used to check the status of a specific jail (number of bans, banned IP list & more):
$ sudo fail2ban-client status sshd
In the above example we’re checking the sshd jail, but you can input any existing jail name available from the previous command.
Last but not least, this command will use tail to read the Fail2ban log file (press Ctrl-C to exit):
$ sudo tail -F /var/log/fail2ban.log
That’s it for now. If you’re curious about how Fail2ban works under the hood, we strongly suggest to take a look at this other awesome post on DigitalOcean that explains just that.