Setup Yubikey OpenPGP - nqngo/talks GitHub Wiki

Why Yubikey

The YubiKey is a device that makes two-factor authentication as simple as possible. Instead of a code being texted to you, or generated by an app on your phone, you press a button on your YubiKey. That's it. Each device has a unique code built on to it, which is used to generate codes that help confirm your identity.

In the context of software development environment, it's a more secure way to store a PGP key. You can use your Yubikey anywhere you use your PGP key. Storing a PGP key on your Yubikey means that your private key will be nearly impossible to steal by malicious software. For example, you might use a password-protected (encrypted) PGP key on your computer. In its password-protected (encrypted) form, it's not very useful. What if malware stole your encrypted key and then ran a keylogger to steal your password? Now it has access to your private key. With a Yubikey, your private key does not leave your Yubikey. Malware can keylog or attack the USB device all they want, the private key is not transmitted to the host computer, and the Yubikey was specifically designed to not give it up.

It also integrates nicely with pass ( password-store) to allow software team to securely share and decrypt shared credentials. Basically:

  • pass keeps all your passwords encrypted with your pgp public keys.
  • When you need to decrypt, it will call your yubikey to get the key.
  • pass has got support built-in, so you could safely store those passwords online in a git repo, check them out somewhere and decrypt them again with pass + pgp.

Setup Yubikeys

Prerequisite

Linux

  • Install ykman. It might be in a package called yubikey-manager or something like that, depending on distro.
  • For starters, try out commands like ykman list and ykman info to make sure things are working okay.
  • If you get an error WARNING: PC/SC not available. Smart card protocols will not function., try enabling pcscd service.
  • Depending on your distro, you may also need to install CCID plugin for pcscd.
  • You might want to disable unneeded functionality using commands such as ykman config nfc -f --disable-all.
  • Reference the OSX section for the rest of the instruction.

Windows

My recommendation is to use Scoop as package manager on Windows. The following steps assume you use scoop:

# (Optional) Add Scoop extra bucket
PS scoop bucket add extras
# Install prerequisiste
PS scoop install yubikey-manager-qt
PS scoop install gnupg

Mac OSX

I use MacPort for package management. If you are using homebrew, substitute with your relevant option.

Note: At the time of writing. GnuPG 2.3+ has a bug that does not recognise the Yubikey. Use gpg 2.2.9, MacPorts current port should be in this version.

# Install gnupg2 (v2.0+), don't use gnupg
sudo port install gnupg2
# Initiate the keyring
gpg -k
# Add the Yubikey parameters to smart card daemon
echo 'reader-port Yubico Yubi' >> ~/.gnupg/scdaemon.conf
# Use standard port and enable ssh support
echo 'use-standard-socket' >> ~/.gnupg/gpg-agent.conf
echo 'enable-ssh-support' >> ~/.gnupg/gpg-agent.conf
# Start gpg-agent and force ssh to use the correct socket
# This depends on your shell, use .profile for bash
# and .zprofile for zsh
echo 'gpgconf --launch gpg-agent' >> ~/.profile
echo 'GPG_TTY="$(tty)"' >> ~/.profile
echo 'export SSH_AUTH_SOCKET=$HOME/.gnupg/S.gpg-agent.ssh' >> ~/.profile

If you are using MacPorts, the default pin entry binary is pinentry-curses. By default, it defaults the tty to the first shell you open. To ensure ssh works correct, add the following to your '~/.ssh/config`:

echo 'Match host * exec "gpg-connect-agent updatestartuptty /bye"' >> ~/.ssh/config

Configure Yubikey OpenGPG

  1. Through powershell on Windows or bash on Linux:
$ gpg --edit-card
# Allow admin command, default admin pin is 12345678.
gpg/card> admin
# Change key attribute to use ED25519 instead RSA2048 (more secure, optional)
gpg/card> key-attr
Changing card key attribute for: Signature key
Please select what kind of key you want:
   (1) RSA
   (2) ECC
Your selection? <- Select 2 (ECC)
# Generate a new GPG key.
gpg/card> generate

Key is valid for? (0)
# Recommend put a 5y validity on the key.
Real name: Your Name
Email address: [email protected]
Comment: 

Change (N)ame, (C)omment, (E)-mail or (O)kay/(Q)uit? O

gpg/card> q
pub   rsa2048/F501696368B1A02B 2019-02-07 [SC] [expires: 2024-02-06]
      47C16705E32B850E3A695505F501696368B1A02B
uid                            Your Name <[email protected]>
sub   rsa2048/CF6EE720E02B86E4 2019-02-07 [A] [expires: 2024-02-06]
sub   rsa2048/E32DEE1D06194861 2019-02-07 [E] [expires: 2024-02-06]
  1. Configure the Yubikey devops name, lang and url. This is needed for the devops to fetch the public key to their GPG keyring.
# Language
gpg/card> lang
Language preferences: en
# Name
gpg/card> name
Cardholder's surname: Ngo
Cardholder's given name: Nhat
# Public GPG key URL
gpg/card> url
URL to retrieve public key: https://github.com/nqngo.gpg
  1. Require touch when reading the private key to ensure malware cannot read the Yubikey without you knowing.
$ ykman openpgp set-touch -a 12345678 -f enc on
$ ykman openpgp set-touch -a 12345678 -f aut on
$ ykman openpgp set-touch -a 12345678 -f sig on
  1. (Optionally) Increase pin-retries counter to avoid locking your key.
# openpgp set-pin-retries PIN-RETRIES RESET-RETRIES ADMIN-RETRIES
# RESET-RETRIES is not used, set it to any arbitrary number
$ ykman openpgp set-pin-retries -a 12345678 -f 10 10 10
  1. Export the new PGP and SSH pubkey to publish on Github.
$ gpg --export --armour F501696368B1A02B > test.gpg
$ gpg --export-ssh-key F501696368B1A02B > test.ssh
  1. Once you have received your programmed Yubikey. Fetch the public key assigned to your GPG keyring.
$gpg --edit-card
# Fetch the public key from the set URL
gpg/card > fetch
  1. Finalise your Yubikey by changing the PIN password:
# Set new user pin, default pin is 123456. Use pin >= 6 digits.
gpg/card > passwd
# Switch to admin
gpg/card > admin
# Set a new admin pin, default pin is 12345678. Use pin >=8 digits.
# (Optional) Personalise your Yubikey further
gpg/card>
name           change card holder's name
url            change URL to retrieve key
fetch          fetch the key specified in the card URL
login          change the login name
lang           change the language preferences
salutation     change card holder's salutation
# Exit and print the key ID
gpg/card> q
pub   rsa2048/F501696368B1A02B 2019-02-07 [SC] [expires: 2024-02-06]
      47C16705E32B850E3A695505F501696368B1A02B
uid                            Your Name <[email protected]>
sub   rsa2048/CF6EE720E02B86E4 2019-02-07 [A] [expires: 2024-02-06]
sub   rsa2048/E32DEE1D06194861 2019-02-07 [E] [expires: 2024-02-06]
  1. (Optionally) send the GPG public key to a keyserver.
# Bewarn that it is impossible to remove your pubkey once it's on a keyserver
# It is recommended to set an expiry date if you are concerned about the key
# being stolen and unable to revoke.
$ gpg --send-keys F501696368B1A02B
  1. Alternatively, send the GPG keys to somewhere more accessible (github.com etc). Fetch the public key from your username shortlink (eg. https://github.com/nqngo.gpg). If you set the URL field in your Yubikey, you can also fetch the URL from gpg --card-edit:
$ gpg --card-edit
> fetch
  1. On your machine, trust the key you generated:
$ gpg --edit-key F501696368B1A02B
> trust
> 5

Export ssh pubkey

If you would like to use the same Yubikey for ssh login, you can generate a ssh pubkey from the GPG key by:

$ gpg --export-ssh-key F501696368B1A02B

Add the generated pubkey to ~/.ssh/authorized_keys

Configure Yubikey PIV (Optional)

This is if you intend to use PIV; at the time of writing, I do not make use of PIV. Use ykman through Bash or access it through Windows at 'C:\Program Files\Yubico\YubiKey Manager\ykman.exe'.

# Generate a new PIV management key and use touch to change keys on it
$ ykman piv change-management-key --generate --protect --touch
# (Optional) Increase the number of retries before PIN is blocked
$ ykman piv access set-retries 5 10
# Change PIV pin, PIN >= 6 digits
$ ykman piv access change-pin
# Change PUK, PUK >= 8 digits
$ ykman piv access change-puk

SSH Authentication

Via GPG through Windows

  1. Install win-gpg-agent:
PS scoop install https://github.com/rupor-github/win-gpg-agent/releases/latest/download/win-gpg-agent.json
  1. Create a shortcut of win-gpg-agent\agent-gui.exe. Default path is "${HOMEPATH}\scoop\apps\win-gpg-agent\current\agent-gui.exe"
  2. Press Cmd+R and open up the Startup directory by:
shell:startup
  1. Drag the newly created shortcut Startup directory.
  2. In WSL2 instance, install socat and soft link sorelay.exe to WSL:
$ sudo apt install socat
$ mkdir -p ~/winhome/.wsl
$ ln -s /mnt/c/Users/$WindowsUser/scoop/apps/win-gpg-agent/current/sorelay.exe /home/$user/winhome/.wsl/sorelay.exe
  1. Setup https://raw.githubusercontent.com/rupor-github/win-gpg-agent/main/docs/win-gpg-agent-relay script:
curl https://raw.githubusercontent.com/rupor-github/win-gpg-agent/main/docs/win-gpg-agent-relay -o $HOME/.local/bin/win-gpg-agent-relay
chmod +x $HOME/.local/bin/win-gpg-agent-relay
  1. Add the following .wsl_gpg_agent.sh script to your $HOME:
#!/bin/bash
# detect what we have
if uname -a | grep -q Microsoft; then
    # WSL 1 could use AF_UNIX sockets from Windows side directly
    if [ "${WSL_AGENT_HOME}" ]; then
        export GNUPGHOME=${WSL_AGENT_HOME}
        export SSH_AUTH_SOCK=${WSL_AGENT_HOME}/S.gpg-agent.ssh
    fi
elif uname -a | grep -q microsoft; then
    # WSL 2 needs help from socat/sorelay
    ${HOME}/.local/bin/win-gpg-agent-relay start
    export SSH_AUTH_SOCK=${HOME}/.gnupg/S.gpg-agent.ssh
fi
  1. Source the script from .bashrc or your startup profile:
source "$HOME/.wsl_gpg_agent.sh"
  1. Reboot your computer.

GPG Agent Forwarding

NOTE: You need at least GPG v2.1.1 for this. If on Xenial, use /usr/bin/gpg2 from gnupg2 package instead of /usr/bin/gpg.

Using GPG forwarding, you can have your Yubikey plugged into your local computer and use the same keys on a remote box (e.g. remote).

On your local box, run the following to get your gpg-agent extra socket.

$ gpgconf --list-dir agent-extra-socket
/Users/nqngo/.gnupg/S.gpg-agent.extra

Log into the remote box. Run the following command to get your gpg-agent socket

$ gpgconf --list-dirs agent-socket
/run/user/8516/gnupg/S.gpg-agent

Set up the following in your ~/.ssh/config

# Example 1-hop remote access
Host remote
  Hostname remote.example.com
  User nqngo
  ForwardAgent yes
  #ExitOnForwardFailure yes
  RemoteForward /run/user/8516/gnupg/S.gpg-agent /Users/nqngo/.gnupg/S.gpg-agent.extra

# Example 2-hops remote access
Host really-remote
  Hostname really-remote.example.com
  User nqngo
  ForwardAgent yes
  ProxyJump remote
  RemoteForward /run/user/1000/gnupg/S.gpg-agent /Users/nqngo/.gnupg/S.gpg-agent.extra

Note that in the following sequence gpgconf --list-dir may show different directories to the below, depending on your OS version. The RemoteForward needs to use exactly the pathname reported by the respective --list-dir commands.

Uncomment the ExitOnForwardFailure line to test whether forwarding is working. If it is not working, you will get an error message like this:

Warning: remote port forwarding failed for listen path ...

On your remote server, you need to set in /etc/ssh/sshd_config

StreamLocalBindUnlink yes
(This should have been done already for RA1 and RA2.)

Finally, you need to import your public key(s) to your account on the server; e.g.

$ gpg --receive-keys F501696368B1A02

If this is all working (including forwarding), you should be able to SSH to your server and do gpg --list-secret-keys and you should see the secret keys corresponding to the public keys that you added.