U2F security keys are handy little devices that give you a strong second factor for authentication. Google, Microsoft, GitHub, and plenty of others already support them. You can use the same standard to add 2FA to your Linux system for login, sudo, and su.
Prerequisites
This works with any security key that speaks U2F. Yubikeys, Titan keys, doesn’t matter. All Yubikey models support U2F.
- A U2F-compatible security key
- A Linux machine
- Root access
- A few minutes
Install pam-u2f
The package name varies by distro:
- Debian/Ubuntu:
sudo apt update && sudo apt install libpam-u2f - Arch Linux:
sudo pacman -Sy pam-u2f - Fedora:
sudo dnf install pam-u2f
Configure Security Keys for Users
The package comes with pamu2fcfg, a tool that handles key registration. You’ve got two choices for where to store the config.
Individual Configuration File
Each user gets their own config file in their home directory. They can add or remove keys without root. This is the better setup if home directories are accessible at login (no full disk encryption).
Create the config
mkdir ~/.config/Yubico && touch ~/.config/Yubico/u2f_keys
Add your keys
pamu2fcfg -u <USER> -o <ORIGIN ID> -i <APP ID> | tee ~/.config/Yubico/u2f_keys
# e.g. pamu2fcfg -u ashu -opam://ashu.io -ipam://ashu.io | tee ~/.config/Yubico/u2f_keys
For additional keys:
pamu2fcfg -n -u <USER> -o <ORIGIN ID> -i <APP ID> | tee -a ~/.config/Yubico/u2f_keys
Central Configuration File
One config file for all users, stored at /etc/pamu2f.cfg. Only root can modify it, which is a tradeoff. You need this approach if you encrypt home directories, since they only unlock after login.
Create the config
sudo touch /etc/pamu2f.cfg
Don’t give users write permission to this file on multi-user systems. It would expose everyone’s keys.
Add your keys
Plug in the key and run:
echo $(pamu2fcfg -u <USER> -o <ORIGIN ID> -i <APP ID>) | sudo tee -a /etc/pamu2f.cfg
# e.g. echo $(pamu2fcfg -u ashu -opam://ashu.io -ipam://ashu.io) | sudo tee -a /etc/pamu2f.cfg
For additional keys, append with:
pamu2fcfg -n -u <USER> -o <ORIGIN ID> -i <APP ID> | tee -a <Path to config file>
Enable pam_u2f.so in PAM
Add this line to the PAM config of whatever service you want to protect:
auth sufficient pam_u2f.so authfile=<Path to config file> appid=<APP ID> origin=<Origin ID> nouserok cue [cue_prompt=<Prompt to show>]
Real example:
auth sufficient pam_u2f.so authfile=/etc/pamu2f.cfg appid=pam://ashu.io origin=pam://ashu.io nouserok cue [cue_prompt="Please touch your Yubikey to Authenticate"]
Put this as the last line in the auth section of these files under /etc/pam.d/:
- sddm — KDE login manager
- sudo — 2FA for sudo
- login — CLI/TTY logins
- su — 2FA for su
File names might differ slightly depending on your distro.
sddm example (BEFORE):
auth include system-login
-auth optional pam_gnome_keyring.so
-auth optional pam_kwallet5.so
account include system-login
password include system-login
-password optional pam_gnome_keyring.so use_authtok
session optional pam_keyinit.so force revoke
session include system-login
-session optional pam_gnome_keyring.so auto_start
-session optional pam_kwallet5.so auto_start
sddm example (AFTER):
auth include system-login
-auth optional pam_gnome_keyring.so
-auth optional pam_kwallet5.so
auth sufficient pam_u2f.so authfile=/etc/pamu2f.cfg appid=pam://ashu.io origin=pam://ashu.io nouserok cue [cue_prompt="Please touch your Yubikey to Authenticate"]
account include system-login
password include system-login
...
Keep in mind: some login methods like sddm won’t actually display the prompt asking you to touch your key. It’ll just wait.
Passwordless sudo with U2F
You can set up sudo so that touching your Yubikey is enough. No password needed.
Edit /etc/pam.d/sudo and put this at the very top:
auth sufficient pam_u2f.so authfile=<Path to config file> appid=<APP ID> origin=<Origin ID> cue [cue_prompt=<Prompt>]
Here’s what a passwordless sudo config looks like:
auth sufficient pam_u2f.so authfile=/etc/pamu2f.cfg [cue_prompt=Please Confirm Your Identity.] cue origin=pam://ashu.io appid=pam://ashu.io
auth include system-auth
account include system-auth
session include system-auth
Let me know if I missed anything.