Tutorial

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

Published on September 30, 2015
How To Set Up Multi-Factor Authentication for SSH on Ubuntu 14.04
Not using Ubuntu 14.04?Choose a different version or distribution.
Ubuntu 14.04

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

Introduction

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.

Prerequisites

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.

  1. sudo apt-get update

Next, install the PAM.

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

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

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

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

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

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

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

  1. 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!

Conclusion

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.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors

Default avatar

staff technical writer

hi! i write do.co/docs now, but i used to be the senior tech editor publishing tutorials here in the community.


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
10 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

This comment has been deleted

    @karlhaworth without seeing your config files here are a couple of things to check that may cause the issue.

    1. In /etc/pam.d/sshd make sure to comment out the line ‘@include common-password’.
    2. To verify it is working, remove your public key from ~/.ssh/authorized_keys, make sure you have a password, and in another session try logging in and see if the MFA works.
    3. Did you run ‘google-authenticator’ as the user you are trying to login as?
    4. Did you restart sshd before testing?
    5. Are you using Ubuntu 14.04 or later?

    If none of these help, post your /etc/pam.d/sshd and /etc/ssh/sshd_config files in a paste bin and post the links.

    When you want to allow a second user to use authenticator, how can I display the QR code again, after the initial setup? I don’t see any hints in the options for googleauthenticator.

    This comment has been deleted

      Does this MFA setup also work with scp?

      @michaelholley For the file edits I see that a \ has been prepended to comment lines.

      I’m pretty sure this is not what you intended considering this would nullify the comment itself and cause those particular lines of code to be executed.

      For instance:

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

      Should be changed to:

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

      Hi @michaelholley , your tutorial is really detailed, I set up and run successfully in my droplet. I love it, but I have a dump question, could you help me?

      I made a mistake, answered “yes” in this question:

      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)
      

      How can I turn it into “no”?

      Thank you.

      Will the multi factor auth work for non networked ubuntu servers? I have a few servers that cannot connect to internet where I want multi factor auth to work.

      Will FileZilla still work over STFP? Will it prompt me for a verification key when I connect?

      Try DigitalOcean for free

      Click below to sign up and get $200 of credit to try our products over 60 days!

      Sign up

      Join the Tech Talk
      Success! Thank you! Please check your email for further details.

      Please complete your information!

      Become a contributor for community

      Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

      DigitalOcean Documentation

      Full documentation for every DigitalOcean product.

      Resources for startups and SMBs

      The Wave has everything you need to know about building a business, from raising funding to marketing your product.

      Get our newsletter

      Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

      New accounts only. By submitting your email you agree to our Privacy Policy

      The developer cloud

      Scale up as you grow — whether you're running one virtual machine or ten thousand.

      Get started for free

      Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

      *This promotional offer applies to new accounts only.