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.