Home Software, Tweaks • Using 2 factor authentication for SSH

Using 2 factor authentication for SSH


Google-Authenticator-iconI maintain my systems mostly via SSH and although SSH is secure enough with it’s encryption I want to secure it a bit more. I do use keys a lot and is the proper way to log in via SSH as passwords are not needed anymore. But since I work remotely sometimes and not having my workstation with me, I have to keep allowing password based logins for SSH but don’t want to rely on the password itself, so I started the implementation of 2 factor authentication for SSH using the Google Authenticator. This post will describe the steps necessary to enable 2FA using the Google Authenticator app for the SSH service.

Notes on the Google Authenticator:
As Google has a reputation to scrape literally every piece of information about you and your behaviour online, the authenticator does not send any data to Google (as they say themselves). The numbers generated by the app are processed on your phone itself instead of coming from a server of Google and the data exchanged is only performed between your computer and the server you are connecting to.

Installing the Google Authenticator software on the server:
In order to enable 2FA we need to install the Google Authenticator software on the server as well because we will be using PAM for the authentication process and the software delivers the appropriate module for use with PAM.

To install it op Debian based systems, do:
apt-get install libpam-google-authenticator

On Arch based systems you need to run:
yaourt -Sy google-authenticator-libpam-git
For Arch you need the AUR repo, therefore have yaourt installed on your system!

For Redhat/CentOS based systems:
There is a rpm for the authenticator module, but it seriously outdated, so we need to compile it ourself. First install the required depencies:
sudo yum install make gcc pam-devel

Download and unzip the sourcecode:
wget -O google-authenticator.zip https://github.com/google/google-authenticator/archive/master.zip
unzip google-authenticator.zip

Now enter the directory and start the build:
cd google-authenticator-master/libpam/
sudo make install

Installing the Google Authenticator app:
Open the app store on your phone and search for the Google Authenticator app and install it.

Configuring the Authenticator on the server:
Login on your server with the user you want to enable the Google Authenticator for and run the following command:

You’ll have to answer the question if you want tokens to be time-based, for extra security I strongly advise you to set it to yes as this will set a expiry time on the token itself.
After this process a URL and a huge QR code will be spewed out on the terminal. The URL shows the same QR code, but in your browser which may be more convenient.

A set of keys and emergency codes are provided, save them in a safe place! You will need these emergency codes in case you lose access to the Google Authenticator app.

3 additional questions will be asked like below:

Your new secret key is: 2IKBRFIFF5FRB7JB
Your verification code is 245699
Your emergency scratch codes are:

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

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 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) y

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

The questions are simple and straightforward and don’t need any extra explanation, for the best security with the tokens you will want to answer yes on all questions except the 3rd question. Note that it says that it will increase the lifetime of a token from 90 seconds to 240 seconds. If you want the best security out of this system you should answer no here but relies heavily on the current system time and your device generating the token, they may not differ more than 90 seconds. If you use NTP on your server the 90 seconds should not be an issue but if it is you should answer yes here.

Setting up the app:
Open the Google Authenticator app on your device and you have the choice to enable via QR code or enter the tokens manually. For this example I will use the manual process, but for faster configuration the QR code is best.


After the name and key is inserted, add the account. After adding the account the app starts generating tokens:


Configuring PAM and SSH:
In order to activate the Google Authenticator module using PAM we need to open the PAM config for SSH:
sudo nano /etc/pam.d/sshd

Add the following line to the top of the file and save it:
auth required pam_google_authenticator.so

Open the SSH config:
sudo nano /etc/ssh/sshd_config

Search for the keyword ChallengeResponseAuthentication
If it's commented out like:
# ChallengeResponseAuthentication no

You need to change it to:
ChallengeResponseAuthentication yes

If the line does not exist within the configuration file, just add it (which is the same as above) and save your changes:
ChallengeResponseAuthentication yes

Now restart SSH to activate the settings changed:

For Debian based systems:
sudo service ssh restart

For Arch based systems:
sudo systemctl restart sshd.service

For Redhat/CentOS based systems:
sudo service sshd restart

Logging in via SSH with the authenticator:
Now that the settings are active we want to test it. I've done it on my local machine, so in this example I'll connect to localhost:

[jeffrey@jeffrey ~]$ ssh localhost
Verification code:
[jeffrey@jeffrey ~]$

As you can see it's working!

Note on SSH-keys:
If you already use SSH-keys to login without password on your server this will bypass the Google Authenticator. Looking at the way keys work and how they need to be deployed you basically have performed a 2FA when logging in to the server and placing your pubkey there. If you work from your private desktop this will work just fine but in my opinion for mobile devices that can be used on-the-go it's not.

For mobile devices that may get stealed or might get lost it's absolutely a pre to sign your SSH-key with a password. this way you will have to enter a password every time to login to the server, but that's only for decrypting the key and then will login with that key.

You can create a key with password via the following line:
ssh-keygen -t rsa -N 'YOUR_PASSWORD'
This will overwrite your current keyfile! Once generated place the publickey on the server and you're done. This will function as 2FA as well and will bypass the Google Authenticator module.

Author:Jeffrey Langerak

15 responses to “Using 2 factor authentication for SSH”

  • Adam Jimerson 08-01-2016 at 16:49 Reply 

    Small problem I found with your post,

    “On Arch based systems you need to run:
    yaourt -Sy google-authenticator-libpam-git
    For Arch you need the AUR repo, therefore have yaourt installed on your system!”

    Just because you are using the AUR, doesn’t mean you have yaourt installed. You can build and install packages using no AUR helper, with just makepkg and pacman (https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages) or you could use any of the AUR helpers avalable to Arch users (https://wiki.archlinux.org/index.php/AUR_helpers).

    Also any reason for using the git package https://aur.archlinux.org/packages/google-authenticator-libpam-git/ (tracks git master), over the non-git one https://aur.archlinux.org/packages/libpam-google-authenticator/ (stable releases). For my system I installed the latter and everything is working as expected.

  • neil patel 08-01-2016 at 20:34 Reply 

    sudo service restart ssh
    should be
    sudo service ssh restart

    • langerak 09-01-2016 at 11:04 Reply 

      Thanks! I’ve fixed it and added the service restart part for CentOS as well as that was missing 🙂

  • Chris Short 10-01-2016 at 11:03 Reply 

    How well does this work with SSH key pairs? Also, If I’m on my local network I’d like to forgo 2FA, know of a convention for that?

    • langerak 10-01-2016 at 12:39 Reply 

      Hi! When using keys 2FA will be used as well but in a different manner as you already had to login to that server and place your key on it. I strongly suggest however to use keys with passwords on them, especially when working from mobile devices that may get stealed.

      You can create a new key with password using:
      ssh-keygen -t rsa -N 'YOUR_PASSWORD'

      You will then need to transfer it to your server again in order to use the key with password.

      • Benny 12-01-2016 at 01:04 Reply 

        I’m using SSH keys and other than this google auth thing I’m 99% sure that it’s otherwise default ubuntu 14.04 configs. Using passwords, the 2FA verification code works fine. If I have my keys loaded in an ssh agent, it skips straight through with no verification code.

        • langerak 12-01-2016 at 08:01 Reply 

          Hi! I’ve checked this and the behavior you see is the correct one. Basically you are already have passed 2FA as you had to login to that server once with your password and add your public key to it’s authorized_keys file. If you want to play safe you want to create a key with a password on it, using it that way you always have 2FA and won’t need the authenticator.

          You can create a new key with password using:
          ssh-keygen -t rsa -N 'YOUR_PASSWORD'

          You will then need to transfer it to your server again in order to use the key with password.

    • Bill B 22-01-2016 at 09:57 Reply 

      I have that working. Put your trusted machines in /etc/security/access.conf, then in /etc/pam.d/sshd put in the line:
      auth sufficient pam_access.so

      So if you aren’t in access.conf you need 2FA, but if you are you just need your keys.

  • Dtective 12-01-2016 at 03:49 Reply 

    When trying to log back in I’m getting, “Using keyboard-interactive authentication.” and then a prompt to put in my password. Only my password isn’t being accepted. Any idea what’s going on? Perhaps something in PAM.

    • langerak 12-01-2016 at 09:07 Reply 


      Can you paste the contents of the /etc/pam.d/ssh file? This file is responsible for the allowed/required methods for SSH logins.

      • Dtective 12-01-2016 at 13:12 Reply 

        My default is:
        auth required pam_sepermit.so
        auth include password-auth
        account required pam_nologin.so
        account include password-auth
        password include password-auth
        # pam_selinux.so close should be the first session rule
        session required pam_selinux.so close
        session required pam_loginuid.so
        # pam_selinux.so open should only be followed by sessions to be executed in the user context
        session required pam_selinux.so open env_params
        session optional pam_keyinit.so force revoke
        session include password-auth

        • langerak 12-01-2016 at 16:47 Reply 

          Hi, it seems okay to me, although I don’t use SELinux nor have experience with. My PAM config for SSH is:
          #auth required pam_securetty.so #disable remote root
          auth required pam_google_authenticator.so
          auth include system-remote-login
          account include system-remote-login
          password include system-remote-login
          session include system-remote-login

          And I see some differences compared with our configs, maybe the pam_nologin is the showstopper here. You may as well try out my config to see if that helps.

  • Amber Yust 12-01-2016 at 21:03 Reply 

    Note: your statement “for the best security with the tokens you will want to answer yes on all questions” is not quite true – the time window question (1m30s -> 4m) decreases security if you answer yes. If you want absolutely the maximum potential security, you should answer “no” to that question as it reduces the number of simultaneously valid OTP codes from 8 to 3.

    • langerak 13-01-2016 at 09:11 Reply 

      Hi! You are correct but it relies heavily on the correct time on the server and the device generating the token, if they differ more than 1:30 it will not work. If you use NTP this is not an issue but I sometimes see servers at work that are behind a couple of minutes making the 4:00 option key. I’ve changed the article with this note, thanks for informing me!

Leave a Reply

Your email address will not be published. Required fields are marked*



This site uses Akismet to reduce spam. Learn how your comment data is processed.