Adding Yubikey 2-factor authentication to SSH and sudo in Debian

Throughout 2014 and 2015 I have been adding two-factor authentication to Debian and Ubuntu servers (SSH, sudo) for some of my customers, using Yubikeys as the authentication device and OTP as the auth method. It's quite straightforward to integrate Yubikey OTP auth into Debian SSH servers, provided you are using Debian 7 (Wheezy) or higher, and that you can use the version of OpenSSH from the Backports repository.

This guide will work for SSH auth, as well as for other server-side tasks such as the use of sudo.

Add dependencies

You need to install the Yubico PAM module, which is available in the Debian repositories as of Debian Wheezy:

apt-get install libpam-yubico

To use 2-factor auth with SSH, you should use OpenSSH 6.2 or higher, because it adds support for the 'AuthenticationMethods' parameter, which allows proper 2-factor auth (no hacky script workarounds needed). Fortunately, Wheezy's Backports repository contains OpenSSH 6.6.

Assuming you have set up the Backports repository in /etc/apt/sources.list.d/ somewhere, you can run:

apt-get -t wheezy-backports install openssh-server

Add your Yubikey's public string to your account

The common way to do this is to store your Yubikey's public string in ~/.yubico/authorized_yubikeys, or else in a common 'map' file (often /etc/yubikeys or similar) which you specify in the PAM configuration. The format of the file is like so:

username:yubikeyID

If a user has multiple Yubikeys, you can add them on the same line, e.g:

johnsmith:fjncjhefwfaa:bcnsafoladeb

In some cases for some of my customers, we have integrated the Yubikey 'public key' into the user's LDAP object, because LDAP backs PAM and SSH (including SSH keys for good measure, via LPK. Wow, that's a lot of acronyms! :) )

Configure PAM

/etc/pam.d/common-auth has this line added:

auth    required        pam_yubico.so mode=client try_first_pass id=XX key=XXXXXXXXXXXXXXXXXXXXX url=https://ykval.example.com/wsapi/2.0/verify?id=%d&otp=%s

in this example above, I am showing how to configure for a self-hosted Yubikey validation server. But by default, you can use the Yubico OTP public validation server (the 'Yubicloud'). The 'id' string is the API 'client ID' that permits your service to talk to the Yubikey validation server. If you are using the hosted Yubicloud service, you can obtain an API key here. If using your self hosted Yubikey validation server such as this one, you would likely generate this with a CLI tool such as ykval-gen-clients.

If you were integrating the Yubikey ID with LDAP user objects, you would add something like this to the line, depending on how your LDAP is structured (in this example I show how to add multiple LDAP replicas for high availability):

ldap_uri=ldaps://ldap1.example.com/,ldaps://ldap2.example.com/,ldaps://ldap3.example.com/ ldapdn=ou=People,dc=example,dc=com user_attr=uid yubi_attr=yubiKeyId

Configuring SSH

The OpenSSH 6.2+ config requires these three settings which allow 2-factor auth:

ChallengeResponseAuthentication no
PasswordAuthentication yes
AuthenticationMethods publickey,password

In fact, this is 3-factor auth: an SSH keypair is required and is validated with 'Authenticated with partial success'. Then SSH prompts for a password, which is actually a concatenation of the user's password followed by a Yubikey OTP string. This means that when you SSH to the server and receive a password prompt, your SSH keypair has already been accepted - you must then type your password, then press the Yubikey button. Don't press enter after entering the password: the carriage return sent by the Yubikey will do this for you.

Miguels-MacBook-Pro:~ miguel$ ssh syrah
Authenticated with partial success.
miguel@syrah's password: (enters password + presses yubikey)
<snip...>

This concatenation of password + yubikey OTP admittedly feels a little counter-intuitive. I would actually prefer it if SSH explicitly prompted for a Yubikey first, then a password (or vice versa) - as well as supporting this 'combination' method. We will see later in this document that this is exactly how the interaction with sudo works, so I'm not sure why SSH doesn't. You get used to it.

And yes, this integrates cleanly with SSH LPK, meaning your password is validated first via LDAP, then your Yubikey validated via the Yubikey validation server :)

Using with sudo

When issuing a sudo command, you will receive a prompt 'Yubikey:' first. You can press the Yubikey button, at which point you will then receive a password prompt for sudo as normal.

miguel@syrah:~$ sudo /sbin/ifconfig
Yubikey for `miguel': (press Yubikey button)
[sudo] password for miguel: (password as normal)
<snip>

You can also speed this process up in the same fashion as SSH: when you receive the Yubikey: prompt, enter your password and then press the Yubikey button. You won't get the second password prompt: the Yubico PAM module will do the heavy lifting of splitting off the password from the OTP and PAM just works it out.

I have experience doing all of this and leveraging Puppet with Hiera for API/key injection into the relevant configs, for efficient auto-deployment to server environments (as well as more general LDAP PAM/SSH key integration for centralised account management across servers, with H/A failover to LDAP replicas). If this sounds like something you're interested in commissioning a consultant to help integrate for you, feel free to get in touch.

Tags: