2FA with SSH

From packets2photons
Jump to navigation Jump to search

Using ssh keys is certainly preferable over traditional passwords. You can use any number of Authenticator apps from Google, Microsoft et cetera to add a second factor. In this example we will use FreeOTP which is is maintained by Red Hat under the Apache 2.0 license, and supports Android and iOS.

The idea here is that if someone has compromised the physical device, such as your laptop, they may be able to ssh into your servers, unless there is a second factor. The second factor in this case, will be your phone. You must have an Android or iOS device with an internet connection to complete this activity. I will also assume that you are sitting on your local computer and will be sshing into a virtual machine for this activity. I will assume that you have already setup ssh keys. If you do not have these please see SSH Keys et al before continuing.

Getting FreeOTP on your phone

Start by installing FreeOTP Authenticator from the Android or iOS store.

Installing the tools on your distro

Install the oath toolkit on your ssh client and server:


sudo apt install oathtool libpam-oath qrencode


pacman -S oath-toolkit qrencode

Configuration on the server

Create your seed with:

head -10 /dev/urandom | sha512sum | cut -b 1-30 >> seed

You should then copy the file seed to /etc/users.oath

cp seed /etc/users.oath

Then edit /etc/users.oath to contain

HOTP/T30/6 user - oethuoenthu----this-is-the-seed-otnehuoetnh

Please ensure that user is changed to your username and you have inserted your seed.

Edit the file /etc/pam.d/sshd on the server and insert the following line at the bottom:

auth sufficient pam_oath.so usersfile=/etc/users.oath window=30 digits=6

Then edit /etc/ssh/sshd_config and ensure that both ChallengeResponseAuthentication and UsePAM options are set:

ChallengeResponseAuthentication yes
UsePAM yes
AuthenticationMethods publickey,keyboard-interactive
PasswordAuthentication no

Also, find the line PermitRootLogin and change to:

PermitRootLogin yes


You can then generate a basic One time password with the following:

oathtool --totp --verbose $(cat seed)

The number at the end is the one that you can use to log in right now. If you are working with a local machine or a local VM, you can now test your configured functionality. The command to restart the SSH server is found at the bottom of the page. If you have ths capacity then you can test your configuration by sshing and entering that key.

Generating the OPT on your phone

Currently, your configuration is a little odd as you need local access to generate the key for remote access. This defeats the purpose. We want to generate the OTP, as the second factor, on your phone.

What we need is the Base32 value. We can get this value redirected into a file called base32 with the following:

oathtool --totp --verbose $(cat seed) | grep Base | cut -d ' ' -f3 > base32

You can check that it is present with

cat base32

Using vim, edit base32 to prepend some text such that base32 looks like:


Ensure that Of course change user, machine and thesecretbase32val match your machine.

Run the following to generate a png qrcode for your phone to scan.

qrencode -o root.png $(cat base32)

You should then open the image that you create. I had to transfer my image to a computer with a screen with:

scp root.png user@ipaddress:/path

Obviously changing user, ipaddress and path as needed.

You can then open the image and scan it using the QRcode scanner on your Free OTP mobile app. You should now be able to generate these one time passes on your phone. Note that if FreeOTP crashes while scanning your QRcode, it is probably that your QRcode is wrong.

Be sure to destroy root.png, as leaving it on your laptop will essentially relegate you back to single factor authentication. If you have progressed this far, congratulations! Somebody needs to compromise your computer and your phone to gain access via SSH to your server.

Troubleshooting & Debugging

The most common problems you will experience are mistyping in this lab or not changing some of the values such as user, to values that match your own machine.

On the client side you can use

ssh user@destination -v

On the server side you can look at:

journalctl -u sshd | tail -100

If you use Ubuntu you can use

tail -f /var/log/auth.log

You can restart sshd but should not do so on a live production system unless you are confident that it will come back up.

systemctl restart sshd