An authentication factor is a single piece of information used to to prove you have the rights to perform an action, like logging into a system. An authentication channel is the way an authentication system delivers a factor to the user or requires the user to reply. Passwords and security tokens are examples of authentication factors; computers and phones are examples of channels.
SSH uses passwords for authentication by default, and most SSH hardening instructions recommend using an SSH key instead. However, this is still only a single factor. If a bad actor has compromised your computer, then they can use your key to compromise your servers as well.
In this tutorial, we’ll set up multi-factor authentication to combat that. Multi-factor authentication (MFA) requires more than one factor in order to authenticate, or log in. This means a bad actor would have to compromise multiple things, like both your computer and your phone, to get in. The different type of factors are often summarized as:
- Something you know, like a password or security question
- Something you have, like an authenticator app or security token
- Something you are, like your fingerprint or voice
One common factor is an OATH-TOTP app, like Google Authenticator. OATH-TOTP (Open Authentication Time-Based One-Time Password) is an open protocol that generates a one-time use password, commonly a 6 digit number that is recycled every 30 seconds.
This article will go over how to enable SSH authentication using an OATH-TOTP app in addition to an SSH key. Logging into your server via SSH will then require two factors across two channels, thereby making it more secure than a password or SSH key alone. In addition, we’ll go over some additional use cases for MFA and some helpful tips and tricks.
To follow this tutorial, you will need:
- One CentOS 7 server with a sudo non-root user and SSH key
- A smartphone or tablet with an OATH-TOTP app installed, like Google Authenticator (iOS, Android).
Step 1 — Installing Google’s PAM
In this step, we’ll install and configure Google’s PAM.
PAM, which stands for Pluggable Authentication Module, is an authentication infrastructure used on Linux systems to authenticate a user. Because Google made an OATH-TOTP app, they also made a PAM that generates TOTPs and is fully compatible with any OATH-TOTP app, like Google Authenticator or Authy.
First, we need to add the EPEL (Extra Packages for Enterprise Linux) repo.
yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
Next, install the PAM. You may be prompted to accept the EPEL key if this is the first time using the repo. Once accepted you won’t be prompted for again to accept the key.
yum install google-authenticator
With the PAM installed, we’ll use a helper app that comes with the PAM to generate a TOTP key for the user you want to add a second factor to. This key is generated on a user-by-user basis, not system-wide. This means every user that wants to use a TOTP auth app will need to log in and run the helper app to get their own key; you can’t just run it once to enable it for everyone (but there are some tips at the end of this tutorial to set up or require MFA for many users).
Run the initialization app.
After you run the command, you’ll be asked a few questions. The first one asks if authentication tokens should be time-based.
Do you want authentication tokens to be time-based (y/n) y
This PAM allows for time-based or sequential-based tokens. Using sequential-based tokens mean the code starts at a certain point and then increments the code after every use. Using time-based tokens mean the code changes randomly after a certain time elapses. We’ll stick with time-based because that is what apps like Google Authenticator anticipate, so answer
y for yes.
After answering this question, a lot of output will scroll past, including a large QR code. At this point, use your authenticator app on your phone to scan the QR code or manually type in the secret key. If the QR code is too big to scan, you can use the URL above the QR code to get a smaller version. Once it’s added, you’ll see a six digit code that changes every 30 seconds in your app.
Note: Make sure you record the secret key, verification code, and the recovery codes in a safe place, like a password manager. The recovery codes are the only way to regain access if you, for example, lose access to your TOTP app.
The remaining questions inform the PAM how to function. We’ll go through them one by one.
Do you want me to update your "/root/.google_authenticator" file (y/n) y
This writes the key and options to the
.google_authenticator file. If you say no, the program quits and nothing is written, which means the authenticator won’t work.
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 answering yes here, you are preventing a replay attack by making each code expire immediately after use. This prevents an attacker from capturing a code you just used and logging in with it.
By default, tokens are good for 30 seconds. 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 +-1min (window size of 3) to about +-4min (window size of 17 acceptable tokens). Do you want to do so? (y/n) y
Answering yes here allows up to 8 valid codes in a moving four minute window. By answering no, you limit it to 3 valid codes in a 1:30 minute rolling window. Unless you find issues with the 1:30 minute window, answering no is the more secure choice.
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
Rate limiting means a remote attacker can only attempt a certain number of guesses before being blocked. If you haven’t previously configured rate limiting directly into SSH, doing so now is a great hardening technique.
Note: Once you finish this setup, if you want to back up your secret key, you can copy the
~/.google-authenticator file to a trusted location. From there, you can deploy it on additional systems or redeploy it after a backup.
Now that Google’s PAM is installed and configured, the next step is to configure SSH to use your TOTP key. We’ll need to tell SSH about the PAM and then configure SSH to use it.
Step 2 — Configuring OpenSSH
Because we’ll be making SSH changes over SSH, it’s important to never close your initial SSH connection. Instead, open a second SSH session to do testing. This is to avoid locking yourself out of your server if there was a mistake in your SSH configuration. Once everything works, then you can safely close any sessions.
To begin we’ll edit the
sshd configuration file. Here, we’re using
nano, which isn’t installed on CentOS by default. You can install it with
sudo yum install nano, or use your favorite alternative text editor.
Add the following line to the bottom of the file.
. . . # Used with polkit to reauthorize users in remote sessions -session optional pam_reauthorize.so prepare auth required pam_google_authenticator.so
Save and close the file.
Next, we’ll configure SSH to support this kind of authentication. Open the SSH configuration file for editing.
ChallengeResponseAuthentication lines. Comment out the
no line and uncomment the
. . . # Change to no to disable s/key passwords ChallengeResponseAuthentication yes #ChallengeResponseAuthentication no . . .
Save and close the file, then restart SSH to reload the configuration files. Restarting the
sshd service won’t close open connections, so you won’t risk locking yourself out with this command.
systemctl restart sshd.service
Last step is to test the service by connecting with SSH to the server to see if it will require verification code. You can see the following screenshot which shows the verification code that keeps on changing time after time and you have to login with it: