I like to self-host my web applications and other applications like blog software and Git servers. This gives you a bit of independence from third-party services and the freedom to install any software with any configuration.
Managing your own server has the drawback that it needs more effort than using a third party service. There is the initial setup that takes time, and after that, you have to manage and update the server periodically.
But even with all these drawbacks, managing your own server is interesting and you can learn a lot and improve your skills.
You don't need a server farm in your apartment to run your own Internet-connected servers, although you could. Instead, you visit the homepage of a VPS provider and rent your VPS server there. There are a lot of VPS hosting providers on the Internet, to name a few: OVH, Linode, DigitalOcean, Scaleway, Vultr.
A VPS is a virtual machine, runs its own copy of an operating system, and you have root access to it. Most VPS run with a Linux operating system, but you also find servers running with Windows.
Renting a VPS server is not that expensive. The cheapest VPS offers I saw cost between 2 and 5 US Dollars per month. Often you can rent them for just one month. A good way to figure out if self-hosting is something for you without wasting much money.
In this blog post, I'm going to describe the initial configuration steps after I rent a new VPS. The provider often gives you a plain operating system with just the SSH server installed. The initial steps, I cover in this blog post, including the updating of the operating system, installing a firewall, and hardening the SSH server.
For this blog post, I'm going to use a VPS from OVH. I'm not affiliated with OVH; it's just the provider that I have the most experience with and where I rented a few VPS. I'm satisfied with the service they provide, and they offer VPS for a very low price. You find the price list here: https://www.ovhcloud.com/en/vps/cheap-vps/
For the operating system, I'm choosing Ubuntu 18.04. I like to use a Debian based Linux, either Debian directly or Ubuntu, because many tutorials you find on the World Wide Web are written for this operating system.
Rent VPS ¶
Visit the homepage of the VPS hosting provider of your choice and select a VPS server. The offers differ in CPU, RAM, and disk space. Often these VPS are not scalable, and the RAM, disk space, and number of CPUs is fixed. If you run out of resources, the only option then is to buy the next bigger VPS and relocate your software. Therefore, before you rent a VPS, you should think about the software you want to install. If you only want to run a self-hosted Git server or a WordPress instance, a VPS with 512MB RAM should be sufficient. If you are planning to run multiple services, a VPS with more RAM or more disk space might be a better choice.
As mentioned before, I'm going to use a VPS from OVH, and I choose the cheapest server (VPS SSD 1). With this VPS you get 2GB RAM, 20GB SSD, a bandwidth of 100 Mbps, unlimited traffic, one static IPv4 and one IPv6 address. OVH also supports an upgrade to the bigger VPS SSD2 und VPS SSD3 without reinstalling the operating system.
During the ordering process, I can choose the location of the datacentre where my VPS should run in, the operating system, and additional offerings like more disk space and snapshots. I can select if I want to rent the VPS for just one month or a longer period. These options differ from provider to provider. You also have to create an account with the provider. At the end of the ordering process, you have to pay.
After the payment is processed, the provider sets up the VPS and sends you an email with information about your new VPS. This process can take a few minutes. In my case, it took about 10 minutes.
Connect with SSH ¶
The email that OVH sends contains the IPv4 and IPv6 address of the VPS and the SSH username and password. Managing a Linux server is usually done over Secure Shell (SSH). SSH provides a secure (encrypted) channel from your computer to the server. When you work on Linux, macOS, or Windows 10 with the latest April 2018 update, you have an SSH client already installed (see this follow-up article about Putty if you work on an older Windows).
In a shell or command prompt, issue the following command. Replace 51.38.124.133 with your IPv4 address.
ssh root@51.38.124.133
The first time you connect with SSH to a server, the client presents a prompt with the server's fingerprint. The fingerprint is a hash of the public key of the server.
The authenticity of host '51.38.124.133 (51.38.124.133)' can't be established.
ECDSA key fingerprint is SHA256:BXdQQAsjdS9P91cCwR5yRRelfyynpsfY+OxTkLSlr/A.
Are you sure you want to continue connecting (yes/no)? yes
Unfortunately, we can't verify the fingerprint; the email from OVH does not contain this information, so we have to trust that this is our server and type yes
. The fingerprint is a security measure to prevent man in the middle attacks, and if you can find this information before you connect, you should compare it.
Next, enter the password, and Ubuntu greets you with a welcome screen.
root@51.38.124.133's password:
Welcome to Ubuntu 18.04 LTS (GNU/Linux 4.15.0-20-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://ubuntu.com/landscape
* Support: https://ubuntu.com/pricing
System information as of Thu Jun 7 15:58:40 CEST 2018
System load: 0.0 Processes: 82
Usage of /: 5.9% of 19.21GB Users logged in: 0
Memory usage: 6% IP address for ens3: 51.38.124.133
Swap usage: 0%
Get cloud support with Ubuntu Advantage Cloud Guest:
https://ubuntu.com/pricing
8 packages can be updated.
8 updates are security updates.
The first thing we should do is change the root password. OVH sent us the password in an unsecured email.
Issue the command passwd
, and it prompts for a new password. To verify that the new password
works close the SSH connection with exit
and try to reconnect.
If something went wrong and you can no longer log in, open the web management console of your VPS provider. There you should find an option where you can either reboot the VPS into a special rescue mode or another
way to reset the root password.
In the OVH manager, you find the Reboot in rescue mode function. A click on this link reboots the VPS into a
special mode where you can fix problems with your Linux installation. The reboot process takes about 3 minutes, and OVH sends you an email with the IP address, SSH username, and password when the rescue mode is ready.
Visit this link to find more information about how to reset the root password in this mode.
As a last resort, if everything fails, most VPS providers support a Reinstall function, which deletes everything and reinstalls the operating system.
Update Operating System ¶
In the previous welcome screen, you see that 8 installed packages can be updated. On Debian and Ubuntu, you install and update software packages with the apt
command.
First, you have to update the local apt database and then upgrade all outdated installed packages.
apt update
apt full-upgrade
The local apt database contains information about all available apt packages. If you update or install a new package, apt
looks in this database and installs the latest version that
is listed in this database. When the database is outdated, apt
installs old packages. Therefore you must call apt update
before you update or install packages. Ubuntu updates the apt database automatically once a day.
The dist-upgrade
command lists the outdated packages and before it installs the updates asks if you want to continue. Type y and apt
installs the updates.
When the update process installs a new kernel (packages starting with linux-*) you should reboot the server. Ubuntu also tells you on the welcome screen if it needs a reboot.
To reboot the server, you issue the command reboot
. This command closes the SSH connection.
Keeping the installed packages up-to-date is, in my opinion, very important, especially for a server that is connected to the Internet. Every server is going to be attacked, and if a crucial application like the SSH server has a weakness, attackers will find it and exploit it. Fortunately, Ubuntu provides a way to install critical updates automatically.
Install the unattended-upgrades
package and then open the configuration file.
apt install unattended-upgrades
nano /etc/apt/apt.conf.d/50unattended-upgrades
Configure the Allowed-Origins
section.
If you only want Ubuntu to install important security updates automatically, comment out everything but the
"${distro_id}:${distro_codename}-security";
line
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}ESM:${distro_codename}";
// "${distro_id}:${distro_codename}-updates";
// "${distro_id}:${distro_codename}-proposed";
// "${distro_id}:${distro_codename}-backports";
};
If you want Ubuntu to automatically upgrade everything uncomment (remove //) all ${distro_id}:*
lines. Save and close the file with ctrl+o and ctrl+x.
Next we need to enable unattended-upgrades
. Run the command nano /etc/apt/apt.conf.d/20auto-upgrades
to open the configuration file and add these lines.
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
Visit the official documentation about unattended-upgrades
for more information:
https://ubuntu.com/server/docs
IPv6 ¶
OVH gives you one free static IPv6 address, but it's not configured yet. If you are not sure if IPv6 is enabled or not issue this command
ping6 www.google.com
If the command prints out connect: Network is unreachable
, IPv6 is not enabled.
In Ubuntu 18.04 netplan is responsible for managing the network interfaces. To add IPv6, we have to open the configuration file.
nano /etc/netplan/50-cloud-init.yaml
On my VPS the file contains this code
network:
version: 2
ethernets:
ens3:
dhcp4: true
match:
macaddress: fa:16:3e:dd:b2:03
set-name: ens3
ens3
is the name of the network interface. You see that only DHCP for IPv4 is enabled. OVH assigns static IPv4 addresses to the VPS over DHCP by maintaining a mapping in the DHCP server from MAC address to IP address, so even it is DHCP, your server always gets the same IPv4 address.
To enable IPv6 support, we have to add an address
and gateway6
element. You find both IPv6 addresses in the OVH web console.
network:
version: 2
ethernets:
ens3:
addresses:
- 2001:41d0:701:1100:0:0:0:e54/64
gateway6: 2001:41d0:0701:1100:0000:0000:0000:0001
dhcp4: true
match:
macaddress: fa:16:3e:dd:b2:03
set-name: ens3
Save (ctrl+o) and close (ctrl+x) the text editor. Now try to load the configuration.
netplan try
With the try
argument, netplan
activates the network configuration, and you have to press enter during the next 120 seconds if you want to keep the settings. If not, netplan
reverts to the old configuration. This prevents any misconfiguration that accidentally disables IPv4 and kicks you out of the SSH session. Just wait 120 seconds, and you should be able to log in again.
To check if IPv6 is enabled, issue the command ping6 www.google.com
. You can also see the configured addresses with ip -6 addr
.
Harden SSH server ¶
The SSH server is the main entry point to the server, and we should make this entry as secure as possible. The current login with username and password is not the most secure authentication method. In this section, we change the SSH configuration and make it more difficult for an attacker to gain access to our server.
Change default port ¶
The first thing I do on a new server is to change the default listening port (22) of SSH.
Open the SSH configuration file, uncomment the Port
line and set it to a random port. For this example
I set the port to 44933.
nano /etc/ssh/sshd_config
Port 44933
Make sure that you do not accidentally choose a port that is used by another service. The following command lists all currently running services that listen on a port.
ss -tulpn | grep LISTEN
Ports from 0 to 1023 are reserved and should not be used. Choose a port higher than 1023 and less than 65535.
This change does not make your server more secure. A targeted attack is going to find all open ports easily. But it helps against drive-by scans. If you run your server for a few hours, you notice in the /var/log/auth.log
file warnings about users that tried to access the server. These are automated scripts that scan the Internet for open ports and look for ways to break in.
By changing the default SSH port from 22 to something else, we can prevent or at least significantly reduce these scans and attacks.
To activate the change restart the SSH server sudo systemctl restart ssh
and check the status sudo systemctl status ssh
. You should see a green Active: active (running)
message. If there is a syntax
error in the configuration file, you see a warning.
Close the SSH connection with exit
and reconnect. Because we have changed the port, we need to specify the port with the -p
option.
ssh -p 44933 root@51.38.124.133
Disable Protocol 1 ¶
SSH supports two protocols, 1 and 2. Version 1 is considered to be less secure and should no longer be used.
Open the configuration file nano /etc/ssh/sshd_config
and insert a new line Protocol 2
. Ctrl+o and Ctrl+x save the change and close the text editor. Restart the SSH server with systemctl restart ssh
.
Disable root login ¶
So far, we logged in with the root user, which has superuser permissions on the server. From a security standpoint, it is better to create an unprivileged user for SSH access and then disable the root login.
Issue the command adduser manager
to create a new Linux user. Instead of manager, you can name the user anything you want.
Enter a new password for the user. The command asks for information like name and phone. It is okay to accept the defaults and leave all fields blank.
This user is going to be the only user that is allowed to log in with SSH. But for managing a server, we need a user that has root privileges. The easiest way is to add this user to the sudo
group. Users in this group can execute commands with the sudo
command and have then the same privileges a root user has.
usermod -aG sudo manager
Close the SSH connection and try to login with the new user ssh -p 44933 manager@51.38.124.133
Verify that the user can run commands with sudo
. Run the command sudo ls -al /root
, when successful lists the contents of the /root directory. The first time you use sudo
in a session, Linux asks for the user's password.
When everything is okay, we can disable the root login in the SSH server and limit the login to the manager
user.
Because we are now logged in with the manager
user and need to change a system file, we have to use sudo
.
Issue the command sudo nano /etc/ssh/sshd_config
, change the line PermitRootLogin yes
to PermitRootLogin no
and insert a new line AllowUsers manager
Restart the server sudo systemctl restart ssh
and check the status sudo systemctl status ssh
.
Exit the SSH connection and try to login with the root user. The client should not be able to establish a connection. Only the login with manager
should work.
//FAIL
ssh -p 44933 root@51.38.124.133
//OK
ssh -p 44933 manager@51.38.124.133
Disable password login ¶
Using public/private keys as authentication is considered to be more secure than using passwords. In this section, we're going to disable password authentication and switch to Ed25519 public/private key authentication (elliptic curve algorithm).
Check if the directory /etc/ssh
contains a file with ed25519 in it's name. If not, create the file with.
sudo ssh-keygen -f /etc/ssh/ssh_host_ed25519_key -N '' -t ed25519
This creates a public and a private key and stores them in the /etc/ssh/
directory.
Open the SSH configuration sudo nano /etc/ssh/sshd_config
and uncomment (remove #) the following line
HostKey /etc/ssh/ssh_host_ed25519_key
Save the change and restart the SSH server sudo systemctl restart ssh
Next, we need to create a public/private key pair on the client computer. Make sure that you open a new shell or close the SSH connection and issue this command
ssh-keygen -N "mysupersecretpassphrase" -t ed25519 -C "mydesktopcomputer"
This command generates a public and private key and stores them in two files (id_ed25519 and id_ed25519.pub) in the home directory of the logged-in user.
The -N
option specifies a passphrase that protects the private key. You could create a private
key that is not password protected, but I would advise against this because everybody that has access to the private key file can log in to the server without entering any password. The -C
is a description of the key.
Next we need to transfer the public key from the client to the server.
ssh -p 44933 manager@51.38.124.133
mkdir /home/manager/.ssh
nano /home/manager/.ssh/authorized_keys
copy and paste the content from your public key <user_home>/.ssh/id_ed25519.pub
into the editor. Close nano with ctrl+o and ctrl+x. We need to change the permission of the authorized_keys
file; SSH ignores the file if it does not has the correct permissions.
chmod 0600 /home/manager/.ssh/authorized_keys
If you work on Linux or macOS, you can issue the ssh-copy-id
command that does all the steps above in one command
ssh-copy-id -i ~/.ssh/id_ed25519.pub manager@51.38.124.133
If ssh-copy-id
is not installed you can run this one liner
cat ~/.ssh/id_ed25519.pub | ssh manager@51.38.124.133 "mkdir -p ~/.ssh && touch ~/.ssh/authorized_keys && chmod -R go= ~/.ssh && cat >> ~/.ssh/authorized_keys"
Close the SSH connection and try to login with the key.
ssh -i <user_home>/.ssh/id_ed25519 -p 44933 manager@51.38.124.133
The client asks for the private key's passphrase, and if everything is okay, opens the connection without asking for another password.
With public/private authentication working, we can disable password authentication in the SSH server. Open the configuration file sudo nano /etc/ssh/sshd_config
, search for the line PasswordAuthentication yes
and change it to PasswordAuthentication no
. Save and close the editor and restart the SSH server.
When you want to access the SSH server from multiple clients, it is recommended to create a key pair on each device and add it to the /home/manager/.ssh/authorized_keys
file. Insert a comment after the key, so you know which client uses which key. If one of the devices gets lost or stolen, you remove the entry in the authorized_keys
file. This makes the private key useless, and nobody can access the server with this key even when they can brute force the passphrase.
Simplify client access ¶
To simplify the command on the client, you can enter all the command line option in the <user_home>/.ssh/config
file. Create the file if it does not already exist.
Host 51.38.124.133
User manager
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Port 44933
Now you can connect to the server with just this command: ssh 51.38.124.133
The only problem left is that we have to enter the passphrase for the private key each time we want to connect. It would be nice if we only have to do that once per session. For that, SSH provides the SSH agent. First start the ssh-agent
and then add the key with this command: ssh-add <user_home>\.ssh\id_ed25519
On Windows 10 (April 2018 Update), ssh-agent is installed as a service. It is not started automatically. To start it automatically, change the Startup Type in the Service panel to Automatic.
Install Firewall ¶
As the last step in this tutorial, we install a firewall. I usually install the Uncomplicated Firewall UFW. UFW is a frontend for iptables
and eases the firewall configuration.
sudo apt install ufw
When we enable UFW, it blocks by default every incoming connection. Therefore, we must allow incoming connections to the SSH server before enabling the firewall.
sudo ufw allow 44933/tcp
Check that the rule is added
sudo ufw show added
Make sure that the port matches the listening port of the SSH server. If there is a mismatch, you can no longer connect to the server.
Enable the firewall
sudo ufw enable
Check the current active firewall rules.
sudo ufw status
Close the SSH connection with exit
and then try to reconnect.
The firewall rule is configured correctly when the SSH client can open a connection.
For more information about UFW visit the official homepage: https://help.ubuntu.com/community/UFW
Our plain server is ready for future endeavors. If there are errors in my description or other useful configuration steps that should be done on a new VPS server, send me a message.
If you can't wait to install software here is an awesome list of software packages you can self-host:
https://github.com/awesome-selfhosted/awesome-selfhosted
See also my other blog posts:
Self-hosted Git server with Gitea:
https://golb.hplar.ch/2018/06/self-hosted-git-server.html
Self-hosted Google Drive alternative with SparkleShare:
https://golb.hplar.ch/2018/06/self-hosted-gdrive-onedrive-dropbox-alternative.html
Sending emails:
https://golb.hplar.ch/2018/06/send-only-email.html
Self-hosted tile server:
https://golb.hplar.ch/2018/07/self-hosted-tile-server.html
Self-host Polyfill.io:
https://golb.hplar.ch/2018/01/Self-host-Polyfill-io.html