Enabling 2-Factor Authentication with SSH

Now we are getting fancy. I didn’t actually know you could do this up until relatively recently.

It turns out you can enable 2-Factor Authentication (2FA) login challenges using Google Authenticator combined with SSH public/private key pairs – which is just a great idea in general.

FYI: You DO NOT need a Google account to use Google Authenticator.

This means even if your keys were somehow compromised an attacker still couldn’t get in without physically having your phone with them AND unlocking it.

So unless your good friends or loved ones are trying to hack you, you should be in pretty good shape 🙂

Installing Google Authenticator

There are two halves to the “installing Google Authenticator” step, at a high level:

  1. Installing it on your phone from the App Store (iOS / Android) – go ahead and do this now. There isn’t any account-creation step, you just install it and then start registering challenges from servers.
  2. Installing it on your server and setting it up, so it can challenge and confirm people logging in.

#1 you can do right now – it’s a 1-step process.

Now, let’s dig in on the server setup…

Configuring it on the server

First, let’s install the libpam-google-authenticator module:

sudo apt-get install libpam-google-authenticator

After it’s installed, head to your $HOME directory and run the authenticator to do the setup:

$ cd ~
$ google-authenticator

The setup will ask you a series of questions that look like the following:

Do you want authentication tokens to be time-based (y/n) y
Warning: pasting the following URL into your browser exposes the OTP secret to Google:
  https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/<your key information>

... A GIANT ASCII QR CODE (COOL!) ...


Your new secret key is: <SECRETS!>
Your verification code is <CODE>
Your emergency scratch codes are:
  <CODE 1>
  <CODE 2>
  <CODE 3>
  <CODE 4>
  <CODE 5>

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

Do you want to disallow multiple ... SNIP ... (y/n) y

By default, a new token is generated ... SNIP ... (y/n) y

If the computer that you are logging into ... SNIP ... (y/n) y

FYI: You need to run google-authenticator as each user you with to use it for. Right now, in this example, it is only setup for my rkalla user.

Critical Step 1 of 2: Open Google Authenticator on your app, click the “+” button and register using a QR code – point your phone at the giant ASCII QR code on your screen.

If you are unable to capture the QR code, register using the “secret key” that is printed out in the terminal right under the QR code and in the Google Auth app.

Critical Step 2 of 2: Backup the 5 “emergency scratch codes” somewhere safe (like a Password Wallet) – these are the codes you can use if you lose your phone or the Google Auth app won’t work any longer.

Excellent, now you have:

  1. (Server) Google Authenticator installed
  2. (Server) Google Authenticator configured for the user rkalla
  3. (Mobile) Google Authenticator installed
  4. (Mobile) Google Authenticator linked to the configured rkalla user on the server.

There is one more thing we need to do…

Connecting Google Auth and the SSH Daemon

Now that everything is ready to go, the last step is to connect the SSH Daemon to the Google Authenticator library to ensure it challenges users with an auth code on login.

First, let’s edit the daemon config:

sudo nano /etc/ssh/sshd_config

Search the file for ChallengeResponseAuthentication, I found it around Line 69 in my config.

The default setting is typically no – change it to yes so it looks like this:

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

Before you leave, search for the UsePAM setting and ensure it is set to yes:

UsePAM yes

Now save the file and exit.

FYI: “PAM” stands for “Pluggable Authentication Module” and is a framework SSH/OpenSSH use to support 3rd party integrations – in this case, Google Authenticator.

Next thing to do is actually modify the PAM configuration for the SSH Daemon:

sudo nano /etc/pam.d/sshd

Near the top of the file (at least on Ubuntu 20.04) you’ll see the lines:

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

Then add a required reference to the Google Auth PAM module, so all the lines look like so:

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

Tip: You can comment out the common-auth module if you ONLY want an auth code enter for your login. Or you can leave them both uncommented if you want the user asked for BOTH a password and auth code – it’s your choice!

Once you are done with your changes, save the file and exist.

Wrapping up

Now restart SSH Daemon to pickup the config changes:

sudo systemctl restart ssh

Now, before you logout of this terminal session, let’s make sure your changes worked!

Open a new terminal and initiate a connection to your server and confirm that you are now being asked for an auth-code and possibly a password (depending on which auth modules you left enabled).

Did it work? Excellent! On to more securing!