Niemand heeft hier nog een SSH-key, ikzelf ook niet
Een private CA als de enige SSH trust root: short-lived certs voor mensen (SSO+MFA), 10-minuten voor machines, en host certs die known_hosts TOFU killen.
Altijd diezelfde saaie vraag van de auditor in mij, deze keer over SSH: wie geraakt er nu op die box, en moest het moeten, krijg ik dat vandaag nog teruggedraaid? Voor de meeste homelabs, de mijne tot voor kort incluis, is dat antwoord om te wenen. Op elke host staat een public key in ~/.ssh/authorized_keys. Ooit met de hand of via Ansible erop gezet, en sindsdien heeft niemand er nog naar omgekeken. Niemand weet nog welke key van wie is, of op hoeveel hosts hij intussen staat. En terugdraaien? Dan mag je op elke box gaan inloggen om een regeltje te schrappen, áls je nog weet welke boxes er allemaal zijn.
De key overleeft de server, het project, soms de mens. Dát is de bug. Geen zwak algoritme, geen ontbrekende passphrase, de standing-ness zelf. Dus heb ik de keys buitengedaan. Bij de agents, bij de automation, en bij mezelf. Hier is de vorm op één bladzijde, dan de walk-through.
De standing key is de echte bug
Loop de failure modes van dat nederige authorized_keys-regeltje af en ze komen allemaal op één eigenschap uit: hij gaat nooit vanzelf weg.
- Sprawl. Diezelfde key kopieert zichzelf over elke host, shadow-clone-jutsu-gewijs, alleen roept niemand de clones ooit nog terug. Eén gestolen private key is een master key, en je weet niet eens hoeveel deuren hij opendoet, want je bent al twee rebuilds geleden gestopt met hosts tellen.
- Geen identity. Een key is geen mens.
ssh-rsa AAAA…in een log zegt dat een key authenticeerde, niet wie het deed, en zeker niet in wiens naam. - Geen expiry. Hij werkt vandaag, volgend jaar, en nog altijd nadat je al lang vergeten bent dat hij bestaat. Een credential die nooit vervalt is geen credential, ‘t is een liability met een commentveld.
- Hij overleeft rebuilds. Golden-template een box en je plakt braaf de key terug, want het alternatief is buitenstaan. Die standing access is load-bearing geworden.
- Revocation is archeologie. Toegang terugdraaien betekent elke host afgaan. Aan de client-kant is het ‘t spiegelbeeld:
known_hostsdoet trust-on-first-use (TOFU), roept “the authenticity of host can’t be established” elke keer dat een box herbouwd is, en zo train je jezelf omyeste typen zonder te lezen.
Deel geen keys uit. Deel trust uit, en laat die vervallen.
Eén CA, drie pijlers
Draai het model om: stop met keys uitdelen en begin met trust uitdelen. Zet een kleine private CA op, ik gebruik step-ca (Smallstep, open source), en maak daarvan het enige dat elke host trust. Ze doet drie jobs (zie de diagram hierboven).
Pijler A, de User CA tekent short-lived login-certificates. Elke cert draagt een principal (wie je bent) en een expiry in minuten of uren. De host trust de User CA via één TrustedUserCAKeys-regel, een geldige cert logt je in, en nergens nog een per-user key.
Pijler B, de Host CA tekent de eigen key van elke host in een host certificate. Clients trusten de Host CA één keer, en de TOFU-prompt is voorgoed weg, zelfs na een rebuild, want de herbouwde host krijgt een verse cert van diezelfde CA. Het plezantste neveneffect van heel dit project: geen known_hosts-warnings meer.
Pijler C, distributie is niks meer dan Ansible: elke host trust de User CA en toont een host cert, elke client pint de Host CA. De trust is configuratie, geen stapel gekopieerde keys.
Mensen: SSO + MFA, dan een cert dat tegen het avondeten vervalt
Dit is het stuk dat mijn eigen dag veranderd heeft. Ik hou geen homelab-SSH-key meer bij. Om binnen te geraken authenticeer ik bij de CA via Authentik, de identity provider die ik toch al draai, in de browser, met MFA. De CA geeft een certificate terug, goed voor een werkdag, rechtstreeks in mijn ssh-agent. Er belandt niks op disk.
step ssh login per dag: SSO + MFA → een paar uur cert in de agent → SSH overal waar het getrust is. Disable me in Authentik en de toegang is weg.Twee details maken dit veilig in plaats van enkel handig. De principal van de cert komt uit mijn identity en group membership, beslist door een template op de CA, ik kan dus geen login vragen waar ik geen recht op heb. En revocation is geen archeologie meer: disable de account in Authentik en er wordt geen nieuwe cert meer gemint, die in mijn agent vervalt tegen het avondeten. ’s Anderendaags respawn ik: één keer SSO + MFA en ik sta er weer met een verse cert, de vorige is dood en hoeft niet eens opgeruimd. Niemand die nog één enkele host moet afgaan.
Machines: een tien-minuten-key die zichzelf mint
Agents en automation krijgen dezelfde behandeling, maar zonder mens in de loop. In plaats van een statische key in een of andere .env authenticeert elk met een provisioner-credential, mint een ≤10-minuten-certificate per connectie, gebruikt het, en laat het vervallen. This cert will self-destruct in ten minutes, alleen ontploft er niks en springt er geen Tom Cruise voor van een gebouw. Geen standing key bij de caller, geen authorized_keys op de target. Die tien-minuten-cap wordt afgedwongen aan de CA zelf, vraag een uur en ze zegt nee.
‘t Is dezelfde reflex als de blast radius van elke agent klein houden, de SSH-laag-tegenhanger van het confused-deputy-stuk, dat inperkt wat een token mag tussen agents. Hier perken we in wat een SSH-credential mag, en voor hoe lang, tussen machines.
Wat het je kost (de eerlijke voetnoot)
Dit is niet gratis, en de auditor in mij laat het stuk niet eindigen op het glossy deel. De CA is nu load-bearing. Je kent die xkcd wel, heel de moderne infrastructuur die balanceert op één klein blokje dat ergens door één onbekende gast onderhouden wordt, en dat blokje ben ik nu zelf. Ligt ze plat, dan geraakt niemand nog aan een nieuwe cert, wat de veilige kant is (fail-closed is beter dan fail-open), maar je voelt het. Dus moet je het recht verdienen om die oude keys te schrappen:
- Hou één verzegelde break-glass-key offline, voor de dag dat de CA én de IdP allebei tegelijk plat liggen. Test ‘m.
- Back-up de CA, en zet die back-up niet op iets dat samen met de CA onderuit gaat.
- Hou de root key offline, enkel de signing key staat online.
- Draai NTP overal, short-lived certs zijn onverbiddelijk over clock skew.
Niks daarvan is exotisch. ‘t Is de prijs om “een key, voor altijd” om te zetten in “een certificate, voor de volgende tien minuten.”
Dus, terug naar die vraag van de auditor. Wie geraakt er nu op die box? Wie een niet-vervallen, CA-getekende cert heeft, en ik kan ze bij naam noemen, want elke uitgifte wordt gelogd met een principal. Krijg ik dat vandaag nog teruggedraaid? Moet niet. Het heeft zichzelf al teruggedraaid. De standing SSH-key heeft een goeie rit gehad, hij mag met pensioen.
(De SSH-laag-tegenhanger van the confused deputy comes for your AI agents, dezelfde reflex, scope en time-box elke credential, deze keer toegepast op wie er op de box geraakt.)