How To Set Up Multi-Factor Authentication for SSH on Ubuntu 14.04

This article uses Ubuntu 14.04
This distro reached end of life (EOL) on Apr 2019.

We recommend upgrading to a more modern version. Read upgrade instructions.

This article has been updated for Ubuntu 16.04. The updated version also includes additional information on how to go beyond basic setup.


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.

To combat that, in this tutorial, we’ll set up multi-factor authentication. 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:

  1. Something you know, like a password or security question
  2. Something you have, like an authenticator app or security token
  3. 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.


To follow this tutorial, you will need:

  • One Ubuntu 14.04 Droplet.

  • A sudo non-root user with an SSH key added, which you can set up by following this Initial Server Setup tutorial.

  • A smartphone or tablet with an OATH-TOTP app installed, like Google Authenticator (iOS, Android).

Step 1 — Installing libpam-google-authenticator

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.

First, update Ubuntu’s repository cache.

  • sudo apt-get update

Next, install the PAM.

  • sudo apt-get install libpam-google-authenticator

With the PAM installed, we’ll use a helper app that got installed 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.

  • google-authenticator

After you run the command, you’ll be asked a few questions. The first one asks if authentication tokens should be time-based.

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 yes.

Do you want authentication tokens to be time-based (y/n) y

After answering this question, a lot of output will scroll past, including a large QR code. Make sure you record the secret key, verification code, the emergency scratch codes in a safe place, like a password manager.

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.

The remaining questions inform the PAM how to function. We’ll go through them one by one.

Do you want me to update your "~/.google_authenticator" file (y/n) y

This basically 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 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) n

Answering yes here allows up to 8 valid codes in a moving four minute window. By answering no, we limit it to 3 valid codes in a 1:30 minute rolling window. Unless you find issues with the 1:30 minute window, 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.

Step 2 — Configuring OpenSSH

The next step now 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.

First, open up the sshd configuration file for editing using nano or your favorite text editor.

  • sudo nano /etc/pam.d/sshd

Add the following line to the bottom of the file.

. . .
# Standard Un*x password updating.
@include common-password
auth required pam_google_authenticator.so nullok

The “nullok” word on the end tells PAM that this authentication method is optional. This allows users without a OATH-TOTP key to still log in using their SSH key. Once all users have an OATH-TOTP key, you can delete “nullok” on this line to make it MFA mandatory.

Save and close the file.

Next, we’ll configure SSH to support this kind of authentication. Open the SSH configuration file for editing.

  • sudo nano /etc/ssh/sshd_config

Look for ChallengeResponseAuthentication and set its value to yes.

. . .
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes
. . .

Save and close the file, then restart SSH to reload the configuration files.

  • sudo service ssh restart

Step 3 — Making SSH Aware of MFA

In this step, we’ll test if the SSH key works.

First, open another terminal and try SSHing into the server now. You’ll notice that you logged into this second session using your SSH key, without entering your verification code or password. This is because an SSH key overrides all other authentication options by default. We need to tell SSH to use the TOTP code and to use your SSH key in place of your password.

Now, open the sshd configuration file again.

  • sudo nano /etc/ssh/sshd_config

Locate the PasswordAuthentication line, uncomment it by deleting the # character the head of the line, and update its value to no. This tells SSH not to prompt for a password.

. . .
# Change to no to disable tunnelled clear text passwords
PasswordAuthentication no
. . .

Next, add the following line at the bottom of the file. This tells SSH which authentication methods are required.

. . .
UsePAM yes
AuthenticationMethods publickey,keyboard-interactive

Save and close the file.

Next, open the PAM sshd configuration file.

  • sudo nano /etc/pam.d/sshd

Find the line @include common-auth and comment it out by adding a # character as the first character on the line. This tells PAM not to prompt for a password; we previously told SSH not to in sshd_config.

. . .
# Standard Un*x authentication.
#@include common-auth
. . .

Save and close the file, then restart SSH.

  • sudo service ssh restart

Now try logging into the server again. You should see that you authenticated partially with your SSH key and then got prompted for your verification code. It will look like this:

Example login output
ssh sammy@your_server_ip

Authenticated with partial success.
Verification code:

Enter your verification code from your OAUTH-TOTP ap, and you’ll log into the server. You now have MFA enabled for SSH!


As with any system that you harden and secure, you become responsible for managing that security. In this case, that means not losing your SSH key or your TOTP secret key. However, sometimes things happen, and you can lose control of the keys to get you in.

Here are a few suggestions to regain access to your server:

  • If you lose, or don’t have access to, your TOTP app, use your recovery codes as a verification code. This happens if you get a new phone and forgot to export your keys out of the old one, or if your phone runs out of power.

  • If you lose your secret key and the backup, use the console via the DigitalOcean control panel to log in. Then either rename or delete the file ~/.google_authenticator. This will make sure PAM is unaware of your configuration, and won’t prompt you for a code. Make sure that /etc/pam.d/sshd still has “nullok” added, like in step 2; if you change this, make sure to restart SSH.

  • If you lose your SSH key, use the console again to log in and remove your old public from ~/.ssh/authorized_hosts. Then, you can either replace it with a new key.

By having two factors (an SSH key + MFA token) across two channels (your computer + your phone), you’ve made it nearly impossible for an outside agent to brute force their way into your machine via SSH and greatly increased the security of your machine.

Creative Commons License