FIDO / Yubikey U2F Local authentication

Many months ago, I purchased some of the following "Hardware Security Key" devices:


Initially, this device sat on my desk for way too long, as a busy life provides few opportunities to tinker with such things in Ubuntu. This weekend, however, I was able to find some time to sit down and mess with it. After some searching online, I discovered that there are pre-built packages available in both the traditional apt repositories as well as a PPA for Yubico Software, on launchpad, which appears to have slightly newer versions of the software.

I decided to live dangerously and try my first attempt by using the packages provided in the Yubico PPA, rather than the stock distribution packages from Ubuntu. After all, I want to use a security device, and security updates are likely to hit the PPA first.

Following the instructions on

sudo add-apt-repository ppa:yubico/stable
sudo apt-get update

The above adds the new Yubico PPA to the package search path, and then updates the apt package database.

Next, I found some helpful documentation on Yubico's official site. Much of the documentation that follows was pulled from the below support article.

Following the above documentation, I installed the PAM library for U2F devices.

sudo apt install libpam-u2f

This also installs a number of other packages that provide a supporting function, such as libu2f-udev.

Setting up the device

The above command installs a number of libraries, applications, and configuration files. Once installed, I recommend rebooting the system so that things like systemd and udev can be ensured to load the new configurations that were installed. In theory, both of these should update at run-time, but depending upon what else has been customized on the system, this is the easiest way to ensure they're loaded.

At this point, I need to plug the device into one of the system's available USB ports.

The next step is to log in as the normal user account, and then generate a new Yubico configuration using the installed tool, pamu2fcfg:

mkdir -p ~/.config/Yubico
pamu2fcfg > ~/.config/Yubico/u2f_keys

The above creates the new Yubico configuration folder (if it doesn't yet exist), and then writes a new file into the folder that will be solely populated with the username and the key information provided by the device.

When pamu2fcfg is run, the system will pause and (in my case) the U2F device blinks indicating that I need to push the button on the device.

Testing the setup

Following the instructions above, I tested out the device by making my system require it in order for sudo to run commands.

First, I opened a new terminal, and then ran the following, to give a root shell:

sudo su -l

The reason for this is mainly to provide an easy exit path if it seems that something has gone wrong. For example, if the security key doesn't work properly, and sudo is configured to require it, I could leave myself without an easy way to undo the change to the sudo authentication configuration.

I opened the /etc/pam.d/sudo file with a text editor (vim, nano, etc.) and added the following line near the top (right under the #%PAM-1.0 line):

auth       required

Then I saved the file. If it worked correctly, then it will automatically be followed the next time sudo is run. To try it out, I opened a new terminal (as my normal user) and ran the following command:

sudo ls

The way that it is configured (u2f near the top of the file), the system first required me to push the button on my security key, and only after I did that, would it ask for my password. Performing both of this actions resulted in running the privileged command.

Next, I ran the following in the same terminal to reset sudo's memory, to force a re-authentication next time it is attempted to be used:

sudo -k

Then, I removed the security key altogether and tried again. The system immediately prompted me for my password, without waiting for the U2F device, but no matter how many times I entered the correct password, it simply said:

Sorry, try again.

Plugging it back in, I verified that the device behaved as intended, by re-running my sudo command. The behavior is subtle and results in a bit of obscurity about why the system is refusing a log in - behavior likely to mystify anyone attempting to use the system without authorization.

At this point, it is safe to exit the root login that was temporarily open. Remember: if this did't work, remove the line added to /etc/pam.d/sudo.

Additionally, I can move the position of the auth require line around in the file, if I want to change the order I will be prompted for the hardware security key confirmation.

Enable it for other authentications

Linux offers fine-grained authentication management through a number of files that are located in the /etc/pam.d/ folder. In the previous section, I demonstrated how I managed to protect sudo usage by requiring the use of my hardware U2F security key. This prevents privileged commands from being run on my system, without the key present.

This may be sufficient control for most cases, but I wanted to further restrict my login authentication, as well. On my Linux laptop, I can log in via the GUI or via one of the command-line terminals. In my Ubuntu installation, the gdm application manages logins via the GUI, and the standard login system manages the console logins.

The configurations for each of these are located in the following files:


I added the auth require line to both of these files, and then logged out and back in. I was then forced to use the hardware security key, and then enter my password, in order to log into the system. Success!

Finally, I use the Enlightenment Desktop Environment, which has its own built-in lock screen mechanism (that doesn't necessarily obey the gdm-password profile). Copying some of the content from /etc/pam.d/other, I generated the following new file in /etc/pam.d/enlightenment:

# /etc/pam.d/enlightenment - Authentication config for Enlightenment lock screen
auth       required

# Inherit system defaults
@include common-auth
@include common-account
@include common-password
@include common-session

Sending Enlightenment to lock mode then forces me to use both my password and the security key to unlock the GUI. Oddly, I am prompted first for the password, and then to activate the hardware key. This may simply be an implementation detail of Enlightenment and its lock screen. On my system, Enlightenment's lock screen always displays the text field for password entry.

In order to find what configuration in the /etc/pam.d/ folder to modify for Enlightenment, I looked up my answer in the source code. In this case, Enlightenment checks a large number of configuration file names. None of the coded ones exist in my Ubuntu 19.04 system, so I made the enlightenment one above.


At the end of this, I found it was relatively easy and painless to configure my system for local authentication using my new hardware security key. The default behavior obscures the requirement of the hardware key, which adds a nice bit of obfuscation to the authentication mechanism: an attacker may simply think they are getting a password wrong, and the system doesn't leak any indicator of the security key requirement.

The next steps would be to investigate additional parameters to the authentication module to further customize the behavior on my system:

Rather than manually editing each service one at a time, I might prefer to add it to the /etc/pam.d/common-auth file, which is included by all the other modules. One reason you might not want to try this route is where remote access to the system is desirable through something like sshd. In that case, I would want to investigate the U2F network server packages, which is itself an even larger task and probably suitable for a future blog post.

Finally, the documentation I cited toward the beginning of this article explains that the software supports multiple hardware keys. A recommendation is provided to always have a spare stored in a safe & secure location, in the event of theft or loss of the primary key. Without a backup key, recovery of the system could be difficult or impossible depending upon the configuration.