urMQTT v0.3.0: lemeztitkosítás, egy boot, amely elutasítja a módosított képet, és amit magunk rontottunk el
Mi történt az urMQTT-ről szóló harmadik írás óta: nyugalmi lemeztitkosítás mért költséggel, egy teszt, amely egyetlen bájtot átír a kernelben, és megmutatja, hogy a betöltő elutasítja, egy auditcsomag 889 megindokolt unsafe helyről, a hibák, amelyeket mi magunk találtunk (és egy állítás, amelyet tévesen írtunk le), valamint a v0.3.0 kiadás azzal a listával, hogy mi nincs még bizonyítva, a piros CI-jal együtt.

A harmadik írás, az urMQTT a VPS után, a „Mi következik" listával zárult: nyilvános demo bróker (D1), második kör a lassú egyetlen folyamra, a sima útvonal a Mosquitto ellen a VPS-en, és „a kiadás után" a bare metal, az IOMMU, a Secure Boot és a lemeztitkosítás. Ez az írás azt mondja el, hogy ebből mi készült el, mi nem, és mit találtunk közben. A lemeztitkosítás és a Secure Boot egy része végül még a kiadás előtt bekerült, mert az audithoz vezető úton álltak. A második kör az egyetlen folyamra és a sima útvonal a Mosquitto ellen nem készült el.
Az előzetes megjegyzés ugyanaz marad: a projekt pre-alpha, a kiadás KVM-es VPS-eket céloz, a számok pedig a kódot, a teszteket és bizonyos kísérleteket mérnek, nem az éles üzemi érettséget.
Hogyan nézett ki ez időben
Az előző írás óta (november 1., 20:41) mostanáig (november 3., 02:43) 37 új commit került a főágba, összesen 215. Mint korábban, a projekt Claude Code-dal készül (a commitok Co-Authored-By: Claude sort viselnek), és ezúttal három ág dolgozott párhuzamosan (D1, H8, a kiadás előkészítése), ezért a commitok sorrendje nem szigorúan a munka sorrendje. A számokat ugyanazzal a módszerrel számoltuk, mint az előző írásban (minden .rs fájl a vendor/ és a fuzz/ könyvtáron kívül, a #[test] függvények, a ## ADR- címsorok a docs/DECISIONS.md fájlban), és a régi commiton ellenőriztük, amely ugyanazokat a számokat adja, mint akkor.
| Időpont | Commit | Idő | Rust-sorok | Tesztek | ADR-ek | Commitok |
|---|---|---|---|---|---|---|
| előző írás | d21e3ec | nov. 1., 20:41 | 112 408 | 926 | 126 | 178 |
| ma | f1a7fbc | nov. 3., 02:43 | 115 148 | 962 | 133 | 215 |
A kód növekedése szerény (+2 740 sor, +36 teszt), mert ezúttal nem volt sok új kód: a munka nagy része tesztek, szkriptek, dokumentáció és ellenőrzések. A szkriptek és a JavaScript nem számítanak bele a sorok számába.
Nyugalmi lemeztitkosítás (S7)
Mit terveztünk. Az adatpartíció opcionális titkosítása, alapból kikapcsolva, mért költséggel. Mit építettünk (ADR-240):
- Egy réteg a lemez alatt. A
CryptDeva blokkeszközt burkolja aDiskalatt, így az elrendezés, a méretek táblája, az SQLite, a telemetria és a biztonsági mentések nyílt szövegen dolgoznak, és nem tudnak a titkosításról. Az algoritmus AES-256-XTS (IEEE 1619), adategységenként egy 512 bájtos szektor, nem 4 KiB-os blokk, ahogy a tervben szerepelt: a tároló szektorokban ír, és egy 4 KiB-os egység minden 512 bájtos írást olvasás-módosítás-írássá változtatna, amelyet egy áramkimaradás kettétörhet. - A szuperblokk nyílt marad. A szuperblokk két slotja egy jelzőbitet és egy kulcsellenőrzést hordoz (egy címke és a lemez UUID-jának HMAC-SHA-256 értéke), soha nem a kulcsot. A partíción minden más titkosított. Az a szektor, amely az adathordozón teljesen nulla, nullaként olvasódik, és nem fejtődik vissza, így a discard, a soha nem írt terület és a lemez növelése a korábbiak szerint működik.
- Konfiguráció.
storage.encryption = "aes-256-xts"ésstorage.disk_key(egy fájl 64 bájt kulccsal). A blob formátuma csak bekapcsolt titkosításnál lesz 4-es, így minden más kép továbbra is elindul régebbi kerneleken. A régebbi olvasó elutasítja a szuperblokk ismeretlen jelzőbitjét. - Hardveres AES nélkül: elutasítás. A kernel elutasítja a
storage.encryptionbeállítást AES-utasítások nélküli processzoron (vagy ha a[cpu] disablekikapcsolja őket), ahelyett, hogy csendben egy lassú szoftveres változatot futtatna. - Az átalakítás offline. A kernel indításkor soha nem titkosít és nem fejt vissza lemezt. Ezt az
urmqtt-disk encrypt,decryptésrekeyvégzi kikapcsolt VM mellett: az átalakítási rekordot írjuk először és töröljük utoljára (amíg létezik, a kernel elutasítja a lemezt), az 1 MiB-os darabokat előbb a gazdagépen lévő naplóba mentjük, majd az átalakított másolatot írjuk, a szuperblokkot pedig utoljára kapcsoljuk át. Mindhárom eljárásnál áramkimaradással minden egyes írás után kipróbálva, tisztán és félig kiírva, eldobott és megtartott gyorsítótárral:--resumeután mindig érvényes lemez, minden adattal.
Mérés. Az XTS áteresztőképessége magonként Apple siliconon (egy MiB szektor egy ciklusban): 3 109 MB/s titkosítás és 3 103 MB/s visszafejtés hardveres AES-szel, szemben a szoftveres 252 és 258 MB/s-mal. Ezért a szoftvert nem engedjük. A db-bench egy VM-ben (aarch64, HVF, 5 000 beszúrás 50 tranzakcióban, mindkét változatból öt futás, felváltva):
| op/s, öt futás | medián | |
|---|---|---|
| titkosítás kikapcsolva | 49 239, 47 401, 50 802, 53 417, 46 531 | 49 239 |
| titkosítás bekapcsolva | 57 790, 46 173, 45 939, 52 787, 49 632 | 49 632 |
A mediánok különbsége +0,8 %, az egy változaton belüli szórás (46 000-től 58 000-ig) pedig nagyobb ennél, ezért a költség ebben a tesztben nem mérhető. Ez várható is: a teljes db-bench futás nagyjából 6 MB-ot ír, ami nagyjából 2 ms AES, a commit pedig a flushra vár, nem a titkosításra.
A Diagnosztikai bejegyzés most az AES-GCM mellett az AES-XTS sort is mutatja (HVF-en a kernel nagyjából 800 MiB/s-ot mér XTS-re, szemben a nagyjából 560 MiB/s-os AES-GCM-mel), és azt is, hogy van-e hardveres AES.

Amit az XTS nem ad. Nincs hitelesítés: a módosított titkosított szektor 16 bájtos blokkokban zajjá fejtődik vissza (ezt a CRC-k és a takarító megtalálják), aki pedig írhat a kötetre, két pillanatkép között egész szektorokat cserélhet vagy állíthat vissza. Az ellopott kötetet vagy pillanatképet védi, nem az ellopott ISO-t, mert a kulcs a kép konfigurációjában van. Látható marad: a GPT, a szuperblokk, az UUID és az, hogy mely szektorokat írták valaha.
Amit közben a saját munkánkban találtunk. A kriptográfiáról szóló fejezet írása közben két dolgot állapítottunk meg. Az XTS tweak szorzási lépésének volt egy a kulcsból származtatott bittől függő ága; most ágmentes. A „IEEE 1619 10. vektorával ellenőrizve" állítás pedig csak az első titkosított blokkot fedte le; most van egy naiv referenciaimplementáció is (u128 aritmetika, blokkonként), amely egész szektorokat ellenőriz mindkét irányban 200 véletlen kulcson és szektoron, a dokumentáció pedig azt írja, mit ellenőriztünk valójában.
Boot, amely elutasítja a módosított képet (H8)
Az audit listáról származó követelmény egyszerű volt és eddig ellenőrizetlen: egyetlen teszt sem mutatta meg, hogy a módosított kernel nem indul el. A hash-rögzítés a limine.conf fájlban megvolt, de bizonyíték nélkül.
- Egy teszt, amely átír egy bájtot. A
scripts/tamper.pymegfordít egy bitet a kernel (vagy a konfigurációs modul) minden másolatában egy elkészített ISO-n vagy raw képen belül, aSMOKE_ONLY=integritypedig megmutatja, hogy a betöltő elutasítja: 24 ellenőrzés ment át aarch64 UEFI-n, x86_64 UEFI-n és x86_64 BIOS-on, olyan kontrollokkal, amelyek elindulnak. Az elutasítás üzenetét a soros portról olvassuk egy csak tesztekre szánt kapcsolón keresztül. BIOS-on az üzenet csak a képernyőre jut el, ezért az a teszt csak azt állítja, hogy a gép nem indul el. - A betöltő konfigurációja beírva (ADR-250). A
limine enroll-configaBOOTAA64.EFI, aBOOTX64.EFIés a BIOS-szakasz esetén működik, így a módosítottlimine.conf(hozzáadott menübejegyzés vagy argumentum) is elutasításra kerül. Azurmqtt-config pack --isoújra beírja. - A Secure Boot útvonal (ADR-251).
scripts/sign-loaders.sh(próbakulcsok, aláírás, ellenőrzés, OVMF változótár),mkiso.sh --sb-keyés asecurebootcsoport x86_64-en: az aláírt ISO elindul és reprodukálhatóan épül, az aláíratlan, a rossz kulccsal aláírt és az aláírás után módosított pedigAccess Deniedválaszt kap (10 ellenőrzés ment át). A Limine Secure Boot alatt is elutasítja a módosított kernelt. - A telepítő bejegyzés szabályai (ADR-253). A terv a parancssorban egyetlen szóról beszélt, az
install-ról. Az éles kernel valójában hármat fogad el (install,live,diag), és csak azinstallír. A teszt (9 ellenőrzés) három gépen futtatja ezt üres lemezzel, amelynek a hash-e nem változhat. - A kép titkossága (ADR-252). Hangos figyelmeztetés, ha éles képet a CI-on kívül építenek, és a kép, az építési naplók és a soros naplók átvizsgálása kulcsbájtok után sok kódolásban (11 ellenőrzés).
- TPM, csak ADR-254. Mit jelentene a szerverkulcs vTPM-hez vagy TPM 2.0-hoz pecsételése. Javaslat: ne építsük meg a VPS-re irányuló kiadáshoz. Nem ellenőriztük, hogy egyáltalán mely szolgáltatók kínálnak vTPM-et.
Mit NEM jelent ez (az AUDIT.md fájlban H8-W1-től H8-W6-ig rögzítve): a telepített lemez nem indul el Secure Boot alatt, mert a konfigurációs moduljának nincs hash-e (ADR-217); az aláírt ISO utólag nem konfigurálható (előbb konfigurálni, aztán aláírni); a BIOS útvonalnak nincs aláírása; nincs védelem a régebbi verzióra való visszaállítás ellen; aarch64-en a firmware itt nem kényszeríti ki a Secure Boot-ot, ezért csak a betöltő aláírását ellenőriztük; nincs projektkulcs, ezért a kiadási képek Secure Bootra nézve aláíratlanok; és fizikai gépen semmit sem próbáltunk ki.
Az auditcsomag (H10)
A docs/AUDIT.md most olyan csomag, amelyet egy külső auditor kérdések nélkül használhat: a rendszer egy oldalon, az alap, amelyben megbízunk (89 612 sor saját Rust a képben, 67 hozzákapcsolt külső csomag), a bemenetek és a parsereik, a védelmek mátrixa a tesztjeikkel (és a teszt nélküli védelmek), a gyengeségek státusszal, a hálózatról elérhető minden ciklus és allokáció táblázata korláttal és teszttel, építési és tesztelési útmutató, egy külső audit javasolt hatóköre tíz területen, és egy red-team lista 31 támadással és eredményekkel. A SECURITY.md komponensenkénti STRIDE táblázatot tartalmaz.
- Az első írás helyesbítése. Az október 29-i szöveg azt írja, hogy minden
unsafeblokknak van// SAFETY:megjegyzése, amelyet egy lint ellenőriz. Ez nem volt igaz: a lint (deny(unsafe_code)) csak azt korlátozza, hol szerepelhetunsafe, a megjegyzéseket pedig a H0 óta egy generált leltár követi, amely már akkor is körülbelül 30 indoklás nélküli helyet talált, az auditkor pedig 108-at (mert közben új fajta helyek jelentek meg, például a#[unsafe(no_mangle)]és alink_sectionattribútumok). Ezért most a leltár, nem a lint tartja őket nulla körül. - 889
unsafehely, mindegyik írásos indoklással. A 108, amelynek nem volt (főleg#[unsafe(no_mangle)]éslink_section, néhányunsafe fn), megjegyzést kapott (egy AI-ügynök írta, csak megjegyzések, én egy mintát átnéztem); a CI-ban a generált leltár minden új, indoklás nélküli helyet rögzít. Az indoklás egy állítás, amelyet át kell nézni, nem bizonyíték, és ez a javasolt hatókör 5. pontja. - Öt nyitott lelet nyitott marad, leírva: nincs fuzzing a PCI és virtio parserekre, nincs teszt, amely a lapozótáblába ír a zárolás után, a P-256, az X25519 és az Ed25519 futási idejét nem mértük, nincs fuzzing a szándékosan kialakított SQLite fájlokra (a H6 korlátozza), és a 67 hozzákapcsolt csomagból 53
cargo-vetkivétel, nem audit.
Hibák, amelyeket mi magunk találtunk
- A TLS-forgalom titkait nem töröltük (H0-W2, az audit első napja óta nyitott): a rögzített titkok táblája egy
Vecvolt, amelyet aremoveés aswap_removemozgatott, és másolatokat hagyott. Most a tábla slotalapú: a slot a helyén törlődik, amikor kivesszük, eldobjuk vagy kiszorítjuk, és semmi sem mozog. - Kulcsfüggő ág az XTS-ben (fent).
- A
log-slow-floodx86_64-en elbukott („log ring full"), a v0.2.1 forráson is. Az ok nem hiba volt, hanem a teszt méretezése: a forgatókönyv minden magot 25 ms-ra hajtott, az ADR-090 viszont 50 ms-on mérte. x86_64-en a COM1 bájtonként csapdázott port-I/O, egy mag, amely a nyelőkbe ír, soronként körülbelül 9 ms-ot igényel, három mag 25 ms-on pedig másodpercenként több mint egy másodpercnyi munkát kér, így a gyűrűk megtelnek (a szándékolt túlcsordulás). A teszt most x86_64-en 50 ms-on fut (átment, 30 a 30-ból az alapforgatókönyveket is tartalmazó futásban); aarch64-en 25 ms marad. - Az ágak egyesítése tönkretette a régi sablonokat. A H8 újrabeírása egy záró jelölőt követelt a
limine.conffájlban, amely a H8 előtti sablonokban nincs meg, ezért azurmqtt-config pack --isoés az építő oldal minden H8 előtti kiadást elutasított, a v0.2.x-et is, amelyet az oldal kínál. A böngészőben futó építő tesztek fogták meg (100-ból 4 bukott), nem a H8 saját tesztje. Javítva (a jelölő nélküli sablon a korábbiak szerint konfigurálódik), és hozzáadtunk egy tesztet, amely az eredeti hibával bukik, ha a javítást kivesszük. - A dokumentációnk tévesen állította, hogy az x86_64-et csak emulátorban ellenőriztük. Az X11 az éles képet egy valódi Contabo KVM VPS-en (4 vCPU AMD EPYC) futtatta terhelés alatt, kölcsönös TLS-sel. Mindenhol javítva: az x86_64 a H7 commitnál valódi KVM VPS-en és emulátorban is futott, de az utána hozzáadott kód (titkosítás, boot-integritás) nem futott KVM-en, és fizikai hardveren sem próbáltuk ki.
D1 és az „a ti szolgáltatásotok unikernelként" ajánlat
A nyilvános demo bróker megépült, de nincs közzétéve: a konfiguráció (live mód, demo/{id}/# ACL, sima MQTT csak a hídhoz), egy szkript, amely közzétett próba-CA-t és demo tanúsítványokat készít, egy WebSocket-TCP híd (csak a Python standard könyvtára, korlátok a kapcsolatokra, a sebességre és a méretre, eredetellenőrzés), egy „Próbáld ki az urMQTT-t élőben" oldal sima JavaScriptben írt MQTT-klienssel, egy runbook és egy fenyegetettségi modell. Loopbacken és QEMU-ban tesztelve (18 forgatókönyv, 26 hídteszt). Nem tesztelt: valódi VPS, DNS, Caddy, a konténerkép. Külön VPS-t és kifejezett jóváhagyást igényel a nyilvános kitettséghez.
Emellett az oldalon közzétettünk egy ajánlatot is, az Unikernel oldalt (15 nyelven, árak nélkül): a ti szolgáltatásotok átalakítása úgy, hogy unikernelként fusson (megvalósíthatósági felmérés, átalakítás, pilot, támogatás), az urMQTT-vel mint referenciával. A szöveg kifejezetten megnevezi a mai alap korlátait: IPv4, a TCP csak szerverként, KVM, a Rust a természetes választás, a C csak elszigetelt workerként.
A v0.3.0 kiadás
A v0.3.0 címkét november 3-án tettük az f1a7fbc commitra, és a kiadási workflow sikeres volt. A kiadás pre-release jelölést kapott, és tartalmazza a fejlesztői aarch64 kernelt, ISO-t és képet, a sablon-ISO-t (kernelek aarch64-re és x86_64-re, valamint üres konfiguráció, amelyből az urmqtt-config pack --iso és az építő oldal a ti képeteket készíti), egy CycloneDX SBOM-ot, a SHA256SUMS fájlt és annak minisign aláírását, valamint SLSA eredetigazolásokat. A közzététel után, üres könyvtárban ellenőrizve: a SHA256SUMS aláírása érvényes, mind az öt fájl egyezik az ellenőrző összegekkel, mindkét igazolás (a kernelé, valamint az ISO-é CycloneDX predikátummal) átmegy, az SBOM 221 komponenst tartalmaz, a sablon pedig konfigurálható (a pack --iso a betöltő hat másolatát írja be újra). Az aláíró kulcs továbbra is próbakulcs.
A sorrend számított, és érdemes leírni: az építő oldalt a címke előtt frissítettük (ismeri a 4-es formátumot és az összes régebbi sablont), és csak ezután vette át a kiadást az aldo.tech tükre. A tükör aláírtként és ellenőrzöttként listázta (ok, 4-es konfigurációs formátum, a sablon sha256 értéke megegyezik a SHA256SUMS fájlbelivel), az élő oldal pedig alapértelmezett kiadásként kínálja, és a böngészőben nagyjából 20 másodperc alatt készít ISO-t. A kiadási jegyzetek azt is megmondják, amit ritkán írnak le: a GitHubon a CI nem zöld (lásd lent), csak a legutóbbi verzió támogatott (a v0.2.1 és a régebbiek megszűnnek), és az auditot maga a projekt végezte.
Az oldalon közben majdnem kárt tettünk magunknak: az oldal közzétételi szkriptje (publish.sh) rsync --delete parancsot futtat, a kiadási tükör pedig egy másik felhasználóé, így minden közzététel 23-as hibával végződött (és elrontotta volna a „Közzététel" gombot a blog adminban). Javítva a /urmqtt/releases/ kizárásával. Hasonló lelet: a deploy.sh a --delete miatt törli az építő oldalt a generátorból, ezért a közzététel előtt vissza kell tenni.
Mi nincs még bizonyítva
- A GitHub CI piros. Nemcsak ma: a címke előtti utolsó teljes CI-futásnak hét bukott jobja volt, és a visszamenő futások október 31-ig többnyire pirosak. Ezek bukták el: lint (shellcheck-figyelmeztetések a szkriptekben), TLS interop (az összes wolfSSL-eset), az ellátási lánc (a
vet-exemptions.mdkivétellista nem naprakész Linuxon), fuzz smoke (a jobból hiányoznak a fejlesztői tanúsítványok), a sanitizerek (a LeakSanitizer szivárgást jelent a tesztekben), a tesztek Linux aarch64-en (az eszköz egy tesztje „Broken pipe" hibával bukik) és az időzítőkerékre (timer wheel) vonatkozó egyik Kani-bizonyítás nem megy át. Még nem válogattuk szét, hogy ezek közül mi a környezet és mi a valódi hiba; amíg ezt nem tesszük meg, nem állítjuk, hogy a kiadás átment a CI-n. - Az aláíró kulcs próbakulcs, a kiadási képek pedig fejlesztői buildek próbatanúsítványokkal (a sablon kivételével).
- A fordításokat az oldalakról és erről az írásról a többi 13 nyelvre AI írta, és anyanyelvi beszélők nem ellenőrizték őket.
- A lemeztitkosítást nem mértük x86_64-en (sem KVM-en, sem fizikai hardveren), és sem a telemetria áteresztőképességét, sem a titkosítással mért indulási időt nem mértük. Az építő oldalon nincs rajta (kulcsmegadó párbeszédablak kell hozzá).
- Független biztonsági audit nincs. A hatókör megvan írva; auditorunk nincs.
- A Secure Boot részleges (fent), fizikai gép nélkül.
- Bare metal, IOMMU, nyilvános demo bróker: nincs kész vagy nincs közzétéve.
Mi következik
- A CI szétválogatása és javítása (előbb a környezet, aztán a Kani-bizonyítás és a teszt, amely aarch64 Linuxon bukik), mert a zöld CI nélküli kiadást nem tekintjük késznek.
- Döntés a D1-ről (külön VPS, DNS, jóváhagyás a nyilvános kitettséghez).
- A PCI és virtio parserek fuzzingja és a zárolt lapozótáblák tesztje (H0-W9, H0-W10).
- Kulcsmegadó párbeszédablak az építő oldalon, hogy a titkosítás parancssor nélkül is elérhető legyen.
- A kiadás után: bare metal (X12-től X16-ig) és IOMMU, olyan sorrendben, amely a tesztgép hardverleltárától függ.
A kód a github.com/zeljan-alduk/urMQTT oldalon van; a docs/PROGRESS.md továbbra is az egyetlen igazságforrás arról, hogy mi működik ténylegesen, a docs/PERFORMANCE.md arról, hogy mit mértünk, a docs/AUDIT.md pedig arról, hogy mi nincs még bizonyítva. Pre-alpha, mint eddig; a hibajelentéseket és a biztonsági bejelentéseket a SECURITY.md útmutatása szerint szívesen fogadjuk.
Hozzászólások
Hozzászólási szabályok