Tutorial: How to Secure the SSH Server
Here is a summary of my personal most important hardening tips concerning the SSH-Server. I want to share the steps with you, and also take them as a reminder for me ;).
I want to harden 3 Parts: The Authentication, the Key-Exchange Algorithms and the the Bruteforce Surface.
If you order a new server, you will most likely get your credentials for the ssh login via mail or the server-control-panel. On every machine I ordered so far, it's the login for the "root" user with a password. This is by far not a secure way to run a server nowadays, so let's change that!
1. Disable the Login as Root
As the "root" user has all the power it needs to just delete your server in a second, we don't want that someone can login as "root" via ssh.
But before we can disable the root-login we have to create a new user!
Create a New User
First, we create a new user which belongs to the group "users" with a working home directory:
useradd -g users -d /home/[username] -s /bin/bash [username]
Now the user gets a password:
passwd [username]
And we need to create the home-directory and give the newly added user the ownership of it:
mkdir /home/[username]
chown [username]:users /home/[username]/
Optional: Add user to the "sudo" group:
usermod -aG sudo [username]
This is the easiest way and is used on Ubuntu/Debian... CentOS for example uses the "wheel" group. Just use a search engine to lookup the right command for you.
sudo
command. These users need to authenticate after the first sudo
with their password.Now we exit
and try to login as the new user with the new password.
PermitRootLogin no
We are going to edit the sshd configuration now with our preferred text editor. I'm using nano:
sudo nano /etc/ssh/sshd_config
We change
PermitRootLogin yes
to
PermitRootLogin no
and save the config file.
sshd -t
command. A sample error message:
/etc/ssh/sshd_config: line 26: Bad configuration option: PermitRootLogins
If there is no output after
sshd -t
there is also no syntax error.The ssh-daemon needs to get restarted!
sudo service ssh restart
(Ubuntu/Debian)sudo service sshd restart
(Fedora,CentOS)
Now we can test, if the login as the root user is still possible or not!
2. Only allow PubKeyAuthentication
We can add another layer of security, if we just permit the login with a keypair. So nobody can login with just a password no more.
Create the Keys
On Linux/OSX
To create the keys we can use:
ssh-keygen -t ed25519
Now we can enter the filename in which the private key should be saved, or just hit enter for the default .ssh/id_ed25519 keyfile.
We are asked for a passphrase now. This step is optional, though I prefer to also use a passphrase for the key!
After that the public key is saved, for example as .ssh/id_ed25519.pub
ssh-keygen
tool you can create keys based on different algorithms. In my example above, I decided to create a key based on the ed25519 algorithm. If you want to read more about the different key algorithms, I can recommend a short article by Nicolas Béguir hereOn Windows
On Windows we need the tool "Puttygen". It's part of the Putty package and available here
If we start "Puttygen", we can decide which type of key we want to generate, for example RSA, DSA, ECDSA, ED25519 and the number of bits. We pick our flavor and hit the "Generate" button.
Now we need to move the mouse to add randomness to our keys as long as the progress bar becomes fully green.
We can specify a passphrase if we want to.
Now we click the "Save private key" button to save the private key.
On the top of the Puttygen window we can see the public key for pasting into OpenSSH.
On Android/iOS
We can also create our keypairs on our Smartphone. I've used the apps Termius and JuiceSSH to create the keys succesfully.
Add the public key on the server
Now that we created the private and public keypair, we need to add the public key to our user, which should login with it.
To do so, we login as the user and create the .ssh folder into the home directory
mkdir .ssh
now we copy our public key out of the created .pub file or Puttygen and paste it to the file
.ssh/authorized_keys
for example we copy the public key out of the Puttygen window, then use nano on the server:
nano .ssh/authorized_keys
Paste the key, save the file and we are set.
Now need to edit/check the sshd config file again:
sudo nano /etc/ssh/sshd_config
and we change (if needed)
PubKeyAuthentication no
to
PubKeyAuthentication yes
We save the file and restart the ssh-daemon again:
sudo service ssh restart
(Ubuntu/Debian)sudo service sshd restart
(Fedora,CentOS)
Now we try to login with the private Key!
Disable Password Logins
To completely disable the possibility to login with just a password, we need to edit the sshd config file again:
sudo nano /etc/ssh/sshd_config
and we edit
PasswordAuthentication yes
to
PasswordAuthentication no
Save the config and restart the ssh-daemon again.
3. Change the SSH-Port
Changing the port basically doesn't increase the security, but it will reduce the amount of bots trying to login.
Edit the configfile:
sudo nano /etc/ssh/sshd_config
And right at the beginning, there is the Port option we want to change from 22 to a port starting at 1024, for example:
Port 22
to
Port 31337
Restart/Reload the ssh-daemon to apply the changes.
4. Other Options to edit
Here I will explain other edits I've added to my configuration:
MaxAuthTries 3
3
as value, because my brain should handle that.LoginGraceTime 30
X11Forwarding no
PermitEmptyPasswords no
5. HostKeys, HostKeyAlgorithms, KexAlgorithms, MACs
The default configuration of the ssh-daemon is, like the default configuration of the majority of applications, not mainly focused on security, first of all it's compatibility.
As I found a great tool called ssh-audit. I discovered four settings I want to share with you, to harden the ssh-authentication:
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
As default, the sshd server is using the rsa, ecdsa and ed25519 hostkeys. As NIST P-curves (ecdsa) are possibly backdoored, we just uncomment the keys for rsa and ed25519.
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
The default HostKeyAlgorithms cover also questionable ones, like ssh-rsa, which is exploitable (SHA-1) and ecdsa-sha2-nistp256, which is, possibly, back-doored. We don't want to use these.
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Kex stands for Key EXchange. The default configuration also covers questionable algorithms, like NIST P-curves, which are, possibly, back-doored. I will just use curve25519 ones.
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
MAC stands for Message Authentication Code. These algorithms are used for data integrity protection. I will only use MACs with the "-etm" tag, as these "encrypt-then-mac" and not "encrypt-and-mac". etm MACs are considered more secure in terms of some attack types.
Restart/Reload the ssh-daemon to apply the changes.
A list of available options can be found in the official docs >>here<<
6. Fail2Ban
Fail2ban is an application which scans your logfiles for failed logins orother suspicious activity and bans IP addresses with too many fails for a specific ammount of time. It bans them by updating the system's firewall, in our example iptables
.
So basically Fail2Ban limits the unauthorized login attempts, most likely caused by botnets.
Installation
Debian/Ubuntu
sudo apt install fail2ban
Fedora/CentOS (EPEL Repo)
sudo yum install fail2ban
Default Configuration
Fail2Ban has a default configuration file - jail.conf. But this file might get overwritten by updates. So the tool also uses the jail.local file which stays persistent.
First we need to create the jail.local. We do this by copying the jail.conf file:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
And now we edit the configuration with our favorite text editor:
sudo nano /etc/fail2ban/jail.local
Scroll down the [DEFAULT] section and you maybe want to edit these values:
bantime = 720m
Bantime: The time an IP gets banned after too many fails. I set this value to 12 hours
findtime = 120m
Findtime: The time window of the failed login attempts. I set this value to 2 hours
maxretry = 3
MaxRetry; The maximum ammount of failed logins between the findtime. I set this value to 3 attempts
Our config:
An IP gets banned for the next 12 hours, if 3 failed logins were logged from this target during the last 2 hours.
We need to restart fail2ban now, to apply the new config:
sudo service fail2ban restart
The SSHD Jail
In my jail.local the sshd jail is enabled by default (scroll down the config the see the relevant settings)
[sshd]
port = sshd
logpath = %(sshd_log)s
backend = %(sshd_backend)s
fail2ban-client
Fail2Ban also has a client which you can use via ssh to check the status of the application:
sudo fail2ban-client status
This command displays the overall status of fail2ban, in our case:
Status
|- Number of jail: 1
`- Jail list: sshd
If we want more information about the sshd jail:
sudo fail2ban-client status sshd
and the output is something like this:
Status for the jail: sshd
|- Filter
| |- Currently failed: 1
| |- Total failed: 3
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: [the banned IPs here]
It seems that fail2ban is working, as it already banned one IP. YWe could also backcheck with sudo iptables -L
and look at the "CHAIN f2b-sshd" - it should also include a REJECT rules for the specific IP.
Conclusion
Here is a summary of the things we have done during this little tutorial:
We hardened:
- the login
We forbid the login as the "root" user. The server only accepts logins as existing users and only via public key.
- the authentication / key-exchange
We deleted the unsafe algorithms and only use modern key algorithms for the authentication.
- the ammount of failed logins / bruteforce attempts
Fail2Ban blocks the IPs of malicious acting targets for a specific amount of time. Therefore these hosts can't try (bruteforce) more login attempts.
Keep in mind that my configuration is likely not a perfect one. It just only reflects my preferences.
Thanks for reading, I hope I motivated you to dig into the sshd_config a little more.
Links
https://man.openbsd.org/sshd_config
https://github.com/jtesta/ssh-audit
https://www.fail2ban.org/wiki/index.php/Main_Page
https://nbeguier.medium.com/a-real-world-comparison-of-the-ssh-key-algorithms-b26b0b31bfd9
Comments