/home/adeel

Passwordless logins with Yubikey

Yubikey is currently the de facto device for U2F authentication. It enables adding an extra layer of security on top of SSH, system login, signing GPG keys, and so on. It is also compatible with several other authentication methods, such as WebAuthn and PAM.

This post will show how to leverage your Yubikey for unlocking the system lock-screen, both with and without using a password. It will then delve into how to automatically lock the screen when the Yubikey is unplugged.

To achieve logins with Yubikeys we require a PAM configuration. PAM or Pluggable Authentication Modules define the authentication flow for common Linux utilities, such as sudo, su, and passwd. We will override the default authentication flow for the xlock lock manager to allow logins with Yubikey.

Note: The above process should be similar across most lock managers, such as i3lock or xscreensaver.

Creating a PAM configuration

We shall first replicate the default authentication provided with xlock using PAM. With this configuration the user should only be able to log in with their password.

All PAM configuration files lie under the /etc/pam.d/ directory. We create a file named xlock which replicates the default authentication:

$ cat /etc/pam.d/xlock
#%PAM-1.0
auth            include         system-auth

Note: For the above configuration file to take effect the tool (xlock) must be PAM compatible. We can confirm that xlock is PAM compatible by inspecting the output of ldd /usr/bin/xlock | grep libpam.so.

The first comment line indicates the PAM version. The lines that follow define the authentication flow:

  • auth is the module interface responsible for verifying the user’s password.
  • include is the PAM control flag which includes the system-auth configuration file (this file defines the default authentication flow). This flag can also be used to load modules, as we shall see later.

Note: There is an excellent documentation provided by RedHat on PAM configuration files.

Supporting Yubikey logins

We shall now add Yubikey login functionality to our PAM configuration, but we first need to install the Yubico module for PAM and set it up.

Yubico, the company behind Yubikeys, exposes the pam_yubico.so module which can be used for Yubikey authentication.

It provides two authentication mechanisms, the client mode and the challenge-response mode. The client mode sends a request to the Yubico server for verifying the user’s OTP, and requires an active Internet connection for the user to login. As this is inconvenient we shall only explore the challenge-response mode in this post.

Before proceeding with the configuration the pam_yubico package must be installed manually. This package is easily available across most Linux distributions. On Arch Linux it can be installed with:

$ pacman -S yubico-pam

We next add Yubikey mappings before setting the challenge-response credential.

Warning: It is recommended that you use a secondary account to perform the next steps as there is a risk of permanently locking your account (in case of PAM misconfiguration).

Adding mappings

Each Yubikey must be paired with a unique public ID which the pam_yubico module uses to uniquely identify the user. The public ID consists of the first 12 characters extracted from the OTP token.

To obtain your Yubikey’s public ID open up your shell and press the Yubikey button. You will see a similar output as below:

vvctffbvkhdnliklfhbbfiecudthfvrvuhnhtirehidr

Now copy take the first 12 characters (vvctffbvkhdn) and add them to a file named yubikey_mappings in the /etc/ directory, along with your username. In our case this will be:

$ cat /etc/yubikey_mappings
adeel:vvctffbvkhdn

Note: This file also allows specifying multiple Yubikey mappings, each separated by a new line.

Setting the challenge response credential

Yubikey needs to somehow verify the generated OTP (One Time Password) when it tries to authenticate the user. It does so by using the challenge-response mode.

To set up the challenge-response mode, we first need to install the Yubikey manager tool called ykman. On Arch Linux it can be installed with:

$ pacman -S yubikey-manger

The ykman tool will generate a secret credential and store it in a local file. Whenever the user tries to login with xlock, the pam_yubico module will verify the generated OTP against the stored credential.

The challenge response credential can be set on slot 2 of the Yubikey with:

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

Warning: Ensure that the slot you’re writing the data to doesn’t already contain any credential, as it might not be recoverable!

After the challenge-response credential is set it needs to be written to a local file which will be later read by pam_yubico.

Yubico provides another tool called ykpamcfg (which should be bundled with the yubikey-manger package) to write this file to disk. It takes the Yubikey slot number as its parameter and writes the secret to a file:

$ ykpamcfg -2
Stored initial challenge and expected response in '/home/adeel/.yubico/challenge-<Serial ID>'.

Updating the Linux PAM configuration

We shall now update the /etc/pam.d/xlock file and add the Yubico PAM at the very beginning.

$ cat /etc/pam.d/xlock
#%PAM-1.0

auth  sufficient  pam_yubico.so debug mode=challenge-response authfile=/etc/yubikey_mappings
auth  include     system-auth

We pass three parameters to the pam_yubico.so module:

  • debug prints all the authentication steps to the console when the ‘Enter’ key is pressed.
  • mode specifies which mode the module will use for authentication (challenge-response or client).
  • authfile points to the credential file written by the ykpamcfg tool.

Setting the module type to sufficient means that if Yubikey authentication succeeds, no further steps will be processed and the user will get logged in. This is the key point which enables passwordless logins. However, in the event of authentication failure, remaining authentication steps will still be applied, i.e. the user can still log in with their password if the Yubikey is not plugged in.

If the module type is set to required instead of sufficient it will enable Two-Factor Authentication (2FA) which will require the user to plug in their Yubikey and enter their password to login.

Note: For passwordless logins the user will need to press the Enter with their Yubikey plugged in to unlock their screen.

At this stage you should be able unlock your screen with they Yubikey.

Note: You may need to replug your Yubikey for the changes to take effect.

Automatically locking the screen when Yubikey is unplugged

Up till locking the screen still requires manually invoking the xlock command. It would be nice if we can somehow automatically lock the screen whenever our Yubikey is unplugged. We can achieve this with Udev.

Udev is the device manager used in Linux which can be used for a myriad of tasks. It tracks the state changes for all external devices, for example, it can be used to identify when a USB device is plugged or unplugged. Each device outputs a series of attributes which can be used to uniquely identify it.

We shall use these attributes to create a Udev rule which triggers an xlock.service Systemd service when the Yubikey is unplugged.

Note: We can also achieve this with a Shell script instead of Systemd, but Udev discourages executing long-running programs using scripts as it terminates them after a certain time period.

Creating the Systemd service

Systemd is the Linux service manager which can be used to launch user processes. We create a file named xlock.service in the /etc/systemd/system/ directory:

$ cat /etc/systemd/system/xlock.service
[Unit]
Description=xlock

[Service]
User=adeel
Type=simple
Environment=DISPLAY=:0
ExecStart=/usr/bin/xlock 

[Install]
WantedBy=multi-user.target
  • The Type=simple implies that this service does not exit after execution.
  • The Environment tag specifies which display should be locked (0 is the default display).
  • The ExecStart tag takes a path of the binary or script it will execute.

Note: Consult the official docs to explore Systemd in detail.

Creating the Udev rule

We first need to identify a set of unique attributes for our device (Yubikey). The udevadm tool allows monitoring Udev output whenever a device state changes. We shall invoke the following command and then remove our Yubikey:

$ udevadm monitor --environment --udev 
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing

UDEV  [461872.738673] remove   /devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.1/1-2.1:1.0/0003:1050:0407.0157/input/input294/event18 (input)
ACTION=remove
ID_VENDOR=Yubico
SUBSYSTEM=input
DEVNAME=/dev/input/event18
ID_INPUT_KEY=1
...

We only show a truncated output above, but once you have identified the attributes you would like to use, create a file named yubikey-actions.rules in the /etc/udev/rules.d/ directory:

$ cat /etc/udev/rules.d/yubikey-actions.rules 
ACTION=="remove", ENV{ID_MODEL_ID}=="0407", ENV{ID_VENDOR_ID}=="1050", RUN+="/usr/bin/systemctl start xlock.service"

It might be worthwhile to reload the configuration for both Systemd and Udev:

$ systemctl daemon-reload
$ udevadm control --reload

If everything worked out fine your screen should now get locked whenever you remove your Yubikey.

References: