pam-u2f, YubiKeys, what the hell?
PAM PAM PAM PAM?
PAM as in Pluggable Authentication Modules is the implementation of a
modular mechanism for authentication in Unix-like operating systems, mostly
GNU/Linux environments. It allows system administrators to define how
authentication, session management, and account policies are enforced,
independently of applications that require authentication.
PAM consists in a set of shared libraries that applications can interact with
to authenticate users. Configuration of these modules is done through
/etc/pam.d/ directory. They can be stacked in order to define security policies.
This is where pam-u2f comes into play.
pam-u2f?
pam-u2f is a PAM module that enables authentication using Universal Second
(2nd) Factor (U2F) security keys (like, oh wait, YubiKeys) as a second factor
for two-factor authentication (usually referred as 2FA), significantly
increasing the system security.
It can also serve as a way to authenticate without typing in a password (passwordless authentication), but in that case one loses the second-factor.
So, in short, pam-u2f has the following main features:
- Two-Factor Authentication: The thing we’ll discuss about here, it requires both a password and physical possession of a U2F key.
- Passwordless Authentication: Can be configured to allow logins solely with a U2F key.
Setting up pam-u2f
To deploy pam-u2f, on a Debian-based system, run:
sudo apt install libpam-u2f
For U2F to work, each user with a YubiKey need a mapping, happily there is a binary for that. By default, it generates a configuration requiring user presence verification (ie touching the key). This can be disabled (but would be less secure), and other options can be enabled to increase security, eg PIN verification (which we will use there).
As an example, one can run the following command to generate a U2F mapping:
pamu2fcfg -N > ~/.config/Yubico/u2f_keys
The file path in the example is the default one for pam-u2f to look when a
user authenticates themselves. This can be configured (which might be useful to
make things more secure by enforcing the U2F keys for each user and making them
non-configurable). This has pros (better control on what is being done), but
also has cons as it offloads the ability to add the key on an administrator
while a user with reasonable practice could work more efficiently if they were
able to add keys on their own.
In a later PAM global config example, I’ll refer to a file being in /etc
to follow this logic. Note that a central authfile expects lines prefixed
with the username (username:keydata), unlike the per-user file which contains
the key data directly.
Also, note that the PIN verification might induce problems with screenlockers
that do not support them. Indeed, currently, under wayland I did not manage
to find a screenlocker that works nicely. The way screenlocking works under
wayland is significantly distinct from the way Xorg was working, and I believe
most screenlocker therefore were worked on in order to actually work. PIN
prompt and presence verification are multi-step conversations with PAM, and
only the gnome-shell screenlocker (there is no longer a dedicated
gnome-screensaver app) seems to work well with that, but it’s not usable on
another desktop environment than Gnome.
Therefore if one works under Wayland, except if using Gnome, some screenlockers are able to handle presence verification, but not PIN+Presence.
Enabling pam-u2f
Depending on the system on which one wants to use U2F, there are multiple ways
to configure it. I’ll describe some generic usages here, and then the one I use
on my laptops, which essentially is putting the pam-u2f in place at the
common-auth step (which means almost all authentication method will need
U2F to work).
Some interesting options:
authfile=/absolute/path: allows to point to the file with the mappings if one wants to enforce central configurationuserpresence=0|1: force the user to show their presence by touching the key (the default is to use the instructions in the mapping file)pinverification=0|1: force the user to type in their pin (the default is to use whatever is set in the mapping file)cue: prompt to touch the key if needednouserok: makes the module return success if the user doesn’t have a mapping file or is not in the central mapping file.
Global setup
At the end of /etc/pam.d/common-auth, add the line:
auth required pam_u2f.so authfile=/etc/u2f_mappings_file nouserok cue
Mode specific setup
If one doesn’t want to have a global setup, here are examples to put U2F only
on specific tools.
sudo
Adding U2F authentication to sudo ensures an additional layer of security
when executing privileged commands:
sudo ${EDITOR} /etc/pam.d/sudo
Add the following line:
auth required pam_u2f.so
SSH Login
Enhance SSH security by requiring a U2F key for login.
This one is a bit trickier. The idea is to secure SSH logins on a remote
machine. SSH has its own authentication mechanism through
PasswordAuthentication or KbdInteractiveAuthentication. In general distros,
I can’t certify whether or not SSH Daemon relies on PAM. In Debian, by default,
we enable the UsePAM bit.
One therefore needs to make sure it’s properly configured in
/etc/ssh/sshd_config. One also needs to make sure that
KbdInteractiveAuthentication is set to yes (ChallengeResponseAuthentication
was the old name, deprecated since OpenSSH 8.7).
The YubiKey needs to be in the client machine logging in the SSH server,
and the U2F keys config file must be on the server, in the user’s
~/.config/Yubico/u2f_keys (or in a central authfile).
sudo ${EDITOR} /etc/pam.d/sshd
Add:
auth required pam_u2f.so
Warning
Ensure KbdInteractiveAuthentication yes is enabled in
/etc/ssh/sshd_config. On older systems (OpenSSH < 8.7) this option was named ChallengeResponseAuthentication.
GDM Login
Let’s say one uses Gnome and wants a second layer at login:
sudo ${EDITOR} /etc/pam.d/gdm-password
Add:
auth required pam_u2f.so
Bonus: self-locking laptop when removing the key
If you’re a bit paranoid like me, you’d expect your laptop to self-lock quite
fast if you’re idling. Under sway, I use a custom script that is called by
swayidle.
If running swayidle with a lock listener, one can add a signal to lock
sessions when removing the YubiKey (which I do at work every time I get off my
desk even to talk to someone). For the swayidle command, listening to a lock
signal could be something like
swayidle -w lock <lock_command> [timeout <seconds> <lock_command> before-sleep <lock_command>]
To achieve this goal, one could use a udev rule like this:
/etc/udev/rules.d/99_yubi_removal.rules:
ACTION=="remove", SUBSYSTEM=="usb", ENV{PRODUCT}=="1050/407/*", RUN+="/usr/bin/loginctl lock-sessions"
Run sudo udevadm control --reload-rules and you can test how it goes by
removing the key.
Note
In case you’re curious, my lock script was just there to
wrap a call to i3lock or gnome-screensaver-command depending whether a
U2F mapping for my user was present (home station was a bit less
paranoid), as i3lock doesn’t handle U2F + Pin + Presence. I’ve just
ported the script using swaylock instead of i3lock when I moved to
sway. Sadly one can’t use gnome-screensaver anymore as the screensaving
is embedded in Gnome Shell.
What I would recommend doing
Every person has their own way of working and their own security expectations, I’m no one to tell somebody else how they should do it, except at work, where it’s part of my job. That being said, I can still recommend or underline some things:
- Using
pam-u2fis not hard; - In general, all administration options should rely on a MFA model, so I
really would suggest to rely to either
U2For someTOTPfor sudo runs; - Having some
U2FforSSHaccess might reduce the impact ofMITM1 attacks. Of course one should still be very wary when they seeSSHhost key changes (and try to avoidTOFU2 model when accessingSSHplatforms for the first time).
In the whole, there is little setup, and this is quite reliable.