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:
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).
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.
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
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:
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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
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!
(y)
This comment has been deleted
@karlhaworth without seeing your config files here are a couple of things to check that may cause the issue.
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:
Should be changed to:
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:
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?