← Blog

· 13 min čítania #rust#mqtt#iot#unikernel#open source#security

urMQTT v0.3.0: šifrovanie disku, boot, ktorý odmietne upravený obraz, a to, čo sme si sami pokazili

Čo sa stalo po treťom texte o urMQTT: šifrovanie disku v pokoji so zmeranou cenou, test, ktorý zmení jeden bajt v jadre a ukáže, že ho zavádzač odmietne, auditný balík s 889 zdôvodnenými miestami s unsafe, chyby, ktoré sme našli sami (a jedno tvrdenie, ktoré sme napísali nesprávne), a vydanie v0.3.0 so zoznamom toho, čo ešte nie je dokázané, vrátane červeného CI.

Tretí text, urMQTT po VPS, skončil zoznamom „Čo bude ďalej": verejný demo broker (D1), druhé kolo pre pomalý jediný tok, obyčajná cesta proti Mosquittu na VPS a „po vydaní" bare metal, IOMMU, Secure Boot a šifrovanie disku. Tento text hovorí, čo z toho je hotové, čo nie a čo sme medzitým našli. Šifrovanie disku a časť Secure Bootu sa nakoniec do vydania dostali, pretože ležali na ceste k auditu. Druhé kolo pre jediný tok a obyčajná cesta proti Mosquittu hotové nie sú.

Poznámka vopred ostáva rovnaká: projekt je pre-alpha, vydanie cieli na VPS s KVM a čísla merajú kód, testy a konkrétne pokusy, nie zrelosť v produkcii.

Ako to vyzeralo v čase

Od minulého textu (1. novembra o 20:41) do dneška (3. novembra o 02:43) pribudlo v hlavnej vetve 37 nových commitov, spolu 215. Ako predtým, projekt sa stavia s Claude Code (commity nesú Co-Authored-By: Claude) a tentoraz bežali paralelne tri vetvy (D1, H8, príprava vydania), takže poradie commitov nie je presne poradie práce. Čísla sú vypočítané rovnakou metódou ako v minulom texte (všetky súbory .rs mimo vendor/ a fuzz/, funkcie #[test], nadpisy ## ADR- v docs/DECISIONS.md) a overené na starom commite, ktorý dáva rovnaké čísla ako vtedy.

Okamih Commit Čas Riadkov Rustu Testov ADR Commitov
minulý text d21e3ec 1. 11., 20:41 112 408 926 126 178
dnes f1a7fbc 3. 11., 02:43 115 148 962 133 215

Rast kódu je skromný (+2 740 riadkov, +36 testov), pretože tentoraz nebolo veľa nového kódu: väčšinu práce tvoria testy, skripty, dokumentácia a kontroly. Skripty a JavaScript sa do počtu riadkov nezapočítavajú.

Šifrovanie disku v pokoji (S7)

Čo sme plánovali. Voliteľné šifrovanie dátovej partície, v predvolenom stave vypnuté, so zmeranou cenou. Čo sme postavili (ADR-240):

  • Vrstva pod diskom. CryptDev obaľuje blokové zariadenie pod Disk, takže rozloženie, tabuľka veľkostí, SQLite, telemetria a zálohy pracujú s čistým textom a o šifrovaní nevedia. Algoritmus je AES-256-XTS (IEEE 1619), jeden sektor s 512 bajtmi na dátovú jednotku, nie blok s 4 KiB, ako počítal plán: úložisko zapisuje po sektoroch a jednotka 4 KiB by každý zápis 512 bajtov premenila na čítanie-úpravu-zápis, ktorý môže výpadok prúdu prerušiť napoly.
  • Superblok ostáva otvorený. Dva sloty superbloku nesú príznak a kontrolu kľúča (HMAC-SHA-256 zo štítku a UUID disku), nikdy kľúč. Všetko ostatné v partícii je šifrované. Sektor, ktorý je na médiu celý nulový, sa číta ako nula a nedešifruje sa, takže discard, nikdy nezapísaný priestor a zväčšovanie disku fungujú ako predtým.
  • Konfigurácia. storage.encryption = "aes-256-xts" a storage.disk_key (súbor so 64 bajtmi kľúča). Formát blobu sa stáva 4 iba keď je šifrovanie zapnuté, takže všetky ostatné obrazy sa naďalej spúšťajú na starších jadrách. Staršia čítačka neznámy príznak v superbloku odmietne.
  • Bez hardvérového AES: odmietnutie. Jadro odmietne storage.encryption na procesore bez inštrukcií AES (alebo keď ich [cpu] disable vypne), namiesto toho, aby potichu bežalo na pomalej softvérovej verzii.
  • Prevod je offline. Jadro pri štarte nikdy disk nešifruje ani nedešifruje. Robia to urmqtt-disk encrypt, decrypt a rekey pri vypnutom VM: záznam o prevode sa zapisuje ako prvý a maže ako posledný (kým existuje, jadro disk odmieta), kúsky po 1 MiB sa najprv uložia do žurnálu na hostiteľovi, potom sa zapíše prevedená kópia a superblok sa prepína ako posledný. Vyskúšané výpadkom prúdu po každom jednotlivom zápise pri všetkých troch postupoch, čisto aj napoly zapísanom, so zahodenou aj zachovanou cache: po --resume vždy platný disk so všetkými dátami.

Zmerané. Priepustnosť XTS na jadro na Apple silicon (jeden MiB sektorov v slučke): 3 109 MB/s šifrovanie a 3 103 MB/s dešifrovanie s hardvérovým AES, oproti 252 a 258 MB/s v softvéri. Preto softvér nepovoľujeme. db-bench vo VM (aarch64, HVF, 5 000 vložení v 50 transakciách, päť behov každého variantu, striedavo):

op/s, päť behov medián
šifrovanie vypnuté 49 239, 47 401, 50 802, 53 417, 46 531 49 239
šifrovanie zapnuté 57 790, 46 173, 45 939, 52 787, 49 632 49 632

Rozdiel mediánov je +0,8 % a rozptyl vnútri jedného variantu (46 000 až 58 000) je väčší, takže cena v tomto teste nie je merateľná. To je aj očakávané: celý beh db-bench zapíše asi 6 MB, čo je asi 2 ms AES, a commit čaká na flush, nie na šifru.

Diagnostická položka teraz popri AES-GCM zobrazuje aj AES-XTS (na HVF jadro nameria asi 800 MiB/s pre XTS oproti asi 560 MiB/s pre AES-GCM) a ukazuje, či je dostupný hardvérový AES.

Diagnostická položka spustená vo webovom emulátore na aldo.tech, v karte prehliadača: v paneli CRYPTO posledný riadok ukazuje AES-XTS a „hw AES". Čísla opisujú emulátor (6 MiB/s), nie urMQTT.
Diagnostická položka spustená vo webovom emulátore na aldo.tech, v karte prehliadača: v paneli CRYPTO posledný riadok ukazuje AES-XTS a „hw AES". Čísla opisujú emulátor (6 MiB/s), nie urMQTT.

Čo XTS nedáva. Nie je tu autentifikácia: upravený šifrovaný sektor sa dešifruje na šum v blokoch po 16 bajtov (CRC a čistič to nájdu) a ten, kto môže zapisovať na zväzok, môže medzi dvoma snímkami zameniť alebo vrátiť celé sektory. Chráni ukradnutý zväzok alebo snímku, nie ukradnuté ISO, pretože kľúč je v konfigurácii obrazu. Viditeľné zostáva: GPT, superblok, UUID a ktoré sektory boli kedy zapísané.

Čo sme pri tom našli vo vlastnej práci. Pri písaní kapitoly o kryptografii sme zistili dve veci. Krok násobenia tweaku v XTS mal vetvu závislú od bitu odvodeného z kľúča; teraz je bez vetvenia. A tvrdenie „overené podľa vektora 10 z IEEE 1619" pokrývalo iba prvý šifrovaný blok; teraz existuje aj naivná referenčná implementácia (aritmetika u128, blok po bloku), ktorá overuje celé sektory v oboch smeroch na 200 náhodných kľúčoch a sektoroch, a dokumentácia hovorí, čo bolo skutočne overené.

Boot, ktorý odmietne upravený obraz (H8)

Požiadavka z auditného zoznamu bola jednoduchá a doteraz neoverená: žiadny test neukázal, že sa upravené jadro nespustí. Pin hashu v limine.conf existoval, ale bez dôkazu.

  • Test, ktorý mení bajt. scripts/tamper.py otočí jeden bit v každej kópii jadra (alebo konfiguračného modulu) vo vnútri zostaveného ISO alebo raw obrazu a SMOKE_ONLY=integrity ukáže, že zavádzač odmietne: 24 kontrol prešlo, na aarch64 UEFI, x86_64 UEFI a x86_64 BIOS, s kontrolami, ktoré sa spúšťajú. Správa o odmietnutí sa číta zo sériového portu cez prepínač určený iba pre testy. Na BIOSe správa dorazí iba na obrazovku, takže tento test tvrdí iba to, že sa stroj nespustí.
  • Zapísaná konfigurácia zavádzača (ADR-250). limine enroll-config funguje na BOOTAA64.EFI, BOOTX64.EFI aj na stupni BIOS, takže sa odmietne aj upravený limine.conf (pridaná položka ponuky alebo argument). urmqtt-config pack --iso ju zapíše znova.
  • Cesta Secure Bootu (ADR-251). scripts/sign-loaders.sh (skúšobné kľúče, podpisovanie, overovanie, úložisko premenných OVMF), mkiso.sh --sb-key a skupina secureboot na x86_64: podpísané ISO sa spustí a zostaví reprodukovateľne, zatiaľ čo nepodpísané, podpísané zlým kľúčom a upravené po podpise dostanú Access Denied (10 kontrol prešlo). Limine aj pod Secure Bootom upravené jadro odmietne.
  • Pravidlá inštalačnej položky (ADR-253). Plán hovoril o jednom slove na príkazovom riadku, install. Produkčné jadro v skutočnosti prijíma tri (install, live, diag) a zapisuje iba install. Test (9 kontrol) to spúšťa na troch strojoch s prázdnym diskom, ktorého hash sa nesmie zmeniť.
  • Tajnosť obrazu (ADR-252). Hlasné varovanie, keď sa produkčný obraz stavia mimo CI, a prehľadanie obrazu, logov zostavenia a sériových logov na bajty kľúčov v mnohých kódovaniach (11 kontrol).
  • TPM, iba ADR-254. Čo by znamenalo zapečatiť kľúč servera do vTPM alebo TPM 2.0. Odporúčanie: nestavať pre vydanie zamerané na VPS. Neoverovali sme, ktorí poskytovatelia vTPM vôbec ponúkajú.

Čo to NEZNAMENÁ (zapísané ako H8-W1 až H8-W6 v AUDIT.md): nainštalovaný disk sa pod Secure Bootom nespustí, pretože jeho konfiguračný modul nemá hash (ADR-217); podpísané ISO sa nedá dodatočne konfigurovať (najprv konfigurovať, potom podpísať); cesta BIOS nemá podpis; nie je tu ochrana proti návratu k staršej verzii; na aarch64 firmvér tu Secure Boot nevynucuje, takže bol overený iba podpis zavádzača; neexistuje projektový kľúč, takže obrazy vydania nie sú pre Secure Boot podpísané; a žiadny fyzický stroj nebol vyskúšaný.

Auditný balík (H10)

docs/AUDIT.md je teraz balík, ktorý môže externý audítor použiť bez otázok: systém na jednej strane, základ, ktorému veríme (89 612 riadkov vlastného Rustu v obraze, 67 prilinkovaných cudzích balíkov), vstupy a ich parsery, matica ochrán s testami (aj ochrany bez testov), slabiny so stavom, tabuľka každej sieťovo dosiahnuteľnej slučky a alokácie s hranicou a testom, návod na zostavenie a testovanie, navrhnutý rozsah externého auditu v desiatich oblastiach a red-team zoznam s 31 útokmi a výsledkami. SECURITY.md má tabuľku STRIDE po komponentoch.

  • Oprava prvého textu. V texte z 29. októbra stojí, že každý blok unsafe má komentár // SAFETY:, ktorý kontroluje lint. To nebola pravda: lint (deny(unsafe_code)) iba obmedzuje, kde smie unsafe byť, a komentáre od H0 eviduje generovaný súpis, ktorý už vtedy našiel asi 30 miest bez zdôvodnenia a pri audite ich bolo 108 (lebo medzitým pribudli nové druhy miest, napríklad atribúty #[unsafe(no_mangle)] a link_section). Preto je to súpis, a nie lint, čo ich teraz drží na nule.
  • 889 miest unsafe, každé s písomným zdôvodnením. Tých 108, ktoré ho nemali (hlavne #[unsafe(no_mangle)] a link_section, niekoľko unsafe fn), dostalo komentár (napísal ho AI agent, iba komentáre, vzorku som prešiel ja); generovaný súpis v CI zaznamená každé nové miesto bez zdôvodnenia. Zdôvodnenie je tvrdenie, ktoré treba preskúmať, nie dôkaz, a to je bod 5 navrhnutého rozsahu.
  • Päť otvorených nálezov ostáva otvorených, zapísané: nie je fuzzing parserov PCI a virtio, nie je test, ktorý zapisuje do stránkových tabuliek po ich uzamknutí, čas behu P-256, X25519 a Ed25519 nebol meraný, nie je fuzzing zámerne vytvorených súborov SQLite (obmedzené H6) a 53 zo 67 prilinkovaných balíkov sú výnimky cargo-vet, nie audity.

Chyby, ktoré sme našli sami

  • Tajomstvá TLS prevádzky sa nemazali (H0-W2, otvorené od prvého dňa auditu): tabuľka zachytených tajomstiev bola Vec, ktorý remove a swap_remove presúvali a nechávali po sebe kópie. Teraz je tabuľka po slotoch: slot sa maže na mieste, keď sa vezme, zahodí alebo vytlačí, nič sa nepresúva.
  • Vetva závislá od kľúča v XTS (vyššie).
  • log-slow-flood na x86_64 padal („log ring full"), aj na zdrojovom kóde v0.2.1. Príčinou nebola chyba, ale dimenzovanie testu: scenár nútil každé jadro na 25 ms, zatiaľ čo ADR-090 ho meral na 50 ms. Na x86_64 je COM1 port-I/O po bajte zachytené do pasce, jedno jadro zapisujúce do sinkov potrebuje asi 9 ms na riadok a tri jadrá na 25 ms žiadajú viac než sekundu práce za sekundu, takže sa kruhy plnia (zamýšľané pretečenie). Test teraz na x86_64 beží na 50 ms (prešiel, 30 z 30 v behu so základnými scenármi); na aarch64 ostáva 25 ms.
  • Zlúčenie vetiev pokazilo staré šablóny. Opätovný zápis konfigurácie v H8 vyžadoval koncovú značku v limine.conf, ktorú šablóny pred H8 nemajú, takže urmqtt-config pack --iso a stránka zostavovača odmietali každé vydanie pred H8, vrátane v0.2.x, ktoré stránka ponúka. Zachytili to testy zostavovača v prehliadači (4 zo 100 zlyhali), nie vlastný test H8. Opravené (šablóna bez značky sa konfiguruje ako predtým) a pridaný test, ktorý s pôvodnou chybou padá, keď sa oprava odstráni.
  • Naša dokumentácia nesprávne tvrdila, že x86_64 bolo overené iba v emulátore. X11 spustil produkčný obraz na skutočnom Contabo KVM VPS (4 vCPU AMD EPYC) s obojstranným TLS pod záťažou. Opravené všade: x86_64 bežalo na skutočnom KVM VPS pri commite H7 a v emulátore, ale kód pridaný po ňom (šifrovanie, integrita bootu) na KVM nebežal a na fyzickom hardvéri nebol vyskúšaný.

D1 a ponuka „vaša služba ako unikernel"

Verejný demo broker je postavený, ale nezverejnený: konfigurácia (live režim, ACL demo/{id}/#, obyčajné MQTT iba pre most), skript, ktorý vytvára zverejnenú skúšobnú CA a demo certifikáty, most WebSocket-na-TCP (iba štandardná knižnica Pythonu, obmedzenia spojení, rýchlosti a veľkosti, kontrola pôvodu), stránka „Vyskúšajte urMQTT naživo" s MQTT klientom v obyčajnom JavaScripte, runbook a model hrozieb. Testované na loopbacku a v QEMU (18 scenárov, 26 testov mosta). Netestované: skutočný VPS, DNS, Caddy, obraz kontajnera. Potrebuje samostatný VPS a výslovný súhlas s verejným vystavením.

Na webe je navyše zverejnená aj ponuka, Unikernel (15 jazykov, bez cien): úprava vašej služby tak, aby bežala ako unikernel (posúdenie realizovateľnosti, úprava, pilot, podpora), s urMQTT ako referenciou. Text výslovne uvádza obmedzenia dnešného základu: IPv4, TCP iba ako server, KVM, Rust prirodzený, C iba ako izolovaný worker.

Vydanie v0.3.0

Tag v0.3.0 bol nastavený 3. novembra na commit f1a7fbc a workflow vydania uspel. Vydanie je označené ako pre-release a obsahuje vývojové jadro pre aarch64, ISO a obraz, šablónu ISO (jadrá pre aarch64 a x86_64 a prázdnu konfiguráciu, ktorú urmqtt-config pack --iso a stránka zostavovača premenia na váš obraz), CycloneDX SBOM, SHA256SUMS a jeho podpis minisign a atestácie pôvodu SLSA. Overené po zverejnení, v prázdnom adresári: podpis SHA256SUMS platí, všetkých päť súborov zodpovedá kontrolným súčtom, obe atestácie (jadro a ISO s predikátom CycloneDX) prejdú, SBOM má 221 komponentov a šablóna sa konfiguruje (pack --iso znova zapíše šesť kópií zavádzača). Podpisový kľúč je stále skúšobný kľúč.

Poradie bolo dôležité a oplatí sa ho zapísať: stránka zostavovača bola aktualizovaná pred tagom (pozná formát 4 a všetky staršie šablóny) a až potom si zrkadlo na aldo.tech vydanie prevzalo. Zrkadlo ho uviedlo ako podpísané a overené (ok, formát konfigurácie 4, sha256 šablóny zhodný s tým v SHA256SUMS) a živá stránka ho ponúka ako predvolené vydanie a v prehliadači vytvorí ISO asi za 20 sekúnd. Poznámky k vydaniu hovoria aj to, čo sa píše zriedka: CI na GitHube nie je zelené (pozri nižšie), podpora je iba pre poslednú verziu (v0.2.1 a staršie končia) a audit vykonal sám projekt.

Na webe sme si pritom takmer sami uškodili: publikačný skript webu (publish.sh) robí rsync --delete a zrkadlo vydania patrí inému používateľovi, takže každá publikácia skončila chybou 23 (a pokazila by tlačidlo „Publikovať" v administrácii blogu). Opravené výnimkou pre /urmqtt/releases/. Podobný nález: deploy.sh s --delete maže stránku zostavovača z generátora, takže sa musí pred publikáciou vrátiť.

Čo ešte nie je dokázané

  • CI na GitHube je červené. Nielen dnes: posledný beh úplného CI pred tagom mal sedem neúspešných jobov a behy spätne až do 31. októbra sú väčšinou červené. Zlyhali: lint (varovania shellcheck v skriptoch), TLS interop (všetky prípady wolfSSL), dodávateľský reťazec (zoznam výnimiek vet-exemptions.md nie je na Linuxe aktuálny), fuzz smoke (v tom jobe chýbajú vývojové certifikáty), sanitizery (LeakSanitizer hlási únik v testoch), testy na Linuxe aarch64 (jeden test nástroja padá s „Broken pipe") a jeden dôkaz Kani pre časové koleso (timer wheel) neprejde. Zatiaľ sme netriedili, čo z toho je prostredie a čo skutočná chyba; kým to neurobíme, netvrdíme, že vydanie prešlo CI.
  • Podpisový kľúč je skúšobný, a obrazy vydania sú vývojové so skúšobnými certifikátmi (okrem šablóny).
  • Preklady stránok a tohto textu do ďalších 13 jazykov napísala AI a rodení hovoriaci ich nekontrolovali.
  • Šifrovanie disku nebolo zmerané na x86_64 (ani na KVM, ani na fyzickom hardvéri) a nebola zmeraná ani priepustnosť telemetrie, ani čas štartu so šifrovaním. Na stránke zostavovača nie je (potrebuje dialóg pre kľúč).
  • Nezávislý bezpečnostný audit neexistuje. Rozsah je napísaný; audítora nemáme.
  • Secure Boot je čiastočný (vyššie), bez fyzického stroja.
  • Bare metal, IOMMU, verejný demo broker: nehotové alebo nezverejnené.

Čo bude ďalej

  1. Roztriediť a opraviť CI (najprv prostredie, potom dôkaz Kani a test, ktorý padá na Linuxe aarch64), pretože vydanie bez zeleného CI nepovažujeme za hotové.
  2. Rozhodnutie o D1 (samostatný VPS, DNS, súhlas s verejným vystavením).
  3. Fuzzing parserov PCI a virtio a test uzamknutých stránkových tabuliek (H0-W9, H0-W10).
  4. Dialóg pre kľúč na stránke zostavovača, aby sa šifrovanie stalo dostupným bez príkazového riadka.
  5. Po vydaní: bare metal (X12 až X16) a IOMMU, v poradí, ktoré závisí od súpisu hardvéru testovacieho PC.

Kód je na github.com/zeljan-alduk/urMQTT; docs/PROGRESS.md ostáva jediným zdrojom pravdy o tom, čo skutočne funguje, docs/PERFORMANCE.md o tom, čo bolo zmerané, a docs/AUDIT.md o tom, čo ešte nie je dokázané. Pre-alpha, ako predtým; hlásenia chýb a bezpečnostné hlásenia podľa pokynov v SECURITY.md sú vítané.

Komentáre

Pravidlá komentovania

Pravidlá komentovania

Komentáre slúžia na zdieľanie skúseností a otázok k témam blogu. Tieto pravidlá udržiavajú diskusiu užitočnú pre všetkých.

  1. Slušnosť Žiadne urážky, vyhrážky, obťažovanie, nenávistné prejavy ani diskriminácia z akéhokoľvek dôvodu. Kritizujte argument, nie osobu.
  2. K téme Komentár sa má týkať príspevku alebo diskusie pod ním.
  3. Bez reklamy a spamu Žiadna inzercia, reťazové správy ani odkazy bez kontextu. Odkazy a obrázky sú vítané, keď diskusiu obohacujú.
  4. Súkromie ostatných Nezverejňujte osobné údaje iných ľudí: celé mená súkromných osôb, adresy, telefóny, e-maily, fotografie bez súhlasu.
  5. Autorské práva Zverejňujte len obsah, ktorý smiete zdieľať. Pri citovaní uveďte zdroj.
  6. Jedna identita Používajte jednu prezývku a nevydávajte sa za niekoho iného.
  7. Potvrdenie e-mailom Komentár sa zverejní až po potvrdení odkazu zaslaného e-mailom. Adresa sa nezobrazuje a na nič iné sa nepoužíva; nepotvrdené komentáre sa po 48 hodinách mažú.
  8. Moderovanie Komentáre porušujúce pravidlá skrývame alebo mažeme bez upozornenia a diskusiu môžeme uzamknúť. Rozhodnutia moderátorov sú konečné.
  9. Zodpovednosť Za obsah komentára zodpovedá jeho autor. Komentáre nevyjadrujú názory ALDO TECH.
  10. Nahlásenie Nevhodný obsah nahláste na info@aldo.tech.