Two-factor SSH authentication using YubiKey OTP

Introduction

Two-factor authentication means that in order to authenticate, a principal should prove its identity using at least two different methods. Often, a principal is required to authenticate using both a password, and a one-time token; this is especially popular on cloud services, where the token is either generated cryptographically or sent through a side channel, such as SMS.

In this document, we'll set up an SSH server that will use SSH public key cryptography as one factor and YubiKey OTP as another. For trusted networks, the YubiKey OTP is never needed, while public key cryptography is always required.

This will leave us with an SSH server that requires an SSH key to log in, but may require a YubiKey OTP as a second factor if authentication is attempted from an untrusted location.

Registering API keys

In order to use pam_yubico against YubiCloud, you need to get an API key. You need an unmodified YubiKey to do this. Go to the Get API Key page, fill out your e-mail address, select the second textbox and press the button on your YubiKey. You will now get a Client ID and Secret key. Take note of these.

Alternatively, you can set up your own validation server, but that is not covered by this document. Please refer to the YubiKey OTP Validation Server for more information.

Setting up pam_yubico

In this case we'll be using FreeBSD, but pam_yubico(8) is available on most UNIX-like operating systems. Install the module.

pkg install pam_yubico

After installation, you must update your pam configuration file. This process is documented on the Yubico/yubico-pam GitHub project, but we'll go through it here as well.

In /etc/pam.d/sshd, before the first auth statement, add the following line.

auth	required	/usr/local/lib/security/pam_yubico.so	id=31415	key=cGFtX3l1Ymljbwo=	authfile=/etc/yubikey_mappings

Remove all other auth statements by commenting them out. Now create the file /etc/yubikey_mappings, which contains a mapping of usernames to one or more YubiKeys. The format is:

username:yubikeyid1:yubikeyid2:yubikeyid3:…

A yubikeyid consists of the first twelve bytes of the OTP generated by a YubiKey when you press the button. The OTP is different every time you press the button, but the first twelve bytes are always the same. This is the YubiKey ID.

Configuring OpenSSH

Since we've configured PAM to always require YubiKey authentication, UsePAM yes will, in combination with AuthenticationMethods keyboard-interactive already provide YubiKey OTP authentication. However, this is not exactly what we want, as this would allow anyone who finds a YubiKey to log in as that user.

This sshd_config will require both SSH public key authentication, and YubiKey authentication. Replace the existing sshd_config with this one:

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

However, we want public key authentication to suffice for hosts in a trusted subnet. This is simply done by appending the following to the end of sshd_config:

Match Address 2001:700:1:4a00::/56
	AuthenticationMethods	publickey

Congratiolations, you've now set up two-factor authentication using YubiKey OTP!

See also