How to protect your server on Ubuntu with Fail2Ban with email alerts and GeoIP filter

Maxim Manylov
11 min readFeb 16, 2021

--

Introduction

This tutorial describes the shortcut to gaining an essential security level for your fresh Ubuntu server. The reason why you should do so is that almost every IP address in the world is being brute-forced 24/7 by large botnets and amount of bots and sophistication of attacks are increasing dramatically. Nowadays the cybersecurity is paramount.

Contents

This tutorial includes three parts:

  1. Disabling root login remotely (essential)
  2. Add cryptographic key authentication (recommended)
  3. Limit your SSH logins using GeoIP (optional)

Let’s start with disabling root login remotely. This requires to log in, create a new user which will be used for remote login and configuring ssh service.

Note: If you don’t know how to copy/paste in the terminal, use the usual commands (CTRL+V, CTRL+C on Linux and Windows) with SHIFT pressed. Read this answer on StackOverflow for more information.

Disabling root login remotely (essential)

Login to your server

This command will help you to log in to your server as aroot user. Replace the server with your server IP address or domain name.

ssh root@server

As a result, you now must be connected to the server and logged in as root. You must see something like this:

Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-042stab120.18 x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Wed Sep 13 03:00:23 2017 from 12.345.67.890

[root@server ~]#

Create a new user

Then add a new user to the system which will be used instead of root for login. Replace the username in the examples below with a name that you like. After executing the command adduser you will be asked for a new user’s password. Enter a strong password and, optionally, fill in any of the additional information if you would like. This is not required and you can just hit ENTER in any field you wish to skip.

adduser username

Now, we have a new user account with regular account privileges. However, we may sometimes need to do administrative tasks.

To avoid having to log out of our normal user and log back in as the root account, we can set up what is known as “superuser” or root privileges for our normal account. This will allow our normal user to run commands with administrative privileges by putting the word sudo before each command.

usermod -aG sudo username

Now your user can run commands with superuser privileges! For more information about how this works, check out this sudoers tutorial.

To disable root SSH login, edit /etc/ssh/sshd_config with your favorite text editor.

The most common terminal text editor amongst developers is Vim. It’s a bit complicated but can save you a lot of time. So I provided a few tips for you who didn’t use it before.

Run the following command (vi is an alias for vim):

vi /etc/ssh/sshd_config

As you press enter you will see the Vim text editor. What you should know about Vim is that:

  1. Vim has several modes available. Just keep in mind that there are command mode and other modes.
  2. You can use only command and input modes to do basic text editing.
  3. By default, Vim is in command mode. To return to command mode from any press ESC.
  4. Vim has its commands. Most commands start with colon (: key).
  5. To exit Vim run :q command.
  6. To edit a file you should use input mode. Press i key to enter the mode from the command mode.
  7. To write to a file use command :w.
  8. You can combine commands, i.e., fire :wq and you will save the file on exit.
  9. To search for a string hit / in command mode and then input the string hit enter.

Vim command reference

save: :w
save and exit: :wq
exit: :q
force: ! (example :w!, :q!)
vertical split: open a document and then type :vsplit /path-to-document/document and this will open the specified document and split the screen so you can see both documents.
copy: y
copy a line: yy
paste: p
cut: d
cut a line: dd

Here is vim tutorial. You can find a lot of helpful video tutorials on youtube.

Enter the input mode, edit the line, and save on exit.

Change this line:

#PermitRootLogin yes

Edit to this:

PermitRootLogin no

Ensure that you are logged into the box with another shell before restarting sshd to avoid locking yourself out of the server.

[root@server ~]# /etc/init.d/sshd restart
Stopping sshd: [ OK ]
Starting sshd: [ OK ]
[root@server ~]#

That’s it. Now you can logout using exit command and try to login with new user credentials.

If you wish your server to be more secured, follow the next step of the tutorial.

Add cryptographic key authentication (recommended)

If you already have a key that you want to use, skip to the Copy the Public Key step.

If you do not already have an SSH key pair, which consists of a public and private key, you need to generate one. You will need this key to work with such services as bitbucket or github.

Generate key pair

Run this command on your local machine to generate the key pair.

ssh-keygen

You will be asked few questions after that the key pair will be generated.

Note: you will be asked for a passphrase, which if you provide one will be asked when login into the server. If not, then you won’t have to type the passphrase every time you log in.

This generates a private key, id_rsa, and a public key, id_rsa.pub, in the .ssh directory of the local user's home directory. Remember that the private key should not be shared with anyone who should not have access to your servers!

Copy the public key

Option 1: Use ssh-copy-id

If your local machine has the ssh-copy-id script installed, you can use it to install your public key to any user that you have login credentials for.

Run the ssh-copy-id script by specifying the user and IP address of the server that you want to install the key on, like this:

ssh-copy-id username@server

After providing your password at the prompt, your public key will be added to the remote user’s .ssh/authorized_keys file. The corresponding private key can now be used to log into the server.

Option 2: Manually Install the Key

Assuming you generated an SSH key pair using the previous step, use the following command at the terminal of your local machine to print your public key (id_rsa.pub):

cat ~/.ssh/id_rsa.pub

This should print your public SSH key, which should look something like the following:

id_rsa.pub contents
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBGTO0tsVejssuaYR5R3Y/i73SppJAhme1dH7W2c47d4gOqB4izP0+fRLfvbz/tnXFz4iOP/H6eCV05hqUhF+KYRxt9Y8tVMrpDZR2l75o6+xSbUOMu6xN+uVF0T9XzKcxmzTmnV7Na5up3QM3DoSRYX/EP3utr2+zAqpJIfKPLdA74w7g56oYWI9blpnpzxkEd3edVJOivUkpZ4JoenWManvIaSdMTJXMy3MtlQhva+j9CgguyVbUkdzK9KKEuah+pFZvaugtebsU+bllPTB0nlXGIJk98Ie9ZtxuY3nCKneB+KjKiXrAvXUPCI9mWkYS/1rggpFmu3HbXBnWSUdf user@machine.local

Select the public key, and copy it to your clipboard.

To enable the use of the SSH key to authenticate as the new remote user, you must add the public key to a special file in the user’s home directory.

On the server, as the root user, enter the following command to temporarily switch to the new user (substitute your own user name):

su - username

Now you will be in your new user’s home directory.

Create a new directory called .ssh and restrict its permissions with the following commands:

mkdir ~/.ssh
chmod 700 ~/.ssh

Now open a file in .ssh called authorized_keys with a text editor. We will use vim to edit the file:

vim ~/.ssh/authorized_keys

Hit i, insert your public key (which should be in your clipboard) by pasting it into the editor (CTRL+SHIFT+V) and save on exit hitting ESC, then :wq.

Now restrict the permissions of the authorized_keys file with this command:

chmod 600 ~/.ssh/authorized_keys

Type this command once to return to the root user:

exit

Now your public key is installed, and you can use SSH keys to log in as your user.

Disable password authentication

To disable password authentication on your server, follow these steps.

As root or your new sudo user, open the SSH daemon configuration:

vim /etc/ssh/sshd_config

Find the line that specifies PasswordAuthentication, uncomment it by deleting the preceding #, then change its value to “no”. It should look like this after you have made the change:

PasswordAuthentication no

Here are two other settings that are important for key-only authentication and are set by default. If you haven’t modified this file before, you do not need to change these settings:

PubkeyAuthentication yes
ChallengeResponseAuthentication no

Type this to reload the SSH daemon:

systemctl reload sshd

Password authentication is now disabled. Your server is now only accessible with SSH key authentication.

Set up firewall

Ubuntu 16.04 servers can use the UFW firewall to make sure only connections to certain services are allowed. We can set up a basic firewall very easily using this application.

Different applications can register their profiles with UFW upon installation. These profiles allow UFW to manage these applications by name. OpenSSH, the service allowing us to connect to our server now, has a profile registered with UFW.

You can see this by typing:

sudo ufw app list

We need to make sure that the firewall allows SSH connections so that we can log back in next time. We can allow these connections by typing:

sudo ufw allow OpenSSH

Afterward, we can enable the firewall by typing:

sudo ufw enable

Type “y” and press ENTER to proceed. You can see that SSH connections are still allowed by typing:

sudo ufw status

If you install and configure additional services, you will need to adjust the firewall settings to allow acceptable traffic in. You can learn some common UFW operations in this guide.

Set up fail2ban

A service called fail2ban can mitigate this problem by creating rules that can automatically alter your iptables firewall configuration based on a predefined number of unsuccessful login attempts. This will allow your server to respond to illegitimate access attempts without intervention from you.

Install and configure fail2ban

Type the following commands to install fail2ban:

sudo apt-get update
sudo apt-get install fail2ban

The fail2ban service keeps its configuration files in the /etc/fail2ban directory. There is a file with defaults called jail.conf.

Since this file can be modified by package upgrades, we should not edit this file in-place, but rather copy it so that we can make our changes safely. For these two files to operate together successfully, it is best to only include the settings you wish to override in the jail.local file. All default options will be taken from the jail.conf file.

awk '{ printf "# "; print; }' /etc/fail2ban/jail.conf | sudo tee /etc/fail2ban/jail.local

You will want to evaluate the destemail, sendernameand mta settings if you wish to configure email alerts. The destemail parameter sets the email address that should receive ban messages. The sendername sets the value of the "From" field in the email. The mta parameter configures what mail service will be used to send mail. Again, add these to the jail.local file, under the [DEFAULT] header and set to the proper values if you wish to adjust them.

Note: the mail service (mta) must be configured properly to send emails. This tutorial can help you configure the sendmail service. It is good practice to configure email alerts for most people do not check /var/log/auth.log file even once a month.

[DEFAULT]
. . .
action = $(action_)s
. . .

This parameter configures the action that fail2ban takes when it wants to institute a ban. The value action_ is defined in the file shortly before this parameter. The default action is to simply configure the firewall to reject traffic from the offending host until the ban time elapses.

If you would like to configure email alerts, add or uncomment the action item to the jail.local file and change its value from action_ to action_mw. If you want the email to include the relevant log lines, you can change it to action_mwl. Make sure you have the appropriate mail settings configured if you choose to use mail alerts.

We’re going to configure an auto-banning policy for SSH and Nginx, just as we described above. We want fail2ban to email us when an IP is banned.

If you don’t already have it, you’ll need nginx, since we’re going to be monitoring its logs, and you’ll need sendmail to mail us notifications. We’ll also grab iptables-persistent to allow the server to automatically set up our firewall rules at boot. These can be acquired from Ubuntu’s default repositories:

sudo apt-get update
sudo apt-get install nginx sendmail iptables-persistent

Stop the fail2ban service for a moment so that we can establish a base firewall without the rules it adds:

sudo service fail2ban stop

When that is finished, we should implement a default firewall.

We’re going to tell it to allow established connections, traffic generated by the server itself, traffic destined for our SSH and web server ports. We will drop all other traffic. We can set this basic firewall up by typing:

sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
sudo iptables -A INPUT -j DROP

These commands will implement the above policy. We can see our current firewall rules by typing:

sudo iptables -S

You can save the firewalls so that they survive a reboot by typing:

sudo dpkg-reconfigure iptables-persistent

Afterward, you can restart fail2ban to implement the wrapping rules:

sudo service fail2ban start

We can see our current firewall rules by typing:

sudo iptables -S

If you want to read more on how to set up a firewall with fail2ban go here.

The next step is optional.

Limit your SSH logins using GeoIP (optional)

As I mentioned before, you should take some actions to protect the server (primarily, against botnets, which scan almost all available ips and try to break into the server and take control). Most often you will see incoming brute-force attacks from Chinese bots.

Install

Install GeoIP using the following command:

sudo apt update
sudo apt install geoip-bin geoip-database

Make sure it works by doing a simple test:

geoiplookup 8.8.8.8

Create the script to filter requests and cut unneeded.
On Ubuntu, you can see messages from sshd service in /var/log/syslog file.

Configure

Save this file in /usr/local/bin/sshfilter.sh:

!/bin/bash
UPPERCASE space-separated country codes to ACCEPT
ALLOW_COUNTRIES="NZ AU"
if [ $# -ne 1 ]; then
echo "Usage: basename $0 " 1>&2
exit 0 # return true in case of config issue
fi
COUNTRY=/usr/bin/geoiplookup $1 | awk -F ": " '{ print $2 }' | awk -F "," '{ print $1 }' | head -n 1
[[ $COUNTRY = "IP Address not found" || $ALLOW_COUNTRIES =~ $COUNTRY ]] && RESPONSE="ALLOW" || RESPONSE="DENY"
if [ $RESPONSE = "ALLOW" ] then exit 0 else logger "$RESPONSE sshd connection from $1 ($COUNTRY)" exit 1 fi

Edit the ALLOW_COUNTRIES to include a list of country codes you want your SSH server to accept connections from. Here you can find country codes.

The last thing we need to do is tell the ssh daemon (sshd) to deny all connections (by default) and to run this script to determine whether the connection should be allowed or not.

In /etc/hosts.deny add the line:

sshd: ALL

and in /etc/hosts.allow add the line

sshd: ALL: aclexec /usr/local/bin/sshfilter.sh %a

Testing

Obviously, you want to test that this is working before you are accidentally logged out of your system. On the terminal, I can do a test with the 8.8.8.8 which I happen to know is from the US, and in my case should be DENIED access. So:

/usr/local/bin/sshfilter.sh 8.8.8.8

The script will not return anything visible, however in /var/log/messages I have the result:

Jun 26 17:02:37 pi root: DENY sshd connection from 8.8.8.8 (US)

Updating GeoIP

To make sure you are up-to-date with your GeoIP (free) country database, you will need to write another script that you can run as a monthly root cron job.

Please note that if you just installed the GeoIP database, or you have never manually updated it (it does not auto-update), then you should run the following script manually after installing it! The default database that gets installed is several years old and very inaccurate.

#!/bin/bashcd /tmp
wget -q https://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
if [ -f GeoIP.dat.gz ]
then
gzip -d GeoIP.dat.gz
rm -f /usr/share/GeoIP/GeoIP.dat
mv -f GeoIP.dat /usr/share/GeoIP/GeoIP.dat
else
echo "The GeoIP library could not be downloaded and updated"
fi

Sources:

  1. Initial Server Setup with Ubuntu 16.04
  2. Vim Basics Tutorial
  3. How To Protect SSH with Fail2Ban on Ubuntu 14.04
  4. Limit your SSH logins using GeoIP

--

--