Recently I wanted to add some additional security layers to a server that I administer. To a seasoned admin, this will be old news, but I’m a hobbyist and just learned about this. Maybe some of you find it useful as well.
If you have a server that is publically accessible, when you look at
/var/log/auth.log log file you will find a lot of unsuccessful authentication attempts from bots from around the world. If you require public keys to authenticate, this doesn’t have to be a problem. If you don’t, you should disable password based authentication and switch to using ssh keys. You can do this in
/etc/ssh/sshd_config by setting this option:
Afterwards, reload the sshd configuration or restart the ssh daemon.
sudo systemctl reload ssh
Also, you can get prevent a lot of unwanted login attempts by using a non standard tcp port for your ssh server. Again, you can do this in you
However, it can still be a good idea to further restrict who can connect to the server in the first place.
Just a little note upfront: my server runs on Ubuntu, for other distributions you will need to adapt the instructions below.
One tool that you can use to increase security is fail2ban: it’s an intrusion prevention system that, once configured, will scan the log files of different services for suspicious log entries and ban IPs from connecting to your server. For every service that fail2ban monitors it creates a so called jail. The default configuration works with ssh and already does a good job filtering out much of the noise at firewall level. Installation is easy:
sudo apt update
sudo apt install fail2ban
Afterwards you need to create a local copy of the default configuration file, so that your changes don’t conflict with possible updates at a later point.
sudo cp jail.conf jail.local
Have a look inside
jail.local, you can adjust different options here (ban time, number of failed authentication attemps, etc.). The default values make sense, be sure to think about your changes so that you don’t create unexpected side effects. For example: if you ban ip’s for a very long time, you might put the ban on some other user after the ip address was reassigned.
If this sounds interesting, check this tutorial for more detailed instructions on how to install Fail2Ban.
Restricting access by geo location
Most of the unsuccessful connection attempts in /var/log/auth.log come from countries that I never use for logins to my server. So it is obvious that we should only allow connection attempts from certain countries.
To achieve this, you can install the GeoIP database:
sudo apt install geoip-bin
The package installs two new commands that enable you to lookup geo information for ipv4 and ipv6 addresses (
geolookup6). In order to check an ip address, we need a filter script.
Next, we need to integrate the script into the ssh authentication process. We can do this by facilitating TCP wrappers, a networking ACL system, used to filter network access. To use them, we need to configure the two hosts access files
Allow rules take precedence over deny rules and the first matching rule terminates validation. Since we want to have an allowlist, we first deny all requests to
/etc/hosts.allow, we call into our filter script:
sshd: ALL: aclexec /usr/local/bin/ssh-filter.sh %a
You don’t need to restart anything, these rules take immediate effect.
You can find this solution on the web already, but I noticed it didn’t work for me. The filter script itself logged the expected outcome and returned the correct result (exit code 1 for blocked countries, exit code 0 for allowed countries), the tcp wrapper also called the script and log entries were created upon ssh connection. But when I tested it, blocked ip’s were still allowed to connect. After some research I found the problem: The solution that I tried to apply didn’t use
aclexec to call the filter script but instead used
spawn. The latter doesn’t actually use the exit code to do anything. It just spawns a child process that executes the given command.
aclexec will take into account the exit code and abort connection attempts.
There are other methods to restrict access of unwanted ips to ssh (i’m thinking of rate limiting, for example), but this is good enough for me, right now.