So you have your SSH server on Linux up and running and you are able to login easily. But you may still be vulnerable to attacks on your SSH server, primary being brute force dictionary attacks. This post will point out some areas of vulnerabilities and also advise some solutions.
Only Allow Certain Users to Login via SSH
Your Linux machine has many users built into it. But only some of them need to have access to the server via SSH. If we can lock down a list of such users, we can reduce the number of users an attacker can pretend to be. For example, there are only three users on my machine who need to login. The good thing is that SSH allows you to setup either a list of users or a group of users who are allowed access.
I prefer the group method. The idea is to create a new Linux group and add users to it. If a user is a member of this group then he/she can login via SSH. If they are not a member they cannot. Then in the sshd config file we can specify this group as being the only one with access. Here’s how we do it.
Create a group
sudo groupadd mysshgroup
Add an existing user to this group. Thanks to Howto: Linux Add User To Group.
sudo usermod -a -G mysshgroup codeghar
Allow access only to this group in /etc/ssh/sshd_config file by making sure the following line exists in it.
Disable root Login
Everyone who knows Linux knows there’s always a root user. So all the attackers out there know they just need to break its password to gain (complete) access to the system. What if we disallow root from logging in directly? This cuts down on a huge threat to your system.
Disable root login in /etc/ssh/sshd_config file by making sure the following line exists in it.
Disable Empty Password
Now that we have disallowed all users except those in the mysshgroup group to login, we have to make sure these users do not use empty passwords. If we don’t restrict this, a user, say codeghar, might have an empty password and an attacker can login very easily. We don’t want that.
Disable empty passwords in /etc/ssh/sshd_config file by making sure the following line exists in it.
Allow Public Key Authentication Only
This means that we disable password-based authentication and only use public key authentication. This completely eliminates dictionary attacks because we just don’t use passwords. This requires a few extra steps, as explained below. Thanks to Setting up public key authentication over SSH for this part of the post.
On your client machine, you need to generate a new public and private key pair using RSA as encryption type and key strength as 4096 bits. You will be asked for a password. You can leave it blank but I recommend you put something in there. The reason is that if your private key gets into the wrong hands, they still need a password to be able to use it.
ssh-keygen -t rsa -b 4096
Now that a public/private key pair has been generated, you can use the keys. You first need to copy the public key from your client machine to your ssh server machine. You copy it to the user’s home directory on the server in the .ssh directory. For example, on the server you would need to copy it to /home/codeghar/.ssh/ directory. If it doesn’t exist, create it.
scp /home/codeghar/.ssh/id_rsa.pub email@example.com:/home/codeghar/.ssh/myclient.pub
On the remote server, do the following so that the public key is appended to the authorized keys file
cat /home/codeghar/.ssh/myclient.pub >> /home/codeghar/.ssh/authorized_keys
Now you can remove the original file because it’s no longer needed.
This has to be done for each client and also for each allowed user on the server. For example, if user codeghar uses two client machines, he/she needs to generate these pairs on each machine and then copy over the public key to the server. And if the server has two allowed users, both need to have these public keys in their own authorized keys files for them to be able to login.
You have copied over the public key but you still need to tell ssh to only use this key for authentication. Disable password authentication and enable public key authentication in /etc/ssh/sshd_config file by making sure the following lines exist in it.
Activate Your Changes
You are now ready to activate these changes. Be aware that if you activate these changes and you made a mistake somewhere, you may lose all access. For this reason, establish an ssh connection first for the ‘just in case’ scenario and do not logout unless you are satisfied all things are working as they should.
To activate the changes, run the following command:
sudo /etc/init.d/ssh restart
Test the changes you have made so far by trying to login using a user who is not in the mysshgroup group, by logging in as root, and by using password authentication. All should work well.
If you are still asked for a password, or you get a Permission denied (publickey) error, then the ssh daemon/process thinks the settings of .ssh directory and/or the authorized_keys file on the server are insecure. To remedy the situation, you should change the permissions as below (hat tip: Public and Private Keys):
chmod 700 /home/codeghar/.ssh
chmod 600 /home/codeghar/.ssh/authorized_keys
Throttle ssh Connections
Although you have taken the aforementioned steps to make your server more secure, attackers are still going to attempt. With the above changes you should be secure but I prefer to add another layer: iptables firewalling. The idea is to restrict three attempts per minute for a new ssh connection from a single IP. This way you still have the ability to try three times a minute in case you provide wrong credentials. But it also means an attacker is restricted to three attempts per minute per IP. It doesn’t completely eliminate brute force attacks but it ought to slow these down.
You need the following three rules. Thanks to Using iptables to rate-limit incoming connections for helping with this section.
sudo iptables --append INPUT --protocol tcp --match tcp --destination-port 22 --in-interface eth0 --match state --state NEW --match recent --set
sudo iptables --append INPUT --protocol tcp --match tcp --destination-port 22 --in-interface eth0 --match state --state NEW --match recent --update --seconds 60 --hitcount 4 -j DROP
sudo iptables --append INPUT --protocol tcp --match tcp --destination-port 22 --jump ACCEPT
The first rule adds the IP of the client machine which initiates a new ssh connection. The second rule checks if this IP has attempted four or more connections in the past 60 seconds. If it has, this attempt is dropped. If it hasn’t, then it is allowed to ssh using the third rule. This way you get to try three times per minute but the fourth and subsequent attempts are blocked for a whole minute.
Other useful resources are: SSH Dictionary Attack Prevention with iptables; ssh – authorized_keys HOWTO; Throttling SSH attacks with pf; Port Knocking – A Cure for the Common SSH Login Attack;