There are two main threat actors for your VPS: bots and real people (manually testing access).
The IP address of your VPS is public knowledge, there are thousands of web crawlers looking for new domains and testing access to your server. After I created my first VPS, within the first 30 minutes, it was already pen-tested by bots (the /var/log/auth.log
file showed plenty of failed login attempts).
There are many options for securing a VPS, here are some suggestions to start with.
1. Update the packages - apt update && apt upgrade
apt update && apt full-upgrade -y
2. Create a new user on the system
Avoid using the root
user and create a regular user. When naming the user, avoid common names (like admin/administrator) and maybe avoid using your first name.
[root@VPS ~]# adduser dogo
# Add the regular user to the sudo group
[root@VPS ~]# usermod -aG sudo dogo
[root@VPS ~]# su - dogo
[dogo@VPS ~]$
Notice the prompt changing from #
(root) to $
(regular user).
3. Use key-based SSH authentication
The VPS most likely includes an SSH account. This will probably have a password, but key-based authentication is more secure.
Again, the cloud provider might already include this type of authentication for your SSH account. Either way, you need to be able to connect as your regular user. So we’ll generate the SSH keys (ssh-keygen
) and transfer the public key to the VPS.
After generating the public key (the one ending in .pub, ex: vps_id_rsa.pub), you can transfer it to the server in two ways:
- login with password is active - use
ssh-copy-id
- login with password is disabled - manually copy the public key
If login with password is active, you can run the command on local machine:
ssh-copy-id -i PATH/TO/PUBLIC_KEY USERNAME@REMOTE_HOST
If login with password is disabled, you can manually copy the public key.
On your local machine run:
# The path and key name might differ
cat ~/.ssh/vps_id_rsa.pub
# Copy the public key string
On the remote VPS session, log in as the regular user you created:
# Create the .ssh directory for the dogo user
mkdir -p ~/.ssh && touch ~/.ssh/authorized_keys
# Important to wrap the string in quotes
echo 'PUBLIC_KEY_STRING_YOU_COPIED' >> ~/.ssh/authorized_keys
Test the SSH connection:
ssh -i ~/.ssh/vps_id_rsa.pub USERNAME@REMOTE_HOST
4. SSH security configs
- Disable SSH password authentication
- Disable root login
- Change the default SSH listening port
Config the SSH to disable access with a password and disable root login.
Also here, change the default SSH listening port (22) to some other port of your choosing (ex: 12345).
This is more of a security through obscurity step, intended for bots. Real attackers can still find your new SSH port rather quickly via port scanning.
Note: I recommend choosing a port number between 1024 and 65535, as others are usually reserved for various system services.
Edit the /etc/ssh/sshd_config
file and change the following rules:
# /etc/ssh/sshd_config
Port 12345
[...]
PermitRootLogin no
[...]
PasswordAuthentication no
Make sure the lines are not commented (remove the leading #
if needed).
Restart the SSH service: sudo systemctl restart sshd.service
Note: on some systems, this is done with the sudo service ssh restart
After changing the port number, you might need to run these commands as well for the changes to work:
sudo systemctl daemon-reload && sudo systemctl restart ssh.socket
Note: you can check if the port is updated with: ss -tlnp
.
On another terminal session, test the connection to the new port:
ssh -i ~/.ssh/vps_id_rsa.pub USERNAME@REMOTE_HOST -p 12345
.
Important: make sure your connection works before proceeding with firewall setup.
5. Set up a firewall
Using the ufw
util which is built on top of iptables
, and is already installed on most Linux distros.
Before enabling the firewall, allow incoming SSH connections to the port you specified above (ex: 12345).
sudo ufw allow 12345/tcp
If the server should accept the HTTP/HTTPS connections, you can repeat this command for ports 80 and 443.
Activate the firewall and check its status:
sudo ufw enable
sudo ufw status
Status: active
To Action From
-- ------ ----
12345/tcp ALLOW Anywhere
12345/tcp (v6) ALLOW Anywhere (v6)
6. Disable open ports you don’t use
List all the currently open ports with netstat
or ss
.
Enabling ufw
will by default block all incoming traffic. Check the ufw status
if there are any rules you ought to block.
Additional recommendations
Set up 2FA authentication
As you already enabled key-based authentication, brute-force attacks are now less likely to be successful. But as the SSH key might still get leaked, setting up a second auth factor is recommended.
2FA can be enabled with the Google’s PAM:
sudo apt-get install libpam-google-authenticator
We use this to generate a TOTP key for the current user (the key is not system-wide).
Run the app and reply y
when prompted:
google-authenticator
Do you want authentication tokens to be time-based (y/n) y
This generates a QR code, which you can read on your auth app.
Note: make sure to save the secret key, verification, and recovery codes.
Do you want me to update your "~/.google_authenticator" file (y/n) y
Go with y
and answer the following questions based on your needs.
Now, configure the SSH to use 2FA. Add these configurations at the end of the file:
# /etc/pam.d/sshd
[...]
auth required pam_google_authenticator.so nullok
auth required pam_permit.so
After this, configure the SSH to support this auth type. Either add or change these lines:
# /etc/ssh/sshd_config
KbdInteractiveAuthentication yes
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,password publickey,keyboard-interactive
Restart the SSH service: sudo systemctl restart sshd.service
Automatically block SSH brute-forcing attempts
The fail2ban
tool can be deployed for this. It scans files like /var/log/auth.log
and bans IP addresses with too many failed login attempts.
sudo apt install fail2ban
Check if the fail2ban service is running: sudo systemctl status fail2ban.service
.
The config files are under /etc/fail2ban/*
. Duplicate the default config file, as we should not edit it directly (it gets overwritten in updates).
sudo cp jail.conf jail.local
Add the rules under [sshd]
in the jail.local
file. Some suggested rules:
# /etc/fail2ban/jail.local
[...]
[sshd]
enabled = true
bantime = 1d # (in seconds or time abbreviation format)
port = 12345 # if you changed the default SSH port
Other common configs are maxretry
and findtime
.
To make sure the config changes are working, you can reload the fail2ban-client:
sudo fail2ban-client reload
.
Resources
- youtube.com/@MentalOutlaw
- greg.molnar.io
- www.digitalocean.com