This document describes how I setup two factor authentication (what I know, my password, and what I have, my U2F device) on my Debian 10 laptop. My goal was to provide two factor protection for local login and for sudo authentication. The following proceedure was tested using a Google Titan version T3 and a YubiKey 4.
NOTE: While this procedure is simple, you can lock yourself out of your system if you mistype commands or skip steps. A good way to prevent this from happening is to create another user that you can use to log into your system in the event that your key file is configured incorrectly. This user should have the ability to become the root user (using su or sudo) in order to repair any mistakes that you might make. You might also want to secure one PAM authorization module (sudo, gdm-password, etc.) at a time until you're sure that it is working correctly.
udev (user space /dev) is a device manager for the Linux kernel. Adding the following udev rule will enable most U2F devices to work on your machine. It is a modified version of https://raw.githubusercontent.com/Yubico/libu2f-host/master/70-u2f.rules but is largely the work of Yubico AB.
# Copyright (C) 2013-2015 Yubico AB
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
# this udev file should be used with udev 188 and newer
#
# Modifications to support Google Titan made by H. Dale McBane 2019
ACTION!="add|change", GOTO="u2f_end"
# Yubico YubiKey
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0113|0114|0115|0116|0120|0200|0402|0403|0406|0407|0410", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Happlink (formerly Plug-Up) Security KEY
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Neowave Keydo and Keydo AES
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1e0d", ATTRS{idProduct}=="f1d0|f1ae", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# HyperSecu HyperFIDO, KeyID U2F
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e|2ccf", ATTRS{idProduct}=="0880", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Feitian ePass FIDO, BioPass FIDO2, KeyID U2F
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0850|0852|0853|0854|0856|0858|085a|085b|085d", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# JaCarta U2F
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="24dc", ATTRS{idProduct}=="0101|0501", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# U2F Zero
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# VASCO SeccureClick
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1a44", ATTRS{idProduct}=="00bb", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Bluink Key
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2abe", ATTRS{idProduct}=="1002", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Thetis Key
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1ea8", ATTRS{idProduct}=="f025", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Nitrokey FIDO U2F
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Google Titan U2F
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="5026", TAG+="uaccess", GROUP="plugdev", MODE="0660"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0858", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Tomu board + chopstx U2F + SoloKeys
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="cdab|a2ca", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# SoloKeys
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="5070|50b0", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Trezor
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", TAG+="uaccess", GROUP="plugdev", MODE="0660"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Ledger Nano S and Nano X
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0001|0004", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Kensington VeriMark
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="06cb", ATTRS{idProduct}=="0088", TAG+="uaccess", GROUP="plugdev", MODE="0660"
# Longmai mFIDO
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="4c4d", ATTRS{idProduct}=="f703", TAG+="uaccess", GROUP="plugdev", MODE="0660"
LABEL="u2f_end"
Once your rule is in place, either reboot (sudo reboot
) or have udev reload the rules (sudo udevadm control --reload
). Now when you insert your U2F device and enter sudo dmesg
from the terminal, you should see something similar to the following:
[ 9264.628579] usb 1-1: new full-speed USB device number 11 using xhci_hcd
[ 9264.781999] usb 1-1: New USB device found, idVendor=1050, idProduct=0407, bcdDevice= 4.37
[ 9264.782007] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=xxxxxx
[ 9264.782011] usb 1-1: Product: Yubikey 4 OTP+U2F+CCID
[ 9264.782015] usb 1-1: Manufacturer: Yubico
[ 9264.786189] input: Yubico Yubikey 4 OTP+U2F+CCID as /devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/0003:1050:0407.000F/input/input37
[ 9264.845283] hid-generic 0003:1050:0407.000F: input,hidraw1: USB HID v1.10 Keyboard [Yubico Yubikey 4 OTP+U2F+CCID] on usb-0000:00:14.0-1/input0
[ 9264.846717] hid-generic 0003:1050:0407.0010: hiddev0,hidraw8: USB HID v1.10 Device [Yubico Yubikey 4 OTP+U2F+CCID] on usb-0000:00:14.0-1/input1
Linux Pluggable Authentication Modules (PAM) is a flexible mechanism for authenticating users on Linux systems. To get Debian 10 to authenticate using your U2F device, we need to install two packages.
sudo apt install libpam-u2f pamu2fcfg
Now insert your U2F device and enter the following to add the keys from the device to your libpam-u2f key file.
pamu2fcfg -u ${USER} | sudo tee -a /etc/u2f_keys
The resulting file will contain something like the following:
<username1>:<key_handle1>,<user_key1>
You can add additional keys by inserting the next device and repeating the pamu2fcfg
command above. My assumption is that you should only plug in one U2F device at a time during this process, but I haven't tested that assumption.
If you add the keys for multiple devices, at this point your /etc/u2f_keys
file will look something like the following.
<username>:<key_handle1>,<user_key1><username>:<key_handle2>,<user_key2><username>:<key_handle3>,<user_key3>
Remove the redundant instances of <username> to get something that looks like this:
<username>:<key_handle1>,<user_key1>:<key_handle2>,<user_key2>:<key_handle3>,<user_key3>
In a terminal, edit the /etc/pam.d/sudo
file (sudo vi /etc/pam.d/sudo
) and add auth required pam_u2f.so
after the line @include common-auth
so that it looks like the following:
#%PAM-1.0
@include common-auth
auth required pam_u2f.so authfile=/etc/u2f_keys
@include common-account
@include common-session-noninteractive
If you want to prompt the user to touch the device, add the cue keyword to the end of the pam_u2f line.
auth required pam_u2f.so authfile=/etc/u2f_keys cue
If you have users authorized to use sudo without two factor authentication, add the keyword nouserok to the end of the line. For example,
auth required pam_u2f.so authfile=/etc/u2f_keys nouserok
or both...
auth required pam_u2f.so authfile=/etc/u2f_keys cue nouserok
Reboot your computer (sudo reboot
) and then test that your changes are functioning correctly by logging in and opening a new terminal. Insert your U2F device and then enter the following in the terminal:
sudo echo test
You should be prompted for your password as you normally would with sudo, but this time nothing should happen until you touch the metal contact on your U2F device. If your device is setup correctly, you should see test
appear in the terminal. Be sure that this works correctly before going to the next step to prevent being locked out of the system.
In a terminal, edit the /etc/pam.d/gdm-password
file (sudo vi /etc/pam.d/gdm-password
) and add auth required pam_u2f.so
after the line @include common-auth
so that it looks like the following:
#%PAM-1.0
auth requisite pam_nologin.so
auth required pam_succeed_if.so user != root quiet_success
@include common-auth
auth required pam_u2f.so authfile=/etc/u2f_keys cue nouserok
auth optional pam_gnome_keyring.so
@include common-account
.
.
.
As before reboot to activate your changes.
Now, when you login using GDM, you'll click your user and enter your password as before, but nothing will happen until you insert your U2F device and touch the metal contact.
Thus far, we've described how to use U2F for specific authentication scenarios (sudo and GDM). If you only want to use U2F in some cases and not others, continue adding auth required pam_u2f.so
after the @include common-auth
line as appropriate for the login method that you're interested in protecting. If you want to use U2F during every authentication attempt, add the auth required pam_u2f.so
line to the end of the /etc/pam.d/common-auth
file instead of following the @include common-auth
line in specific authentication files.
If you have difficulty getting your U2F device to work, you can turn on debug logs for PAM U2F to help you identify the problem. Open a terminal and enter the following to create the log file:
sudo touch /var/log/pam_u2f.log
Append debug debug_file=/var/log/pam_u2f.log
to the end of the auth required pam_u2f.so
line in any of the files that you modified in the /etc/pam.d
folder so that they look like the following:
auth required pam_u2f.so authfile=/etc/u2f_keys cue nouserok debug debug_file=/var/log/pam_u2f.log
After rebooting, your log file (/var/log/pam_u2f.log
) should contain entries describing login attempts.
On the destination server (the one that you will be logging into via SSH)
sudo apt update
sudo apt upgrade -y
sudo apt install libpam-yubico -y
Create the file /etc/ssh/authorized_yubikeys
.
sudo touch /etc/ssh/authorized_yubikeys
Populate this file with the usernames and YubiKey IDs in the following format:
username1:yubikeyid1
username2:yubikeyid2:yubikeyid3:yubikeyid4
username3:yubikeyid5:yubikeyid6
You can retrieve the YubiKey ID from your device by opening a text editor (in insert mode if you're using vim) and touching the metal contact on the YubiKey. Use the first 12 characters from the resulting string as the yubikeyid in the /etc/ssh/authorized_yubikeys
file.
Navigate to https://upgrade.yubico.com/getapikey/ to retrieve the <client id> and <secret key> used in the next step.
Add auth required pam_yubico.so id=<client id> key=<secret key> authfile=/etc/ssh/authorized_yubikeys
to the top of the /etc/pam.d/sshd
file replacing <client id> and <secret key> with the values retrieved in the previous step. The required keyword in the line above tells the system to require the YubiKey in addition to a password when authenticating. If you replace required with sufficient in the line above, the system will allow you to sign in with the YubiKey or a password. The resulting /etc/pam.d/sshd should look like the following:
# PAM configuration for the Secure Shell service
auth required pam_yubico.so id=##### key=########## authfile=/etc/ssh/authorized_yubikeys
# Standard Un*x authentication.
@include common-auth
.
.
.
or
# PAM configuration for the Secure Shell service
auth sufficient pam_yubico.so id=##### key=########## authfile=/etc/ssh/authorized_yubikeys
# Standard Un*x authentication.
@include common-auth
.
.
.
Make the following changes to /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
UsePAM yes
And if you want to only use the YubiKey for single-factor authentication, add the following to /etc/ssh/sshd_config
PasswordAuthentication no
Restart the sshd service to have the changes take effect.
sudo systemctl restart sshd
The changes that you make to the PAM configuration affects all users authenticating to your machine. Any user that does not have a U2F device configured, will be unable to login following the changes described above.
- https://blog.stevegriffith.nyc/posts/titan-key-arch/
- https://gist.github.com/jmlemetayer/20e936a2ef4c7e10804a69fdacab9ca4
- Yubico/pam-u2f#31
- https://support.yubico.com/support/solutions/articles/15000011356-ubuntu-linux-login-guide-u2f
- https://www.linode.com/docs/security/authentication/how-to-use-yubikey-for-two-factor-ssh-authentication/