In this post, I will cover the usages of a Yubikey for:
- Protecting local sudo commands,
- Protecting the local GNOME login screen,
- Protecting the local TTY screen,
- Protecting remote SSH connections.
PROTECTING LOCAL SUDO
sudo apt update && sudo apt upgrade -y sudo apt install libpam-u2f -y mkdir -p ~/.config/Yubico pamu2fcfg > ~/.config/Yubico/u2f_keys sudo nano /etc/pam.d/sudo
Add the line in bold after the mentioned line:
@include common-auth auth required pam_u2f.so
Done. Try using the sudo command with and without the Yubikey connected.
The Yubikey will flash, asking you to touch its button to allow the command to go through.
PROTECTING LOCAL GNOME LOGIN
sudo nano /etc/pam.d/gdm-password
Add the line in bold after the mentioned line:
@include common-auth auth required pam_u2f.so
PROTECTING LOCAL TTY SCREEN
sudo nano /etc/pam.d/login
Add the line in bold after the mentioned line:
@include common-auth auth required pam_u2f.so
PROTECTING REMOTE SSH
sudo apt update && sudo apt upgrade -y sudo add-apt-repository ppa:yubico/stable sudo apt install libpam-yubico -y sudo nano /etc/ssh/yubikeys
Add one line per user, followed by the first 12 characters of their Yubikey:
usera:f8v6d6f687d6 userb:df98b67d7b68:adf1n98b7kf5 userc:edf8n98b7nf6
Create API credentials on the Yubico website: https://upgrade.yubico.com/getapikey.
Edit the PAM configuration file for the SSH server:
sudo nano /etc/pam.d/sshd
Add the following line at the top of the file, replacing ID and KEY with your API credentials:
auth required pam_yubico.so id=ID key=KEY mode=client debug authfile=/etc/ssh/yubikeys
The most common keyboard-interactive options are:
- required (both OTP and password must be provided),
- sufficient (either OTP or password is enough).
Then edit the file:
sudo nano /etc/ssh/sshd_config
Make sure both of the following settings are enabled:
ChallengeResponseAuthentication yes UsePAM yes AuthenticationMethods publickey,keyboard-interactive
This requires both authentication methods:
- publickey (SSH key),
- keyboard-interactive (handled by PAM).
To allow a specific user to connect using only an SSH key, append:
Match User userd
AuthenticationMethods publickey
Restart the service:
sudo systemctl restart ssh
PROTECTING REMOTE SUDO
sudo nano /etc/pam.d/sudo
Add the following line at the top of the file, replacing ID and KEY with your API credentials:
auth required pam_yubico.so id=ID key=KEY mode=client authfile=/etc/ssh/yubikeys
PROTECTING REMOTE RASPBERRY PI SSH
Following the same steps to protect SSH, you may notice that the repository below does not exist for RPi:
sudo add-apt-repository ppa:yubico/stable
Do not let that stop you. The rest of the process works the same way.
USING ANSIBLE WITH 2FA
There are several workaround solutions that simply create bypass conditions for Ansible, such as allowing root access from a specific address or network:
Match Address 10.1.1.0/24,192.168.111.222 PermitRootLogin yes
Or suppressing 2FA for a dedicated Ansible user:
Match User ansible_user AuthenticationMethods publickey
However, the cleanest approach is to configure Ansible to handle the OTP prompt directly. Add the following to /etc/ansible/ansible.cfg:
[ssh_connection] ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s -o PreferredAuthentications=publickey,keyboard-interactive
Note: Ansible executes tasks across multiple servers in parallel for each module. If more than one server requires an OTP, the prompts may overlap. To avoid this, serialize the execution order:
serial: 1 order: inventory
The order options are: inventory, reverse_inventory, sorted, reverse_sorted, or shuffle.