YubiKey

This article contains tips & tricks for using YubiKeys for authentication.

sudo over SSH using YubiKey

Remembering root passwords for machines is annoying and error-prone. You can reuse the same passwords and be insecure. You can use different passwords and forget them. You can type them into IRC and then have to reset them. But there is a better way.

In modern Linux & Unix systems, Pluggable Authentication Modules (PAM) can provide multiple ways of authenticating a user. The most common one is "asking for a password", but they can also query external network services or require a second factor. The module pam_ssh_agent_auth authenticates users by checking against SSH keys from a connected SSH agent, which means that we can have passwordless sudo when logged in remotely.

Using pam_ssh_agent_auth with a YubiKey is both more convenient and more secure than passwords: you only need to tap the YubiKey, and because you need to tap the YubiKey, becoming root requires a physical action on your part.

NB: Technically this allows all SSH keys loaded into the SSH Agent to be used to become root. If you have a mixture of YubiKey keys and on-disk SSH keys, I would recommend using ssh-askpass / SSH_ASKPASS to require confirmation.

Debian & Ubuntu

TL;DR:

On your remote machine, first install the SSH agent authentication PAM:

$ apt install libpam-ssh-agent-auth

Then, enable and configure the PAM module for use with sudo:

$ cat /etc/pam.d/sudo
#%PAM-1.0

# Allow users to use their regular authorized SSH keys for sudo,
# and allow them to manage the keys themselves.
auth sufficient pam_ssh_agent_auth.so file=~/.ssh/authorized_keys allow_user_owned_authorized_keys_file

# # Alternatively, have a single central key file, owned by root.
# # This is useful if you only want a subset of SSH keys to grant root permissions.
# auth sufficient pam_ssh_agent_auth.so file=/etc/ssh/sudo_authorized_keys

@include common-auth
@include common-account
@include common-session-noninteractive

Allow sudo to maintain access to the SSH agent by keeping SSH_AUTH_SOCK:

$ cat /etc/sudoers
Defaults    env_keep += SSH_AUTH_SOCK
Defaults    env_reset
Defaults    mail_badpass
Defaults    secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

To test it, connect to the machine with ssh -A server.example.com, and try using sudo: it should not ask for a password, and the YubiKey should flash for a tap.

For convenience, add ForwardAgent yes to the relevant hosts your SSH config to set -A by default:

$ cat ~/.ssh/config
Host server.example.com
    ForwardAgent yes

Host 192.168.16.*
    ForwardAgent yes

NixOS

On NixOS, the configuration for the server is much simpler:

$ cat /etc/nixos/configuration.nix
{ config, pkgs, ... }:
{
  . . .
  security.pam.enableSSHAgentAuth = true;
  security.pam.services.sudo.sshAgentAuth = true;

  users.users.eth.openssh.authorizedKeys.keys = [
    "ssh-rsa . . .",
    "ssh-rsa . . .",
  ];
}

To test it, connect to the machine with ssh -A server.example.com, and try using sudo: it should not ask for a password, and the YubiKey should flash for a tap.

For convenience, add ForwardAgent yes to the relevant hosts your SSH config to set -A by default:

$ cat ~/.ssh/config
Host server.example.com
    ForwardAgent yes

Host 192.168.16.*
    ForwardAgent yes