A web server is just another computer and requires basic security configuration. In the first part, we show you how to secure your SSH access and configure your firewall. You can use this configuration for any type of server.
- Step by step guide for basic server security
- External files
Always stay in the loop!
Subscribe to our RSS/Atom feeds.
- server with operating system installed (e.g. Debian, Ubuntu, CentOS)
- basic knowledge of shell commands
- SSH client on your client
- security token (YubiKey, Nitrokey) or app (FreeOTP) with OATH-TOTP support
- nmap (optional)
We will use Debian 9 in this series.
Step by step guide for basic server security
First of all, you have to install a server operating system. After that, you will be able to log in using SSH on your computer:
$ ssh root@[IP address of your server].
When connecting for the first time, SSH will ask for your confirmation that you connect to the right server. Type
yes. After confirming, you have to enter your initial password. That’s all.
Step 0: Check for updates
When you are connected to your server for the first time, use
apt (Advanced Package Tool or the package manager of your OS) to check for updates:
# apt update and install updates, if available.
Step 1: Install and configure ufw (your firewall)
Download and install ufw (Uncomplicated Firewall) since ufw is one of the easiest ways to configure your firewall:
# apt install ufw.
Then, you can check the status of ufw:
# ufw status which will most likely show
Change the default rules to:
# ufw default deny incoming
# ufw default allow outgoing
Now you have to enable the SSH port 22:
# ufw allow ssh
However, this enables this port for everyone on the internet. Every possible IP address can connect to port 22 of your server. There are hundreds of automated bots on the internet which try to connect to port 22 in order to get into your server. We observed these bots for two hours and saw about 8,000 login attempts. A good practice is to enable SSH just for your personal IP address:
# ufw allow from [IP address of your computer] to any port 22
The problem is that your ISP may change your public IP address from time to time. This means that you are unable to connect to your server until you get your configured IP address back. You can observe your IP address for several days and most likely you will see that it doesn’t change much. So, you likely want to allow a subnet to connect with your server:
# ufw allow from [IP address of your computer]/[subnet] to any port 22
This is the common CIDR notation. Your suffix [subnet] will be “24” to “26” in most cases. This drastically restricts the number of hosts which can connect to your port 22.
Finally, you have to start and enable ufw:
# ufw enable. Confirm that this command may disrupt existing SSH connections by entering
y. You should see
Firewall is active and enabled on system startup.
Please note that we only allow connections to port 22 so far. Later, we will also enable ports 80 (HTTP) and 443 (HTTPS), so people can connect to your website. If you don’t plan to run a web server but other server software, only allow the according port number(s).
Step 2: Create a non-root account
A basic security measure of all operating systems is to use non-root accounts for the most time. This ensures that an attacker doesn’t get all privileges if he exploits a program which runs in a non-root context.
On Debian, enter
# adduser [username]. You will be prompted for a new password twice. Skip the user information by pressing Enter several times if you are the only user of this server and confirm this by pressing
The new user doesn’t have the possibility to get administrative rights by default. To change this, you have to add the new user to the
sudo group (other operating systems call this group
# usermod -aG sudo [username] to add the new user to the
sudo group. You can see all groups of a user by entering
# groups [username]. In some cases, you also have to install sudo itself:
# apt install sudo.
Finally, switch to your new account:
# su [username]. From now on, you have to explicitly enter root mode by preceding each command with
sudo, if necessary.
Step 3: Harden your SSH configuration
The default SSH configuration allows password-based and root login. It is considered more secure to allow only non-root users and use keys instead of passwords. Furthermore, there are dozens of legacy algorithms allowed by default which we disable.
Step 3a: Switch from passwords to keys
You have to decide if you want to use RSA keys or newer Ed25519 keys. There is no clear recommendation for either RSA or Ed25519 and there is no big difference when using them.
Generate a key pair on your client, not on your server:
$ ssh-keygen -o -f ~/.ssh/id_[servername] -t rsa -b 4096(4096 bit RSA key pair)
$ ssh-keygen -f ~/.ssh/id_[servername] -t ed25519(256 bit Ed25519 key pair)
This will generate either a RSA or Ed25519 key pair and store it in your local
~/.ssh folder. You can optionally enter a password which you have to enter each time you use your SSH key to connect to your server. You will find two files:
You now have to copy the public key to the server by using the following command:
$ ssh-copy-id -i ~/.ssh/id_[servername].pub [username]@[IP address of your server]
You will have to enter the password of
[username] and finally you will see: “Number of key(s) added: 1”. Immediately, open a second terminal windows and try to connect to your server without logging out before:
$ ssh [username]@[IP address of your server]. If configured correctly, there will be no password prompt since your new key is used for authentication.
Step 3b: Configure sshd
When you successfully checked that your new key is in use, connect to your server using SSH and open the configuration file of sshd:
/etc/ssh/sshd_config. Be sure that you actually open
ssh_config (no d) is the configuration file for clients.
Change the file accordingly:
PasswordAuthentication no(disable password-based login)
PermitRootLogin no(disable root login)
LogLevel VERBOSE(log login attempts)
# Ciphers and keying add:
# Authentication: add:
$ sudo systemctl restart sshd (or use the command of your operating system). Immediately, open a second terminal windows and try to connect to your server without logging out before:
$ ssh [username]@[IP address of your server]. If configured correctly, you are logged in without any prompts or errors.
Step 4: Enable 2FA for SSH
First of all, we have two install
libpam-google-authenticator. This is a module for PAM (pluggable authentication module):
$ sudo apt install libpam-google-authenticator. This also installs
After installing, configure the module by entering:
$ google-authenticator. When asked “Do you want authentication tokens to be time-based (y/n)”, confirm by pressing
Recommended configuration is:
- Do you want me to update your “/home/[username]/.google_authenticator” file (y/n) →
- Do you want to disallow multiple uses of the same authentication token? →
- … we allow an extra token before and after the current time … Do you want to do so? (y/n) →
- Do you want to enable rate-limiting (y/n) →
Now, scan the provided QR code using a TOTP app. Write down the emergency codes and store them in a secure place.
OMG, there is a Google link and Google knows my secret! Yes, there is actually a Google link. You will find your QR code there, on a Google website. Does this mean that Google knows your secret? No, since this link is generated locally on your server. If you don’t click it, no data will be transferred to Google. Google can display any QR code using this URL.
Finally, we must tell SSH that there is a 2FA PAM module. Open your
/etc/pam.d/sshd file on the server:
auth required pam_google_authenticator.soto the bottom of the file
Save the file and quit.
/etc/ssh/sshd_config as before. Change:
Save all changes, quit and restart sshd:
$ sudo systemctl restart sshd. When you try to connect to your server in future, there will be a “Verification code:” prompt. Enter the TOTP generated by your app or your emergency codes when you lost your app/phone etc.
On GitHub, we provide a Gist showing a minimal configuration file: sshd_config on GitHub.
Step 5: Backup your SSH keys
As always, it is important to backup your cryptographic keys. On your client, go to
~/.ssh/ and backup all files in there. You should also backup the
/etc/ssh/sshd_config of your server.
Step 6: Test the configuration
Finally, we want to test our configuration. Most of the configuration like “Does SSH use my public key?” or “Does SSH use 2FA?” can directly be tested by restarting sshd and connecting with your server again. But you can’t see if SSH uses only modern algorithms.
There are several online tools available, however, we blocked most IP addresses. So we have to use a tool on our local computer which is allowed to connect to port 22.
nmap is perfect for this test. Enter
nmap -Pn --script ssh2-enum-algos [IP address of your server].
nmap will show:
- kex_algorithms: (2)
- server_host_key_algorithms: (1)
- encryption_algorithms: (3)
- mac_algorithms: (3)
- compression_algorithms: (2)
Optional: Configure your local SSH client
You can also tell your client to only use modern and secure algorithms. Open the
~/.ssh/config file on your client and add:
Furthermore, add this to your local
/etc/ssh/ssh_config file for all users, if needed.
This article is part of the "Web server security" series.
Read other articles of this series.
Done! You configured your firewall, so only your client can connect with your server using port 22. Moreover, you hardened your SSH configuration and tested it. Finally, you created a backup. An attacker has find a valid IP address to connect with your server and then he has to provide the correct private RSA/Ed25519 key and the TOTP.
Advanced users can set an SSHFP record (Secure Shell fingerprint record) on their DNS server.
In an upcoming part of this series, we will show you how to use Fail2ban to block brute-force attempts.