urMQTT v0.3.0: šifriranje diska, zagon, ki zavrne popravljeno sliko, in to, kar smo pokvarili sami
Kaj se je zgodilo po tretjem besedilu o urMQTT: šifriranje diska v mirovanju z izmerjenim stroškom, test, ki spremeni en bajt v jedru in pokaže, da ga nalagalnik zavrne, revizijski paket z 889 utemeljenimi mesti unsafe, napake, ki smo jih našli sami (in ena trditev, ki smo jo napisali napačno), ter izdaja v0.3.0 s seznamom tega, kar še ni dokazano, vključno z rdečim CI.

Tretje besedilo, urMQTT po VPS-ju, se je končalo s seznamom »Kaj sledi«: javni demo posrednik (D1), drugi krog za počasen en sam tok, navadna pot v primerjavi z Mosquittom na VPS-ju in, »po izdaji«, bare metal, IOMMU, Secure Boot in šifriranje diska. To besedilo pove, kaj od tega je narejeno, kaj ni in kaj smo med tem odkrili. Šifriranje diska in del Secure Boota sta vendarle prišla pred izdajo, ker sta bila na poti do revizije. Drugi krog za en sam tok in navadna pot v primerjavi z Mosquittom nista narejena.
Opomba vnaprej ostaja enaka: projekt je pred-alfa, izdaja cilja na VPS-je s KVM, številke pa merijo kodo, teste in določene poskuse, ne zrelosti v produkciji.
Kako je to izgledalo v času
Od prejšnjega besedila (1. novembra ob 20:41) do danes (3. novembra ob 02:43) je v glavni veji 37 novih commitov, skupaj 215. Kot prej se projekt gradi s Claude Code (commiti nosijo Co-Authored-By: Claude), tokrat pa so vzporedno delale tri veje (D1, H8, priprava izdaje), zato vrstni red commitov ni strogo vrstni red dela. Številke so izračunane po isti metodi kot v prejšnjem besedilu (vse datoteke .rs zunaj vendor/ in fuzz/, funkcije #[test], naslovi ## ADR- v docs/DECISIONS.md) in preverjene na starem commitu, ki daje enake številke kot takrat.
| Trenutek | Commit | Čas | Vrstic Rusta | Testov | ADR-jev | Commitov |
|---|---|---|---|---|---|---|
| prejšnje besedilo | d21e3ec | 1. novembra, 20:41 | 112 408 | 926 | 126 | 178 |
| danes | f1a7fbc | 3. novembra, 02:43 | 115 148 | 962 | 133 | 215 |
Rast kode je skromna (+2 740 vrstic, +36 testov), ker tokrat ni bilo veliko nove kode: večina dela so testi, skripte, dokumentacija in preverjanja. Skripte in JavaScript niso všteti v število vrstic.
Šifriranje diska v mirovanju (S7)
Kaj smo načrtovali. Neobvezno šifriranje podatkovne particije, privzeto izklopljeno, z izmerjenim stroškom. Kaj smo zgradili (ADR-240):
- Plast pod diskom.
CryptDevovije blokovno napravo podDisk, zato razporeditev, tabela velikosti, SQLite, telemetrija in varnostne kopije delujejo nad odprtim besedilom in o šifriranju ne vedo nič. Algoritem je AES-256-XTS (IEEE 1619), en sektor po 512 bajtov na enoto podatkov, ne blok 4 KiB kot v načrtu: shramba piše po sektorjih, enota 4 KiB pa bi vsak zapis 512 bajtov spremenila v branje-spremembo-pisanje, ki ga izpad toka lahko prepolovi. - Superblok ostane odprt. Dve reži superbloka nosita zastavico in preverjanje ključa (HMAC-SHA-256 oznake in UUID-ja diska), nikoli ključa. Vse drugo v particiji je šifrirano. Sektor, ki je na mediju v celoti ničle, se prebere kot ničla in se ne dešifrira, zato discard, nikoli pisan prostor in rast diska delujejo kot prej.
- Konfiguracija.
storage.encryption = "aes-256-xts"instorage.disk_key(datoteka s 64 bajti ključa). Format bloba postane 4 samo ko je šifriranje vklopljeno, zato se vse druge slike še vedno zaženejo na starejših jedrih. Starejši bralnik zavrne neznano zastavico v superbloku. - Brez strojnega AES: zavrnitev. Jedro zavrne
storage.encryptionna procesorju brez ukazov AES (ali ko jih[cpu] disableizklopi), namesto da bi tiho poganjalo počasno programsko različico. - Pretvorba poteka brez zagnanega sistema. Jedro pri zagonu nikoli ne šifrira ali dešifrira diska.
urmqtt-disk encrypt,decryptinrekeyto naredijo z ugasnjenim VM-jem: zapis pretvorbe se napiše prvi in izbriše zadnji (dokler obstaja, jedro disk zavrne), kosi po 1 MiB se najprej shranijo v dnevnik na gostitelju, nato se zapiše pretvorjena kopija, superblok pa se preklopi zadnji. Preizkušeno s prekinitvijo napajanja po vsakem posameznem zapisu za vse tri postopke, čisto in napol zapisano, z odvrženim in ohranjenim predpomnilnikom: po--resumevedno veljaven disk z vsemi podatki.
Izmerjeno. Prepustnost XTS na jedro na Apple siliciju (en MiB sektorjev v zanki): 3 109 MB/s šifriranje in 3 103 MB/s dešifriranje s strojnim AES, v primerjavi z 252 in 258 MB/s programsko. Zato programske različice ne dovolimo. db-bench v VM-ju (aarch64, HVF, 5 000 vstavljanj v 50 transakcijah, pet zagonov vsake različice, izmenično):
| op/s, pet zagonov | mediana | |
|---|---|---|
| šifriranje izklopljeno | 49 239, 47 401, 50 802, 53 417, 46 531 | 49 239 |
| šifriranje vklopljeno | 57 790, 46 173, 45 939, 52 787, 49 632 | 49 632 |
Razlika median je +0,8 %, razpon znotraj ene različice (od 46 000 do 58 000) pa je večji od tega, zato strošek v tem testu ni merljiv. To tudi pričakujemo: celoten zagon db-bench zapiše okoli 6 MB, kar je okoli 2 ms AES-a, commit pa čaka na flush, ne na šifro.
Diagnostični vnos zdaj prikazuje tudi AES-XTS ob AES-GCM (na HVF jedro izmeri okoli 800 MiB/s za XTS proti okoli 560 MiB/s za AES-GCM) ter pokaže, ali je strojni AES na voljo.

Česa XTS ne da. Overjanja ni: spremenjen šifriran sektor se dešifrira v šum v blokih po 16 bajtov (CRC-ji in čistilec to najdejo), kdor lahko piše po nosilcu, pa lahko zamenja ali povrne cele sektorje med dvema posnetkoma. Ščiti ukraden nosilec ali posnetek, ne ukradenega ISO, ker je ključ v konfiguraciji slike. Ostane vidno: GPT, superblok, UUID in kateri sektorji so bili kdaj pisani.
Kaj smo med tem našli v lastnem delu. Ob pisanju poglavja o kriptografiji smo ugotovili dve stvari. Korak množenja tweaka v XTS je imel vejo, odvisno od bita, izpeljanega iz ključa; zdaj je brez veje. In trditev »preverjeno proti vektorju 10 iz IEEE 1619« je pokrivala samo prvi šifrirani blok; zdaj obstaja še naivna referenčna implementacija (aritmetika u128, blok za blokom), ki preverja cele sektorje v obeh smereh na 200 naključnih ključih in sektorjih, dokumentacija pa pove, kaj je bilo res preverjeno.
Zagon, ki zavrne popravljeno sliko (H8)
Zahteva z revizijskega seznama je bila preprosta in doslej nepreverjena: noben test ni pokazal, da se spremenjeno jedro ne zažene. Pripetje hasha v limine.conf je obstajalo, a brez dokaza.
- Test, ki spremeni bajt.
scripts/tamper.pyobrne en bit v vsaki kopiji jedra (ali konfiguracijskega modula) znotraj zgrajenega ISO-ja ali surove slike,SMOKE_ONLY=integritypa pokaže, da nalagalnik zavrne: 24 preverjanj uspešnih, na aarch64 UEFI, x86_64 UEFI in x86_64 BIOS, s kontrolami, ki se zaženejo. Sporočilo o zavrnitvi se bere s serijskih vrat prek stikala samo za teste. Na BIOS-u sporočilo pride le na zaslon, zato ta test trdi samo, da se stroj ne zažene. - Vpisana konfiguracija nalagalnika (ADR-250).
limine enroll-configdeluje naBOOTAA64.EFI,BOOTX64.EFIin stopnji BIOS, zato se zavrne tudi spremenjenlimine.conf(dodan vnos menija ali argument).urmqtt-config pack --isovpiše znova. - Pot Secure Boot (ADR-251).
scripts/sign-loaders.sh(preizkusni ključi, podpisovanje, preverjanje, shramba spremenljivk OVMF),mkiso.sh --sb-keyin skupinasecurebootna x86_64: podpisan ISO se zažene in ponovljivo zgradi, nepodpisan, podpisan z napačnim ključem in spremenjen po podpisu pa dobijoAccess Denied(10 preverjanj uspešnih). Limine tudi pod Secure Bootom še vedno zavrne spremenjeno jedro. - Pravila namestitvenega vnosa (ADR-253). Načrt je govoril o eni besedi v ukazni vrstici,
install. Produkcijsko jedro dejansko sprejme tri (install,live,diag), piše pa samoinstall. Test (9 preverjanj) to požene na treh strojih s praznim diskom, katerega hash se ne sme spremeniti. - Skrivnost slike (ADR-252). Glasno opozorilo, ko se produkcijska slika gradi zunaj CI-ja, in pregled slike, dnevnikov gradnje in serijskih dnevnikov za bajte ključev v številnih kodiranjih (11 preverjanj).
- TPM, samo ADR-254. Kaj bi pomenilo zapečatiti strežniški ključ v vTPM ali TPM 2.0. Priporočilo: tega ne graditi za izdajo, usmerjeno na VPS-je. Nismo preverjali, kateri ponudniki sploh ponujajo vTPM.
Kaj to NE pomeni (zapisano kot H8-W1 do H8-W6 v AUDIT.md): nameščen disk se pod Secure Bootom ne zažene, ker njegov konfiguracijski modul nima hasha (ADR-217); podpisanega ISO-ja ni mogoče naknadno konfigurirati (najprej konfigurirati, nato podpisati); pot BIOS nima podpisa; zaščite pred vrnitvijo na starejšo različico ni; na aarch64 programska oprema (firmware) tukaj Secure Boota ne uveljavlja, zato je preverjen samo podpis nalagalnika; projektnega ključa ni, zato slike izdaje za Secure Boot niso podpisane; in noben fizični stroj ni bil preizkušen.
Revizijski paket (H10)
docs/AUDIT.md je zdaj paket, ki ga zunanji revizor lahko uporablja brez vprašanj: sistem na eni strani, baza, ki ji zaupamo (89 612 vrstic lastnega Rusta v sliki, 67 povezanih tujih paketov), vhodi in njihovi razčlenjevalniki, matrika zaščit s testi (in zaščite brez testov), slabosti s statusom, tabela vsake omrežno dosegljive zanke in alokacije z mejo in testom, navodila za gradnjo in testiranje, predlagan obseg zunanje revizije v desetih področjih in seznam red team z 31 napadi in rezultati. SECURITY.md ima tabelo STRIDE po komponentah.
- Popravek prvega besedila. V besedilu z dne 29. oktobra piše, da ima vsak blok
unsafekomentar// SAFETY:, ki ga preverja lint. To ni bilo res: lint (deny(unsafe_code)) samo omejuje, kje sme bitiunsafe, komentarje pa je od H0 beležil generiran popis, ki je že takrat našel okoli 30 mest brez utemeljitve, ob reviziji pa jih je bilo 108 (ker so medtem prišle nove vrste mest, na primer atributa#[unsafe(no_mangle)]inlink_section). Zato je popis, ne lint, tisto, kar jih zdaj drži pri nič. - 889 mest
unsafe, vsako s pisno utemeljitvijo. 108, ki je niso imela (večinoma#[unsafe(no_mangle)]inlink_section, nekajunsafe fn), je dobilo komentar (napisal ga je agent AI, samo komentarji, jaz sem pregledal vzorec); generiran popis v CI-ju zabeleži vsako novo mesto brez utemeljitve. Utemeljitev je trditev, ki jo je treba pregledati, ne dokaz, in to je točka 5 predlaganega obsega. - Pet odprtih ugotovitev ostaja odprtih, zapisanih: ni fuzzinga razčlenjevalnikov PCI in virtio, ni testa, ki piše v tabele strani po zaklepu, čas izvajanja P-256, X25519 in Ed25519 ni bil izmerjen, ni fuzzinga namenoma izdelanih datotek SQLite (omejeno s H6), in 53 od 67 povezanih paketov so izjeme
cargo-vet, ne revizije.
Napake, ki smo jih našli sami
- Skrivnosti prometa TLS se niso brisale (H0-W2, odprto od prvega dne revizije): tabela zajetih skrivnosti je bila
Vec, ki sta garemoveinswap_removepremikala in pustila kopije. Zdaj je tabela po režah: reža se na mestu izbriše, ko se vzame, zavrže ali izrine, nič se ne premika. - Veja, odvisna od ključa, v XTS (zgoraj).
log-slow-floodna x86_64 je padal (»log ring full«), tudi na izvorni kodi v0.2.1. Vzrok ni bila okvara, temveč dimenzioniranje testa: scenarij je vsako jedro gnal na 25 ms, ADR-090 pa ga je meril na 50 ms. Na x86_64 je COM1 vhod/izhod vrat po bajtu ujet v past, eno jedro, ki piše v ponore, potrebuje okoli 9 ms na vrstico, tri jedra na 25 ms pa zahtevajo več kot sekundo dela na sekundo, zato se obroči napolnijo (zamišljen preliv). Test zdaj na x86_64 teče na 50 ms (uspešen, 30 od 30 v zagonu z osnovnimi scenariji); na aarch64 ostane 25 ms.- Združitev vej je pokvarila stare predloge. Ponovni vpis konfiguracije v H8 je zahteval končno oznako v
limine.conf, ki je predloge pred H8 nimajo, zato staurmqtt-config pack --isoin stran graditelja zavračala vsako izdajo pred H8, vključno z v0.2.x, ki jo stran ponuja. Ujeli so jo testi graditelja v brskalniku (4 od 100 jih je padlo), ne H8-ov lastni test. Popravljeno (predloga brez oznake se konfigurira kot prej) in dodan test, ki pade z izvirno napako, ko se popravek odstrani. - Naša dokumentacija je napačno trdila, da je bil x86_64 preverjen samo v emulatorju. X11 je zagnal produkcijsko sliko na pravem VPS-ju Contabo s KVM (4 vCPU AMD EPYC) z vzajemnim TLS pod obremenitvijo. Popravljeno povsod: x86_64 je tekel na pravem VPS-ju s KVM pri commitu H7 in v emulatorju, a koda, dodana po tem (šifriranje, celovitost zagona), na KVM ni tekla in ni bila preizkušena na fizični strojni opremi.
D1 in ponudba »vaša storitev kot unikernel«
Javni demo posrednik je zgrajen, a ne objavljen: konfiguracija (način live, ACL demo/{id}/#, navaden MQTT samo za most), skripta, ki naredi objavljen preizkusni CA in demo certifikate, most WebSocket-na-TCP (samo standardna knjižnica Pythona, omejitve povezav, hitrosti in velikosti, preverjanje izvora), stran »Preizkusite urMQTT v živo« z odjemalcem MQTT v navadnem JavaScriptu, runbook in model groženj. Preizkušeno na loopbacku in v QEMU (18 scenarijev, 26 testov mostu). Ni preizkušeno: pravi VPS, DNS, Caddy, slika vsebnika. Potrebuje ločen VPS in izrecno odobritev za javno izpostavitev.
Poleg tega je na spletišču objavljena tudi ponudba, Unikernel (15 jezikov, brez cen): prilagoditev vaše storitve, da teče kot unikernel (ocena izvedljivosti, prilagoditev, pilot, podpora), z urMQTT kot referenco. Besedilo izrecno navaja omejitve današnje osnove: IPv4, TCP samo kot strežnik, KVM, Rust naraven, C samo kot izoliran delavec.
Izdaja v0.3.0
Oznaka v0.3.0 je bila postavljena 3. novembra na commit f1a7fbc, delovni tok izdaje pa je uspel. Izdaja je označena kot pre-release in vsebuje razvojno jedro za aarch64, ISO in sliko, predlogo ISO (jedri za aarch64 in x86_64 ter prazna konfiguracija, kar urmqtt-config pack --iso in stran graditelja spremenita v vašo sliko), CycloneDX SBOM, SHA256SUMS in njegov podpis minisign ter atestacije izvora SLSA. Preverjeno po objavi, v praznem imeniku: podpis SHA256SUMS velja, vseh pet datotek se ujema s kontrolnimi vsotami, obe atestaciji (jedro in ISO s predikatom CycloneDX) uspeta, SBOM ima 221 komponent, predloga pa se da konfigurirati (pack --iso znova vpiše šest kopij nalagalnika). Podpisni ključ je še vedno preizkusni ključ.
Vrstni red je bil pomemben in ga je vredno zapisati: stran graditelja je bila posodobljena pred oznako (pozna format 4 in vse starejše predloge), šele nato je zrcalo na aldo.tech prevzelo izdajo. Zrcalo jo je navedlo kot podpisano in preverjeno (ok, format konfiguracije 4, sha256 predloge enak tistemu v SHA256SUMS), živa stran pa jo ponuja kot privzeto izdajo in v brskalniku naredi ISO v okoli 20 sekundah. Opombe k izdaji povedo tudi to, kar redko piše: CI na GitHubu ni zelen (glej spodaj), podpora je samo za zadnjo različico (v0.2.1 in starejše prenehajo), in da je revizijo opravil sam projekt.
Na spletišču smo med tem skoraj naredili sebi škodo: skripta za objavo strani (publish.sh) izvaja rsync --delete, zrcalo izdaj pa pripada drugemu uporabniku, zato se je vsaka objava končala z napako 23 (in bi tako pokvarila gumb »Objavi« v blog adminu). Popravljeno z izjemo za /urmqtt/releases/. Podobna ugotovitev: deploy.sh z --delete izbriše stran graditelja iz generatorja, zato jo je treba pred objavo vrniti.
Kaj še ni dokazano
- CI na GitHubu je rdeč. Ne samo danes: zadnji zagon polnega CI-ja pred oznako je imel sedem padlih opravil, zagoni nazaj do 31. oktobra pa so večinoma rdeči. Padli so: lint (opozorila shellcheck v skriptah), TLS interop (vsi primeri wolfSSL), dobavna veriga (seznam izjem
vet-exemptions.mdna Linuxu ni posodobljen), fuzz smoke (v tem opravilu manjkajo razvojni certifikati), sanitizerji (LeakSanitizer javlja puščanje v testih), testi na Linux aarch64 (en test orodja pade z »Broken pipe«) in en dokaz Kani za časovno kolo (timer wheel) ne uspe. Še nismo razvrstili, kaj od tega je okolje in kaj prava pomanjkljivost; dokler tega ne naredimo, ne trdimo, da je izdaja prestala CI. - Podpisni ključ je preizkusni, slike izdaje pa so razvojne s preizkusnimi certifikati (razen predloge).
- Prevode strani in tega besedila v preostalih 13 jezikov je napisala AI in jih niso preverili izvorni govorci.
- Šifriranje diska ni izmerjeno na x86_64 (niti na KVM niti na fizični strojni opremi), izmerjena pa tudi ni prepustnost telemetrije ali čas zagona s šifriranjem. Na strani graditelja ga ni (potrebuje pogovorno okno za ključ).
- Neodvisne varnostne revizije ni. Obseg je napisan; revizorja nimamo.
- Secure Boot je delen (zgoraj), brez fizičnega stroja.
- Bare metal, IOMMU, javni demo posrednik: ni narejeno ali ni objavljeno.
Kaj sledi
- Razvrstiti in popraviti CI (najprej okolje, nato dokaz Kani in test, ki pade na aarch64 Linuxu), ker izdaje brez zelenega CI-ja ne štejemo za dokončano.
- Odločitev o D1 (ločen VPS, DNS, odobritev za javno izpostavitev).
- Fuzzing razčlenjevalnikov PCI in virtio ter test zaklenjenih tabel strani (H0-W9, H0-W10).
- Pogovorno okno za ključ na strani graditelja, da šifriranje postane dostopno brez ukazne vrstice.
- Po izdaji: bare metal (X12 do X16) in IOMMU, v vrstnem redu, ki je odvisen od popisa strojne opreme testnega PC-ja.
Koda je na github.com/zeljan-alduk/urMQTT; docs/PROGRESS.md ostaja edini vir resnice o tem, kaj res deluje, docs/PERFORMANCE.md o tem, kaj je izmerjeno, in docs/AUDIT.md o tem, kar še ni dokazano. Pred-alfa, kot prej; prijave napak in varnostne prijave po navodilih v SECURITY.md so dobrodošle.
Komentarji
Pravila komentiranja