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 calledyubikey-manager
or something like that, depending on distro. - For starters, try out commands like
ykman list
andykman 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 enablingpcscd
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. Usegpg 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
- Through
powershell
on Windows orbash
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]
- Configure the Yubikey devops
name
,lang
andurl
. This is needed for the devops tofetch
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
- 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
- (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
- Export the new PGP and SSH pubkey to publish on Github.
$ gpg --export --armour F501696368B1A02B > test.gpg
$ gpg --export-ssh-key F501696368B1A02B > test.ssh
- 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
- 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]
- (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
- 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 fromgpg --card-edit
:
$ gpg --card-edit
> fetch
- 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
- Install win-gpg-agent:
PS scoop install https://github.com/rupor-github/win-gpg-agent/releases/latest/download/win-gpg-agent.json
- Create a shortcut of
win-gpg-agent\agent-gui.exe
. Default path is"${HOMEPATH}\scoop\apps\win-gpg-agent\current\agent-gui.exe"
- Press
Cmd+R
and open up theStartup
directory by:
shell:startup
- Drag the newly created shortcut
Startup
directory. - In WSL2 instance, install
socat
and soft linksorelay.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
- 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
- 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
- Source the script from
.bashrc
or your startup profile:
source "$HOME/.wsl_gpg_agent.sh"
- 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.