How to harden a new Linux VPS

Basic security and monitoring practices for hardening a new Linux server.

Masternodes have been designed to work in a hot/cold setup. The actual 1000 coins you need to setup your Helium masternode can be stored in an offline cold wallet (your personal PC, for example) and then you appoint an online hot wallet with no actual coins to act on behalf of the cold storage node. This allows you to always keep control of your private keys. To sum up, the private key is not on the VPS.

The objective of an attacker doesn't have to be your wallet, it can be your VPS as well.

🚧

Disclaimer

This guide is in development and should be further tested. Although there is no danger of damaging anything, I am not responsible for the loss of time if the result obtained is not the desired one.

Ubuntu Completed: ✔️ / Tested: ✔️
Debian Completed: ❌ / Tested: ✔️
CentOS Completed: ❌ / Tested: ❌
Fedora Completed: ❌ / Tested: ❌

1. Update your server

The first thing you always need to do. You can update your VPS with the following command:

apt-get update -y && apt-get upgrade -y
yum -y update

This command will update all your system’s packages to their latest version.

2. Create a non-root user

We recommend creating a limited user account and using that at all times. Administrative tasks will be done using sudo to temporarily elevate your limited user’s privileges so you can administer your VPS.

1.1-Create the user, replacing example_user with your desired username. You’ll then be asked to assign the user a password:

adduser example_user
// Note: for CentOS and Fedora you have to add a password too

useradd example_user && passwd example_password
// Debian does not include sudo by default so it must be installed:
apt install sudo

// Create the user, replacing example_user with your desired username. 
// You’ll then be asked to assign the user a password:
adduser example_user

1.2-Add the user to the sudo group so you’ll have administrative privileges:

adduser example_user sudo
usermod -aG wheel example_user

// In CentOS 6 a wheel group is disabled by default for sudo access. You must to configure it manually. Type from root: `/usr/sbin/visudo`. Then find the line `# %wheel` and uncomment this line. To began typing in vi, press `a`. To save and exit press `Escape`, then type `:w`(press enter), `:q`(press enter)
adduser example_user sudo

After creating your limited user, disconnect from your VPS and log back in as your new user.Now you can administer your VPS from your new user account instead of root. Nearly all superuser commands can be executed with sudo (example: sudo iptables -L -nv) and those commands will be logged to /var/log/auth.log.

3. Create an Authentication Key-pair

This is done on your local computer, not your VPS, and will create a 4096-bit RSA key-pair. During creation, you will be given the option to encrypt the private key with a passphrase. This means that it cannot be used without entering the passphrase unless you save it to your local desktop’s keychain manager. We suggest you use the key-pair with a passphrase, but you can leave this field blank if you don’t want to use one. Some VPS providers, including Digital Ocean, offer Linux installations with key authentication included. Especially when setting up multiple nodes this option will save you a lot of time. Read more here.

2.a) Linux / OS X

❗️

Caution

If you’ve already created an RSA key-pair, this command will overwrite it, potentially locking you out of other systems. If you’ve already created a key-pair, skip this step. To check for existing keys, run ls ~/.ssh/id_rsa*.

ssh-keygen -b 4096

Press Enter to use the default names id_rsa and id_rsa.pub in /home/your_username/.ssh before entering your passphrase.

Now upload the public key to your VPS. Replace example_user with the name of the user you plan to administer the VPS as, and 203.0.113.10 with your VPS’s IP address.

// From your local computer:
ssh-copy-id [email protected]
// On your VPS (while signed in as your limited user):
mkdir -p ~/.ssh && sudo chmod -R 700 ~/.ssh/

// From your local computer:
scp ~/.ssh/id_rsa.pub [email protected]:~/.ssh/authorized_keys

2.b) Windows

Follow this awesome guide from Linode Docs

4. Change SSH config

By default, password authentication is used to connect to your VPS via SSH. A cryptographic key-pair is more secure because a private key takes the place of a password, which is generally much more difficult to brute-force. In this section, we’ll create a key-pair and configure the VPS to not accept passwords for SSH logins.

// Install nano text editor (if you know how to use any other you can use it too)
sudo apt-get install nano -y

// Open the config file
sudo nano /etc/ssh/sshd_config
  
// Press Ctrl + X to close nano.
// If you have made changes the program will ask you if you want to save it.

All these configurations are commented (with a # at the beginning of the line) or are disabled by default. You will have to manually search for the settings you want to change in the file.

4.1-Disallow root logins over SSH. This requires all SSH connections be by non-root users. Once a limited user account is connected, administrative privileges are accessible either by using sudo or changing to a root shell using su -.

# Authentication:
PermitRootLogin no

4.2-Disable SSH password authentication. This requires all users connecting via SSH to use key authentication. Depending on the Linux distribution, the line PasswordAuthentication may need to be added, or uncommented by removing the leading #.

# Change to no to disable tunnelled clear text passwords
PasswordAuthentication no

📘

Note

You may want to leave password authentication enabled if you connect to your Linode from many different computers. This will allow you to authenticate with a password instead of generating and uploading a key-pair for every device.

4.3-Listen on only one internet protocol. The SSH daemon listens for incoming connections over both IPv4 and IPv6 by default. Unless you need to SSH into your VPS using both protocols, disable whichever you do not need. This does not disable the protocol system-wide, it is only for the SSH daemon.

Use the option:

# To listen only on IPv4.
AddressFamily inet
  
# To listen only on IPv6.
AddressFamily inet6

The AddressFamily option is usually not in the sshd_config file by default. Add it to the end of the file:

// Execute this command
echo 'AddressFamily inet' | sudo tee -a /etc/ssh/sshd_config

4.4-Change default port. The Secure Shell (SSH) Protocol by default uses port 22. Accepting this value does not make your system insecure, nor will changing the port provide a significant variance in security. However, changing the default SSH port will stop many automated attacks and a bit harder to guess which port SSH is accessible from. In other words, a little security through obscurity.

🚧

Note

The Internet Assigned Numbers Authority (IANA) is responsible for the global coordination of the DNS Root, IP addressing, and other Internet protocol resources. It is good practice to follow their port assignment guidelines. Having said that, port numbers are divided into three ranges: Well Known Ports, Registered Ports, and Dynamic and/or Private Ports. The Well Known Ports are those from 0 through 1023 and SHOULD NOT be used. Registered Ports are those from 1024 through 49151 should also be avoided too. Dynamic and/or Private Ports are those from 49152 through 65535 and can be used. Though nothing is stopping you from using reserved port numbers, our suggestion may help avoid technical issues with port allocation in the future.

# SSH Port - Change the number 50022 to the port number you want.
Port 50022

4.5-Restart the SSH service to load the new configuration.

sudo systemctl restart sshd
sudo service ssh restart

5. Install a firewall.

Using a firewall to block unwanted inbound traffic to your VPS provides a highly effective security layer. By being very specific about the traffic you allow in, you can prevent intrusions and network mapping. A best practice is to allow only the traffic you need and deny everything else.

UFW is included in Ubuntu by default but must be installed in CentOS, Fedora and Debian. Debian will start UFW’s systemd unit automatically and enable it to start on reboots. This is not the same as telling UFW to enable the firewall rules, as enabling UFW with systemd or upstart only tells the init system to switch on the UFW daemon.

// Install UFW
sudo apt-get install ufw
// By default, UFW is not available in CentOS repository.
// So you will need to install the EPEL repository to your system.
yum install epel-release -y

//Then, we can install it from the EPEl repository.
yum install --enablerepo="epel" ufw -y

Set Default Rules. Most systems will need a only a small number of ports open for incoming connections, and all remaining ports closed. To deny all incoming and allow all outgoing connections, run:

sudo ufw default allow outgoing
sudo ufw default deny incoming

Add Rules. Rules can be added in two ways: By denoting the port number or by using the service name.

// To allow both incoming and outgoing connections on port 50022 for SSH.
// Change the number 50022 to the port number you set earlier in the SSH configuration.
sudo ufw allow 50022
  
// Similarly, to deny traffic on a certain port (in this example, 111)
sudo ufw deny 111
  
// To remove a rule, add delete before the rule implementation.
sudo ufw delete allow 80
  
// We have to open the port for the Helium masternode
sudo ufw allow 9009

Enable the Firewall. To enable UFW and enforce your firewall rules:

// Check status
sudo ufw status

// Enable UFW
sudo ufw enable

// Disable UFW
sudo ufw disable

That's all we need. If you need more info check the original doc.

6. Install Fail2Ban

Fail2Ban is an application that bans IP addresses from logging into your server after too many failed login attempts. Since legitimate logins usually take no more than three tries to succeed (and with SSH keys, no more than one), a server being spammed with unsuccessful logins indicates attempted malicious access.

// Install it
sudo apt-get install fail2ban
// Install it
sudo yum install fail2ban

// Start and enable Fail2ban
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
// Install it
sudo dnf install fail2ban
  
// Start and enable Fail2ban
sudo systemctl start fail2ban
sudo systemctl enable fail2ban

Fail2Ban can monitor a variety of protocols including SSH, HTTP, and SMTP. By default, Fail2Ban monitors SSH only and is a helpful security deterrent for any server since the SSH daemon is usually configured to run constantly and listen for connections from any remote IP address.

With the default configuration is enough, but if you want to configure it you can see the full guide.

7. Set the timezone and install NTP

Setting your server's clock and timezone properly is essential in ensuring the healthy operation of distributed systems and maintaining accurate log timestamps.

// Check if dbus is installed
sudo apt-get install dbus

// List all timezones
timedatectl list-timezones

// Press Space to scroll to the next page, b to scroll back a page.
// Once you find the timezone you want to use, press q to go back to the command line.

// Set the desired timezone - replace desired_timezone with the timezone you selected
sudo timedatectl set-timezone desired_timezone

// Verify that the timezone has been set properly
timedatectl

// Install NTP for synchronization
sudo apt-get install ntp
// List all timezones
timedatectl list-timezones

// Press Space to scroll to the next page, b to scroll back a page.
// Once you find the timezone you want to use, press q to go back to the command line.

// Set the desired timezone - replace desired_timezone with the timezone you selected
sudo timedatectl set-timezone desired_timezone

// Verify that the timezone has been set properly
timedatectl

// Install NTP for synchronization
sudo yum install ntp

// Start and enable the service
sudo systemctl start ntpd
sudo systemctl enable ntpd
// List all timezones
timedatectl list-timezones

// Press Space to scroll to the next page, b to scroll back a page.
// Once you find the timezone you want to use, press q to go back to the command line.

// Set the desired timezone - replace desired_timezone with the timezone you selected
sudo timedatectl set-timezone desired_timezone

// Verify that the timezone has been set properly
timedatectl

// Install NTP for synchronization
sudo dnf -y install ntp

// Start and enable the service
sudo systemctl start ntpd 
sudo systemctl enable ntpd

8. Secure shared memory

Shared memory can be used in an attack against a running service, so it is always best to secure that portion of memory. You can do this by modifying the /etc/fstab file.

// First, you must open the file for editing
sudo nano /etc/fstab

// Next, add the following line to the bottom of that file:
tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0
// To do

9. Prevent IP spoofing

This one is quite simple and will go a long way to prevent your server's IP from being spoofed.

// Open config file
sudo nano /etc/host.conf

// It will look like this:
# The "order" line is only used by old versions of the C library.
order hosts,bind
multi on

// Change it to this:
# The "order" line is only used by old versions of the C library.
order bind,hosts
nospoof on
// To do

10. Harden the networking layer

There is a very simple way to prevent source routing of incoming packets (and log all malformed IPs) on your Ubuntu Server. Open a terminal window, issue the command sudo nano /etc/sysctl.conf, and uncomment or add the following lines:

# IP Spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0 
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0

# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Block SYN attacks
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5

# Log Martians
net.ipv4.conf.all.log_martians = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0 
net.ipv6.conf.default.accept_redirects = 0

# Ignore Directed pings
net.ipv4.icmp_echo_ignore_all = 1
// To do

11. DDoS Protection

Servers that have a masternode hosted will be targets of automatic DDoS attacks from owners of other masternodes that want to increase their profits at the expense of shutting down or restarting the servers of their competition.

This configuration will allow you to avoid the most basic and common attacks, but even so, it is recommended to have a server that comes with some type of protection or mitigation service for DDoS attacks.

The following instructions are going to be added to the UFW rules, so I hope you followed the instructions to install and configure it.

// Edit the UFW rules file
sudo nano /etc/ufw/before.rules

// Add those lines after *filter near the beginning of the file
:ufw-http - [0:0]
:ufw-http-logdrop - [0:0]

// Add these lines near the end of the file, before the COMMIT
### Start HTTP ###

# Enter rule
-A ufw-before-input -p tcp --dport 80 -j ufw-http
-A ufw-before-input -p tcp --dport 443 -j ufw-http

# Limit connections per Class C
-A ufw-http -p tcp --syn -m connlimit --connlimit-above 50 --connlimit-mask 24 -j ufw-http-logdrop

# Limit connections per IP
-A ufw-http -m state --state NEW -m recent --name conn_per_ip --set
-A ufw-http -m state --state NEW -m recent --name conn_per_ip --update --seconds 10 --hitcount 20 -j ufw-http-logdrop

# Limit packets per IP
-A ufw-http -m recent --name pack_per_ip --set
-A ufw-http -m recent --name pack_per_ip --update --seconds 1 --hitcount 20 -j ufw-http-logdrop

# Finally accept
-A ufw-http -j ACCEPT

# Log
-A ufw-http-logdrop -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW HTTP DROP] "
-A ufw-http-logdrop -j DROP

### End HTTP ###

# Prevent ping flood
-A INPUT -p icmp -m limit --limit 6/s --limit-burst 1 -j ACCEPT
-A INPUT -p icmp -j DROP
  
  
// Now close the file. 
// Before reloading all the rules, you need to make sure IPv6 is disable:
sudo nano /etc/default/ufw
  
// Change the setting to no. The line should be at the beginning.
IPV6=no
  
// Make sure UFW runs and reload everything:
sudo ufw reload
// To do

12. References