My Journey to Self-Hosting: Part 2 - Securing the Server

In the last article, I se tup Infomaniak's VPS Lite server and logged in via SSH. The server is up and running, but a brand-new sever is like an open front door, anyone can try to enter. In this second part, I'll walk you through, step by step, how I secure the system.
Updates
The first thing I do after logging in is make sure all packages are up to date:
sudo apt update
sudo apt upgrade
apt update updates the list of available packages and apt upgrade installs the latest versions.
It is important to pay attention to the messages in the terminal, as shown in this example:
Diagnostics:
The currently running kernel version is not the expected kernel version
6.8.0-124-generic.
Restarting the system to load the new kernel will not be handled automatically,
so you should consider rebooting.
If such a kernel update is pending (an update to the core of the operating system), the server must be restarted:
sudo reboot
Automatic Security Updates
To avoid having to install important updates manually all the time, I'll set up automatic security updates:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
dpkg-reconfigure reopens the unattended-upgrades configuration. Using --priority=low displays all configuration prompts, including the simple ones, so I can explicitly confirm each step. When prompted, I select Yes, after which security updates are automatically installed in the background.
Disable Password Login
By default, SSH login with a password is enabled. This means bots can constantly try to log in using different passwords. To see how many failed attempts there have been so far, you can check the server's log files. On Ubuntu, there are two ways to do this:
# Option 1
sudo grep "Failed password" /var/log/auth.log | wc -l
# Option 2
sudo journalctl -u ssh --no-pager | grep "Failed password" | wc -l
Since I have already set up an SSH key, I am completely disabling password-based login to prevent such attempts in the future:
sudo nano /etc/ssh/sshd_config
this command opens the sshd_config configuration file in the selected text editor (nano) so that I can edit it. Many lines in the file are commented out with #, which means they are ignored and the default value is used. To change a setting, you must remove the # and adjust the value.
So I search the file for the line PasswordAuthentication, remove the #, and set it to no. It then looks like this:
PasswordAuthentication no
Then just save and confirm (Ctrl+X → Y → Enter on a Mac).
Since the changes to the configuration file don't take effect until the service is restarted, I'll do that right away:
sudo systemctl restart ssh
Firewall (UFW)
The next step is to set up a firewall. UFW (Uncomplicated Firewall) is a tool in Ubuntu that controls network traffic, meaning which connections are allowed and which are blocked.
Without a firewall, all of the server's ports are open and accessible. With UFW, you can explicitly specify that only SSH is allowed, everything else is blocked:
sudo ufw allow ssh
sudo ufw enable
Important: Be sure to run sudo ufw allow ssh first, otherwise you'll lock yourself out 😉
With this command you can check whether UFW is running correctly:
sudo ufw status
Fail2ban
Even when passwords are disabled, bots continue to hammer away at my server's digital door. The "Fail2ban" program monitors log files for failed login attempts and blocks the attackers' IP addresses.
Use this command to install it:
sudo apt install fail2ban
With this command you can check whether Fail2ban has been enabled (use q to exit the view):
sudo systemctl status fail2ban
If it is not yet enabled, enable it as follows:
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
By default Fail2ban protects SSH access on port 22 immediately after installation using the default settings. However, for optimal protection, I should make additional adjustments:
Create a custom configuration file
Adjust the most important settings
Check SSH protection or enable it explicitly
The main configuration file /etc/fail2ban/jail.conf is overwritten during updates, so you should never modify it. Instead, create your own file named jail.local. This file will then take precedence and remain intact during updates.
First I'll copy the original configuration file so I don't have to start my own file from scratch:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Now I'll open the file and change the most iportant values:
sudo nano /etc/fail2ban/jail.local
ignoreip
ignoreip = 127.0.0.1/8 ::1 MY_IP
ignoreip is commented out by default. I remove the # and add my IP address at the end, which prevents me from ever accidentally locking myself out. If you don't know your IP address, you can easily find it by entering the command curl ifconfig.me in a separate terminal.
bantime
bantime = 1d
Since modern attack bots are patient, they wait briefly and then continue indefinitely. A one day block immediately stops the attack and also reduces the number of firewall operations, since each block and unblock rewrites the firewall rules.
findtime
findtime = 10m
The time window during which failed attempts are counted. 10 minutes is a good average, short enough to detect actual attacks and long enough not to prenalize accidental typos.
maxretry
maxretry = 3
A legimitate user might make a typo once or twice, if there are more than three, that suggest an attacker.
sshd
I need to make the final change in the sshd section of the file. I add the line enable = true right above the line port = ssh. The finished block then looks like this:
enable = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
Then save and confirm (Ctrl+X → Y → Enter on a Mac) and restart Fail2ban:
sudo systemctl restart fail2ban
With this last command, I can check whether Fail2ban is running correctly and monitoring SSH:
sudo fail2ban-client status sshd
Disable Root Login
The root user exists on every Linux system and has unrestricted privileges across the entire system. Attackers are therefore already familiar with this username and often attempt to log in directly as root.
Although Infomaniak blocks direct logins as the root user by default for security reasons, I still explicitly disable this option in the server's SSH configuration file:
sudo nano /etc/ssh/sshd_config
From:
#PermitRootLogin prohibit-password
To:
PermitRootLogin no
Now restart SSH so that the change takes effect:
sudo systemctl restart ssh
What's next?
The server is now basically secure, though not yet perfect. There are additional steps, such as changing the SSH port, but I'll cover those at a later time.
In the next part, I'll install Docker, which will allow me to run applications in isolated containers.


