How to Harden SSH
Keep your servers safe with a few extra steps. SSH is essential to server management.
This post will walk you though some of the options available to harden OpenSSH. The instructions may work for other flavors of Linux but has been tested only on Ubuntu 16.04 LTS.
Warning: Messing with how SSH works can be dangerous. You can very easily lock yourself out of the server. Be careful.
OpenSSH Server Configuration
The settings file for OpenSSH on Ubuntu 16.04 is located at
/etc/ssh/sshd_config. You will need to be
root or use
sudo to edit and control the SSH server.
Backup Configuration File
It's always a good idea to make a backup of any configuration files before editing them.
cp /etc/ssh/sshd_config /etc/ssh/backup.sshd_config
Editing the Configuration File
I'm not fancy so, I use
nano for configuration file edits.
SSH Configuration Test
After editing the configuration file you should test that it is valid before reloading the service.
Reload the Configuration File
Once you think your edits are good, reload the SSH daemon.
sudo systemctl reload sshd
Check the Protocol
Our very first edit will be very simple. It is really more of a double check than an edit. Open
/etc/ssh/sshd_config and check the line that starts with Protocol. Make sure it is set to 2 and not 1. The current default is 2.
Instead of using
root, we should be using connecting as user with
sudo permission. Make sure you have
sudo setup properly before continuing. So let’s disable the ability of root to login using SSH. Inside the configuration file find the line:
Change that to no:
Disconnect Idle Sessions
Idle sessions can be dangerous. It is a good idea to log people out after a set amount of inactivity. The
ClientAliveInterval is the amount of time in seconds before the server will send an alive message to the client after no data has been received.
ClientAliveCountMax is the number of times it will check before disconnecting. In the example below, the server will check on the client after 5 minutes of inactivity. It will do this twice then disconnect.
ClientAliveInterval 300 ClientAliveCountMax 2
We can limit the users that are allowed to log in SSH. This is a whitelist. Only users in this list will be allowed. Everyone else will be denied. Let’s say that I want to allow user
norton to log in remotely through SSH. We will add the line:
Don’t forget to add your username to the
My second least favorite way of hardening SSH is changing the default port. Normally SSH runs on port 22. The idea is that most script kiddies are only going to target that port. If you change you default port, maybe your attacks will decrease. I don’t do this or recommend it. But, maybe you disagree. In the configuration file find the line:
Then change it to another available like maybe 2222.
By default you log into the system through SSH with a username and a password. These can be brute forced. People will try an enormous amount of username and password combinations until they find one works. So, instead of using passwords we should use SSH keys.
Generating a Key Pair
If you already have a key pair, skip ahead.
We are going to make some public key encryption keys. They come in pairs. Private and public. If you are not familiar with this system of encryption, than check out my video, A Very Brief Introduction to Public-key Cryptography.
Run the following command to generate your keys on the client machine. Do not run this command with
sudo. It will ask you for a passphrase to protect the key. You can keep this blank but I do not recommend that. A private SSH key with no passphrase protection can be used by anyone with possession of that key to access the server.
Share Your Public Key
Use ssh-copy-id to send you public key to the server.
ssh-copy-id [email protected]
Now try logging in. You may be asked for your passphrase.
You should get a message back that looks similar too:
The authenticity of host '192.168.1.1 (192.168.1.1)' can't be established. ECDSA key fingerprint is ff:fd:d5:f9:66:fe:73:84:e1:56:cf:d6:ff:ff. Are you sure you want to continue connecting (yes/no)?
Say yes and you should be logged in without a password.
Disable Password Authentication
If we have SSH keys working we can just disable all password authentication. Find the line:
And change that to no.
This guide is intended for use with remote servers. Generally speaking, there is no reason to use a GUI for a remote server. So disable X11 forwarding. Find the line:
and change that to no.
This is a great program that can scan logs and ban temporarily ban IPs based on possible malicious activity. You will need to install Fail2ban.
apt-get install fail2ban
Once installed, we copy the fail2ban configuration file.
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
/etc/fail2ban/jail.local files and find the spot that starts
[sshd]. Edit it like so, adding
enabled = true:
[sshd] enabled = true port = ssh logpath = %(sshd_log)s
Then restart fail2ban
service fail2ban restart
Fail2ban will monitor your SSH logs for possible malicious activity and then temporarily ban the source IP.
We can also user TOTP (Time-Based One-Time Passwords) to harden our SSH security. In this example we will be using Google Authenticator. When we attempt to log into the system we will be challenged to provide a verification code. We will use the Google Authenticator app to generate that code. First we need to install some software.
sudo apt-get install libpam-google-authenticator
Then run the initialization
It will ask:
Do you want authentication tokens to be time-based (y/n) and we need to say yes. Then it will print out the QR code and ask if want to update our
.google_authenticator file. We do.
Don’t worry this code was never used and the server no longer exists.
Scan that code into the Google Authenticator app and save those emergency codes! You will next be asked a few more question. We will answer them all with yes.
Do you want to disallow multiple uses of the same authentication token? This restricts you to one login about every 30s, but it increases your chances to notice or even prevent man-in-the-middle attacks (y/n) y By default, tokens are good for 30 seconds and in order to compensate for possible time-skew between the client and the server, we allow an extra token before and after the current time. If you experience problems with poor time synchronization, you can increase the window from its default size of 1:30min to about 4min. Do you want to do so (y/n) y If the computer that you are logging into isn't hardened against brute-force login attempts, you can enable rate-limiting for the authentication module. By default, this limits attackers to no more than 3 login attempts every 30s. Do you want to enable rate-limiting (y/n) y)
Edit the PAM rule file
/etc/pam.d/sshdadding the follow at the end:
auth required pam_google_authenticator.so
Edit the ssh configuration file.
UsePAM yes ChallengeResponseAuthentication yes
And restart the SSH server. The system will now require a verification code when you log into the server.
Banners and MOTD
My least favorite way of hardening SSH is adding legal mumbo jumbo to the ssh banner and MOTD. Usually some redundant statement saying “unauthorized access is prohibited”. This is security theater. The advice is usually given by the same armchair lawyers who add “confidentiality notices” at the end of emails. If an actual US lawyer says that you need this to be protected by the CFAA, you need a new lawyer. I think this myth originated from a poor understanding of the UK’s Computer Misuse Act 1990 and basic legal jurisdiction. Also if you ask an undercover cop if they are a cop, they don’t have to say yes.
Often people will talk about the banner leaking system info. They will leave out the fact that the Banner is disabled by default in Ubuntu 16.04. Let us enable it to see what happens. This banner is sent out before authentication. Everyone attempting to connect through SSH will see this banner. You may need to enable PasswordAuthentication to see the banner.
Edit the SSH configuration file then find and uncomment:
Now try to connect to the server with a fake user:
We receive back.
Ubuntu 16.04.3 LTS [email protected]’s password:
As you can see the system has announced some system info.
We can edit this message by editing
/etc/issue.net. I am going to add a little ascii art bunny to welcome my “guests”.
______________________ | | | Welcome Leet Haxor | |____________________| || (\_/) || ( *,*) || (")_(")
Now try to connect to the server with a fake user:
ssh [email protected]
And get the banner message greeting:
______________________ | | | Welcome Leet Haxor | |____________________| || (\_/) || ( *,*) || (")_(") [email protected]'s password:
Those hacker now will think twice before messing with us.
After logging in users are show the message of the day (MOTD). It will look something like this:
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.13.17-x86_64-linode69 x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage Last login: Mon Feb 19 16:01:33 2018 from 192.168.1.1
Ubuntu 16.04 uses a dynamic MOTD. We will just be editing the header of the message. Before we change the anything, let’s make a backup of that original header.
cp /etc/update-motd.d/00-header /etc/update-motd.d/backup.00-header
/etc/update-motd.d/00-header add the following to the end of the file:
figlet "No Trespassing"
Now when we connect we get:
Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.14.17-x86_64-linode99 x86_64) _ _ _____ _ | \ | | ___ |_ _| __ ___ ___ _ __ __ _ ___ ___(_)_ __ __ _ | \| |/ _ \ | || '__/ _ \/ __| '_ \ / _` / __/ __| | '_ \ / _` | | |\ | (_) | | || | | __/\__ \ |_) | (_| \__ \__ \ | | | | (_| | |_| \_|\___/ |_||_| \___||___/ .__/ \__,_|___/___/_|_| |_|\__, | |_| |___/ * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage
Now we are in compliance with the laws of make believe.
So far we have been covering the basics. Now we move into some more advanced SSH hardening. SSH Audit is a Python script that will scan your SSH server for some security issues. Download it and run it like any other python script, just point it at your target SSH server.
python ssh-audit.py labs.seattlebot.net
Then we get a rather large report.
This report gives us a peek behind the SSH curtain. This is a report on the ciphers and algorithms used by your SSH server to secure communications with the client. If you have done work with OpenSSL some things might look familiar. As you may have learned using OpenSSL, not all ciphers and algorithms are equal. Some are strong and some are weak. Eliminating the weak ones can help harden your system.
HeadingChange Hostkey Preference
We will be following the advice of stribika, mozilla, and the SSH audit report. We will change our HostKey preferences. Remove the current HosyKey entries in the ssh configuration file. Replace them with the following.
HostKey /etc/ssh/ssh_host_ed25519_key HostKey /etc/ssh/ssh_host_rsa_key
HeadingChange Default Ciphers and Algorithms
Continuing to follow the advice of stribika, mozilla, and the SSH audit report. We change our Key exchange algorithms, symmetric ciphers and, message authentication codes. Add or replace the following to the ssh configuration file.
KexAlgorithms [email protected] Ciphers [email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr MACs [email protected],[email protected],[email protected]
Rerun the Audit
Let us see if our changes made the SSH audit happy.
python ssh-audit.py 18.104.22.168
Green is good. That is looking better.
/etc/ssh/moduli file contains prime numbers and generators used by the SSH server for the Diffie-Hellman key exchange. Your current
/etc/ssh/moduli is probably not unique. Generating a new file may harden your server. Generating these file might take awhile.
ssh-keygen -G moduli-2048.candidates -b 2048 ssh-keygen -T moduli-2048 -f moduli-2048.candidates cp moduli-2048 /etc/ssh/moduli rm moduli-2048
Hopefully you have found this useful and your server will now be just a bit more hard to break into now. There is much more to learn about OpenSSH. Good Luck.