These Linux Pluggable Authentication Modules (Linux-PAM) configuration
rules support using both "Universal 2nd Factor" (U2F) and "Time-based
One-time Password" (TOTP) or "HMAC-based One-time Password" (HOTP) for
"Two-Factor Authentication" (2FA) by using Google's
pam_google_authenticator.so
library and Yubico's pam_u2f.so
library.
Because I wanted to have a similar log-in workflow in my computers to the 2FA workflow that some online services provide:
- Validate the user's password (part of
common-auth
). - Prompt for a U2F key if the user registered at least one.
- If the user doesn't have their key or hasn't registered one, fallback to a TOTP/HTOP code from a TOTP/HTOP authenticator app.
- If the user doesn't have both their key, nor the TOTP/HTOP app fallback to a rescue code (from the authenticator app).
- Succeed if the user hasn't registered any 2FA methods.
This allows one to introduce other family members or co-workers to a 2FA workflow, and it helps system administrators to reduce the risk of account takeover by malicious actors because of weak passwords.
Please make sure you have already met these two prerequisites:
- having installed and configured
pam_google_authenticator.so
- having installed and configured
pam_u2f.so
Let's, for example, add the custom-2fa
rules to /etc/pam.d/sudo
:
- copy
custom-2fa
file into/etc/pam.d/
:sudo cp custom-2fa /etc/pam.d/
- edit (and don't close until we verify it works)
/etc/pam.d/sudo
:sudo nano /etc/pam.d/sudo
- include
@include custom-2fa
after@include common-auth
line, and save file without exiting the editor or closing the terminal emulator (see example below with included comments) - in another terminal emulator window or tab, verify installation:
sudo -k && sudo echo "Hello World!"
- if you were able to successfully authenticate, you may close both windows
- if there was an error, see the Debugging section
- worst case scenario, you can comment the
@include custom-2fa
line to disablecustom-2fa
in the editor that still has root privileges that you totally didn't close.
You may probably want to add custom-2fa
to the following rule files:
/etc/pam.d/sudo
/etc/pam.d/gdm-password
/etc/pam.d/login
Additionally, you may also want to place some comments, so you remember how you configured your system. I use the following format:
# custom-2fa
# - Prompts for FIDO2-capable U2F device
# - If present, request touch of device
# - Otherwise request a TOTP/HOTP validation code
# - Allow for users without 2FA to authenticate with password
# note: see /etc/pam.d/custom-2fa for implementation details
@include custom-2fa
Make sure you have several terminal emulators with root privileges open so that you can undo changes that would leave you without superuser access.
user@computer:~$ sudo echo "Hello, World!"
And the expected workflow in the terminal is:
[sudo] password for user:
insert key and/or press ENTER:
touch key:
Hello, World!
This can also be set as a prompt when logging in (see Installation):
The files are made of lists of rules. Each rule is a space separated collection of tokens:
service type control module-path module-arguments
The files in /etc/pam.d/
lack the "service" field. Some info
regarding each field:
- service
usually carry the name of the service or name of a familiar application, like "login" and "sudo". - type
the management group that the rule corresponds to - control
indicates behavior of PAM-API if module fails to authenticate. Two types of syntax are used: simple key word, and square-bracketed value=action pairs. - module-path
full filename (begins with "/
") of the PAM to be used or relative path-name from default location (which could be either/lib/security/
,/lib64/security/
, or/lib/x86_64-linux-gnu/security/
)
These Linux-PAM configuration rules support using both U2F and
TOTP/HOTP for 2FA by using pam_google_authenticator.so
and
pam_u2f.so
.
- Prompt for a U2F device, and to then press ENTER.
- In case the U2F device is known, prompt the user to press the tactile trigger.
- In case the U2F device is not known or present, prompt for code for verification from TOTP/HOTP app (like Google Authenticator).
- Allow users that are not configured to use U2F or TOTP/HOTP to log in.
- management group type
auth
module type that authenticates the user
- control values
default=ignore
the module's return status will not contribute to the return code the application obtainsignore=ignore
PAM module wants its result to be ignorednew_authtok_reqd=ok
new authentication token is requiredsuccess=1
Jump over the N modules in the stack on success
- module path
pam_u2f.so
use Yubico'spam_u2f
- module arguments
authfile=/etc/2fa/u2f/u2f_mappings
Sets the location of the file that holds the mappings of user names to keyHandles and user keys; should have0600
permissionsinteractive
Set to prompt a message and wait before testing the presence of a FIDO device. Recommended if your device doesn't have a tactile triggernouserok
Set to enable authentication attempts to succeed even if the user trying to authenticate is not found inside authfile or if authfile is missing/malformed[prompt=insert key and/or press ENTER: ]
Specify the prompt to insert a U2F key and press ENTER; hint at TOTP optionuserpresence=0
If1
, request user presence during authentication. If0
, do not request user presence during authentication. Otherwise, fallback to the authenticator's default behavior.
auth \
[success=1 new_authtok_reqd=ok ignore=ignore default=ignore] \
pam_u2f.so \
authfile=/etc/2fa/u2f/u2f_mappings \
interactive \
nouserok \
[prompt=insert key and/or press ENTER: ] \
userpresence=0
- management group type
auth
module type that authenticates the user
- control values
default=bad
should be thought of as indicative of the module failingignore=ignore
PAM module wants its result to be ignorednew_authtok_reqd=ok
new authentication token is requiredsuccess=1
Jump over the N modules in the stack on success
- module path
pam_google_authenticator.so
google-authenticator-libpam, by Google, will be used
- module arguments
[authtok_prompt=Type in token: ]
set token promptnullok
OK if user doesn't have TOTP/HOTP 2FA rolled outsecret=/etc/2fa/totp/${USER}/.totp_secrets
the nonstandard location for the file holding the secrets; it should have0600
permissions
auth \
[success=1 new_authtok_reqd=ok ignore=ignore default=bad] \
pam_google_authenticator.so \
[authtok_prompt=type in token: ] \
nullok \
secret=/etc/2fa/totp/${USER}/.totp_secrets
- management group type
auth
module type that authenticates the user
- control values
required
failure of such a PAM will ultimately lead to the PAM-API returning failure but only after the remaining stacked modules (for this service and type) have been invoked. This is a shorthand for the following values:[success=ok new_authtok_reqd=ok ignore=ignore default=bad]
- module path
pam_u2f.so
use Yubico'spam_u2f
- module arguments
authfile=/etc/2fa/u2f/u2f_mappings
Sets the location of the file that holds the mappings of user names to keyHandles and user keys; should have0600
permissionscue
Set to prompt a message to remind to touch the device[cue_prompt=Touch key: ]
Specify prompt to touch keynouserok
Set to enable authentication attempts to succeed even if the user trying to authenticate is not found inside authfile or if authfile is missing/malformed.userpresence=1
If1
, request user presence during authentication. If0
, do not request user presence during authentication. Otherwise, fallback to the authenticator's default behavior.
auth \
required \
pam_u2f.so \
authfile=/etc/2fa/u2f/u2f_mappings \
cue \
[cue_prompt=touch key: ] \
nouserok \
userpresence=1
First, it may be useful to identify which files use custom-2fa: grep -irlE "^#?@include custom-2fa" /etc/pam.d/ --exclude=custom
After that, try to pinpoint if the problem is with pam_u2f
(rules 1
and 3), or with pam_google_authenticator
(rule 2). Then enable
debug logging, try authenticating again, and inspect the output.
Remember to escape previous line breaks with "\
" when adding module
arguments to the PAM rules.
For pam_u2f
(rules 1 and 3):
- Enable debug logging:
sudo touch /var/log/pam_u2f.log
- add "
debug
" as an additional module argument - optionally, also add "
debug_file=/var/log/pam_u2f.log
"
- Try authenticating again:
sudo -k && sudo echo "Hello World!"
- Inspect debug logs:
- if
debug_file
was not specified, output will be on stderr - if
debug_file
was specified:- inspect it:
nano /var/log/pam_u2f.log
- remove it:
sudo rm /var/log/pam_u2f.log
- inspect it:
- if
For pam_google_authenticator
(rule 2):
- Enable debug logging
- add "
debug
" as an additional module argument
- add "
- Begin monitoring syslog:
- open shell and monitor with:
tail -f /var/log/auth.log
- open shell and monitor with:
- Try authenticating again:
- in another shell:
sudo -k && sudo echo "Hello World!"
- in another shell:
Well, ... why not both? Honestly, sometimes you don't want to be scouring through the Internet to try to grok rules that you wrote several months ago. So I decided to include most of this README in the actual source as comments.
custom-2fa
Copyright 2021, Rolando Garza.
License GPLv3+: GNU GPL version 3 or later,
https://gnu.org/licenses/gpl.html.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Rolando Garza.
- [-] Ask Yubico's pam-u2f developers if they could expand
%u
variable so that we could get something like:authfile=/etc/2fa/u2f/%u/u2f_mappings
- see: Yubico/pam-u2f#218
- contribute?