I want to be able to run sudo commands by authenticating via a fingerprint reader, but only when I run them interactively. This shows how to do it without breaking ansible/pyinfra usage of sudo.
Enabling sudo fingerprint authentication on debian Link to heading
I’ve a debian unstable system and at the time of writing, it didn’t configure fingerprint authentication for sudo
out of the box. Adding support for it was two steps:
# Installing the required software
λ sudo apt install fprintd libpam-fprintd
# Enabling it in debians pam stack
# -> select "fingerprint authentication"
λ sudo pam-auth-update
This changed the following in /etc/pam.d/common-auth
:
-auth [success=1 default=ignore] pam_unix.so nullok
+auth [success=2 default=ignore] pam_fprintd.so max-tries=1 timeout=10 # debug
+auth [success=1 default=ignore] pam_unix.so nullok try_first_pass
(Fedora 34 had fingerprint support for sudo enabled out of the box in this laptop, if I remember correctly)
Working with ansible / pyinfra Link to heading
The above configures pam to first authenticate against the fingerprint pam module (pam_fprintd.sp
) and only afterwards
against the password module (pam_unix.so
). If you have a script which runs sudo with a password command (e.g.
ansible/pyinfra, both python apps), this means every sudo call will ask for fingerprint authentication. Which made me
quite mad when I tried it, and I quickly gave up on this. But since
sudo 1.9.9, sudo
now supports
a different pam
profile when sudo --askpass
(or sudo -A
) is used:
pam_askpass_service On systems that use PAM for authentication, this is the service name used when the -A option is specified. The default value is either “@pam_service@” or “sudo”, depending on whether or not the -i option is also specified. See the description of pam_service for more information. This setting is only supported by version 1.9.9 or higher. – sudoers(5)
So, lets create a sudo-askpass
pam
configuration:
# sudo/pam do not understand uppercase, so sudo-A as a filename does NOT work!
λ sudo cp /etc/pam.d/sudo-i /etc/pam.d/sudo-askpass
λ sudo nano /etc/pam.d/sudo-askpass
This needs now a pam_unix.so
line before including common-auth
. Afterwards, in my case, it read:
#%PAM-1.0
# Set up user limits from /etc/security/limits.conf.
session required pam_limits.so
# ADDED
# First try password authentication (= the askpass helper) and then anything else
auth [success=1 default=ignore] pam_unix.so nullok
# ADDED
@include common-auth
@include common-account
@include common-session
What’s left is to tell sudo to use this file when using sudo -A
:
# use visudo to get your changes validated
# -> don't get yourself a broken sudo which you cannot fix without sudo...
λ sudo visudo /etc/sudoers.d/sudo-askpass
That file should afterwards contain:
Defaults pam_askpass_service=sudo-askpass
And now it should work: sudo <command>
should still behave as before but when using sudo -A <command>
, it should not
ask for fingerprint authentication.
Tested be creating a sudo password helper sudo_pass.sh
:
#!/bin/sh
echo mypassword
and then calling sudo -A
with it,
like pyinfra does
λ SUDO_ASKPASS=./sudo_pass.sh sudo -H -A -k cat /etc/sudoers
Sidenote Link to heading
During my experimentation with sudo
for the above, I noticed an interesting fact: At least my fingerprint reader in my
Lenovo X1 Carbon (7th Gen) seems to only support a single process asking for a password and any other process then
errors. This also makes the pam_fprintd.so
pam module fail directly and pam then runs the password
module pam_unix.so
next. Which is exactly what I want. So a workaround seems to be to start a fprintd-verify
in one
terminal window and then start the sudo
using script in another…
Another sidenote: At least once (ctrl+c
during fprintd-verify
?) I got the fingerprint reader in a bad state:
λ fprintd-verify
Using device /net/reactivated/Fprint/Device/0
failed to claim device: GDBus.Error:net.reactivated.Fprint.Error.AlreadyInUse: Device was already claimed
I could only get it back to working by restarting the fprintd
service:
λ sudo systemctl restart fprintd.service