How to Harden SSH Access to a Linux Box

In a previous article I explained how to create and manage Linux user accounts. For better security, root access and password authentication should be disabled, and Fail2Ban should be installed and configured. These steps will significantly reduce the odds of a successful brute-force SSH attack on your server.

Disable root access

Stopping anyone from being able to SSH into a Linux box as the root user is all that is needed to prevent the most common form of brute-force attack.

Before you disable root access, be sure to create for yourself a limited user account with root-level privileges. Follow these instructions.

SSH into the box using your limited user account. Elevate to root and edit the SSH configuration file in your favourite text editor.

sudo su -
vi /etc/ssh/sshd_config

Find the PermitRootLogin setting and change its value to no.

PermitRootLogin no

Save your changes.

Restart the SSH service.

systemctl restart sshd.service

Logout of your limited user account and try logging in again as root. It should fail with an "Access Denied" message.

Disable password authentication

Now all users must login to the server using their individual limited user accounts. Brute-force attacks are still possible, so for the ultimate security users should be required to submit passphrase-protected private key files, in place of traditional passwords, when they SSH into the box.

Implement the following steps for your own user account, then repeat these steps for every other user. Be sure to generate a unique public/private cryptographic key pair for each user.

Public and private key pairs can be generated on the server, but in these instructions I'll explain how to generate them on a desktop computer using PuTTYgen. This is a free Windows application, but similar tools are available for other operating systems.

Download PuTTYgen from here: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html

Open PuTTYgen. Leave the defaults in place and click Generate. Follow the instructions about wriggling your mouse around to create some randomness. PuTTYgen will generate a strong public key, which you can see at the top of the dialog box.

In the "Key comment" field add a comment that describes the key. I like to use this to write the user's full name and the current date, e.g. "Kieran Potts 2017-04-01". It tells me who the key was for and when it was made. This comment will be embedded within both the public and private keys, and it will be shown to the user when he tries to login to the server.

Copy the public key from the top of the PuTTYgen dialog box into a text file, and save it to your desktop. You will need it later. Do not use the "Save public key" option, which exports the public key string in the wrong format. Instead, copy the text at the top of the PuTTYgen window, and be sure to copy the full text. The public key will look something like this:

ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAklmPYUPtR8nn+pDj/LoSXG....094vw== Kieran Potts 2017-04-01

Now to generate the accompanying private key. Whereas the public key will be stored on the server, the private key will be issued to the user, who will use it to SSH into the box. For ultimate security, private keys should be protected with a strong passphrase.

Enter a passphrase in the "Key passphrase" text field, and enter it a second time to confirm. The passphrase can be any combination of letters and numbers. Make it a good one.

Click on the "Save private key" button. This will save the private key, which is passphrase protected, to your desktop as a .ppk file. I tend to use the Linux username as the filename, e.g. kieranpotts.ppk.

Close PuTTYgen.

Login to your limited user account using PuTTY or KiTTY or an SSH client of your choice. Elevate yourself to root.

sudo su -

Navigate to the relevant user's home directory. Use the following command, replacing <username> with the correct username.

cd /home/<username>

The combination of commands shown below will create directory called .ssh in the user's home directory, add a blank file called authorized_keys to it, and set appropriate access permissions and ownership.

mkdir .ssh
touch .ssh/authorized_keys
chmod 700 .ssh
chmod 600 .ssh/authorized_keys
chown -R <username>:<username> .ssh

Edit the newly-created authorized_keys file with a text editor such as vi.

vi .ssh/authorized_keys

Copy the contents of the public key, which you saved to your desktop earlier, into the authorized_keys file. Again, be sure to copy-and-paste the full text of the public key. If you miss a bit, the key will be corrupted. (Multiple keys may be entered into this file. Start each key on a new line.)

Save the authorized_keys file and logout of the box. Now try logging in again. This time, login to the user account that you are hardening, which may or may not be your own account. And this time, use the private key to connect. To do that, you need to tell your SSH client about the private key file. In PuTTY or KiTTY, go to Connection > SSH > Auth, and type the local path to the "private key for authentication".

SSH into the box with the username for the account for which you are applying the cryptographic key pair. You should see a message similar to "Authenticating with public key 'Kieran Potts 2017-04-01'", and then you will be prompted for the "Passphrase for key 'Kieran Potts 2017-04-01'" rather than being asked for the user's normal password. Type the passphrase that you used earlier to encrypt the private key. You should now be logged in. If instead you see the message "Server refused our key", something is wrong will the configuration and you should repeat the steps above to resolve the issue. For now, you can still login with the user's normal password.

The public/private key pair and associated passphrase act as a proxy for the user's true password. However, users who sudo into root privileges will still be prompted for their normal password to confirm the action. So, for each user account there will be a password an a passphrase-protected private key file. It is important to understand that the user's password and key passphrase are different things. All of this information needs to be stored securely locally. Ideally, private key files and their corresponding passphrases should be kept separate. Public keys need only be stored on the server – there's no need to keep local copies of these.

Repeat the steps above for every user account. Be sure to generate a new public/private key pair for each user. When everyone has been issued with their private keys and passphrases, and you have tested that everyone can SSH into the box successfully with these credentials, you can safely disable password authentication. This will prevent anyone from being able to SSH into the box using their normal password. After this step, all users will need to use their private key and passphrase for SSH access. (They will use their password only to confirm root-level commands.)

With root privileges, open the SSH configuration file in vi.

vi /etc/ssh/sshd_config

Find the PasswordAuthentication setting and change its value to no. Check that the line is un-commented by removing the # in front of it, if there is one.

PasswordAuthentication no

Restart the SSH service.

systemctl restart sshd.service

Logout of the box, and try logging in again to a limited user account using the user's standard password (do not submit the private key). It should fail with the message "No supported authentication methods available".

Pageant

At this juncture, it is worth me mentioning Pageant.

Pageant is a handy Windows tool that makes it simpler to authenticate with servers when you've got lots of SSH keys to manage. Pageant runs in the background and holds one or more private keys in memory. Several popular SSH clients including PuTTY, FileZilla and WinSCP will automatically read private keys from Pageant when it is running. The effect is that you don't need to remember to provide the relevant key file and passphrase every time that you connect to a server.

You can download Pageant from here: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html

On startup, Pageant is immediately minimized to the Windows system tray. Right click on its icon and select Add Key. Choose a .ppk file. If the private key is passphrase protected, you will be prompted to enter the passphrase.

That's all there is to it. You can add as many private keys as you wish, one for each server that you manage. Try connecting to your server using PuTTY, but don't give PuTTY the private key file. You will be prompted to provide your username, then PuTTYgen will automatically supply the passphrase for the private key. You will be logged in immediately.

Install Fail2Ban

Fail2Ban provides further protection against brute-force SSH attacks. It will automatically blacklist clients by their IP address, preventing them from logging in to a server after too many failed attempts. It works by monitoring system logs, looking for symptoms of automated attacks based on rules that you configure. It will automatically add new rules to iptables, blocking the IP addresses of suspected attackers, either for a set amount of time or permanently. Fail2Ban can also be configured to send email alerts when an attack is suspected.

Fail2Ban can be used to monitor other protocols including HTTP and SMTP, but it is primarily focused on providing protection against SSH attacks.

Fail2Ban is not available from the default CentOS package repositories, but it is available from EPEL (Extra Packages for Enterprise Linux), a repository dedicated to distributing a high quality set of optional packages for Linux Enterprise distributions, including CentOS and Red Hat Enterprise Linux (RHEL).

sudo yum install epel-release

With the EPEL repository added to the Red Hat Package Manager (RPM), you can now install Fail2Ban using the yum front-end:

sudo yum install fail2ban

For email support you will also need to install Sendmail, if it is not installed already.

sudo yum install sendmail

Enable both Fail2Ban and Sendmail as services, and start them up.

sudo systemctl enable fail2ban
sudo systemctl enable sendmail

sudo systemctl start fail2ban
sudo systemctl start sendmail

Fail2Ban uses a cascading configuration system: .conf files contain the default settings, while .local files extend and override the defaults. So, any configuration changes should generally be done in .local files.

Fail2Ban's configuration files are stored in the /etc/fail2ban directory. In there you will find a file called fail2ban.conf. This file configures Fail2Ban logging, defines the socket used to communicate with the Fail2Ban daemon, and sets the location of the PID file that identifies the process ID of the program. The default settings are pretty good and probably won't need changing. If you do want to adjust these settings, do so in a .local clone.

cd /etc/fail2ban
sudo cp fail2ban.conf fail2ban.local
sudo vi fail2ban.local

There's another configuration file called jail.conf. This sets the criteria by which suspected brute-force attacks are identified, and it determines the jail terms of banned clients. You will want to make changes to the default jail settings. Again, first create a .local clone of the master .conf file, and make your changes there.

cd /etc/fail2ban
sudo cp jail.conf jail.local
sudo vi jail.local

Set the backend property to "systemd".

backend = systemd

The main settings to pay attention to are maxretry, findtime and bantime. The maxretry setting determines how many invalid login attempts can be made from any one IP address before that IP address is blacklisted. The findtime setting determines the number of seconds in which maxtries are allowed. The bantime setting determines the length of time in seconds for which an IP address remains blacklisted; a negative number makes the ban permanent. Adjust these numbers as you see fit. These are the settings that I tend to use:

bantime  = 86400 # 1 day
findtime = 3600  # 1 hour
maxretry = 3

If you want to be alerted by email whenever a client is banned, use the following settings:

  • destemail: The email address where you would like to receive the emails.
  • sender: The "from" email address that Fail2Ban will use when it sends emails. It does not need to be a real, working email address.

Example:

destemail = [email protected]
sender    = [email protected]

The action setting defines what actions occur when a client is banned.

  • %(action_)s: Bans the client.
  • %(action_mw)s: Bans the client and sends an email with a WHOIS report.
  • %(action_mwl)s: Bans the client and sends an email with a WHOIS report plus all relevant lines from the log file.

Example:

action = %(action_mw)s

Close and save the jail.local file and restart the Fail2Ban service for the changes to take effect.

sudo systemctl start fail2ban