Bezpečnostný kľúč namiesto hesla pri štarte: cesta k FIDO2 LUKS unlocku (a osem pokusov, kým to skutočne fungovalo)
Poznámka: článok je z júla 2026. Tento laptop dnes beží na Fedore, postup opisuje vtedajší Pop!_OS.
Súčasť série o čerstvej migrácii pracovného laptopu na Pop!_OS s LUKS+Btrfs a hardvérovým FIDO2 kľúčom: o tejto jednej časti si to zaslúži vlastný priestor, lebo "enrolluj kľúč a hotovo" bola len prvá desatina práce.
Zadanie, ktoré znie jednoducho
Šifrovaný disk (LUKS), heslo pri každom boote. Cieľ: namiesto písania hesla len priložiť hardvérový bezpečnostný kľúč a dotknúť sa ho. FIDO2 podpora v cryptsetup na to existuje, libfido2 je dávno nainštalovaná, enrollment kľúča do LUKS slotu prebehne bez problémov.
A napriek tomu: po reštarte prišlo len obyčajné heslo. Žiadny FIDO2 pokus, žiadna chyba, nič.
Prvá vrstva: aký typ initrd vôbec máš
Skutočná príčina bola hlbšia, než chýbajúci balík. Tento konkrétny systém bootuje cez initramfs-tools (busybox-based, nie systemd-based initrd) a jeho cryptroot skript volá cryptsetup open s explicitným --key-file. Pri explicitnom key-file sa LUKS2 externé tokeny (FIDO2 je jeden z nich) vôbec neskúšajú: bez ohľadu na to, čo je v initrd zabalené.
Riešenie nebolo v úprave stock súborov, ale vo vlastnom skripte, ktorý sa spustí pred cryptroot-om (initramfs-tools garantuje poradie cez prereqs()), skúsi systemd-cryptsetup attach ... fido2-device=auto sám, a keď uspeje, cryptroot uvidí mapovanie už hotové a heslo vôbec nepýta. Keď zlyhá, ticho spadne na pôvodné heslo. Fallback sa nikdy nemal rozbiť.
Vyzeralo to hotovo. Nasadené, initrd rebuild, teoreticky pripravené. Nasledovalo osem reštart-testov a každý odhalil inú vrstvu problému.
Test 1: kľúč nebliká, lebo nevie kedy
Prvý reálny reštart: opäť len heslo. Ukázalo sa, že skript pred pokusom o unlock nič nevypisoval. Bezpečnostný kľúč len ticho zabliká LED, bez textovej výzvy. Bez signálu, kedy presne sa má kľúča dotknúť, pokus pravdepodobne ticho vypršal (timeout) a spadol na heslo presne podľa návrhu fallbacku.
Fix: skript vypíše "touch your security key now..." pred pokusom. Nasadené, overené priamo v zabalenom initrd.
Test 2 a 3: neviditeľný text za grafickým logom
Ďalší reštart, opäť nič, dokonca ani nová hláška nebola vidno. Vysvetlenie: kernel cmdline mal quiet splash, takže Plymouth preberá konzolu vlastným grafickým framebufferom. Obyčajný echo na /dev/console je fyzicky zapísaný, ale vizuálne prekrytý splash logom. Jednoducho to nebolo vidieť, hoci sa to dialo.
Prvý pokus opravy (plymouth display-message) narazil na to, že aktuálna verzia/theme takýto príkaz vôbec nemá medzi platnými. Namiesto ladenia presného Plymouth API padlo rozhodnutie na univerzálnejší mechanizmus: plymouth hide-splash pred pokusom (zhodí grafiku na holú textovú konzolu) a show-splash po ňom. Nezávislý od konkrétnej témy či verzie.
Test 4: falošné víťazstvo zo starého pokusu
Tu prišiel najzradnejší moment celej sagy. Boot vyzeral úspešne, PIN prompt sa zobrazil, kľúč sa dotkol, systém sa odomkol bez hesla. Zapísané ako vyriešené.
Bol to omyl. Ďalší "klasický" reštart o pár minút neskôr znova skočil rovno na heslo. Spätná diagnostika cez journalctl --list-boots odhalila, že ten úspešný boot bežal na úplne inom initrd (initrd-dracut-fido2.img). Samostatný, dávnejší a nikde poriadne nezapísaný experiment s dracutom namiesto initramfs-tools, ktorý som si sám kedysi rozbehol a potom naň zabudol. Fungoval preto, lebo dracut beží s plným systemd ako PID1, ktorý FIDO2 PIN prompt (cez systemd ask-password) obsluhuje automaticky. Bol to jednorazový výber v boot menu, nie skutočná oprava. Ďalší reštart sa vrátil na pôvodný, stále rozbitý initramfs-tools variant.
Poučenie zapísané rovno na mieste: priebežné zapisovanie vlastného postupu nie je formalita: bez neho jeden rozbehnutý pokus nevie o druhom, a úspech na nesprávnej vetve sa ľahko vydá za vyriešený problém.
Testy 5-6: závod s časom a rozhodnutie zrušiť splash natrvalo
Ďalšia hypotéza: možno je zlyhanie príliš rýchle na zbadanie okom. Pridaný vynútený sleep 1 okolo pokusu. Kľúč fyzicky overený zapojený. Reštart: znova úplne nič.
To vylúčilo aj teóriu "príliš rýchlo", aj teóriu "zlý initrd" (priamo overené lsinitramfs-om, že skript aj knižnice tam skutočne boli). Namiesto ďalšieho naháňania Plymouth verzií padlo rozhodnutie na tvrdší, ale spoľahlivejší krok: natrvalo odstrániť splash z kernel cmdline. Plymouth potom vôbec neprevezme konzolu, takže čokoľvek sa vypíše, bude vidno vždy: bez závislosti na tom, ktorá verzia/téma akurát podporuje aké API.
Test 7: konečne čitateľná chyba
Prvý reštart bez splash priniesol niečo, čo predtým chýbalo najviac. Konkrétnu chybovú hlášku: Device UUID=... does not exist or access denied. crypt_init() failed: Block device required.
Skript beží zámerne pred cryptroot-om, teda aj pred tým, než udev stihne vytvoriť /dev/disk/by-uuid/<UUID> pre LUKS partíciu. Jednorazový udevadm settle negarantuje, že "add" event bol na rýchlom boote vôbec spracovaný. Fix: aktívny polling na existenciu by-uuid cesty (každých 100 ms, max 10 s) namiesto jedného settle-volania.
Test 8: tri bugy naraz, odhalené priamym sledovaním konzoly
S čitateľnou konzolou stačilo po každom reštarte pozorne sledovať obrazovku, a odhalili sa tri ďalšie, dovtedy neviditeľné chyby naraz:
UUID=xxxje syntax pre/etc/crypttab, ktorú samotnásystemd-cryptsetupbinárka neprekladá. Treba jej dať priamo/dev/disk/by-uuid/xxx.- Keyfile argument
-spúšťal interaktívny heslový prompt namiesto FIDO2 cesty. Správna hodnota (overená priamo zosystemctl catunitu) je literálny reťazecnone. - Po oprave oboch bodov LUKS attach konečne štartoval správne, ale zamrzol bez PIN promptu a bez blikania kľúča. Príčina: FIDO2 PIN ide cez systemd
ask-passwordmechanizmus, ktorý potrebuje bežiaceho agenta na obsluhu na konzole. Dracut initrd (ten jediný "náhodne úspešný" test vyššie) má plný systemd ako PID1 a rieši to automaticky. Initramfs-tools beží na busybox inite. Nič z toho tam samo od seba nie je, treba to doplniť ručne.
Fix: pridaný systemd-tty-ask-password-agent --watch na pozadí, spustený pred samotným attach volaním.
Konečné víťazstvo, a prečo bolo treba ho overiť nezávisle
Piaty reálny test s touto verziou konečne fungoval tak, ako mal od začiatku: PIN naslepo, dotyk kľúča, odomknutie bez hesla, rovno na login obrazovku. Po skúsenosti s falošným pozitívom z testu 4 prišlo tentoraz aj nezávislé overenie: journalctl --list-boots, uptime -s a /proc/cmdline potvrdili čerstvý boot na defaultnom systemd-boot zázname, žiadny jednorazový alternatívny výber. Nebol to náhodný úspech na inej vetve. Bola to opakovateľná, bežná cesta.
Ako drobný dodatok ešte prišlo doladenie: PIN sa pri zadávaní zobrazoval čitateľne (kozmetický bezpečnostný nedostatok). Opravené ručným stty -echo/stty echo okolo behu ask-password agenta. A samostatne overený aj edge case bez zapojeného kľúča: PIN prompt sa zobrazí, po zlyhaní korektne spadne na normálne heslo, presne podľa návrhu.
Bonus kolo: prečo login obrazovka nežiadala dotyk, hoci sudo áno
Po tom, čo fungoval unlock disku pri boote, prišla ďalšia otázka. Prečo login obrazovka (greeter) nežiada dotyk kľúča, keď to na starom systéme fungovalo. Porovnanie s pripojeným starým diskom (read-only) ukázalo rozdiel: na starom systéme bol pam_u2f.so riadok v /etc/pam.d/common-auth, ktorý includujú všetky PAM služby vrátane login greetera. Na novom systéme bol ten istý riadok pridaný len do samostatného sudo súboru: do common-auth sa pri migrácii nedostal, lebo je to root-owned zmena mimo bežného $HOME presunu.
Namiesto úpravy globálneho common-auth (čo by ovplyvnilo všetky PAM služby naraz) bol riadok pridaný scoped, priamo do konfigurácie login greetera, s korektným fallbackom na heslo pri zlyhaní.
Čo z toho zostáva ako poučenie
- "Enrolluj kľúč" je len prvý krok. Skutočná cesta k tomu, aby to fungovalo pri každom reštarte, závisí od typu initrd, boot splash nástroja a od toho, či beží plný systemd alebo len busybox init.
- Splash obrazovka dokáže skryť aj funkčný kód. Ak niečo "nefunguje" bezo stopy, over najprv, či to vôbec vidíš, nie, či to beží.
- Falošné pozitívum je horšie než zjavné zlyhanie. Overenie „na akom boot zázname presne bežal tento test" zachránilo pred nesprávnym uzavretím veci ešte raz.
- Priebežný zápis stavu nie je byrokracia. Práve to odhalilo, že "úspech" pochádzal z celkom inej, paralelnej vetvy práce.
- Priamy pohľad na konzolu je diagnostický nástroj, nie núdzové riešenie, keď nemáš log, to, čo vidno priamo na obrazovke, býva jediný dôkaz, ktorý máš.
Výsledok stál osem kôl testovania, ale dnes stačí priložiť kľúč a dotknúť sa ho. Heslo ostáva len ako fallback, presne tak, ako to malo byť od prvého pokusu.