Make your Ubuntu Server VPS more secure against unauthorized access

When creating a new VPS you need to make sure that you secure your server from the scary internet. The only one that should be able to administrate the server should be you and only you!

There are few (simple) steps involved in how you can secure your server and have a good sleep at night, without fearing someone might hack your server.

Prerequisites

You should have an existing or freshly installed server running Ubuntu 20.08 VPS available and able to log in.

If you don’t know how you can create your own server, check out my blog post Create your first VPS on Vultr to deploy your very first VPS using Vultr*.

Vultr is just my choice for hosting servers on the internet, but you can use other providers such as Linode or DigitalOcean* too. You can’t be wrong choosing any of the mentioned providers.

Disclaimer: links containing a * at the end are affiliate links, which means if you signup using that link I would get a small commission and it helps me out!

Creating a new user to administrate the server

When you create a new VPS you usually get access to the root user with a password. For security reasons, we create a new user that will be used to administrate the server. It is good practice to only work with the least amount of privileges you need and use sudo when you need more permissions.

We will disable the possibility to log in as the root user using a password, but we will allow it to log in using public-key authentication.


To add a new user you can use the adduser command.

adduser phiilu

You will be asked a few questions including what password you want to use. Make sure the password is secure and in the best case random generated and stored in a password manager like 1Password or Lastpass.

Adding user `phiilu' ...
Adding new group `phiilu' (1000) ...
Adding new user `phiilu' (1000) with group `phiilu' ...
Creating home directory `/home/phiilu' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for phiilu
Enter the new value, or press ENTER for the default
	Full Name []: Florian
	Room Number []:
	Work Phone []:
	Home Phone []:
	Other []:
Is the information correct? [Y/n]

Now we have a new user that we can use to log in. If you are the root user you can just type

su phiilu

and you will be logged in as the user without prompted a password. You can also use log in with the login command.

login phiilu

Next, we want to give the user permission to administrate the server. Right now we have a new user, but the user does not have any permission to make any changes as a superuser.

You can confirm this when trying to update the packages:

sudo apt install
[sudo] password for phiilu:
phiilu is not in the sudoers file.  This incident will be reported.

As you can see, we need to add the new user to the sudoers file to be able to execute commands as the superuser.

We need to exit our current session with exit. You should now be logged in as the root user.

If you are root, you can type visudo to check the current sudoers file.

#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults        env_reset
Defaults        mail_badpass
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"

# Host alias specification

# User alias specification

# Cmnd alias specification

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

# See sudoers(5) for more information on "#include" directives:

#includedir /etc/sudoers.d

You can see that if a user is in the sudo group, he should be able to execute commands as root. There is also the admin group in my sudoers file which will allow the user in that group to execute some commands as root. We want to be able to do the same things the root user can do so we will choose the sudo group.

To add a user to a group you can use the usermod command using the options -a for add and -G for alternative groups followed by the group’s name and the user you want to modify.

usermod -a -G sudo phiilu

With id you can check the groups (and ids) of a user and can confirm that the user is now part of the sudo group.

id phiilu
uid=1000(phiilu) gid=1000(phiilu) groups=1000(phiilu),27(sudo)

Now that our user is in the sudo group, you can log in as that user and try to update the packages again and this time it should work!

Securing SSH

Now that we have a new user that can execute commands as root, we can go forward and harden our server even more by changing some settings for ssh.

Before doing that we want to set up public-key authentication for both our users root and the one we created before.

Using public key authentication

This is a recap of the “Skip passwords with public key authentication” section of my Use Visual Studio Code Remote to edit files on servers post.

Public key authentication is a secure way to connect to a server with SSH using a set of key pairs. The public key is saved on the server and the private key should be kept on your local machine. This is the key to unlock the server and must never be leaked or shared with someone else.

If you are using Windows you must have Git for Windows installed and use the following commands inside the Git Bash.

The following commands should be the same for Windows and macOS except you might have to end the commands on Windows with .exe.

Generating SSH Key

If you already have an SSH key that you might already use with git you can skip this step.

ssh-keygen -b 4096

Which results in the following output.

Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/flori/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /c/Users/flori/.ssh/id_rsa.
Your public key has been saved in /c/Users/flori/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx flori@Windows

You can set a passphrase for the key if you want, but then you have to enter a password for the log in which we want to avoid.

Copy the public key to the server

You can either manually paste the key to the server or you can use a program called ssh-copy-id which I prefer.

On macOS you have to install ssh-copy-id using brew on Windows it should be already present in the Git Bash.

brew install ssh-copy-id

To copy the key to the server you just have to log in to the server using the ssh-copy-id utility. Do this step twice for both users.

replace VPS_IP with the real IP of your VPS

ssh-copy-id root@VPS_IP
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/Users/florian/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@VPS_IP's password:

Number of key(s) added:        1

Now try logging into the machine, with:   "ssh 'root@VPS_IP'"
and check to make sure that only the key(s) you wanted were added.

Verifying the public key authentication

Now if you try to log in with ssh you should not be asked for a password.

If you are still asked for a password check if the permissions on the authorized_keys file on the server are correct:

ls -la ~/.ssh
drwx------ 2 root root 4096 Sep 18 17:23 .
drwx------ 7 root root 4096 Sep 21 14:48 ..
-rw------- 1 root root  416 Sep 18 17:23 authorized_keys

If your permissions are different you might have to change them:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

A more secure ssh config

To change the configuration for ssh open the /etc/ssh/sshd_config file with your favorite text editor or you can check out my blog post Use Visual Studio Code Remote to edit files on servers to use a GUI editor to modify your server files.

First of all, I want to change that the root user is not able to log in with a password, but should be able to use public-key authentication to sign in.

Find the line that says PermitRootLogin yes and change it to PermitRootLogin without-password.


If you want to only allow public-key authentication find the following configuration keys and change them:

PubkeyAuthentication yes
PasswordAuthentication no

Last but not least only allow users that you explicitly defined to log in to your server. Go to the end of the file and add the following line:

AllowUsers phiilu root dokku

Here you should add the usernames separated with space. In my case, I am allowing the users phiilu root and dokku to log in into my server using ssh.


After changing the configuration you must restart the sshd server.

sudo service sshd restart

Banning bad people with fail2ban

The Internet is a scary place and you need to be aware that everyone out there wants to take over your server. It is very important to make sure bad people or in this case, bad IP addresses get sent to jail and never will bother our server again.

This is why we will install fail2ban. Fail2ban is a small service that will run on your server and will read your log files and makes sure to ban IP addresses that are trying to log in to your server.

In this post, I will only show you how you configure fail2ban to only check the logs of the ssh service, but fail2ban can also be used to check other log files like nginx or mail.

Installation and configuration

Installation is very easy and only one command away.

sudo apt install fail2ban

That is it, we don’t need to make any additional configuration, fail2ban is now working and will make sure it bans bad log in attempts.

If you want to make changes to the config file, you must copy the original /etc/fail2ban/jail.conf file and create a /etc/fail2ban/jail.local file

cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Now you could make changes in jail.local and adjust ban time or retries, but I think the default is already pretty good.

Unban an IP address

Sometimes you may want to unban an IP address because you somehow locked yourself out, but you still got access using the VPS providers web console.

Luckily fail2ban lets us easily unban IP addresses using the included client:

sudo fail2ban-client set sshd unbanip IP

Here you can see an example of how I unbanned an IP.

root@dokku:~# fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	1114
|  `- File list:	/var/log/auth.log
`- Actions
   |- Currently banned:	2
   |- Total banned:	241
   `- Banned IP list:	199.38.121.35 49.88.112.112
root@dokku:~# fail2ban-client set sshd unbanip 199.38.121.35
1
root@dokku:~# fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	1114
|  `- File list:	/var/log/auth.log
`- Actions
   |- Currently banned:	1
   |- Total banned:	241
   `- Banned IP list:	49.88.112.112

Check how many IPs are already banned

Fail2ban comes with an easy to use client, where you can check the status of a specific service and see how many IPs are already banned

fail2ban-client status sshd

This is my server after being online for about a week. You can see and confirm that the Internet is a scary place and everyone wants to take over your server!

Status for the jail: sshd
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	663
|  `- File list:	/var/log/auth.log
`- Actions
   |- Currently banned:	1
   |- Total banned:	129
   `- Banned IP list:	49.88.112.76

Firewall

What is a Firewall? you may ask. A firewall defines rules in which services can connect to our server (incoming) or are allowed to talk to others on the Internet (outgoing). A server without a firewall will be allowed to do everything and has no restriction and can talk to everyone, but as we already learned we don’t want that because we can’t trust anyone on the Internet.

A lot of VPS providers are allowing you to configure a firewall within their dashboard, but for those of you that are not able to do that, you can still use a software firewall like ufw. UFW is a software firewall and we can secure our server with just four commands!

ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw enable

Now your server is only allowed to respond to requests that are talking on port 22 (ssh) and every other service is disabled.

ufw allow ssh is just an easy way to write ufw allow 22/tcp. Some services are known to run on a specific port so ufw already knows a lot of them!

If you have a webserver running too, you probably want to enable http and https. So just type ufw allow http and ufw allow https and your server is now allowed to respond to http and https requests!

For additional services you may run on your server you will eventually open more ports.

Conclusion

Hosting your server can be a bit scary, but if you followed the post closely you should be fine. Of coures, you should still log in into the server from time to tome and run updates with sudo apt update && sudo apt upgrade to make sure you get the latest security patches.

Let me know in the comments if this was useful and if I might have missed something that every server should configure to be more secure!


Want blog post updates? Sign up for my newsletter.