Hier is de beloning vóór het werk: ik plug ’s ochtends mijn YubiKey in, typ één PIN, en de rest van de dag gebeurt mijn authenticatie gewoon vanzelf. SSH naar een server, geen wachtwoord. Git commit signen, geen passphrase. Een secret uit pass trekken, gewoon de key touchen. Eén fysiek ding zit in een USB-poort en de frictie is weg.

Daar komen kostte me ongeveer drie avonden van vloeken tegen gpg-agent. Nu het draait voelt teruggaan ondenkbaar.

Waarom ik de moeite nam

Het probleem was een stapel authenticatiemethoden die allemaal anders werkten.

  • SSH keys op disk (encrypted, ja, maar nog steeds een bestand dat iemand kan kopiëren)
  • GPG keys voor git signing (een andere key, een andere passphrase)
  • pass voor wachtwoorden (weer diezelfde GPG key, weer die passphrase)
  • 2FA codes in een authenticator app (telefoon pakken, turen, overtypen)

Elke actie begon met een wachtwoord typen of naar mijn telefoon grijpen. Dat is frictie, en bij frictie ga ik security-bochten afsnijden om mezelf de ergernis te besparen. Een setup die me irriteert is een setup die ik stilletjes saboteer. Het doel was dus één key die alles afhandelt, met de secrets op hardware die ik fysiek in handen heb in plaats van in bestanden verspreid over mijn disk. Dat is de soevereiniteitskant: ik wil precies weten waar mijn private keys staan, en ik wil ze ergens hebben waar ik ze uit de laptop kan trekken en in mijn zak kan steken.

Het antwoord was één YubiKey die het allemaal doet.

De stack, in het kort

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 dit geheel aan elkaar plakt is gpg-agent. Die kan zich voordoen als SSH agent, wat betekent dat de GPG key op je YubiKey tegelijk je SSH key is. Eén key, drie taken.

Eerst de simpelste versie

Voordat ik de volledige setup laat zien, hier de minimale versie zodat je ziet waar dit heen gaat. Je hebt een GPG key met een authentication subkey nodig, je verplaatst die naar de YubiKey, en je wijst SSH naar gpg-agent. Drie stappen en je kunt al SSH’en met de hardware key. De rest van deze post stapelt pass, git signing, touch policies en backups daarbovenop. Stop je na het SSH-deel, dan heb je nog steeds iets bruikbaars.

De volledige setup

1. GPG key op de YubiKey zetten

Je hebt een GPG key nodig met drie subkeys:

  • Sign (voor git commits)
  • Encrypt (voor pass)
  • Authenticate (voor SSH)

Als je al een GPG key hebt, kun je subkeys toevoegen. 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

Nu de keys naar de YubiKey verplaatsen:

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

Let op: keytocard verplaatst de key, hij kopieert niet. Maak eerst een backup van je .gnupg directory, of je leert dit op de harde manier zoals ik bij YubiKey nummer één.

2. 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

3. SSH key exporteren

Je GPG authentication key is nu ook je SSH key. Exporteer de public key:

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

Voeg deze toe aan ~/.ssh/authorized_keys op je servers, of aan GitHub/GitLab.

Check of het werkt:

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

De praktische lagen erbij

Het SSH-deel hierboven is de kern. Alles vanaf hier voegt een praktische laag toe: een password manager, gesignde commits, en de touch policy die bepaalt hoe paranoïde je wilt zijn.

4. pass configureren

pass is ongeveer zo simpel als een password manager wordt. Hij encrypt elk wachtwoord als een GPG-bestand op disk, en omdat de GPG key nu op je YubiKey staat, betekent een wachtwoord lezen dat je de key touchet.

# 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

5. Git signing

Git commits signen met je GPG key:

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 YubiKey touch. Geen extra wachtwoord, gewoon een fysieke tik om te bewijzen dat de commit van mij komt en niet van een of ander proces dat een key van disk heeft opgepikt.

De dagelijkse workflow

Zo ziet een echte dag eruit als het eenmaal draait:

# 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 timeout (600 seconden standaard voor de PIN, terwijl touch vereist blijft zolang ik de policy aan laat staan).

Handige aliassen

# 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'

Geavanceerd: touch policy

Hier bepaal je hoeveel de key je lastigvalt. YubiKey ondersteunt een paar touch 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. Elke git commit en SSH login vereist een fysieke touch. Dat is bewust. Een touch policy is mijn struikeldraad: als de key knippert en ik heb niks gevraagd, gebruikt iets de key dat dat niet hoort te doen. Ik ruil een klein beetje frictie voor de zekerheid dat ik weet wanneer mijn key in gebruik is.

# 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

Geavanceerd: backup strategie

Een YubiKey kan breken, verzuipen, of verdwijnen in een jas die je weggeeft. Resilience betekent hier plannen voor het verdwijnen van de key, niet hopen dat het nooit gebeurt:

  1. Backup YubiKey: Zet dezelfde keys op een tweede YubiKey, bewaar die veilig
  2. Paper backup: Print je master key en bewaar die offline
  3. Revocation certificate: Genereer en bewaar dit apart
# Revocation certificate genereren
gpg --gen-revoke <KEY_ID> > revoke.asc
# Bewaar dit veilig, apart van je key backup

Voor pass kun je de hele store syncen met git:

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 encrypted, dus zelfs als de repo lekt zijn ze nutteloos zonder je GPG key.

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 PIN pogingen blokkeert de YubiKey. Reset met de Admin PIN:

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

Admin PIN óók vergeten? Dan is de YubiKey een baksteen voor die keys, en precies daarom zet je een backup YubiKey op voordat je hem nodig hebt.

Waarom dit voor mij werkt

Het standaardverhaal is dat security en gemak tegen elkaar in trekken. Meer security betekent meer frictie. Deze setup keert dat voor mij stilletjes om:

  • Minder wachtwoorden typen: één PIN per sessie, daarna alleen een touch
  • Lastig te phishen: de fysieke key moet aanwezig zijn, en er is geen herbruikbaar wachtwoord om te stelen
  • Eén workflow: één key voor SSH, signing en pass, zonder springen tussen tools en denkmodellen
  • Offline bruikbaar: geen internet, geen cloud account, geen afhankelijkheid van een vendor die online blijft

Het deel dat het meest telt is dat ik het daadwerkelijk gebruik. Een security setup die tegenwerkt omzeil ik binnen een week. Deze denk ik nooit over na, en dat is precies de bedoeling.

Het volledige plaatje: minimale versie

Als je dit wilt proberen zonder alles in één keer te configureren, hier het kortste pad dat je toch de kern oplevert. Het is de simpele versie van bovenaan de post, nu met de context erbij:

  1. Koop een YubiKey 5 (de NFC variant is handig voor telefoon)
  2. Genereer een GPG key met authentication subkey
  3. Zet de keys op de YubiKey
  4. Configureer gpg-agent voor SSH
  5. Test met ssh-add -L

Daarna stapel je pass, git signing en de touch policies erbovenop, in het tempo dat jou uitkomt.

Was het de moeite waard

De eerste setup is niet triviaal. Je moet snappen wat GPG subkeys zijn, gpg-agent in het gareel krijgen, en het denkmodel vasthouden van hoe de stukken in elkaar passen. Dat is echte tijd, en het vloeken tijdens mijn drie avonden was ook echt.

Maar de YubiKey staat nu in het centrum van hoe ik me bij alles authenticeer, en dat was precies het punt waar ik wilde komen toen ik begon: mijn private keys staan op hardware die ik in mijn zak kan steken, en ze gebruiken is één tik. Plug in, PIN, en go.

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

Als dat allemaal werkt zonder errors: je bent klaar.