⟵ Home

Fancy Linux Security with YubiKey

September 12, 2023 ∙ 6 minute read

At $workplace, folks with access to sensitive systems and infrastructure are provided with YubiKeys to be used as a multi-factor authentication mechanism. In the end, it is even better, as we can authenticate against services and systems without even needing to enter a username/password combination. The device alone is enough to bypass the authentication page. But this post is not about what you can do on the web using YubiKeys. No. I’m more interested on how to leverage the key to also lock and unlock my workstation based on its presence. The workstation itself counts with disk encryption and other protections, so those areas are already covered. But what if I could authenticate sudo or even lock/unlock the computer using the key that I already need to have with me at all times whenever I’m handling anything work-related?

Today we will configure Linux to work with a YubiKey for authenticating sudo password challenges, and also locking/unlocking the machine depending on the presence of the key itself!

The Environment

I will be working on a machine running Ubuntu LTS. But should be equivalent in other distros (YMMV). The key is an YubiKey 5C NFC in a keychain form factor.

Hic Sunt Dracones

Throughout this post we will be dealing with PAM. In case PAM sounds like something new, it may be enough to know that it is responsible for handling authentication in Linux. PAM stands for Linux Pluggable Authentication Modules. To quote the project:

PAM provides a way to develop programs that are independent of authentication scheme. These programs need “authentication modules” to be attached to them at run-time in order to work. Which authentication module is to be attached is dependent upon the local system setup and is at the discretion of the local system administrator.

Pretty fancy, eh? Anyway. If you intend to follow along the first precaution you should take is make sure you don’t get locked out of your system. This can be simply accomplished by having a spare terminal session as root, so in case of an emergency, you will be able to rollback the system to an usable state. Also, please do notice all shell commands here are prefixed with $: they are meant to be executed by your non-priviledged account.

Step 1: Installing Dependencies

Of course we will need some extra modules. Fire up a terminal, and sudo apt install the following packages:

libpam-yubico yubikey-personalization yubikey-manager

Step 2: Configure the YubiKey

Each YubiKey has two “slots” on its OTP application. The IT department already configured the first one and tied it to my account, but the second one is empty, so I’ll use the later. Before continuing, make sure your slot is empty. This can be done by using ykman we just installed:

$ ykman otp info
Slot 1: programmed
Slot 2: empty

Empty is good! So let’s use that one. What we want to do here is configure that slot to contain a “Challenge-response” configuration, which is basically what the key already does, but without requiring the key to be touched. This of course is a reductive summary of what the configuration really is, and extensive documentation regarding it can be found on Yubico’s Website.

Now that we ensured our second slot is empty, it is time to configure it. Doing so is really simple. We want to use ykman to configure OTP’s chalresp on slot 2:

$ ykman otp chalresp -g 2
Using a randomly generated key: ...
Program a challenge-response credential in slot 2? [y/N]:

ykman wants a confirmation that we are sure we want to program slot 2. Hit y with your favourite finger, and confirm.

Step 3.1: Store the Initial Challenge

The Initial Challenge file contains the expected challenge and response to be obtained in the next authentication round. It will be used by PAM later to assert whether a valid key is connected to the machine. As you may guess, this file contains sensitive information! We will need to store it with care.

First, let’s extract it. For that step, we will need ykpamcfg:

$ ykpamcfg -2
Directory /home/vitosartori/.yubico created successfully.
Sending 63 bytes HMAC challenge to slot 2
Sending 63 bytes HMAC challenge to slot 2
Stored initial challenge and expected response in '/home/vitosartori/.yubico/challenge-12345678'

That was easy! However, there’s an important information right there: ykpamcfg said it stored the challenge and expected response in a file. That file is called challenge-<SERIAL>, where SERIAL is the serial of that specific key you are using. In the example above, the key’s serial is 12345678. We will need that later!

Step 3.2: Moving the Initial Challenge to a System Directory

libpam-yubico will look for that challenge file in a few different directories, but in order to accomplish our objective, we will need to move it to a specific one. So let’s do it:

# Create the directory
$ sudo mkdir /var/yubico

# Ensure it belongs to root
$ sudo chown root:root /var/yubico

# And restrict access to it
$ sudo chmod 0700 /var/yubico

Finally, move the generated challenge to the directory we created. I’ll break this into two steps cause the first one requires a bit extra attention:

sudo mv /home/vitosartori/.yubico/challenge-12345678 /var/yubico/vitosartori-12345678

The name of destination file is important! It must comprise your username, followed by a dash, followed by the key’s serial. Effectively, we must move it to /var/yubico and replace the challenge part of the filename with your username.

Finally, fix file permissions and ownership:

sudo chown root:root /var/yubico/*
sudo chmod 0600 /var/yubico/*

Step 4: Configuring libpam-yubico

We will now configure Yubico’s PAM to use challenge-response mode, look for challenges on /var/yubico, and temporarily enable a debug mode (so we make sure everything is alright and figure out in case anything goes awry during authentication tests.)

For this step we will leverage dkpg-reconfigure to do some magic. To get a complete list of available options, check yubico-pam’s repository.

This procedure will be performed in two steps, since they require some extra attention. First, run dpkg-reconfigure:

$ sudo dpkg-reconfigure libpam-yubico

It will show a dialog explaining operation modes and such. Select the text field and erase all its contents. Then, enter the following parameters:

mode=challenge-responds chalresp_path=/var/yubico debug

Then, confirm the changes. Another dialog will be displayed, asking to select which behaviours are to be enabled. Look up for the Yubico authentication with YubiKey, and make sure it checked, then confirm changes.

Finally, it’s time to just change a small setting. By default, the new PAM module will be configured as required, which means it will be the sole authentication method on the system. This is not what we want; I think having YubiKey support is interesting, BUT being able to type my long password is also important. Let’s change this.

Fire up your preferred editor to edit /etc/pam.d/common-auth. I’ll use vim:

$ sudo vim /etc/pam.d/common-auth

Then, look for a line defining pam_yubico.so as an authentication module. It should look like this:

auth     required     pam_yubico.so mode=challenge-response chalresp_path=/var/yubico debug

Then, replace required with sufficient, and save the file.

Step 5: Test

Finally! Ensure your key is still connected to your system, open a new terminal session, and run sudo -s:

$ sudo -s
debug: ../pam_yubico.c:838 (parse_cfg): called.
debug: ...lots of
debug: ../pam_yubico.c:1220 (pam_sm_authenticate): done. [success]
# whoami

If you are now in a root shell, congratulations! It is done! Now disconnect the key, open a new shell, and try sudo -s. Authenticate using your password. If it works, then we are done! There’s just one last step…

Step 6: Disable Debugging

Of course we don’t want that much log whenever we run sudo. If everything is as it should be, we don’t need that debug parameter any longer. Open your editor on /etc/pam.d/common-auth, locate the pam_yubico.so, and remove the last debug parameter. Save, and it’s done! Even GNOME’s screen lock works out of box!