Howto secure your personal Linux VPS
This is the third part of a small "Linux VPS howto" series and it talks about securing the default linux installation.
Articles of this series
2021-08-04 Howto setup your personal XMPP server
2021-07-01 Howto setup your personal CalDAV/CardDAV server
2021-02-12 Howto proxy your self-hosted services using web server
2021-01-08 Howto setup and secure web server
2020-12-30 Services you can selfhost on you personal Linux VPS
2020-09-22 Howto secure your personal Linux VPS
2020-08-21 Howto setup your personal Linux VPS
2020-07-20 Why setup your personal Linux VPS
So our VPS is up and running. In most cases, what you have now is a minimal installation of a distribution you selected. This is good, because we want to have installed only those packages we really need and nothing more. You can check what is installed/running and remove some additional packages if possible. Probability of an exploitable vulnerability raise with every single installed package, especially if that application can be reached remotely. If I will show any real examples in this article, then consider it to be for Debian/Ubuntu as these two together are prevalent option amongs personal VPS. (I failed to find the article to confirm that claim though).
Important thing in the beginning is to update all installed packages to up to date state by using distro specific (package manager specific) set of commands. For example, in case of Debian it will be: 'apt update && apt upgrade && apt dist-upgrade && apt autoremove && apt clean'. Be aware that hardening steps in this article are valid for VPS where all users are trusted users. In case of multi user machine, where the users can not be trusted, you should apply much more rules and restriction than the ones below and those are not covered in this article.
So you are logged in as root. Change your password to something different than the generated one. Then create another not privileged user with the name of your choice. For example 'john'. You can setup sudo for this user, or stick to classic 'su', it doesn't matter in this case. Now open /etc/ssh/sshd_config, we are going to harden ssh access. Make these changes:
This will change the amount of time in seconds you have to finish login. Default is 2 minutes. Nobody need that much.
Disable ability to login directly as root. It is best practice to not have your privileged user accessible via SSH.
Enable ssh server strict mode. When enabled, system will apply more checks and controls.
Enable additional restrictions during unauthenticated incoming network traffic.
Automatically logout after specified duration of inactivity in seconds. It is a good practice to do so in case you forget to logout manually. But this setting will surely get on your nerves later :)
Disable ability to run X applications remotely via SSH. In most cases you won't need this on your personal VPS.
Disable ability to authenticate using password. Best practice is to login via public keys, as this will effectively disable brute force dictionary attacks on users passwords.
Enable authentication using public keys. When enabled and password login disabled, you need to generate set of private and public keys for every user that will login using ssh. There are plenty of howtos on the internet. for example this one.
Alow only your newly created user(s) to log in using SSH
Change port on which SSH server listens to some high port. There is a controversy over this setting. If you leave your SSH port on 22, you will be (in most cases) target of significant amount of automated dictionary attacks and it will spam your logs and monitoring outputs. If your SSH is setup correctly those attacks are not harmful, but they will trigger many false positive monitoring alerts and make your logcheck outputs harder to read. This all can be solved by moving SSH to not standard high port. Automated attacks don't scan your machine for SSH, because those scripts don't want to waste time as they need to go through huge amount of IP addresses. They just go for port 22. Problem is, that in possix compliant systems, all ports below 1024 are privileged ports and require to be root to start listening on it, so you can be sure, that service listening on 22 is really your SSH server. If you move it for example to 48277, any local user can spawn daemon listening on that port trying to act as SSH server and potentially read your passwords. But for this, you will have to use passwords to login, also someone will already must have access to your system and also be able to kill already running SSH server, that runs under the root account. I personally always go for high port, because I like to have my monitoring set sensitively and I read all reports coming to my email. With SSH on port 22 I would either get huge amount of false positives, or I would have to restrict SSH monitoring. For more information why not to reconfigure your SSH server to high port, check this link
Default firewall in linux for many years was netfilter/iptables. Currently there is also a successor available called nftables. Both of them have similar syntax and it is hard for an unfamiliar person to master it. So for the sake of this article I will use UFW, which is shortcut for "uncomplicated firewall" and it uses iptables under the hood. Basically, it is an app that simplifies iptable usage for you. First you must install it using distro package manager. Then we will apply default set of rules by executing these commands:
ufw default deny incoming
Deny all incoming traffic by default. It is best practice to deny everything that is coming in and allow only specific rules manually.
ufw default allow outgoing
Make no restrictions for outgoing traffic, because by default we do not fear what is going out. That is because outgoing traffic is initiated either by a service we installed and trust, or by an user we allowed and in case of our setup most likely also impersonate.
ufw allow 48277
Allow incoming traffic on port 48277 we chose as an example for custom high SSH port. If you've chosen different port, or went for default 22, then put here your selected port. This rule is crucial, otherwise we would cut ourselves out from SSH and our remote connection may be lost.
ufw allow 80
Allow incoming traffic on port 80. Allow this only if you plan to host a web server, or use a web server as a proxy for some other services. Port 80 should be enabled only for backward compatibility with browsers and other clients. Best practice is to serve everything over HTTPS, which is port 443. So if someone requests data using HTTP (port 80), web server should upgrade (redirect) communication to HTTPS at first and then continue to serve whatever client requested. We will take care of it in different article where we will cover web server setup.
ufw allow 443
Allow incoming traffic on port 443. Allow this only if you plan to host web server.
Firewall is by default disabled after the installation. This will enable it and it will automatically apply all rules we have defined so far. Definition of rules is persistent, so it will survive both firewall restart and OS restart.
ufw status verbose
Print out current firewall status with all applied rules.
You can do much more with the firewall and it is important to at least know what you are able to do with it. There are many articles talking about ufw capabilities, or you can also use ufw man pages. Definitely check some docs out, because sooner or later you would like to enable your new service, or remove some automatically applied IP block created by fail2ban, which we will cover later in this article.
This is another highly debated step. World already depleted all IPV4 segments, but IPV4 is still dominant. Unfortunatelly, there is much higher rate of suspicious activities on IPV6 than IPV4 and the main rule when trying to secure your VPS is: disable/delete/remove everything you don't need. Therefore I by default always turn IPV6 off if it is not needed. To do so, you need to modify /etc/sysctl.conf file like so:
net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 net.ipv6.conf.lo.disable_ipv6 = 1 net.ipv6.conf.eth0.disable_ipv6 = 1 net.ipv6.conf.eth1.disable_ipv6 = 1 net.ipv6.conf.ppp0.disable_ipv6 = 1 net.ipv6.conf.tun0.disable_ipv6 = 1
As you can see, we are disabling ipv6 globally and then also for every network interface present on the machine. Double check what interfaces you have present and modify lines above according to your findings. To turn off IPV6 for UFW, you need to set 'IPV6=no' in /etc/default/ufw. You may need to manually turn it off for other services you will host. For example in case of postfix mail server, you would have to set 'inet_protocols = ipv4' in /etc/postfix/main.cf
Fail2Ban is an intrusion detection and prevention software that protects your VPS from brute-force attacks. It monitors log files and takes actions according to findings and based on how it is configured. It covers automatically many log formats and supports many standard services out there. It mainly consists of actions, filters and jails. Actions define what should be done when something happens. Filters define how to detect that something happened. Jails put actions and filters together with some additional configurations and settings. For now, we have no service installed and exposed to public besides SSH and networking, so fail2ban should target these two by detecting port knocking and ssh connection failures. There are plenty of howtos for most types of jails. For example one for port scan is here and one for ssh is here. Don't forget to set the correct sender email address in /etc/fail2ban/jail.d/jail.local
Logcheck is a simple utility that can read many log formats and summarize them into email. Destination email can be configured in /etc/logcheck/logcheck.conf. Log files that logcheck will add to final summarization can be defined in /etc/logcheck/logcheck.logfiles. You can define what in those log files should be ignored and not added to summary. Make sure, that all log files you want to have scanned are readable by logcheck. Logcheck is not executed automatically, you should take care of it by scheduling it using cron.
Fail2ban, logcheck and also your future monitoring utilities will depend on ability to send emails from your VPS. You don't have to setup whole email server for that, the only thing you need is ability to send mail. For the sake of this series, I will use postfix, because in later articles I will explain how to setup self-hosted email server using postfix. There are many alternatives like sendmail, exim, qmail and others. You can check the differences and make a personal decision. One such article is here
Debian for example, has an automatic, after install, curses based setup, where you will only select to use an 'Internet site' configuration and then define your domain as the system mail name. But everything can be done manually too using two main postfix configuration files: /etc/postfix/main.cf and /etc/postfix/master.cf. It is also good practice to set your mail domain in /etc/mailname file if your distro supports it.
Automatic package updates
Many distros have the ability to automate package updating. Regularly updated OS is very important when it comes to security, so be sure you are doing so manually or by setting up an automated procedure. In case of debian based distros, it will be package 'unattended-upgrades' with a simple configuration using config files located in /etc/apt/
Automatic package checksum control
Some distributions will allow you to check checksum validity of installed packages. Thanks to such mechanism you are able to detect any corrupted and altered files. In case of debian, the package is called debsums. It can even check if package log file is not smaller compared to last check, which can point out to manual log file modification. Be sure to add the debsum log file amongs files that are summarized by logcheck.
Security-Enhanced Linux is a kernel module that can define access controls for the users, applications, processes and files. So for example if you have a database that should run under specific user and accessing only data in specified directory, you can define set of rules that will constrict the running process to obey those rules. It can be configured to run in enforcing or permissive mode. In latter case it will only log detected issues. SELinux uses users and groups in its rules, but these are not your OS users and groups, this is confusing for many and you should remember that. SELinux is a huge topic and is covered in detail in official documentations of every big distro out there.
If you apply everything above, your VPS is secured and you are ready to deploy some useful services. In [next article](https://mizik.eu/blog/what-service-you-can-host-on-your-personal-linux-vps/) I will talk about what interesting services can be self hosted and why.