· · 9 min leestijd Intermediate

YubiKey + pass + GPG + SSH: één key om ze allemaal te beheren

Hoe je YubiKey gebruikt met pass (password-store), GPG en SSH voor naadloze authenticatie. Setup guide en workflow voor maximaal gemak met hardware security.

Eén klein sleuteltje aan een koord opent elke rackdeur terwijl een wezen met veertig sleutels nog zoekt

TL;DR Eén YubiKey draagt een GPG-key met een signing-, een encryption- en een authentication-subkey. gpg-agent geeft de authentication-subkey aan SSH, git signt met de signing-subkey en pass ontsleutelt met de encryption-subkey, dus één PIN ’s ochtends en een touch per actie vervangen elk wachtwoord dat ik vroeger typte. Het SSH-deel alleen is drie stappen en op zichzelf al de moeite.

Elke ochtend plug ik m’n YubiKey in, typ één PIN, en dat is het laatste geheim dat ik typ tot ik hem er weer uittrek. SSH naar een server, geen wachtwoord. Een commit signen, geen passphrase. Een secret uit pass halen, key aanraken. Eén ding in een USB-poort, en de rest van de dag regelt m’n authenticatie zichzelf.

Daar komen kostte me drie avonden vloeken op gpg-agent. Ik zou het zo weer doen.

Waarom ik de moeite nam

Vóór de key had ik vier manieren om te bewijzen wie ik was, en die werkten allemaal anders. SSH-keys op disk, versleuteld, tuurlijk, maar nog steeds een bestand dat iedereen met toegang tot m’n homedirectory kan kopiëren. Een GPG-key voor git signing, met z’n eigen passphrase. pass, dat diezelfde GPG-key gebruikte en diezelfde passphrase nog een keer vroeg. En 2FA-codes in een app op m’n telefoon, dus telefoon pakken, turen, overtypen.

Elke actie begon met een wachtwoord typen of naar m’n telefoon grijpen. Precies daar ga ik security-bochten afsnijden, dat weet ik van mezelf. Een setup die me irriteert heb ik binnen een week omzeild.

Dus wilde ik één key die alles doet, met de private keys op hardware die ik uit de laptop kan trekken en in m’n zak kan steken, in plaats van in bestanden verspreid over m’n disk. Dat is het soevereiniteitsstuk. Ik wil precies weten waar m’n private keys zijn, en het antwoord moet “in m’n broekzak” zijn.

Eén YubiKey doet dat nu allemaal.

De stack

ToolFunctie
YubiKeyHardware security key met smartcard functionaliteit
GPGEncryption en signing
passCLI password manager (gebruikt GPG)
gpg-agentAgent die GPG keys cached en SSH auth doet
SSHJe weet wat SSH is

Het stuk dat het aan elkaar plakt is gpg-agent. Die kan zich voordoen als SSH-agent, en daarmee is de authentication-subkey op de YubiKey meteen je SSH-key. Dezelfde key signt dus commits, ontsleutelt wachtwoorden en logt je in op servers.

Alleen SSH, in drie stappen

Je hebt de hele setup niet nodig om hier iets aan te hebben. Maak een GPG-key met een authentication-subkey, zet die op de YubiKey, en wijs SSH naar gpg-agent. Drie stappen, en je logt in op servers met een hardware-key. pass, git signing, touch policies en backups komen daarna.

De volledige setup

GPG-key op de YubiKey zetten

Je hebt een GPG-key nodig met drie subkeys: signing voor git-commits, encryption voor pass, authentication voor SSH. Heb je al een GPG-key, voeg de subkeys daaraan toe. Anders maak je een nieuwe.

# Nieuwe key genereren (of bestaande gebruiken)
gpg --full-generate-key

# Subkeys toevoegen
gpg --edit-key <KEY_ID>
gpg> addkey  # Sign
gpg> addkey  # Encrypt
gpg> addkey  # Authenticate (kies "set your own capabilities")
gpg> save

Dan verplaats je ze naar de kaart:

gpg --edit-key <KEY_ID>
gpg> key 1
gpg> keytocard  # Kies slot voor signing
gpg> key 1
gpg> key 2
gpg> keytocard  # Kies slot voor encryption
gpg> key 2
gpg> key 3
gpg> keytocard  # Kies slot voor authentication
gpg> save

keytocard verplaatst de key, hij kopieert niet. Maak eerst een backup van je .gnupg-directory. Dat deed ik niet, bij YubiKey nummer één, en zo heb ik het verschil geleerd.

gpg-agent configureren

Dit is de stap die het zware werk doet. Edit ~/.gnupg/gpg-agent.conf:

# Cache timeout (in seconden)
default-cache-ttl 600
max-cache-ttl 7200

# SSH support aanzetten
enable-ssh-support

# Pinentry programma (voor PIN invoer)
pinentry-program /usr/bin/pinentry-gnome3
# Of voor terminal: /usr/bin/pinentry-curses
# Of voor macOS: /usr/local/bin/pinentry-mac

En in je ~/.bashrc of ~/.zshrc:

# GPG agent als SSH agent gebruiken
export GPG_TTY=$(tty)
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)

# Agent herstarten als nodig
gpgconf --launch gpg-agent

Herstart je shell of source het bestand:

source ~/.zshrc

SSH-key exporteren

De authentication-subkey is nu ook je SSH-key. Exporteer de publieke helft:

# SSH public key van je GPG key
gpg --export-ssh-key <KEY_ID>

Zet die in ~/.ssh/authorized_keys op je servers, of bij GitHub/GitLab. Check dan of de agent hem ziet:

ssh-add -L
# Toont je GPG key als SSH key

pass en git erbovenop

Het SSH-deel is de kern. Wat volgt is een password manager, gesignde commits, en de touch policy die bepaalt hoe paranoïde je de key wilt hebben.

pass configureren

pass is zo ongeveer de simpelste password manager die er is. Elk wachtwoord is één GPG-versleuteld bestand op disk, en omdat de GPG-key nu op de YubiKey staat, is een wachtwoord lezen de key aanraken.

# Initialiseer pass met je GPG key
pass init <KEY_ID>

# Of met meerdere keys (handig voor backup)
pass init <KEY_ID_1> <KEY_ID_2>

Wachtwoorden toevoegen:

# Nieuw wachtwoord
pass insert servers/prod-db

# Genereer random wachtwoord
pass generate -n 32 servers/api-key

# Bekijk wachtwoord (vraagt om YubiKey touch)
pass servers/prod-db

Git-commits signen

git config --global user.signingkey <KEY_ID>
git config --global commit.gpgsign true
git config --global tag.gpgsign true

Elke commit vraagt nu om een touch. Geen extra wachtwoord, een tik op de key, en de commit komt aantoonbaar van de hardware in m’n USB-poort en niet van een proces dat ergens een key op disk vond.

Een dag met de key erin

Zodra het draait ziet een dag er zo uit:

# Ochtend: plug YubiKey in, typ PIN één keer
gpg --card-status  # Triggert PIN prompt

# SSH naar server - geen wachtwoord
ssh prod-server

# Wachtwoord pakken - touch de key
pass show infra/grafana | head -1

# Git commit - touch de key
git commit -m "Fix critical bug"

# Alles werkt, YubiKey blijft in USB

Na die eerste PIN blijft alles werken tot de cache verloopt, 600 seconden voor de PIN met de config hierboven. De touch blijft verplicht zolang de policy aanstaat, dus de cache maakt de key nooit stil.

Aliassen die ik gebruik

# Snelle pass lookup met fzf
alias p='pass show $(find ~/.password-store -name "*.gpg" | sed "s|$HOME/.password-store/||;s|\.gpg$||" | fzf)'

# Kopieer wachtwoord naar clipboard
alias pc='pass show -c $(find ~/.password-store -name "*.gpg" | sed "s|$HOME/.password-store/||;s|\.gpg$||" | fzf)'

# YubiKey status
alias yk='gpg --card-status'

# SSH keys tonen
alias sshkeys='ssh-add -L'

Touch policy

Hier bepaal je hoe vaak de key je lastigvalt. Er zijn een paar policies:

PolicyBetekenis
OffGeen touch nodig
OnTouch voor elke operatie
FixedTouch nodig, niet te wijzigen
CachedTouch geldig voor X seconden

Ik draai “On” voor signing en authentication, dus elke commit en elke SSH-login wil een fysieke touch. Dat is bewust. Knippert de key en heb ik niks gevraagd, dan gebruikt iets hem dat dat niet hoort te doen. Dat is me een seconde per commit waard.

# Touch policy instellen via ykman
ykman openpgp keys set-touch sig on
ykman openpgp keys set-touch aut on
ykman openpgp keys set-touch enc on

Als de key weg is

Een YubiKey kan breken, mee de wasmachine in, of in een jas zitten die je hebt weggegeven. Dus plan ik voor de dag dat hij weg is. Dat betekent een tweede YubiKey met dezelfde keys erop, ergens anders bewaard. Een print van de master key, offline. En een revocation certificate, nu gegenereerd en apart van die twee bewaard.

# Revocation certificate genereren
gpg --gen-revoke <KEY_ID> > revoke.asc
# Bewaar dit veilig, apart van je key backup

pass zelf kun je met git syncen:

cd ~/.password-store
git init
git remote add origin git@github.com:user/pass-store-private.git
git push -u origin main

De wachtwoorden zijn versleutelde bestanden, dus een gelekte repo is zonder de GPG-key nutteloos.

Troubleshooting

“No secret key”

# Check of YubiKey zichtbaar is
gpg --card-status

# Niet zichtbaar? Check USB
lsusb | grep -i yubi

# GPG weet niet waar de key is? Refresh
gpg-connect-agent "scd serialno" "learn --force" /bye

SSH werkt niet

# Check of gpg-agent als SSH agent draait
echo $SSH_AUTH_SOCK
# Moet iets zijn als: /run/user/1000/gnupg/S.gpg-agent.ssh

# Keys laden
ssh-add -L
# Moet je GPG key tonen

# Niet? Agent herstarten
gpgconf --kill gpg-agent
gpgconf --launch gpg-agent

PIN geblokkeerd

Na drie foute PINs gaat de YubiKey op slot. Reset hem met de Admin PIN:

gpg --edit-card
gpg/card> admin
gpg/card> passwd
# Kies optie 2: unblock PIN

Admin PIN óók kwijt? Dan is de YubiKey voor die keys een baksteen, en dan is de tweede YubiKey van twee koppen terug je redding.

Waarom deze bleef hangen

Het standaardverhaal is dat security en gemak elkaar in de weg zitten, en dat meer van het één je wat van het ander kost. Bij mij loopt deze setup de andere kant op. Ik typ één PIN per sessie en raak daarna de key aan. Er is geen herbruikbaar wachtwoord om te phishen, want de fysieke key moet in de poort zitten. SSH, signing en pass zijn één workflow in plaats van drie, met één denkmodel. En het werkt zonder internet, zonder cloud-account, zonder vendor die online moet blijven.

Wat het meest telt is dat ik het gebruik. Deze werkt niet tegen, en dus heb ik hem nooit omzeild. De meeste dagen denk ik er niet eens over na.

Waar je begint

Je hoeft dit niet in één zitting op te zetten. Koop een YubiKey 5, de NFC-variant als je hem ook met je telefoon wilt gebruiken. Maak een GPG-key met een authentication-subkey, zet die op de key, configureer gpg-agent voor SSH en test met ssh-add -L. pass, git signing en de touch policy komen erbovenop in het tempo dat jou uitkomt.

Wat het kostte

Drie avonden, het meeste aan gpg-agent. Je moet snappen wat GPG-subkeys zijn, gpg-agent in het gareel krijgen, en ondertussen het plaatje van hoe de stukken passen in je hoofd houden. Dat is echte tijd, en er is flink gevloekt.

Wat ik ervoor terugkreeg is één stuk hardware in het midden van elke login die ik doe. M’n private keys zitten op iets wat in m’n broekzak past, en ze gebruiken is een tik. Wil je weten of de jouwe nog werkt, dan dekt één regel alle drie:

# Check of alles werkt
gpg --card-status && ssh-add -L && pass show test/dummy

Draait dat zonder fouten, dan ben je klaar. Bij mij kostte het drie avonden. Jij hebt deze post.