Sun. Jul 14th, 2024

Anyone with a linux server has experienced alot of login tries in SSH. A quick “fix” for that is to change the default port away from 22, but even then, someone WILL try to bruteforce you.

It is rather simple to ensure that noone will bruteforce your server. Let’s first discuss the differences in authentication factors. In general there are up to three factors of authentication.

  • Knowledge – Something you know, which can be told to others. (Password, PIN etc)
  • Possession – Something you have, which can be handed to others (Keys, magnetic stribe cards, smart cards etc.)
  • Inherence – Something you are, which cannot be given or told others. (Biometric, eye scanning, fingerprint etc.)

Most services in the world is based on knowledge, since you got a username and password. Both can be told to others. This is also most likely the most used authentication when using SSH. Knowledge can rather easily be brute forced, stolen, fished and so on which is why we need a second factor. Just like most banks who require you have a username/password and a keycard or similar.

The inherence factor is very rarely used for consumers. That said, many people have the fingerprint reader on their laptop. This is normally not used, and hard to implement, for SSH.

This means we have two factors left – knowledge and possession – which is perfect since this post is regarding two factor authentication. Most likely you will already use passwords, so this factor will not be covered here. Not much to cover really.

In this post I will cover two different possibilities of two factor authentication. Keys and validation.


I’ve always liked keys, because they are supported almost everywhere. There are only two things you need to master when handling keys. How to store them and how to name them. Many people has several keys for different purposes/services to increase security. Here, I will only cover a single key pair.

First, we need to create the keys. On your linux/unix/mac write this command

ssh-keygen -t rsa

It will ask your for a location and filename to save the file. Just press enter.

Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):

Next, you will be prompted for a password. You should always use passwords for your keys. If you dont, it’s no better than just using a username/password login instead. To password protect your keys is what ensure you have two factor. Because you need to key and you need the password for the key also. Neither will work on their own. So enter that password now!

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

Now you are set. You should get this output:

Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/
The key fingerprint is:
04:03:8f:0f:82:3d:3e:06:05:29:34:7b:d6:51:9d:92 root@vps
The key's randomart image is:
+--[ RSA 2048]----+
|o=. .o+.o . |
|o+o .o.E o |
|+.+oo.. o |
| ooo o . |
| + . S |
| . . |
| |
| |
| |

Now you have two keys in your .ssh folder. A id_rsa (private key) and (public key). id_rsa is the key you need to backup safely and keep out of reach from everyone else.


There are several libraries and possibilities for how to add a validator, but currently I’m using the Google Authenticator. And since my servers are almost always Centos I’ll focus on latest release, version 7.

Make sure CentOS is updated

yum -y update

Next, install the ‘pam-devel‘ package which allows you to set authentication policies without having to recompile programs that handle authentication.

yum -y install pam-devel

Make sure that ntpd is installed and running because the TOTP security tokens are very time sensitive

yum -y install ntp
/etc/init.d/ntpd start
chkconfig ntpd on

Download and unpack the Google authenticator package. You may wanna browse google to make sure you get the latest stable release and not just the one I’m linking.

cd /opt/
tar -xvzf libpam-google-authenticator-1.0-source.tar.bz2
cd libpam-google-authenticator-1.0

Compile and install the Google authenticator module

make install

If you like, you can delete the build folder. Nive to clean up a bit.

rm -rf /opt/libpam-google-authenticator-1.0

For each user, you will have torun the following command to setup the “config” for that user


It will ask you four questions which you most likely will answer yes to. Read them and decide for yourself. Before the questions it will also output a QR code and some codes/keys

What you should notice from above, is three thing. Secret key, verification code and emergency scratch codes.

Secret key is the most important and most secret peice of information. This key is what you will need for the authenticator application. Wether it’s Android, Apple OS, Windows or some other library, the key is what you need to keep a secret. It is the core information when calculating temporary codes.

Verification code is not important since it’s only valid for the next 30 seconds or so. This is just an example of what you can expect from your authentication application.

The emergency scratch codes should be saved and locked away. These codes are always valid, no matter what the authentication apllication says. This is your way in, if you loose your phone or if you are simply unable to read the verification code.

The QR code is for your convenience. If you have Google Authenticator on your phone, or chrome extension, you can simply scan to setup the authenticator. Otherwise, just use the secret key.

Activate GAuth

You have now installed the Google authentication module, but it is not yet activated. Edit the pam ssh configuration.

vim /etc/pam.d/sshd

Add the following line. Personally i would put it at the top ensure that users are first prompted by the verification code, and next their username and password. It will make it almost impossible to attempt to bruteforce.

auth required nullok

Next, you need to tell SSH to ask for the verification code by editing the config.

vim /etc/ssh/sshd_config

Look for this line and make sure it’s set to yes and there is no hash sign in front of if.

ChallengeResponseAuthentication yes

We have to restart the SSH deamon. On CentOS 7 it is done as such:

systemctl restart sshd.service

If you try to ssh to the server now, you will be prompted for a verification code before you can enter username and password. Congratulations, you have two factor security now!

Note: If you already use SSH keys, you will NOT be prompted for a verification code! But that is ok since SSH keys is already two factor.


If you are not using SSH keys, it can be annoying over time time keep entering the validation key each time. There are different ways to mitigate this, but I will only show one solution here which covers the most.

We need to edit pam ssh settings again.

vim /etc/pam.d/sshd

You remmeber we entered an auth line at the top. What we need to do now is to insert this line ABOVE the previously inserted line.

auth [success=1 default=ignore] accessfile=/etc/security/access-local.conf

The access file needs to be created and get values.

vim /etc/security/access-local.conf

Insert this in the file, but remember to modify it to suit your needs.

# Two-factor can be skipped on local network
+ : ALL :
+ : acoon : ALL
- : ALL : ALL

The file will be read top-down. The plus sign is the entries that will NOT be asked to use verification code. Meaning that logins from will not be asked for 2FA, but just regular authentication. The user ‘acoon’ is never asked for 2FA not matter what IP the connection originate from.

Lastly, I ensure that everyone else is using verification codes.

You can read more on access config here.

Leave a Reply

Your email address will not be published. Required fields are marked *