• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Linux How to secure a Linux server (2024 edition)

222222

Advanced OT User
Joined
Jul 3, 2007
Messages
228
Reaction score
183
This tutorial is a slight update and improvement to my previous tutorial on how to secure a Linux server. I recommend following these steps on a fresh and new Debian 12 server. I'm going to cover a few different things, such as securing SSH access, setting up a firewall and how you can mitigate brute force attacks. A few things regarding logging changed with the release of Debian 12. If you're using Fedora or Arch, some steps in this tutorial will not be the same.



Section 1: Creating a non-root user

The very first thing you should always do on a Linux server is to setup a non-root user account. Every Linux server comes with a user called "root" by default, which is the administrator account. It's what you use to access your server via SSH. In order to improve security and make it much harder for someone to get in to your server, we will create a new user with a custom username that only you know about. We will later on only allow this user to sign in via SSH and disable root login.

Sign in to your server and then execute the following command to create a new user.
Change "<name>" to your desired username. Like in my previous tutorial, I recommend generating a random username.
I typically choose one of the old suggested names from Tibia. It's essentially just a "consonant+vowel" repeated many times.

Bash:
useradd -m -U -s /bin/bash -G users,sudo <name> && passwd <name>

If you want to generate such "old Tibia"-style random names, you can use this JavaScript code and run it in your browser (F12). It will generate 50 suggested named for you.

JavaScript:
function randomName() {
    let name = "";
    let vowels = ["a", "e", "i", "o", "u"];
    let consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"];
    let length = Math.floor(Math.random() * (15 - 7)) + 7;

    for (let i = 0; i < length; i++) {
        if (i % 2 === 0) name += consonants[Math.floor(Math.random() * consonants.length)];
        else name += vowels[Math.floor(Math.random() * vowels.length)];
    }

    return name;
}

for (let i=0; i<50; i++) console.log(randomName());

Once you have created your user, it will ask you to create a password for it. I highly recommend that you use a password manager such as KeePassXC to both generate and store your passwords. I typically make sure my passwords are at least 30 characters long, including a mix of numbers, letters and special characters. An example password with high entropy would look like: Z5CtPDL8J#?o&.3hlv#0e2lH9Xlr-W.

Make sure you can access your server via SSH using your new user account.

Bash:
ssh <user>@<ipAddress>



Section 2: Changing SSH port number

The default SSH port number is 22. If we change this number to something else, it makes it slightly more difficult for attackers to get in to your server. To edit the SSH port number on a Debian server, edit the sshd service configuration file using the following command:

Bash:
sudo nano /etc/ssh/sshd_config

A few lines down in the file you will see the following text, "#Port 22". Simply uncomment the line by removing the hashtag and change the number to something else. In this case, we will change it to "Port 38291", but you can use pretty anything you want. Save the file by pressing Ctrl+O → Enter → Ctrl+X. Then restart the sshd service on the server to apply the new settings.

Bash:
sudo systemctl restart sshd

At this point in time, I recommend that you open a second terminal and try accessing your server via SSH, so that we don't accidentally lock ourselves out. It should no longer allow you to sign in by entering:

Bash:
ssh <user>@<ipAddress>

Instead, you will now be required to provide the new port number, by doing this:

Bash:
ssh <user>@<ipAddress> -p 38291

Verify that it works as expected then we can continue to the next section.



Section 3: Enabling SSH key authentication

So far we have accessed the server via SSH by providing a username and password. This is not very safe, due to the fact that a brute force attack could let an attacker get in. So to secure SSH authentication, we will setup a SSH key pair. What this means is that we will create two matching files and place one on the server and one on the computer you're trying to access from. This means an attacker would have to have your key file in order to try get into the server, instead of just guessing a username and password.

Open up a terminal (Powershell on Windows) on your computer and run the following command to create a secure SSH key pair using Ed25519. Make sure you do not run this on the server, but on your home computer. The "-C" flag is optional and is used as a comment. You can enter anything you want there. It's just to label the key, so you know which key goes to which server, in case you have many servers. I typically follow a naming convention for my servers. Such as city names, Star Wars planets, football teams, or whatever you like.

Bash:
ssh-keygen -a 100 -t ed25519 -C "<serverName>"

1714832270276.png

When you generate your SSH key pair it will ask you where to store it. For simplicity's sake just leave it as default and press Enter. You are then asked to create a passphrase for the key. Again, I recommend using KeePassXC to both generate and store this passphrase. Make sure you never give this passphrase to anyone, unless you want to give them access to your server.

The two key files should now be created. Depending on your operating system, the location for them is:
  • Windows: %USERPROFILE%\.ssh
  • Linux: ~/.ssh
You should have two files named "id_ed25519" (private key) and "id_ed25519.pub" (public key). The idea is to place the public key on your Linux server and have the private key on the computer you're connecting from. Never share your private key with anyone else unless you want to give them access to your server.

To install the public key file on to your server, sign in to your server via SSH (on your new user account) and then run the following commands:

Bash:
sudo mkdir ~/.ssh
sudo nano ~/.ssh/authorized_keys

Now copy and paste all the content from your public key ("id_ed25519.pub") to the "authorized_keys" file that was created on your server. If you want to install multiple keys, you have to place them on separate lines in the file. Then save the file (using Ctrl+O → Enter → Ctrl+X).

1714830530708.png

Once the file has been edited and saved, run the following commands to give your user permission to access it:

Bash:
sudo chown -R $USER:$USER ~/.ssh
sudo chmod 700 ~/.ssh
sudo chmod 600 ~/.ssh/authorized_keys

Now we have to configure a few settings in the sshd service to enable these changes. Open up the SSH configuration file:

Bash:
sudo nano /etc/ssh/sshd_config

And change the following settings in the file. With the following changes, we enforce SSH key login and disable root and password login.

Old setting valueNew setting value
Include /etc/ssh/sshd_config.d/*.conf#Include /etc/ssh/sshd_config.d/*.conf
#LogLevel INFOLogLevel VERBOSE
PermitRootLogin yesPermitRootLogin no
#PubkeyAuthentication yesPubkeyAuthentication yes
#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2AuthorizedKeysFile .ssh/authorized_keys
#PasswordAuthentication yesPasswordAuthentication no
#AllowAgentForwarding yesAllowAgentForwarding no
#AllowTcpForwarding yesAllowTcpForwarding no
X11Forwarding yesX11Forwarding no

Save the changes by pressing Ctrl+O → Enter → Ctrl+X. Then restart the SSH service:

Bash:
sudo systemctl restart sshd

Now try accessing your server via SSH. Again, I recommend doing this via a new terminal so you don't accidentally lock yourself out. It should no longer work with a username and password. Instead, we have to provide both the port number and our SSH key file. Simply do the following to access the server now, where "<privateKey>" is the file path of your private key file ("id_ed25519").

Bash:
ssh <user>@<ipAddress> -p 38291 -i <privateKey>

If prompted for your SSH key passphrase, simply type it in and you should be signed in to your server. SSH keys has now been installed properly.



Section 4: Installing a firewall

To install and enable a firewall on the server, we can use the "Uncomplicated Firewall" (ufw) software. Let's install it on our server:

Bash:
sudo apt update
sudo apt dist-upgrade
sudo apt install ufw

Now let's start it up and adjust a few settings. We will set to only allow port 80 and 443 (for website access), our SSH port (38291) and our OT server port (7171). We will also add rate limiting to all SSH access attempts. Run the following to do this:

Bash:
sudo systemctl start ufw
sudo systemctl enable ufw

sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 38291/tcp
sudo ufw limit 38291/tcp comment 'SSH Rate Limiting'
sudo ufw allow 7171/tcp
sudo ufw enable
sudo systemctl restart ufw

Double check that the firewall status is active and lists the port rules we set up.

Bash:
sudo systemctl status ufw
sudo ufw status verbose

The firewall has now been setup and it's automatically enabled whenever the server starts as well. If you make any changes to the firewall rules, remember to restart the firewall service.



Section 5: Mitigating brute force attacks

To stop bots from trying to access your server, let's setup Fail2ban on the server. It's a popular tool that is used to look through logs and detect bots performing brute force attacks. One of the most common ways servers get breached is through SSH brute force attacks, so it's a good idea to monitor all login attempts. To install Fail2ban, run the following:

Bash:
sudo apt update
sudo apt dist-upgrade
sudo apt install fail2ban python3-systemd

The first thing we have to do is create a few Fail2ban configuration files. For example, we want to make sure we also monitor IPv6 addresses and not just IPv4 addresses. To do this, run the following commands to create the first configuration file:

Bash:
sudo nano /etc/fail2ban/fail2ban.local

And add the following inside the file.

Code:
[DEFAULT]
allowipv6 = yes

Next we want to create our "jail" configuration file. The way Fail2ban works is that we have different so called "jails" which are essentially rules on what services/applications/files to monitor and detect brute force attacks in. In this case, we will create a jail configuration file and set it to monitor the SSH service ("sshd"). You do that by running the following:

Bash:
sudo nano /etc/fail2ban/jail.local

And add the following code to it. This will enable Fail2ban to monitor all authentication attempts on the SSH service. We also exclude local IP addresses from the server. This means that if someone tries to login 7 times within 10 minutes with incorrect credentials, their IP address will be blocked for 7 days.

Code:
[DEFAULT]
backend = systemd
bantime = 2592000
findtime = 600
maxretry = 10

[sshd]
enabled = true
port = 22,38291
bantime = 604800
maxretry = 7
filter = sshd[mode=aggressive]
ignoreip = 127.0.0.1/8 ::1/128 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 169.254.0.0/16

Save the file and start the Fail2ban service and check its status. You should now see that Fail2ban is running and monitoring all SSH login attempts.

Bash:
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
sudo systemctl status fail2ban
sudo fail2ban-client status sshd

If you want to check your SSH logs manually as well, you can use Journald to do that. In older Debian versions, we previously used a log file inside "/var/log/auth.log". But with the release of Debian 12, all logs are now added to Journald instead. To view the SSH logs, run the following:

Bash:
sudo journalctl _SYSTEMD_UNIT=sshd.service + _COMM=sshd



With these changes in place, you have greatly improved the overall security of your GNU/Linux Debian 12 server. There are many more things that can be done, but these changes are sufficient for most users. You can also for example use CrowdSec instead of Fail2Ban. But they are essentially the same. Every Linux server should at least have these changes in place.

Every time you add a service that is targeted by brute force logins, you should add it to Fail2ban. For example a website running behind NGINX.
 
Last edited:
Yet another great tutorial! I was wondering why the old log file did not work in fail2ban. Did not know they changed that to journal. Is it only debian 12 that did it?
 
Yet another great tutorial! I was wondering why the old log file did not work in fail2ban. Did not know they changed that to journal. Is it only debian 12 that did it?

I think Journald is more performant than Syslog, so that's probably why they changed it. And it is a centralized place for all service logs on your system, instead of splitting up into individual log files. By setting the backend = systemd , you tell Fail2ban to log to Journald automatically, which is very convenient.

I have not tested this in other distros, but I would assume any Debian 12 based distribution has this change in place.
 
Thanks to @Gesior.pl and his advice in my other thread regarding NGINX:

If you plan to host your website (port 80 and 443) behind Cloudflare, it may be a good idea to only allow Cloudflare's IPv4 and IPv6 ranges to connect directly to the server IP (not the domain or CF IP). In case anyone ever finds your server's IP address (behind the reverse proxy), then you might get attacked that way as they can just bypass Cloudflare. But by blocking every other IP address and only allowing CF, then you're lowering the risk of getting attacked.

Simply change these two lines in the ufw configuration:
Lua:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

And replace it with:
Code:
sudo ufw allow proto tcp from 173.245.48.0/20 to any port 80,443
sudo ufw allow proto tcp from 103.21.244.0/22 to any port 80,443
sudo ufw allow proto tcp from 103.22.200.0/22 to any port 80,443
sudo ufw allow proto tcp from 103.31.4.0/22 to any port 80,443
sudo ufw allow proto tcp from 141.101.64.0/18 to any port 80,443
sudo ufw allow proto tcp from 108.162.192.0/18 to any port 80,443
sudo ufw allow proto tcp from 190.93.240.0/20 to any port 80,443
sudo ufw allow proto tcp from 188.114.96.0/20 to any port 80,443
sudo ufw allow proto tcp from 197.234.240.0/22 to any port 80,443
sudo ufw allow proto tcp from 198.41.128.0/17 to any port 80,443
sudo ufw allow proto tcp from 162.158.0.0/15 to any port 80,443
sudo ufw allow proto tcp from 104.16.0.0/13 to any port 80,443
sudo ufw allow proto tcp from 104.24.0.0/14 to any port 80,443
sudo ufw allow proto tcp from 172.64.0.0/13 to any port 80,443
sudo ufw allow proto tcp from 131.0.72.0/22 to any port 80,443
sudo ufw allow proto tcp from 2400:cb00::/32 to any port 80,443
sudo ufw allow proto tcp from 2606:4700::/32 to any port 80,443
sudo ufw allow proto tcp from 2803:f800::/32 to any port 80,443
sudo ufw allow proto tcp from 2405:b500::/32 to any port 80,443
sudo ufw allow proto tcp from 2405:8100::/32 to any port 80,443
sudo ufw allow proto tcp from 2a06:98c0::/29 to any port 80,443
sudo ufw allow proto tcp from 2c0f:f248::/32 to any port 80,443
sudo ufw deny proto tcp from any to any port 80,443

You may want to keep an eye on these IP addresses. Cloudflare has not changed them in years but check in on them once a month just to make sure they are up to date. You find these addresses here:


If you already added these two lines to ufw:
Lua:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Then you must first delete those rules:
Code:
sudo ufw delete allow 80/tcp
sudo ufw delete allow 443/tcp

Then restart ufw:
Code:
sudo systemctl restart ufw
 
Back
Top