Mkinitcpio tpm2 encrypt

Napsal uživatel Stopka dne

Všechny moje osobní i firemní počítače běží na Arch Linuxu s šifrovanými úložišti. To přináší nepohodlí v podobě nutnosti zadávat dvě hesla při startu počítače. Jendo odemkne šifrované úložiště, druhé mě přihlásí k uživatelskému účtu. Neexistuje způsob jak odemknout systémový disk automaticky? Například moderní telefony taky šifrují svá úložiště a heslo se pro jejich odemčení nezadává.

Naštěstí jsou k tomuto účelu novější notebooky vybaveny tak zvaným trusted platform modulem, neboli TPM. Exisují dvě vzájemně nekompatibilní verse, starší TPM 1.2 a nový TPM 2. Návod jak nastavit TPM verzi 1.2 lze najít v oficiální Arch Wiki, pro TPM 2 jsem ale našel jen několik blogových příspěvků, které již nejsou aktuální, nebo nepopisují způsob nastavení dostatečně detailně.

TPM2

Pro naše použití bychom měli vědět, že TPM je čip na základní desce, který zjednodušeně řečeno sleduje průběh startování počítače. V každé fázi startu spočítá hash několika různých hodnot a uloží je do jedno ze svých Platform Configuration Register (PCR). Které hodnoty jsou uloženy v kterém registru lze najít v následující tabulce. V některých registrech je uložen hash programu který se procesor chystá spustit, v jiných je hash konfiguračních hodnot.

Číslo Použití
0 BIOS
1 BIOS configuration
2 Option ROMs
3 Option ROM configuration
4 MBR (master boot record)
5 MBR configuration
6 State transitions and wake events
7 Platform manufacturer specific measurements
8–15 Static operating system
16 Debug
23 Application support

Například, kdybychom sledovali změny v registrech 0,2,4 a 7, zjistili bychom, že se po startu hashe změní vždy když se změní konfigurace biosu, když flashneme novou verzi firmwaru biosu nebo když se aktualizuje linuxový kernel.

Na Arch Linux je v oficiálních repozitářích balíček tpm2-tools, který obsahuje vše co budeme potřebovat pro práci s tpm2 zařízením. Nalezneme tam program tpm2_pcrread, který vypíše aktuální stav PCR registrů.

TPM zařízení pak umí takto nastavené registry využít k zapečetění nějakého tajemství, třeba právě šifrovacího klíče k úložišti. Při zapečetění tajemství si zvolíme několik registrů a tpm2 na základě nich vygeneruje veřejný a privátní klíč. Později když tpm2 čipu předložíme vygenerované klíče, ten zkontroluje registry a odhalí původní tajemství.  Vtip je v tom, že čip vydá tajemství jen pokud jsou hashe ve zvolených PCR registrech stejné jako při zapečetění. Tj. vydá tajemství jen pokud nikdo nezměnil firmware, nastavení nebo jiné sledované části systému.

Zapečetění klíče

V prvním kroku vyčistíme tpm zařízení od případných předchozích pokusů a převezmeme jeho vlastnictví. Pokud jsme již dříve uložili nějaký objekt do tpm, musíme jej odstranit.

tpm2_clear
tpm2_startup -c

#odstraní uložený objekt
tpm2_evictcontrol -C o -c 0x81000001

V tuto chvíli bychom měli mít tpm zařízení připravené pro zapečetění našeho klíče. Vytvoříme objekt a uložíme jej. Potom vytvoříme politiku, tj. seznam registrů a hashovací funkce, které budou použiti pro zapečetění. Nakonec vezmeme objekt společně s politikou a provedeme zapečetění našeho klíče uloženého v souboru ssd-crypt.key.

#vytvoříme objekt
tpm2_createprimary -c primary.ctx

#uložíme objekt
tpm2_evictcontrol -c primary.ctx 0x81000001

#vytvoříme politiku
tpm2_createpolicy --policy-pcr -l "sha256:0,2,7" -L "pcr.policy"

#zapečetíme klíč
tpm2_create -C "0x81000001" \
  -a 'fixedtpm|fixedparent|adminwithpolicy|noda' \
  -i ./ssd-crypt.key -L ./pcr.policy \
  -r ssd-crypt.priv.key -u ssd-crypt.pub.key

V této fázi máme klíč zapečetěný pomocí tpm. V pracovním adresáři bychom měli mít nově vytvořený veřejný a privátní klíč.

Můžeme zkusit získat klíč zpět následujícími kroky. Po zavolání těchto příkazů bychom měli mít v adresáři nový soubor ssd-crypt.unseal.key, který bude obsahovat původní klíč.

#načte uložený objekt
tpm2_load -C "0x81000001" \
  -r ssd-crypt.priv.key -u ssd-crypt.pub.key
  -c primary.unseal.ctx

#vydá klíč
tpm2_unseal -c primary.unseal.ctx \
  -o ssd-crypt.unseal.key -p "pcr:sha256:0,2,7"

Odemknutí úložiště po spuštění

Hlavní systémové úložiště musí být odemčeno ve velmi rané fázi statru počítače, v tzv initramfs. Initramfs je soubor obsahující init systém, jaderné moduly, aplikace a konfigurační soubory potřebné pro připojení hlavního úložiště. Jádro načte jeho obsah do RAM paměti a využije obsažené nástroje k tomu aby nalezl a připojil úložiště.

V Arch Linuxu je soubor Initramfs generován programem mkinitcpio, který se konfiguruje pomocí /etc/mkinitcpio.conf. Zde je seznam modulů, které se mají přibalit, seznam tzv hooků (balíčků knihoven a konfigurací) a i pořadí v jakém se mají volat při startu.

Z Arch User Repository (AUR) lze nainstalovat balíček mkinitcpio-tpm2-encrypt. Ten do systému přidá mkinitpio hook tpm2. Hook pomůže odemknout úložiště odpečetěním klíče pomocí tpm2 zařízení.

V Arch Linuxu si můžete zvolit mezi dvěma init systémy: Busybox init and Systemd init. V závislosti na zvoleném init systému je potřeba správně upravit konfiguraci  /etc/mkinitcpio.conf. V případě, že používáte Busybox init, stačí přidat hook tpm2 před hook encrypt.

HOOKS="... block tpm2 encrypt filesystems ..."

Pokud ale používáte systemd init, tady se konečně dostáváme k mému přínosu. Podařilo se mi totiž nově balíček rozšířit o podporu systemd initu. Proto i se systemd můžete jednoduše nastavit odpečetění klíče pomocí tpm2 přidáním hooku sd-tpm2 před hook sd-encrypt v souboru /etc/mkinitcpio.conf.

HOOKS="... block sd-tpm2 sd-encrypt filesystems ..."

V závislosti na dalších nastavení se může hodit přidat do initramfs i soubory se soukromým a veřejným klíčem.

FILES="/etc/tpm2-encrypt/key.pub /etc/tpm2-encrypt/key.priv"

Hook odpečetí klíč k úložišti pomocí tpm2 zařízení do souboru /crypto_keyfile.bin a tím umožní odemčení úložiště bez potřeby zadávat heslo. Hook získává všechny potřebné údaje z kernel parametrů. Takto lze například předat parametry potřebné k odpečetění klíče zapečetěného v příkladu výše.

tpmkey=rootfs:/etc/tpm2-encrypt/key:0x81000001 tpmpcr=sha256:0,2,7

Posledním krokem je nastavit systém tak, aby odemkl úložiště odepečetěným souborem s klíčem. Pokud odpečetění selže, nevadí. Stále se budete moct dostat do počítače. Systém se prostě jen zeptá na heslo k úložišti jako dříve.

Aktualizace jádra

Může se stát, že nastavíme PCR politiku příliš striktně a bude tak potřeba klíč znovu zapečetit po každé aktualizaci jádra, protože změna jádra přináší změnu některých registrů. Pečetění klíče není něco co bychom chtěli dělat nějak často ani to nelze nějak snadno automatizovat. Možná si říkáte, že prostě vybereme jen registry, které se nemění při změně jádra. Jenže nechat jádro bez kontroly otevírá útočníkovi možnost upravit jádro a tím získat nechtěný přístup k systému. Jádro je totiž jedna z mála částí systému, která je v nešifrované části úložiště. Neexistuje nějaký způsob jak donutit počítač spouštět jen námi prověřené jádro?

Odpovědí je secure boot. Pokud do biosu nahrajeme vlastní podepisovací sadu klíčů a budeme si jádro těmito klíči podepisovat, máme jistotu že počítač spustí jen naše podepsaná jádra, nic jiného.

Výsledek

V EFI mám zapnutý secure boot s mými vlastními klíči. Na hlavním disku mám dva oddíly.

První je malý, nešifrovaný, naformátovaný FAT32. Obsahuje pouze kernel a initramfs společně zabalené do jedné binárky linux.efi (pomocí efi stub). Ta je podepsaná mýmu secure boot klíči. V tomto oddílu EFI hledá operační systém ke spuštění.

Druhý oddíl je lvm volume group šifrovaný pomocí luks. V lvm mám vyčleněný oddíl pro swap, aby fungovala hibernace, a hlavní root oddíl.

Efi prostě spustí binárku, načte jádro, připojí initramfs a rozběhne jednotlivé služby. Initramfs obsahuje tpm2 nástroje a klíče k pečeti, takže může odpečetit klíč k oddílu a použít získaný klíč k odemčení a připojení root souborovému systému a swapu.

Po pár sekundách mě vítá přihlašovací obrazovka gdm. Jediné heslo které při startu zadávám je tak to ke svému účtu v systému.

K pečetění používám jen registry, které hlídají bios firmware a konfiguraci. Jádro se mi se při každé aktualizaci podepisuje mým secure boot klíčem automaticky, používám k tomu AUR balíček sbupdate-git. Tímto je zajištěno že i po aktualizaci jádra vše naběhne jak má.

A je to bezpečné. Pokud někdo změní v efi klíče, zmení se i hashe v PCR registrech a tpm nevydá klíč. Úložiště zůstane zamčeno. Pokud někdo upraví jádro, nebo zkusí spusit něco co jsem já nepodepsal, efi odmítne takový program spustit a data zůstávají v bezpečí.