Skip to content

Instantly share code, notes, and snippets.

@dmcbane
Last active November 29, 2019 20:22
Show Gist options
  • Save dmcbane/8cf81a5bef25ed195e4c3b2e3208c70b to your computer and use it in GitHub Desktop.
Save dmcbane/8cf81a5bef25ed195e4c3b2e3208c70b to your computer and use it in GitHub Desktop.

Universal Two Factor (U2F) Authentication for Debian 10 Buster

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.

Have udev Recognize Your Device

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

Getting PAM to Work with Your Device

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>

Use U2F with Sudo

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.

Use U2F with Graphical Login

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.

Always Use U2F During Authentication

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.

Enabling Debug Mode

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.

Secure Shell Authentication with YubiKey OTP

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

Final Notes

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.

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment