YubiKey on FreeBSD HOWTO


This page will walk you through setting up two-factor SSH authentication with a Yubico YubiKey on a per-user basis. Although most of the instructions are generally applicable on most systems, the install method and directory paths are FreeBSD specific. For more complex configurations and additional options, see the full documentation of the Yubico PAM module.

Update 12/2013:
FreeBSD 9.2 users: If you run into a situation where the Yubikey prompt never returns after you submit an OTP, you must change your curl port to use "Asynchronous DNS resolution via c-ares", instead of "Threaded DNS resolver". Thanks to Frederique Rijsdijk for reporting this issue.

Update 10/2013:
If you are using the OpenSSL from ports (/usr/ports/security/openssl) then you MUST also use openssh-portable (/usr/ports/security/openssh-portable) for your SSH connections. The short explanation for this is that the base SSH is linked to the base OpenSSL, but the Yubico PAM module is linked to the ports OpenSSL. This ends up with a crash at runtime which manifests as the Yubikey prompt being presented to you repeatedly. There is currently no other viable solution for this. If you absolutely cannot run anything but the base SSH and ports OpenSSL, or are wondering why the port can't overcome this issue, send me an email and I'll give you the not-so-viable workaround and reasons why this can't easily be dealt with. Thanks to the people who reported and helped with troubleshooting this issue.

Please send any questions or comments on this document and the FreeBSD Yubico ports to me. Thanks!


Getting Started (for FreeBSD beginners)

You should have an updated copy of the FreeBSD ports tree installed. This is in /usr/ports by default. The official method to download and keep up to date the ports tree as of the writing of this document is with subversion. If you're not sure if you have an updated ports tree, and don't know where to start, something like this will probably get you what you need (as root):

# Make sure we have subversion
pkg_add -r subversion

# Move the old (if any) ports tree out of the way
mv /usr/ports /usr/ports.bak

# And finally, check out the new copy
svn checkout svn://svn.freebsd.org/ports/head /usr/ports
						


Install the Ports

Now that we have an updated copy of the ports tree, the next step is to install the needed PAM modules (pam_yubico and pam_per_user).

# Install the pam_yubico port and its dependencies
cd /usr/ports/security/pam_yubico && make install

# and pam_per_user
cd /usr/ports/security/pam_per_user && make install
						


Configure PAM

Now on to configuring PAM, which is the system that handles authentication, among other things. I like to keep additions and edits to /usr/local as much as possible, so the first thing we'll do is move our stock sshd PAM file out of the way, and create a /usr/local area for our PAM configuration.

# Move the system version of the sshd PAM file out of the way
cp -a /etc/pam.d/sshd /etc/pam.d/sshd.bak

# Make our local pam.d directory if it doesn't already exist
mkdir -p /usr/local/etc/pam.d

# Now move the system version we already backed up to our new 'sshd-default'
mv /etc/pam.d/sshd /usr/local/etc/pam.d/sshd-default
						
sshd-default is what users who are not going to use a YubiKey will default to, as it is exactly like the stock system version they've always used.

Now, create a new /usr/local/etc/pam.d/sshd to replace the /etc/pam.d/sshd that we moved out of the way. It should contain the following contents.

# Route all ssh users through the pam_per_user system
auth required /usr/local/lib/security/pam_per_user.so.1
Next up, we need to create /usr/local/etc/pam.d/sshd-yubikey which is the PAM config file for the users we want to authenticate with a YubiKey. Before we do that though, you need to get an API key from Yubico that the pam_yubico module will use to communicate with the Yubico servers when authenticating the OTP. To get an API key, make sure you have your YubiKey handy and go to the Get API Key page. It should give you a Client ID and a Secret key.

Now we're ready to populate /usr/local/etc/pam.d/sshd-yubikey. The file should look like what you see below, making sure you replace CLIENTID and SECRETKEY with your actual Client ID and Secret key that you got from Yubico.

# PAM configuration for the "sshd" service using a YubiKey

# auth
auth        requisite   /usr/local/lib/security/pam_yubico.so id=CLIENTID key=SECRETKEY
auth        required    pam_unix.so     no_warn try_first_pass

# account
account     required    pam_nologin.so
account     required    pam_login_access.so
account     required    pam_unix.so

# session
session     required    pam_permit.so

# password
password    required    pam_unix.so     no_warn try_first_pass
						
And finally, we need to configure the pam_per_user PAM module, which allows us to specify which users will use the YubiKey auth method, and which will use the normal SSH auth method.

Create the file /etc/pam_per_user.map, and populate it with something like the following.

myuser : sshd-yubikey
myuser2 : sshd-yubikey
* : sshd-default
That example says that myuser and myuser2 will authenticate with their YubiKey, and everyone else will default to the old method. If you want to do something a little more complex, or aren't sure what you need to do, consult the pam_per_user man page.


Configure SSH

Now, on to making sure SSH works the way we want. This is pretty quick, all we need to do is edit our /etc/ssh/sshd_config file.

The three settings that we care about and their proper values are

  • ChallengeResponseAuthentication yes
  • PasswordAuthentication no
  • UsePAM yes
Here is a small bit of shell that'll do everything you need.

# Back up our sshd_config
cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.preyubikey

# Run sshd_config through sed to change everything we need changed
cat /etc/ssh/sshd_config | sed -e 's/^#*ChallengeResponseAuthentication .*/ChallengeResponseAuthentication yes/' -e 's/^#*PasswordAuthentication .*/PasswordAuthentication no/' -e 's/^#*UsePAM .*/UsePAM yes/' > /etc/ssh/sshd_config
						
And finally, completely stop and restart the sshd service by running:

/etc/rc.d/sshd stop && /etc/rc.d/sshd start

Don't worry if you're currently connected via SSH, this won't kick you off.


Configure the User

Finally, we need to configure each user that will authenticate with a YubiKey (all of the users we listed in our /etc/pam_per_user.map). There are two options for this: a system-wide configuration (maintained by the sys admin), or a per-user configuration (done by/as the user).

User

For each user who wants to use a YubiKey to authenticate, we need to list their token ID. All of the following should be done as the user (in this example myuser, not root).

Each user needs their token ID, which is the first 12 characters of an OTP generated by a YubiKey (or you can get it from the Modhex Calculator), and also a .yubico directory created in the user's home.

mkdir ~myuser/.yubico && chmod 750 ~myuser/.yubico
Now make ~myuser/.yubico/authorized_yubikeys which has the format of user:token, like so:

# In the format of username:tokenID
myuser:abcdefghijkl
And finally make sure the permissions are correct.

chmod 640 ~myuser/.yubico/authorized_yubikeys

System

If as the system operator you want to maintain a master file of everyone's YubiKey IDs that they will use to authenticate, there is a couple steps to go through.

First, we need to go back to /usr/local/etc/pam.d/sshd-yubikey and add in an argument that specifies the file we want to use to keep track of our users. We need to add the authfile option to the end of the first auth line, for example:

# PAM configuration for the "sshd" service using a YubiKey

# auth
auth        requisite   /usr/local/lib/security/pam_yubico.so id=CLIENTID key=SECRETKEY authfile=/usr/local/etc/yubikey_mappings
						
Now we will create the file we specified, in this case /usr/local/etc/yubikey_mappings. It is of the exact same format as the ~myuser/.yubico/authorized_yubikeys file we talked about in the User section above.

# In the format of username:tokenID:tokenID2
myuser:abcdefghijkl
joeblow:aaaaabbbbcgd:qwertyuioplk
anotheruser:ccccccccjjjj


Test & Enjoy

Now try to connect in and see if it works. You should see something like this.

myuser@remote% ssh myuser@my.host.local
Yubikey for `myuser': <hit the button on your YubiKey>
Password: <type in the password for myuser>
myuser@my.host.local% 

Some things to note: if you are using private key authentication then you will NOT see the Yubikey or Password prompt. If you want to turn that off so users are forced to use the Yubikey, you'll have to turn it off system wide in /etc/ssh/sshd_config. The setting is PubkeyAuthentication yes. However, this is usually not a good idea. If you use something that doesn't allow for use of a Yubikey OTP (e.g. an iPhone SSH client), then the user will not be able to connect without the use of their private key.

That's it, enjoy!