Deploying & Securing a VPS

Mar 24, 2022


Having a VPS may unlock numerous possibilities regarding the usage of online services for ordinary internautes. I believe anyone who is somewhat tech-savvy should get one, provided that she can afford it. This is a short guide that goes over the basic steps to quickly set up a VPS - either on OpenBSD or on Linux - and make it secure so that we can reliably run various services on it.

First of all, we need to choose a VPS hosting provider to host our server and make an account. I can personally recommend Vultr as I haven't had any problems using it, and it also provides a ready-made image to quickly deploy OpenBSD servers, in addition to Linux distributions and FreeBSD. I will also include images to provide assistance with the set-up process on Vultr. DigitalOcean and Linode are other popular providers.

After creating an account, we can provide the necessary payment info and deploy a new server with the preferred configuration.

The graphical Web interfaces are fairly straightforward and easy to navigate, but I will mention a few key points regarding the properties of the server.

  • For your first VPS, choose the cheapest plan available. This should give you a single core of shared vCPU, 512 or 1024 MB of RAM, some storage and bandwidth. You don't need a lot of resources for hosting simple services, and you can always upgrade later or use a snapshot to redeploy.

  • The server location should not matter a whole lot, but it might be a good idea to choose a location that is close to you, or to other potential users who might connect to the server.

  • As for the operating system, I would highly recommend OpenBSD if you just want a simple and secure system to work with and host your website as well as other services without hassle. It is very pleasant and a great choice to become familiar with how a UNIX system works.

  • If you want to use Linux or depend on software that runs on Linux, I would recommend that you go with Fedora if you need a fairly stable system with the latest software, or Rocky Linux which is downstream of Fedora and is considered to be even more stable. Debian and Ubuntu should also be mentioned as they are the most popular distributions.

  • Choose the latest OS version, unless you have a reason not to.

  • You may disable auto backups if you don't wish to pay extra for them. At your own risk, of course.

  • You may still enable IPv6 as an additional option.

  • Choose a logical hostname and a label.

Note: Make sure that the server size you choose is not for IPv6-only. IPv4 is the standard for now, and you will need it.

After creating the server with these settings, information about the server including the public IP address and the root password should be available in the user panel of our VPS provider. It might take a few minutes to finish running the initialization scripts, and we should be able to log in to the server after that.

In order to log in remotely and execute commands, we will be using ssh. OpenSSH should be pre-installed on most Linux, BSD and MacOS systems, and on more recent versions of Windows. We can log in to the new server as the superuser using the public IP (Entering the root password when prompted).

user@void: ~$ ssh root@<public-IP>

The server is now accessible. We can now do the following things to improve security.

  • Create a regular user on the system and disallow remote root logins (use su instead)

  • Create an SSH keypair and log in as the user with an authorized key.

  • Change the default SSH port in order to mitigate automated bruteforce attacks.

Note: Even though these attacks may not succeed, they will at least fill your firewall and sshd logs with hundreds of lines of bruteforce attempts from IPs in China. It is best to change the default SSH port just to have cleaner logs.

We start by creating a regular user with a home directory.

root@server: ~# useradd -m username
Note: The username here is "username", but this is for demonstration purposes. You should ideally set a username that is harder to guess.

We set a password for the new user.

root@server: ~# passwd username

We also add the user to the "wheel" group for su.

root@server: ~# usermod -G wheel username

We now go back to our client PC and create a keypair, preferably with the ed25519 algorithm. We may also add a comment to help identify it.

user@void: ~$ ssh-keygen -t ed25519 -C "My key"
Note: It is possible to change the name/location of the key when prompted, which will be helpful when creating multiple keys for different purposes and organizing them in a coherent manner. I will change it to .ssh/id_ed25519_0.
Note: It is beneficial to also set a password for the key when prompted. This provides an additional layer of security.

Losing the keyfile or the password to it will lock you out of the server, so it is important to make sure that they are safe.

We can add our key to .ssh/authorized_keys in the remote user's home directory manually, but ssh-copy-id provides an easier way to copy keys to a remote host.

user@void: ~$ ssh-copy-id -i ~/.ssh/id_ed25519_0 username@<public-IP>

We need to make a few changes in the /etc/sshd_config file on the server. Log in now as a regular user, and use su to gain root access.

user@void: ~$ ssh username@<public-IP> -i ~/.ssh/id_ed25519_0
username@server: ~$ su
root@server: ~# vi /etc/ssh/sshd_config

We will disallow remote root logins and disable all password logins/interactive keyboard logins. This leaves public key authentication as our only authentication method when logging in. We may also change the default SSH port now (to 44400 in this example). We find, uncomment or add the following lines, adjusting the settings as shown below.

Port 44400
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
Note: Depending on how your system was set up, there might be files inside the /etc/ssh/sshd_config.d/ directory overriding the settings we just defined in the /etc/ssh/sshd_config file. Make sure to check the contents of this directory and delete files/lines where necessary.

sshd needs to be restarted so that the changes take effect.

Note: Do not restart sshd yet if you have a firewall running. You might get locked out if the new port is not open. Temporarily disable the firewall, or check the firewall section of this guide first.
root@server: ~# rcctl restart sshd
Note: rcctl is for OpenBSD. Use systemctl restart sshd on Fedora (and on all Linux distributions with systemd).

It is also possible to use a config file on our client for convenience.

user@void: ~$ vim ~/.ssh/config

We write the following entry and save the file.

Host server
    HostName <public-IP>
    User username
    Port 44400
    IdentityFile ~/.ssh/id_ed25519_0

We can now log in simply like this:

user@void: ~$ ssh server

Finally, we can set some firewall rules to further improve security. This part is platform-specific.

Note: These are just some very basic rules for firewalls. This is not a detailed firewall guide in any way.

Firewall for OpenBSD

Firewalling on OpenBSD is handled by pf. All connections are passed by default. Rules can be defined in /etc/pf.conf.

root@server: ~# vi /etc/pf.conf

Here is a crude ruleset with some comments.

# Define the ssh port as a variable
port_ssh = "{44400}"

# Do not filter on the loopback interface
set skip on { lo }

# Drop packets that are blocked
# This gives less information to potential attackers
# Also saves bandwidth
set block-policy drop

# Block everything by default
block drop all

# Allow limited ICMP traffic (bleh)
pass in log on egress proto icmp max-pkt-rate 5/1

# Allow incoming ssh connections
pass in log on egress proto tcp from any to any port $port_ssh

# Allow all outgoing connections
pass out on egress from any to any
Note: Pay attention to the lines concerning ssh. You might lock yourself out of the server with a typo.

pf should be enabled by default. We check the configuration and then load the new ruleset.

root@server: ~# pfctl -nf /etc/pf.conf
root@server: ~# pfctl -f /etc/pf.conf

Firewall for Fedora/Linux

Firewalld is the default firewall program on Fedora and other Linux distributions related to the RHEL ecosystem, and it uses nftables as a backend. On other distributions with systemd, firewalld can be install through the package manager. Using ufw or using iptables/nftables directly are other options for firewalling on Linux, but this guide only covers some basic commands with firewalld.

Here is how we can set up basic firewall functionality using firewalld's command-line interface: firewall-cmd.

First of all, we check if firewalld is running.

root@server: ~# systemctl status firewalld

If not, we enable and start it.

root@server: ~# systemctl enable --now firewalld

We can check the current configuration with:

root@server: ~# firewall-cmd --list-all

This lists the active zones and their configurations. In firewalld, a network interface cannot be assigned to more than one zone. The external interface of your server should be assigned to the "public" zone, or some other custom zone. We can verify this by reading the output of the command above; under the zone name, it should contain a line that looks like:

    interface: enp1s0

In this case, enp1s0 is the external interface. We can make sure that the zone of this interface (I am assuming that it is "public", but it could be something else on your system) is the default one for our commands.

root@server: ~# firewall-cmd --get-default-zone

If not:

root@server: ~# firewall-cmd --set-default-zone public

Now that we have the active zone linked to our external interface as our default zone, we can proceed. firewall-cmd will show us the services and the ports that are open.

root@server: ~# firewall-cmd --list-all

Read the output, and look for lines starting with "services" and "ports".

    services: cockpit dhcpv6-client ssh

First, we can remove the services that we don't need. Cockpit is a Web interface for servers. Feel free to keep it enabled, but I will remove it as an example, since it is not necessary. Here is how you remove services with firewall-cmd.

root@server: ~# firewall-cmd --remove-service=cockpit

Next, we add our alternative ssh port (44400) to the list of allowed ports. On Fedora, SELinux might prevent ssh connections on different ports, so we need to let it know that we are explicitly allowing ssh on port 44400.

The default port for ssh is port 22.

root@server: ~# semanage port -l | grep ssh


ssh_port_t                      tcp      22

We allow the new port:

root@server: ~# semanage port -a -t ssh_port_t -p 44000

And confirm that the new port was added:

root@server: ~# semanage port -l | grep ssh

The output should contain the new port.

ssh_port_t                      tcp      44000, 22

We can now open the port with firewall-cmd.

root@server: ~# firewall-cmd --add-port=44000/tcp

We may check if everything was configured the way we intended.

root@server: ~# firewall-cmd --list-all

When satisfied, we can make the configuration persistent across reboots.

root@server: ~# firewall-cmd --runtime-to-permanent

Here are a few things you can do on your VPS (Updated list):