urMQTT v0.3.0: šifrování disku, boot, který odmítne upravený obraz, a to, co jsme si sami pokazili
Co se stalo po třetím textu o urMQTT: šifrování disku v klidu se změřenou cenou, test, který změní jeden bajt v jádře a ukáže, že ho zavaděč odmítne, auditní balíček s 889 zdůvodněnými místy s unsafe, chyby, které jsme našli sami (a jedno tvrzení, které jsme napsali špatně), a vydání v0.3.0 se seznamem toho, co ještě není prokázáno, včetně červeného CI.

Třetí text, urMQTT po VPS, skončil seznamem „Co bude dál": veřejný demo broker (D1), druhé kolo pro pomalý jediný tok, obyčejná cesta proti Mosquittu na VPS a „po vydání" bare metal, IOMMU, Secure Boot a šifrování disku. Tento text říká, co z toho je hotovo, co ne a co jsme mezitím našli. Šifrování disku a část Secure Bootu se nakonec do vydání dostaly, protože ležely na cestě k auditu. Druhé kolo pro jediný tok a obyčejná cesta proti Mosquittu hotové nejsou.
Poznámka předem zůstává stejná: projekt je pre-alpha, vydání cílí na VPS s KVM a čísla měří kód, testy a konkrétní pokusy, ne zralost v produkci.
Jak to vypadalo v čase
Od minulého textu (1. listopadu ve 20:41) do dneška (3. listopadu ve 02:43) přibylo v hlavní větvi 37 nových commitů, celkem 215. Jako dřív se projekt staví s Claude Code (commity nesou Co-Authored-By: Claude) a tentokrát běžely paralelně tři větve (D1, H8, příprava vydání), takže pořadí commitů není přesně pořadí práce. Čísla jsou spočítána stejnou metodou jako v minulém textu (všechny soubory .rs mimo vendor/ a fuzz/, funkce #[test], nadpisy ## ADR- v docs/DECISIONS.md) a ověřena na starém commitu, který dává stejná čísla jako tehdy.
| Okamžik | Commit | Čas | Řádků Rustu | Testů | ADR | Commitů |
|---|---|---|---|---|---|---|
| minulý text | d21e3ec | 1. 11., 20:41 | 112 408 | 926 | 126 | 178 |
| dnes | f1a7fbc | 3. 11., 02:43 | 115 148 | 962 | 133 | 215 |
Růst kódu je skromný (+2 740 řádků, +36 testů), protože tentokrát nebylo mnoho nového kódu: většinu práce tvoří testy, skripty, dokumentace a kontroly. Skripty a JavaScript se do počtu řádků nezapočítávají.
Šifrování disku v klidu (S7)
Co jsme plánovali. Volitelné šifrování datového oddílu, ve výchozím stavu vypnuté, se změřenou cenou. Co jsme postavili (ADR-240):
- Vrstva pod diskem.
CryptDevobaluje blokové zařízení podDisk, takže rozložení, tabulka velikostí, SQLite, telemetrie a zálohy pracují s čistým textem a o šifrování nevědí. Algoritmus je AES-256-XTS (IEEE 1619), jeden sektor o 512 bajtech na datovou jednotku, ne blok o 4 KiB, jak počítal plán: úložiště zapisuje po sektorech a jednotka 4 KiB by každý zápis 512 bajtů proměnila v čtení-úpravu-zápis, který může výpadek proudu přetrhnout napůl. - Superblok zůstává otevřený. Dva sloty superbloku nesou příznak a kontrolu klíče (HMAC-SHA-256 ze štítku a UUID disku), nikdy klíč. Všechno ostatní v oddílu je šifrované. Sektor, který je na médiu celý nulový, se čte jako nula a nedešifruje se, takže discard, nikdy nezapsaný prostor a zvětšování disku fungují jako dřív.
- Konfigurace.
storage.encryption = "aes-256-xts"astorage.disk_key(soubor s 64 bajty klíče). Formát blobu se stává 4 jen když je šifrování zapnuté, takže všechny ostatní obrazy se dál spouštějí na starších jádrech. Starší čtečka neznámý příznak v superbloku odmítne. - Bez hardwarového AES: odmítnutí. Jádro odmítne
storage.encryptionna procesoru bez instrukcí AES (nebo když je[cpu] disablevypne), místo aby tiše běželo na pomalé softwarové verzi. - Převod je offline. Jádro při startu nikdy disk nešifruje ani nedešifruje. Dělají to
urmqtt-disk encrypt,decryptarekeypři vypnutém VM: záznam o převodu se zapisuje jako první a maže jako poslední (dokud existuje, jádro disk odmítá), kousky po 1 MiB se nejdřív uloží do žurnálu na hostiteli, pak se zapíše převedená kopie a superblok se přepíná jako poslední. Vyzkoušeno výpadkem proudu po každém jednotlivém zápisu u všech tří postupů, čistě i napůl zapsaném, se zahozenou i zachovanou cache: po--resumevždy platný disk se všemi daty.
Změřeno. Propustnost XTS na jádro na Apple silicon (jeden MiB sektorů ve smyčce): 3 109 MB/s šifrování a 3 103 MB/s dešifrování s hardwarovým AES, oproti 252 a 258 MB/s v softwaru. Proto software nepovolujeme. db-bench ve VM (aarch64, HVF, 5 000 vložení v 50 transakcích, pět běhů každé varianty, střídavě):
| op/s, pět běhů | medián | |
|---|---|---|
| šifrování vypnuto | 49 239, 47 401, 50 802, 53 417, 46 531 | 49 239 |
| šifrování zapnuto | 57 790, 46 173, 45 939, 52 787, 49 632 | 49 632 |
Rozdíl mediánů je +0,8 % a rozptyl uvnitř jedné varianty (46 000 až 58 000) je větší, takže cena v tomto testu není měřitelná. To je také očekávané: celý běh db-bench zapíše asi 6 MB, což je asi 2 ms AES, a commit čeká na flush, ne na šifru.
Diagnostická položka nyní vedle AES-GCM zobrazuje i AES-XTS (na HVF jádro naměří asi 800 MiB/s pro XTS oproti asi 560 MiB/s pro AES-GCM) a ukazuje, zda je dostupný hardwarový AES.

Co XTS nedává. Není tu autentizace: upravený šifrovaný sektor se dešifruje na šum v blocích po 16 bajtech (CRC a čistič to najdou) a ten, kdo může zapisovat na svazek, může mezi dvěma snímky zaměnit nebo vrátit celé sektory. Chrání ukradený svazek nebo snímek, ne ukradené ISO, protože klíč je v konfiguraci obrazu. Viditelné zůstává: GPT, superblok, UUID a které sektory kdy byly zapsány.
Co jsme při tom našli ve vlastní práci. Při psaní kapitoly o kryptografii jsme zjistili dvě věci. Krok násobení tweaku v XTS měl větvení závislé na bitu odvozeném z klíče; nyní je bez větvení. A tvrzení „ověřeno podle vektoru 10 z IEEE 1619" pokrývalo jen první šifrovaný blok; nyní existuje i naivní referenční implementace (aritmetika u128, blok po bloku), která ověřuje celé sektory v obou směrech na 200 náhodných klíčích a sektorech, a dokumentace říká, co bylo skutečně ověřeno.
Boot, který odmítne upravený obraz (H8)
Požadavek z auditního seznamu byl jednoduchý a dosud neověřený: žádný test neukázal, že se upravené jádro nespustí. Pin hashe v limine.conf existoval, ale bez důkazu.
- Test, který mění bajt.
scripts/tamper.pyotočí jeden bit v každé kopii jádra (nebo konfiguračního modulu) uvnitř sestaveného ISO nebo raw obrazu aSMOKE_ONLY=integrityukáže, že zavaděč odmítne: 24 kontrol prošlo, na aarch64 UEFI, x86_64 UEFI a x86_64 BIOS, s kontrolami, které se spouštějí. Zpráva o odmítnutí se čte ze sériového portu přes přepínač určený jen pro testy. Na BIOSu zpráva dorazí jen na obrazovku, takže tento test tvrdí jen to, že se stroj nespustí. - Zapsaná konfigurace zavaděče (ADR-250).
limine enroll-configfunguje naBOOTAA64.EFI,BOOTX64.EFIi na stupni BIOS, takže se odmítne i upravenýlimine.conf(přidaná položka nabídky nebo argument).urmqtt-config pack --isoji zapíše znovu. - Cesta Secure Bootu (ADR-251).
scripts/sign-loaders.sh(zkušební klíče, podepisování, ověřování, úložiště proměnných OVMF),mkiso.sh --sb-keya skupinasecurebootna x86_64: podepsané ISO se spustí a sestaví reprodukovatelně, zatímco nepodepsané, podepsané špatným klíčem a upravené po podpisu dostanouAccess Denied(10 kontrol prošlo). Limine i pod Secure Bootem upravené jádro odmítne. - Pravidla instalační položky (ADR-253). Plán mluvil o jednom slovu na příkazové řádce,
install. Produkční jádro ve skutečnosti přijímá tři (install,live,diag) a zapisuje jeninstall. Test (9 kontrol) to spouští na třech strojích s prázdným diskem, jehož hash se nesmí změnit. - Tajnost obrazu (ADR-252). Hlasité varování, když se produkční obraz staví mimo CI, a prohledání obrazu, logů sestavení a sériových logů na bajty klíčů v mnoha kódováních (11 kontrol).
- TPM, jen ADR-254. Co by znamenalo zapečetit klíč serveru do vTPM nebo TPM 2.0. Doporučení: nestavět pro vydání zaměřené na VPS. Neověřovali jsme, kteří poskytovatelé vTPM vůbec nabízejí.
Co to NEZNAMENÁ (zapsáno jako H8-W1 až H8-W6 v AUDIT.md): nainstalovaný disk se pod Secure Bootem nespustí, protože jeho konfigurační modul nemá hash (ADR-217); podepsané ISO nelze dodatečně konfigurovat (nejdřív konfigurovat, pak podepsat); cesta BIOS nemá podpis; není tu ochrana proti návratu ke starší verzi; na aarch64 firmware zde Secure Boot nevynucuje, takže byl ověřen jen podpis zavaděče; neexistuje projektový klíč, takže obrazy vydání nejsou pro Secure Boot podepsané; a žádný fyzický stroj nebyl vyzkoušen.
Auditní balíček (H10)
docs/AUDIT.md je nyní balíček, který může externí auditor použít bez dotazů: systém na jedné stránce, základ, kterému věříme (89 612 řádků vlastního Rustu v obrazu, 67 přilinkovaných cizích balíčků), vstupy a jejich parsery, matice ochran s testy (i ochrany bez testů), slabiny se stavem, tabulka každé síťově dosažitelné smyčky a alokace s mezí a testem, návod k sestavení a testování, navržený rozsah externího auditu v deseti oblastech a red-team seznam s 31 útoky a výsledky. SECURITY.md má tabulku STRIDE po komponentách.
- Oprava prvního textu. V textu z 29. října stojí, že každý blok
unsafemá komentář// SAFETY:, který kontroluje lint. To nebyla pravda: lint (deny(unsafe_code)) jen omezuje, kde smíunsafebýt, a komentáře od H0 eviduje generovaný soupis, který už tehdy našel asi 30 míst bez zdůvodnění a při auditu jich bylo 108 (protože mezitím přibyly nové druhy míst, například atributy#[unsafe(no_mangle)]alink_section). Proto je to soupis, a ne lint, co je teď drží na nule. - 889 míst
unsafe, každé s písemným zdůvodněním. Těch 108, která ho neměla (hlavně#[unsafe(no_mangle)]alink_section, několikunsafe fn), dostalo komentář (napsal ho AI agent, jen komentáře, vzorek jsem prošel já); generovaný soupis v CI zaznamená každé nové místo bez zdůvodnění. Zdůvodnění je tvrzení, které je třeba přezkoumat, ne důkaz, a to je bod 5 navrženého rozsahu. - Pět otevřených nálezů zůstává otevřených, zapsáno: není fuzzing parserů PCI a virtio, není test, který zapisuje do stránkových tabulek po jejich uzamčení, doba běhu P-256, X25519 a Ed25519 nebyla měřena, není fuzzing záměrně vytvořených souborů SQLite (omezeno H6) a 53 z 67 přilinkovaných balíčků jsou výjimky
cargo-vet, ne audity.
Chyby, které jsme našli sami
- Tajemství TLS provozu se nemazala (H0-W2, otevřeno od prvního dne auditu): tabulka zachycených tajemství byla
Vec, kterýremoveaswap_removepřesouvaly a nechávaly po sobě kopie. Nyní je tabulka po slotech: slot se maže na místě, když se vezme, zahodí nebo vytlačí, nic se nepřesouvá. - Větvení závislé na klíči v XTS (viz výše).
log-slow-floodna x86_64 padal („log ring full"), i na zdrojovém kódu v0.2.1. Příčinou nebyla chyba, ale dimenzování testu: scénář nutil každé jádro na 25 ms, zatímco ADR-090 ho měřil na 50 ms. Na x86_64 je COM1 port-I/O po bajtu zachycené do pasti, jedno jádro zapisující do sinků potřebuje asi 9 ms na řádek a tři jádra na 25 ms žádají víc než sekundu práce za sekundu, takže se kruhy plní (zamýšlené přetečení). Test nyní na x86_64 běží na 50 ms (prošel, 30 z 30 v běhu se základními scénáři); na aarch64 zůstává 25 ms.- Sloučení větví rozbilo staré šablony. Opětovný zápis konfigurace v H8 vyžadoval koncovou značku v
limine.conf, kterou šablony před H8 nemají, takžeurmqtt-config pack --isoa stránka sestavovače odmítaly každé vydání před H8, včetně v0.2.x, které stránka nabízí. Zachytily to testy sestavovače v prohlížeči (4 ze 100 selhaly), ne vlastní test H8. Opraveno (šablona bez značky se konfiguruje jako dřív) a přidán test, který se stejnou původní chybou padá, když se oprava odstraní. - Naše dokumentace chybně tvrdila, že x86_64 bylo ověřeno jen v emulátoru. X11 spustil produkční obraz na skutečném Contabo KVM VPS (4 vCPU AMD EPYC) s oboustranným TLS pod zátěží. Opraveno všude: x86_64 běželo na skutečném KVM VPS u commitu H7 a v emulátoru, ale kód přidaný po něm (šifrování, integrita bootu) na KVM neběžel a na fyzickém hardwaru nebyl vyzkoušen.
D1 a nabídka „vaše služba jako unikernel"
Veřejný demo broker je postavený, ale nezveřejněný: konfigurace (live režim, ACL demo/{id}/#, obyčejné MQTT jen pro most), skript, který vytváří zveřejněnou zkušební CA a demo certifikáty, most WebSocket-na-TCP (jen standardní knihovna Pythonu, omezení spojení, rychlosti a velikosti, kontrola původu), stránka „Vyzkoušejte urMQTT živě" s MQTT klientem v obyčejném JavaScriptu, runbook a model hrozeb. Testováno na loopbacku a v QEMU (18 scénářů, 26 testů mostu). Netestováno: skutečný VPS, DNS, Caddy, obraz kontejneru. Potřebuje samostatný VPS a výslovný souhlas s veřejným vystavením.
Na webu je navíc zveřejněna i nabídka, Unikernel (15 jazyků, bez cen): úprava vaší služby tak, aby běžela jako unikernel (posouzení proveditelnosti, úprava, pilot, podpora), s urMQTT jako referencí. Text výslovně uvádí omezení dnešního základu: IPv4, TCP jen jako server, KVM, Rust přirozený, C jen jako izolovaný worker.
Vydání v0.3.0
Tag v0.3.0 byl nastaven 3. listopadu na commit f1a7fbc a workflow vydání uspěl. Vydání je označeno jako pre-release a obsahuje vývojové jádro pro aarch64, ISO a obraz, šablonu ISO (jádra pro aarch64 a x86_64 a prázdnou konfiguraci, kterou urmqtt-config pack --iso a stránka sestavovače promění ve váš obraz), CycloneDX SBOM, SHA256SUMS a jeho podpis minisign a atestace původu SLSA. Ověřeno po zveřejnění, v prázdném adresáři: podpis SHA256SUMS platí, všech pět souborů odpovídá kontrolním součtům, obě atestace (jádro a ISO s predikátem CycloneDX) projdou, SBOM má 221 komponent a šablona se konfiguruje (pack --iso znovu zapíše šest kopií zavaděče). Podpisový klíč je stále zkušební klíč.
Pořadí bylo důležité a stojí za zapsání: stránka sestavovače byla aktualizována před tagem (zná formát 4 a všechny starší šablony) a teprve potom si zrcadlo na aldo.tech vydání převzalo. Zrcadlo ho uvedlo jako podepsané a ověřené (ok, formát konfigurace 4, sha256 šablony shodný s tím v SHA256SUMS) a živá stránka ho nabízí jako výchozí vydání a v prohlížeči vytvoří ISO asi za 20 sekund. Poznámky k vydání říkají i to, co se píše zřídka: CI na GitHubu není zelené (viz níže), podpora je jen pro poslední verzi (v0.2.1 a starší končí) a audit provedl sám projekt.
Na webu jsme si přitom málem sami uškodili: publikační skript webu (publish.sh) dělá rsync --delete a zrcadlo vydání patří jinému uživateli, takže každá publikace skončila chybou 23 (a rozbila by tlačítko „Publikovat" v administraci blogu). Opraveno výjimkou pro /urmqtt/releases/. Podobný nález: deploy.sh s --delete maže stránku sestavovače z generátoru, takže se musí před publikací vrátit.
Co ještě není prokázáno
- CI na GitHubu je červené. Nejen dnes: poslední běh úplného CI před tagem měl sedm neúspěšných jobů a běhy zpětně až do 31. října jsou většinou červené. Selhaly: lint (varování shellcheck ve skriptech), TLS interop (všechny případy wolfSSL), dodavatelský řetězec (seznam výjimek
vet-exemptions.mdnení na Linuxu aktuální), fuzz smoke (v tom jobu chybí vývojové certifikáty), sanitizery (LeakSanitizer hlásí únik v testech), testy na Linuxu aarch64 (jeden test nástroje padá s „Broken pipe") a jeden důkaz Kani pro časové kolo (timer wheel) neprojde. Zatím jsme netřídili, co z toho je prostředí a co skutečná vada; dokud to neuděláme, netvrdíme, že vydání prošlo CI. - Podpisový klíč je zkušební, a obrazy vydání jsou vývojové se zkušebními certifikáty (kromě šablony).
- Překlady stránek a tohoto textu do dalších 13 jazyků napsala AI a rodilí mluvčí je nezkontrolovali.
- Šifrování disku nebylo změřeno na x86_64 (ani na KVM, ani na fyzickém hardwaru) a nebyla změřena ani propustnost telemetrie, ani doba startu se šifrováním. Na stránce sestavovače není (potřebuje dialog pro klíč).
- Nezávislý bezpečnostní audit neexistuje. Rozsah je napsán; auditora nemáme.
- Secure Boot je částečný (viz výše), bez fyzického stroje.
- Bare metal, IOMMU, veřejný demo broker: nehotovo nebo nezveřejněno.
Co bude dál
- Roztřídit a opravit CI (nejdřív prostředí, pak důkaz Kani a test, který padá na Linuxu aarch64), protože vydání bez zeleného CI nepovažujeme za hotové.
- Rozhodnutí o D1 (samostatný VPS, DNS, souhlas s veřejným vystavením).
- Fuzzing parserů PCI a virtio a test uzamčených stránkových tabulek (H0-W9, H0-W10).
- Dialog pro klíč na stránce sestavovače, aby se šifrování stalo dostupným bez příkazové řádky.
- Po vydání: bare metal (X12 až X16) a IOMMU, v pořadí, které závisí na soupisu hardwaru testovacího PC.
Kód je na github.com/zeljan-alduk/urMQTT; docs/PROGRESS.md zůstává jediným zdrojem pravdy o tom, co skutečně funguje, docs/PERFORMANCE.md o tom, co bylo změřeno, a docs/AUDIT.md o tom, co ještě není prokázáno. Pre-alpha, jako dřív; hlášení chyb a bezpečnostní hlášení podle pokynů v SECURITY.md jsou vítána.
Komentáře
Pravidla komentování