Enabling a firewall and securing SSH are two essential first steps when setting up a new server. In this tutorial I'll take you through the process I go through for every server I spin-up regardless of its use. I'll be using Ubuntu 18.04LTS on Linode, but this should apply to almost any recent Debian based distribution. If you're using a different VPS host or are on your own hardware there may be differences regarding root access and packages installed by default, I'll try to make note of this when needed. So, let's get to it.

SSH Keys

On your local machine, move into the ~/.ssh directory and generate an SSH key pair for the new server. If you haven't used SSH on this machine, it may be necessary to create the directory.

mkdir ~/.ssh  # if directory not present
cd ~/.ssh
ssh-keygen -t ed2559

Having filled in the form you should see something like below. You can name your key anything you like but I've found the format of id_ed25519_example.com to work best for me as it makes keeping track of multiple keys very easy. A password is not required but I highly recommend creating a strong, unique password for each key pair you generate.

Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/user/.ssh/id_ed25519): id_ed25519_<domain-name-or-ip>
Enter passphrase (empty for no passphrase): <highly recommended>
Enter same passphrase again: <highly recommended>
Your identification has been saved in id_ed25519_<domain-name-or-ip>.
Your public key has been saved in id_ed25519_<domain-name-or-ip>.pub.
The key fingerprint is:
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx user@hostname
The key's randomart image is:
+--[ED25519 256]--+
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |

Now you can add the newly created key to ssh-agent, this should work on most popular distributions as the agent should already be running. If you created a password in the previous step you will be prompted for it now.

ssh-add ~/.ssh/id_ed25519_<domain-name-or-ip>

You should get confirmation that the key was added, if not try the below and then re-run the command above.

eval `ssh-agent`

Server Firewall

Now from your local machine, SSH into your server using either the IP address assigned to you by your host or domain name if you've already set one up. Depending on your host you should have had to provide a root password or been asked to create a non-root user during the VPS setup. If the latter, substitute root with that user and use sudo as necessary from here on out.

ssh root@<domain-name-or-ip>

Now that we're on the server let's go through the initial firewall configuration and enable it. We're going to be using Uncomplicated Firewall or UFW as it's easy to use and installed by default on Ubuntu.

First, lets make sure UFW has the default application profile for OpenSSH available.

ufw app list

You should at least see OpenSSH listed under available applications.

Available applications:

Now, add OpenSSH to the allowed applications with the below command.

ufw allow OpenSSH

This will, once UFW is enabled, allow traffic on the default port of 22 for SSH, but we don't want to stop here because later we will be changing the default port used for SSH for more security. Now is the time to choose which port you want to use, anything between say 5000 and 65535 is typical but it's a good idea to check here and pick a port not used by other services. So, let's create a new rule and add it to the allowed applications list. We'll also limit the attempted connections allowed to this port with the default limit of 6 tries in 30 seconds, again for a bit more security.

ufw allow <chosen-port>/tcp
ufw limit <chosen-port>/tcp
ufw reload

The only thing left to do now is enable the firewall.

ufw enable

Let's check our work now and make sure all is as it should be.

ufw status

You should get output similar to this:

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW IN    Anywhere
<chosen-port>/tcp          LIMIT IN    Anywhere
OpenSSH (v6)               ALLOW IN    Anywhere (v6)
<chosen-port>/tcp (v6)     LIMIT IN    Anywhere (v6)

That's it for the firewall for now, later we'll come back and remove the default OpenSSH rule once we've changed the port in the SSH configuration file.

Add a New User

If you've been required to create a regular user by your host already then you can skip this step. If your only access right now is via root, you'll want to create a regular user for yourself now.

Run the following command with a user name of your choosing. A good password is strongly recommended but the default/blank options for all other questions are fine, it's up to you.

adduser <new-user-name>

Now we need to make sure this new user has the sudo command available to them. We can do this by adding them to the sudo group.

usermod -aG sudo <new-user-name>

You can confirm that this worked using the id command. You should see ‘sudo’ listed in the groups section.

id <new-user-name>

Next, in a separate terminal without logging out of the first terminal, SSH into your server using the new user to make sure all is well. We'll be removing root access through SSH next so it is very important to make sure you have full access with sudo working for the new account before moving forward. We're keeping the first terminal with root login open for safety so we can correct any problems if they arise.

# On your local machine, in a second terminal
ssh <new-user-name>@<domain-name-or-ip>

You should now have two terminals open, one logged in with root and another with your new user account. Check that you can successfully run sudo commands with your new user account. A simple sudo ls -al should be all you need to confirm. If you get an error, check back through the sections above and make sure nothing was skipped. If all is good, it's time to move on to editing the SSH configuration.

Disable Root Access & Change SSH Port

Now we need to edit a few options in the SSH configuration. Remember, keep that original terminal open with root access as a backup if something goes wonky but do all of this as the new user in the second terminal.

sudo nano /etc/ssh/sshd_config

We need to edit two lines and add a new one. Near the top of the file you should see the port entry, it should be commented out and set to the default 22. Uncomment this line and change 22 to your choice of port from earlier. Create a new line just below it and add the ‘AllowUsers’ entry. Set this to your new user. Last, move down in the file and look for the ‘PermitRootLogin’ entry. Uncomment it if necessary and make sure it is set to ‘no’. The three entries should look like below.

Port <chosen-port>
AllowUsers <new-user-name>
PermitRootLogin no

To activate the new configuration we need to restart the SSH service. A nice feature of SSH is that even though we are restarting the service with new configuration, it won't close our open connections.

sudo systemctl restart ssh

Now, in the second terminal using the new user, exit out of the SSH session and try out the new settings. Note the -p <chosen-port> being added to the ssh command, you will need to use this from here on and keep this in mind for other commands you might need in the future such as scp, rsync, etc.

ssh -p <chosen-port> <new-user-name>@<domain-name-or-ip>

If all went well you should be back on the server using your new user. You will no longer be able to SSH into the server using the root account and only be able to connect through the new port you chose. The last thing we need to do is remove the old UFW rules for the default port 22.

First, get the status of UFW again but this time add numbered to the end of the command to make things easier.

sudo ufw status numbered

Should get output similar to this:

Status: active

     To                         Action      From
     --                         ------      ----
[ 1] OpenSSH                    ALLOW IN    Anywhere
[ 2] <chosen-port>/tcp          LIMIT IN    Anywhere
[ 3] OpenSSH (v6)               ALLOW IN    Anywhere (v6)
[ 4] <chosen-port>/tcp (v6)     LIMIT IN    Anywhere (v6)

You need to delete both entries for OpenSSH, just issue the ‘delete’ command with the corresponding number for each entry.

sudo ufw delete 1
sudo ufw delete 3

Now, reload UFW and check its status one more time.

sudo ufw reload
sudo ufw status

Should get output similar to this:

Status: active

To                         Action      From
--                         ------      ----
<chosen-port>/tcp          LIMIT IN    Anywhere
<chosen-port>/tcp (v6)     LIMIT IN    Anywhere (v6)

Disable Password Authentication

Our last step is to copy the public key we generated earlier on our local machine over to the server, check that it works, then disable password authentication for SSH.

On your local machine, terminate the SSH session of your new user in the second terminal and issue the command to copy over your public key. You will be asked for the password of your new user once more and should get confirmation that the key was successfully copied over.

cd ~/.ssh
ssh-copy-id -i id_ed25519_<domain-name-or-ip>.pub -p <chosen-port> <new-user-name>@<domain-name-or-ip>

Now, start a new SSH session. You should be able to log in now without needing a password.

ssh -p <newport> <newuser>@<server>

Once back on the server we need to edit the SSH configuration file one more time. This time to disable password authentication.

sudo nano /etc/ssh/sshd_config

Find the following line, uncomment it if necessary, and make sure it's set to ‘no’.

PasswordAuthentication no

Finally, restart the SSH service and check that you can still connect one final time. If everything works you can now update the server and close the original root terminal. I generally like to reboot the system and run a quick apt autoremove and apt autoclean after it's initial update.

sudo systemctl restart ssh
sudo apt update && sudo apt dist-upgrade
sudo reboot
# SSH back into the server once it's up and running again then:
sudo apt autoremove && sudo apt autoclean

Hopefully I haven't missed anything and this was helpful.

Feel free to send me an email with any questions, comments, or requests and if you're feeling generous, help keep me caffeinated.