Securing your new Ubuntu server Link to heading
I’ve setup so many Ubuntu servers at this point that I’ve developed a methodology to make it secure and accessible, or at least… I think I have, I’m making notes here so I don’t forget.
Configuring SSH Link to heading
If you’ve ever watched your SSH logs before (sudo journalctl -fu ssh
) you might notice constant, methodical attempts to access your server; these are automated bots going through a list of IP addresses attempting popular usernames and passwords. They are testing for the low-hanging fruit of insecure servers, using the most common username and password combinations with the most common access methods, thousands of times per day, every day, for millions of IPs, all over the world. If they find a working combination, chances are the bot will automatically bring its new victim into the fold and have it start making the same intrusion attempts to other IPs, at this point your server is part of a botnet, and it’s probably going to be sold as part of a Denial of Service… uhh service, or something even more nefarious, and if you’re really unlucky you’ll be on the hook for any legal ramifications down the road…
Creating your own user Link to heading
If you’ve been provided with only a root
user then you should first create your own. Why? root
is by far the most common username of any system, changing it is an easy way to deter 99% of automated intrusion attempts. It also means you won’t have explicit root permissions, you’ll need to use sudo
to elevate your permissions to that of the root
user whenever you need to, this is standard practice in Linux and is similar to User Access Control in Windows; it helps avoid accidents, makes it easier to audit administrative changes and restricts elevated access behind your password.
- Create a new user:
sudo adduser <username>
. - Add your new user to the sudo group:
sudo adduser <username> sudo
. - Exit your root login:
exit
. - Re-connect with your new username:
ssh <username>@<host>
.
Switching to key-pair authentication Link to heading
Key pairs are used for a lot of things, mainly encryption adjacent, here it will be used in-place of a password.
Passwords are flawed.
“The password is dead” has been a popular concept since 2004. Every day, computers get faster at password cracking so, we make passwords longer and more complicated, then people have to remember them, but they can’t, so they use simpler passwords, or find some trick to make it easy to remember, which in-turn is used to crack them faster. Password security is a never-ending arms race of which humans have lost. A password wallet will go a long way to improving your situation, and making all your passwords incredibly long is good practice, but for SSH at least, key-pairs are the industry standard for an easier, convenient, and secure solution.
-
On your own computer (not the server), open a terminal and use the command:
ssh-keygen -t ed25519 -C <username>
This creates a key-pair using the ed25519 type, which is the best™ one.
-C
means comment, providing your username just makes it easier to differentiate between different key-pairs if you have lots of them; you could also add the hostname or IP address of your server to the comment if you wanted, i.e:
-C <username>@hostname
-
Add your new public key to the server:
ssh-copy-id -i ~/.ssh/id_ed25519.pub <username>@<server>
The path
~/.ssh/id_ed25519
should be the path to your new Public Key, thessh-keygen
command in the previous step will have provided this path in its output:
Your public key has been saved in /home/hugo/.ssh/id_ed25519.pub
.This handy command conducts the deceptively complex process of importing your public key into
~/.ssh/authorized_keys
on your server, creating the file if needed, and setting the correct permissions on the~/.ssh
directory, (otherwise SSH will refuse to use it) which should only be accessible by your own user and group, with permissions of 700. A daunting process for many people especially those just getting started, and one riddled with easy mistakes that have high consequences (being locked out of your server, permanently).Check the output of
ssh-copy-id
, if successful it will inform you on theNumber of key(s) added:
, you can now SSH into your server without needing a password, hurray! 🎉Be sure this works before proceeding because we’re about to disable password authentication entirely, simply run your SSH command, it shouldn’t prompt you for a password any more:
ssh <username>@server
-
We’re now going to restrict SSH access to members of a specific group called
ssh-users
, then disableroot
SSH access and password authentication.- Create the group:
sudo groupadd ssh-users
. - Add yourself to this group:
sudo adduser <username> ssh-users
. - Create a new config extension file:
sudo nano /etc/ssh/sshd_config.d/99-secure.conf
. - Paste in the following:
PermitRootLogin no AllowGroups ssh-users PasswordAuthentication no
- Save and close the file.
Most SSH configurations are fairly self-explanatory, here we’ve used:
PermitRootLogin
determines whether theroot
user can use SSH, if set tono
thenroot
is denied.AllowGroups
restricts SSH access to only the users within the specified group, we’ve specified the one groupssh-users
. Technically, adding this line negatesPermitRootLogin
entirely, even if it was still enabledAllowGroups
would still take precedence, but both are included here to ensure root is disabled even if you opt to not useAllowGroups
.PasswordAuthentication
is used to control whether a password can be used for SSH authorisation, we’ve disabled it.
- Reload the SSH service configuration:
sudo systemctl reload ssh
. - Test you can still connect to the SSH server, I recommend opening a new terminal and starting a new SSH connection without closing your current one, otherwise if it doesn’t work you’ll be locked out with no way to fix it! You should also try connecting with the root account, to confirm it no-longer works.
- Assuming all that works, congratulations SSH is now a little more secure! Hurray! 🎉 🎊
- Create the group:
Some useful tips to keep in mind using SSH in future: Link to heading
- Your private keys are saved inside of
~/.ssh
on your computer, you should keep these safe, consider making a backup somewhere so, if your PC breaks or you buy a new one you can restore this directory and retain access. - If you do end up losing your private key, depending on your type of server you might still be able to gain access through a couple different methods:
- If you’re renting a VPS (Virtual Private Server), some providers like Hetzner or Digital Ocean provide a virtual console where you can login using a password still.
- If you’re using a local server running on a computer at home, you can always connect a monitor and keyboard and login to the local terminal with your password that way.
- If you’re using a Raspberry Pi, as well as connecting a monitor you can also connect a serial console using UART on the IO pins, but you’ll need a USB-UART adapter (or another Raspberry Pi and some wires) to try this.
- Of course, with all the above being considered you should take measures to protect access to any virtual console and the physical security of your server, use multifactor authentication where available and utilise a military-grade fire-proof reinforced safe with anti-lock-picking measures and 24/7 security detail where appropriate.
Configuring the firewall with UFW Link to heading
UFW stands for Uncomplicated Firewall, or at least that’s what the developers claim. It handles all the complex IPTables commands and provides a really simple interface to allow or deny ports and IP Addresses, as well as a secure default rule set once enabled.
One fancy feature of UFW is that applications can bundle rule set templates with their package, making it easy to allow multiple ports without having to check the documentation specific to that program, for example to allow all ports relevant to SSH we can do sudo ufw allow OpenSSH
. You can see the full list of application templates available by running sudo ufw app list
:
$ sudo ufw app list
Available applications:
Bind9
CUPS
Nginx Full
Nginx HTTP
Nginx HTTPS
OpenSSH
Postfix
Postfix SMTPS
Postfix Submission
Samba
If your application isn’t listed, or you want a custom configuration, you can allow specific ports by doing sudo ufw allow 22
. For comparison, the iptables
command to do the same thing would be iptables -I INPUT -p tcp --dport 22 -j ACCEPT
, but this also assumes you’ve configured additional rules to drop incoming connections that aren’t explicitly allowed, rules that UFW will configure for us automatically when we enable it. With UFW we can also make use of automatic rate-limiting features, it will temporarily block connections from IPs are that spamming your server, this is useful for example to block repeated login attempts to your SSH port by malicious actors. To enable automatic rate-limiting of a port we use the limit
command instead of allow
, i.e. sudo ufw limit OpenSSH
.
-
Allow any applications and ports you need, such as SSH, web-servers etc:
# Opens ports required for SSH access with automatic rate-limiting. sudo ufw limit OpenSSH # Opens ports required for the NGINX web-server (you might need this if you're hosting a website etc.) sudo ufw allow "Nginx Full" # Opens just port 3389 (Used for RDP access) sudo ufw allow 3389
The above are just examples, if you don’t have any specific applications running that need open ports, then the only rule you need to add right now is OpenSSH.
-
Enable the firewall:
sudo ufw enable
-
Test you can still SSH into the server. Open a new terminal and start a new SSH connection, don’t close your current one yet, or you’ll lose access permanently if the firewall is blocking new connections.
-
If everything is working as expected then you have configured your firewall successfully! Hurray! 🎉 🎊 🚀
Automatic updates Link to heading
To ensure your server is always up-to-date, especially for the latest security fixes, you should configure and enable Unattended Upgrades which handles the automatic download and installation of updated APT packages. APT manages the installation of most software in Ubuntu and Debian as well as important kernel packages.
-
Install Unattended Upgrades:
sudo apt install unattended-upgrades
-
Check out the configuration file, don’t edit it yet:
sudo cat /etc/apt/apt.conf.d/50unattended-upgrades
-
This is a pretty long file and with no syntax highlighting it can look a bit daunting at first, but each section includes helpful comments describing what they do, we’ll go through the relevant sections that will be included and then summarise with a total file at the end. Rather than edit this file, we’ll create a new configuration snippet, which will override the default settings.
-
First area to note is the
Unattended-Upgrade::Allowed-Origins
line, this section lists which source packages will be automatically upgraded from:Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}"; "${distro_id}:${distro_codename}-security"; // Extended Security Maintenance; doesn't necessarily exist for // every release and this system may not have it installed, but if // available, the policy for updates is such that unattended-upgrades // should also install from here by default. "${distro_id}ESMApps:${distro_codename}-apps-security"; "${distro_id}ESM:${distro_codename}-infra-security"; // "${distro_id}:${distro_codename}-updates"; // "${distro_id}:${distro_codename}-proposed"; // "${distro_id}:${distro_codename}-backports"; };
By default, only security updates are configured, specified by these lines:
"${distro_id}:${distro_codename}";
and"${distro_id}:${distro_codename}-security";
Next are lines enabling Extended Security Maintenance (ESM), this is a paid feature offered by Canonical, if you have the feature enabled then Unattended Upgrades will automatically install these too by default. If your serve is for personal use then you can register and get Pro features for free.
Next isupdates
,proposed
andbackports
, we only needupdates
and will include it in our configuration file at the end.proposed
are beta packages not yet fully tested andbackports
are updates included in the next version of Ubuntu, these might cause conflicts, so it’s best to leave them disabled. -
AutoFixInterruptedDpkg
enables automatically resolving an interrupted update process. This is a good idea to enable - if your server is interrupted, possibly due to a power-outage or system crash, it can continue installing updates once normal process is restored, otherwise it will hang until you resolve it manually:Unattended-Upgrade::AutoFixInterruptedDpkg "true";
-
Remove-Unused-Dependencies
is useful for removing unused packages left-over after a package that uses them has been uninstalled or simply no-longer requires it:Unattended-Upgrade::Remove-Unused-Dependencies "true";
-
Automatic-Reboot
when required this will schedule a reboot either at the specified time, or immediately depending on how you configure the next few settings.Unattended-Upgrade::Automatic-Reboot "true";
-
Automatic-Reboot-WithUsers
by default reboots won’t happen if users are logged in, this setting forces a reboot anyway. This is useful if you’re not scheduling reboots, to prevent immediately interrupting someone’s work.Unattended-Upgrade::Automatic-Reboot-WithUsers "true";
-
Automatic-Reboot-Time
specifies a time of day to do reboots instead of immediately. Even when configured, reboots will only be scheduled if required by an update.Unattended-Upgrade::Automatic-Reboot-Time "02:00";
-
These are all the settings we’re going to configure, be sure to read through the rest of the template file and add in anything else you want to include, our final config file looks like this:
Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}"; "${distro_id}:${distro_codename}-security"; "${distro_id}ESMApps:${distro_codename}-apps-security"; "${distro_id}ESM:${distro_codename}-infra-security"; "${distro_id}:${distro_codename}-updates"; }; Unattended-Upgrade::AutoFixInterruptedDpkg "true"; Unattended-Upgrade::Remove-Unused-Dependencies "true"; Unattended-Upgrade::Automatic-Reboot "true"; Unattended-Upgrade::Automatic-Reboot-WithUsers "true"; Unattended-Upgrade::Automatic-Reboot-Time "02:00";
-
-
Create your new config file and paste in the above contents:
sudo nano /etc/apt/apt.conf.d/52unattended-upgrades
If you check the contents of the configuration directoryls -al /etc/apt/apt.conf.d/
you might notice all the files are prepended with numbers:$ ls -al /etc/apt/apt.conf.d/ total 68 drwxr-xr-x 2 root root 4096 Jun 19 00:16 . drwxr-xr-x 8 root root 4096 Jun 12 13:35 .. -rw-r--r-- 1 root root 630 Apr 8 2022 01autoremove -rw-r--r-- 1 root root 92 Apr 8 2022 01-vendor-ubuntu -rw-r--r-- 1 root root 129 Jan 20 18:50 10periodic -rw-r--r-- 1 root root 108 Jan 20 18:50 15update-stamp -rw-r--r-- 1 root root 311 Apr 6 13:48 20apt-esm-hook.conf -rw-r--r-- 1 root root 85 Jan 20 18:50 20archive -rw-r--r-- 1 root root 80 Feb 19 2021 20auto-upgrades -rw-r--r-- 1 root root 1040 Feb 17 2022 20packagekit -rw-r--r-- 1 root root 625 Dec 8 2021 50command-not-found -rw-r--r-- 1 root root 6133 Jun 13 19:31 50unattended-upgrades -rw-r--r-- 1 root root 182 Feb 20 2022 70debconf -rw-r--r-- 1 root root 50 Jun 12 13:35 99hetzner -rw-r--r-- 1 root root 338 May 16 2022 99needrestart -rw-r--r-- 1 root root 305 Jan 20 18:50 99update-notifier
Configuration files like this are loaded in-order alphanumerically, the config file that comes higher in the alphanumeric rating will be loaded in first, and its settings will be overwritten by the config file that loads in next. By starting the name of our config with
52
we ensure our settings will load in after the default settings in50unattended-upgrades
overwriting them with the settings we’ve specified. -
Finally, restart
unattended-upgrades
and enable it to run on start-up:
sudo systemctl restart unattended-upgrades
sudo systemctl enable unattended-upgrades
Congratulations! Your server is now Secure!™Yaaaay! 🎉 🎊 🚀 🚀*
Further reading Link to heading
- sudo: https://www.redhat.com/sysadmin/sudo
- Key-Pairs: https://help.ubuntu.com/community/SSH/OpenSSH/Keys
- “The password is dead”: https://en.wikipedia.org/wiki/Password#%22The_password_is_dead%22
- List of password managers: https://en.wikipedia.org/wiki/List_of_password_managers
- Unattended upgrades README: https://github.com/mvo5/unattended-upgrades
*This article is not security advice, your data is at risk, always consult a licensed security practitioner before storing data online. This article and its contents are provided with absolutely no warranty, use at your own risk.