urMQTT v0.3.0: šifriranje diska, boot koji odbija prepravljenu sliku i ono što smo sami pokvarili
Što se dogodilo nakon trećeg teksta o urMQTT-u: šifriranje diska u mirovanju s izmjerenim troškom, test koji mijenja jedan bajt u kernelu i pokazuje da ga loader odbija, revizijski paket s 889 opravdanih unsafe mjesta, greške koje smo sami pronašli (i jednu tvrdnju koju smo krivo napisali), te izdanje v0.3.0 uz popis onoga što još nije dokazano, uključujući crveni CI.

Treći tekst, urMQTT nakon VPS-a, završio je popisom "Što slijedi": javni demo broker (D1), druga runda za spor jedan tok, običan put protiv Mosquitta na VPS-u i, "poslije izdanja", bare metal, IOMMU, Secure Boot i šifriranje diska. Ovaj tekst kaže što je od toga napravljeno, što nije, i što smo usput pronašli. Šifriranje diska i dio Secure Boota ipak su ušli prije izdanja, jer su bili na putu prema reviziji. Druga runda za jedan tok i običan put protiv Mosquitta nisu napravljeni.
Napomena unaprijed ostaje ista: projekt je pred-alfa, izdanje cilja VPS-ove s KVM-om, a brojke mjere kod, testove i određene pokuse, ne zrelost u produkciji.
Kako je to izgledalo u vremenu
Od prošlog teksta (1.11. u 20:41) do danas (3.11. u 02:43) u glavnoj grani je 37 novih commitova, ukupno 215. Kao i prije, projekt se gradi uz Claude Code (commitovi nose Co-Authored-By: Claude), a ovaj put su tri grane radile paralelno (D1, H8, priprema izdanja) pa redoslijed commitova nije strogo redoslijed rada. Brojke su izračunate istom metodom kao u prošlom tekstu (svi .rs fajlovi izvan vendor/ i fuzz/, #[test] funkcije, naslovi ## ADR- u docs/DECISIONS.md) i provjerene na starom commitu, koji daje iste brojke kao tada.
| Trenutak | Commit | Vrijeme | Rust redaka | Testova | ADR-ova | Commitova |
|---|---|---|---|---|---|---|
| prošli tekst | d21e3ec | 1.11. 20:41 | 112 408 | 926 | 126 | 178 |
| danas | f1a7fbc | 3.11. 02:43 | 115 148 | 962 | 133 | 215 |
Rast koda je skroman (+2 740 redaka, +36 testova), jer ovaj put nije bilo mnogo novog koda: većina rada su testovi, skripte, dokumentacija i provjere. Skripte i JavaScript ne ulaze u brojku redaka.
Šifriranje diska u mirovanju (S7)
Što smo planirali. Neobavezno šifriranje podatkovne particije, isključeno zadano, s izmjerenim troškom. Što smo izgradili (ADR-240):
- Sloj ispod diska.
CryptDevomata blok uređaj ispodDisk, pa raspored, tablica veličina, SQLite, telemetrija i sigurnosne kopije rade nad otvorenim tekstom i ne znaju za šifriranje. Algoritam je AES-256-XTS (IEEE 1619), jedan sektor od 512 bajta po jedinici podataka, a ne 4 KiB blok kao u planu: pohrana piše u sektorima, a jedinica od 4 KiB pretvorila bi svaki zapis od 512 bajta u čitanje-izmjenu-pisanje koje prekid može prepoloviti. - Superblok ostaje otvoren. Dva slota superbloka nose zastavicu i provjeru ključa (HMAC-SHA-256 od oznake i UUID-a diska), nikad ključ. Sve ostalo u particiji je šifrirano. Sektor koji je na mediju potpuno nula čita se kao nula i ne dešifrira se, pa discard, nikad pisan prostor i rast diska rade kao prije.
- Konfiguracija.
storage.encryption = "aes-256-xts"istorage.disk_key(datoteka s 64 bajta ključa). Format blob-a postaje 4 samo kad je šifriranje uključeno, pa se sve ostale slike i dalje pokreću na starijim kernelima. Stariji čitač odbija nepoznatu zastavicu u superbloku. - Bez sklopovskog AES-a: odbijanje. Kernel odbija
storage.encryptionna procesoru bez AES instrukcija (ili kad ih[cpu] disableisključi), umjesto da tiho radi sporu softversku inačicu. - Pretvorba je izvanmrežna. Kernel nikad ne šifrira niti dešifrira disk pri pokretanju.
urmqtt-disk encrypt,decryptirekeyrade to s ugašenim VM-om: zapis pretvorbe se piše prvi i briše zadnji (dok postoji, kernel odbija disk), komadi od 1 MiB se prvo spremaju u dnevnik na domaćinu, pa se upisuje pretvorena kopija, a superblok se prebacuje posljednji. Isprobano prekidom napajanja nakon svakog pojedinog zapisa za sva tri postupka, čisto i poluzapisano, s ispuštenim i zadržanim predmemorijom: uvijek valjan disk sa svim podacima nakon--resume.
Izmjereno. Propusnost XTS-a po jezgri na Apple siliciju (jedan MiB sektora u petlji): 3 109 MB/s šifriranje i 3 103 MB/s dešifriranje sa sklopovskim AES-om, naspram 252 i 258 MB/s u softveru. Zato softver ne dopuštamo. db-bench u VM-u (aarch64, HVF, 5 000 umetanja u 50 transakcija, pet pokretanja svake varijante, naizmjenično):
| op/s, pet pokretanja | medijan | |
|---|---|---|
| šifriranje isključeno | 49 239, 47 401, 50 802, 53 417, 46 531 | 49 239 |
| šifriranje uključeno | 57 790, 46 173, 45 939, 52 787, 49 632 | 49 632 |
Razlika medijana je +0,8 %, a raspon unutar jedne varijante (46 000 do 58 000) veći je od toga, pa trošak nije mjerljiv u ovom testu. To je i očekivano: db-bench u cijelom pokretanju piše oko 6 MB, što je oko 2 ms AES-a, a commit čeka flush, ne šifru.
U Dijagnostičkom unosu sada piše i AES-XTS uz AES-GCM (na HVF-u jezgra mjeri oko 800 MiB/s za XTS prema oko 560 MiB/s za AES-GCM) te pokazuje je li sklopovski AES dostupan.

Što XTS ne daje. Nema autentikacije: izmijenjen šifrirani sektor dešifrira se u šum u blokovima od 16 bajta (CRC-ovi i čistač to nalaze), a netko tko može pisati po volumenu može zamijeniti ili vratiti cijele sektore između dvije snimke. Štiti ukradeni volumen ili snimku, ne ukradeni ISO, jer je ključ u konfiguraciji slike. Ostaje vidljivo: GPT, superblok, UUID i koji su sektori ikad pisani.
Što smo usput pronašli u vlastitom radu. Pisanjem poglavlja o kriptografiji ustanovili smo dvije stvari. Korak množenja tweaka u XTS-u imao je granu ovisnu o bitu koji je izveden iz ključa; sada je bez grane. I tvrdnja "provjereno prema IEEE 1619 vektoru 10" pokrivala je samo prvi šifrirani blok; sad još postoji i naivna referentna implementacija (aritmetika u128, blok po blok) koja provjerava cijele sektore u oba smjera na 200 nasumičnih ključeva i sektora, a dokumentacija kaže što je stvarno provjereno.
Boot koji odbija prepravljenu sliku (H8)
Zahtjev iz revizijskog popisa bio je jednostavan i do sada neprovjeren: nijedan test nije pokazivao da se izmijenjen kernel ne pokreće. Pin hash-a u limine.conf postojao je, ali bez dokaza.
- Test koji mijenja bajt.
scripts/tamper.pyokrene jedan bit u svakoj kopiji kernela (ili konfiguracijskog modula) unutar izgrađenog ISO-a ili raw slike, paSMOKE_ONLY=integritypokazuje da loader odbija: 24 provjere prošle, na aarch64 UEFI, x86_64 UEFI i x86_64 BIOS, uz kontrole koje se pokreću. Poruka odbijanja čita se sa serijskog porta preko prekidača samo za testove. Na BIOS-u poruka stiže samo na ekran, pa taj test tvrdi samo da se stroj ne pokreće. - Upisana konfiguracija loadera (ADR-250).
limine enroll-configradi naBOOTAA64.EFI,BOOTX64.EFIi BIOS stupnju, pa se i izmijenjenlimine.conf(dodan unos izbornika ili argument) odbija.urmqtt-config pack --isoponovno upisuje. - Secure Boot putanja (ADR-251).
scripts/sign-loaders.sh(probni ključevi, potpisivanje, provjera, OVMF pohrana varijabli),mkiso.sh --sb-keyi grupasecurebootna x86_64: potpisan ISO se pokreće i ponovljivo gradi, a nepotpisan, potpisan krivim ključem i izmijenjen nakon potpisa dobivajuAccess Denied(10 provjera prošlo). Limine i dalje odbija izmijenjen kernel pod Secure Bootom. - Pravila instalacijskog unosa (ADR-253). Plan je govorio o jednoj riječi na naredbenom retku,
install. Produkcijski kernel zapravo prihvaća tri (install,live,diag), a piše samoinstall. Test (9 provjera) pokreće to na tri stroja s praznim diskom čiji se hash ne smije promijeniti. - Tajnost slike (ADR-252). Glasno upozorenje kad se produkcijska slika gradi izvan CI-ja, i skeniranje slike, logova gradnje i serijskih logova za bajtove ključeva u mnogim kodiranjima (11 provjera).
- TPM, samo ADR-254. Što bi značilo zapečatiti ključ poslužitelja u vTPM ili TPM 2.0. Preporuka: ne graditi za izdanje usmjereno na VPS. Nismo provjeravali koji pružatelji uopće nude vTPM.
Što to NE znači (zapisano kao H8-W1 do H8-W6 u AUDIT.md): instaliran disk se ne pokreće pod Secure Bootom jer njegov konfiguracijski modul nema hash (ADR-217); potpisan ISO se ne može naknadno konfigurirati (konfigurirati pa potpisati); BIOS put nema potpis; nema zaštite od vraćanja na stariju verziju; na aarch64 firmware ovdje ne provodi Secure Boot, pa je provjeren samo potpis učitača; nema projektnog ključa, pa su slike izdanja nepotpisane za Secure Boot; i nijedan fizički stroj nije probano.
Revizijski paket (H10)
docs/AUDIT.md je sada paket koji vanjski revizor može koristiti bez pitanja: sustav na jednoj stranici, baza kojoj vjerujemo (89 612 redaka vlastitog Rusta u slici, 67 povezanih tuđih paketa), ulazi i njihovi parseri, matrica zaštita s testovima (i zaštite bez testova), slabosti sa statusom, tablica svake mrežno dostupne petlje i alokacije s granicom i testom, upute za gradnju i testiranje, predloženi opseg vanjske revizije u deset područja i red-team popis s 31 napadom i rezultatima. SECURITY.md ima STRIDE tablicu po komponenti.
- Ispravak prvog teksta. U tekstu od 29.10. piše da svaki
unsafeblok ima komentar// SAFETY:koji provjerava lint. To nije bilo točno: lint (deny(unsafe_code)) samo ograničava gdjeunsafesmije biti, a komentare je od H0 bilježio generirani popis, koji je već tada našao oko 30 mjesta bez obrazloženja, a pri reviziji njih 108 (jer su u međuvremenu došle nove vrste mjesta, na primjer atributi#[unsafe(no_mangle)]ilink_section). Zato je popis, a ne lint, ono što ih sada drži na nuli. - 889 mjesta
unsafe, svako s pisanim obrazloženjem. 108 koja ga nisu imala (uglavnom#[unsafe(no_mangle)]ilink_section, nekolikounsafe fn) dobila su komentar (napisao ga je AI agent, samo komentari, ja sam pregledao uzorak); generirani popis u CI-ju bilježi svako novo mjesto bez obrazloženja. Obrazloženje je tvrdnja koju treba pregledati, ne dokaz, i to je točka 5 predloženog opsega. - Pet otvorenih nalaza ostaje otvoreno, zapisano: nema fuzzinga PCI i virtio parsera, nema testa koji piše u tablicu stranica nakon zaključavanja, vrijeme izvođenja P-256, X25519 i Ed25519 nije mjereno, nema fuzzinga namjerno izrađenih SQLite datoteka (ograničeno H6), i 53 od 67 povezanih paketa su
cargo-vetiznimke, ne revizije.
Greške koje smo sami pronašli
- Tajne TLS prometa nisu se brisale (H0-W2, otvoreno od prvog dana revizije): tablica snimljenih tajni bila je
Veckoji suremoveiswap_removepomicali i ostavljali kopije. Sad je tablica po slotovima: slot se briše na mjestu kad se uzme, odbaci ili istisne, ništa se ne pomiče. - Grana ovisna o ključu u XTS-u (gore).
log-slow-floodna x86_64 padao je ("log ring full"), i na izvornom kodu v0.2.1. Uzrok nije kvar, nego dimenzioniranje testa: scenarij je tjerao svaku jezgru na 25 ms, a ADR-090 ga je mjerio na 50 ms. Na x86_64 je COM1 port-I/O po bajtu uhvaćen u zamku, jedna jezgra koja piše u sinkove treba oko 9 ms po retku, a tri jezgre na 25 ms traže više od sekunde posla u sekundi, pa se prstenovi pune (zamišljeno preljevanje). Test sada na x86_64 radi na 50 ms (prošao, 30 od 30 pri pokretanju s osnovnim scenarijima); na aarch64 ostaje 25 ms.- Spajanje grana je slomilo stare predloške. Ponovni upis konfiguracije u H8 zahtijevao je završni marker u
limine.conf, kojeg predlošci prije H8 nemaju, pa suurmqtt-config pack --isoi stranica graditelja odbijali svako izdanje prije H8, uključujući v0.2.x koje stranica nudi. Uhvatili su ga testovi graditelja u pregledniku (4 od 100 palo), a ne H8-ov vlastiti test. Popravljeno (predložak bez markera konfigurira se kao prije) i dodan test koji pada s izvornom greškom kad se popravak makne. - Naša dokumentacija je krivo tvrdila da je x86_64 provjeren samo u emulatoru. X11 je pokrenuo produkcijsku sliku na pravom Contabo KVM VPS-u (4 vCPU AMD EPYC) s uzajamnim TLS-om pod opterećenjem. Ispravljeno na svim mjestima: x86_64 je pokrenut na pravom KVM VPS-u pri H7 commitu i u emulatoru, ali kod dodan nakon toga (šifriranje, boot integritet) nije vrtio na KVM-u, i nije probano na fizičkom hardveru.
D1 i ponuda "vaš servis kao unikernel"
Javni demo broker je izgrađen, ali ne objavljen: konfiguracija (live način, ACL demo/{id}/#, običan MQTT samo za most), skripta koja pravi objavljeni probni CA i demo certifikate, WebSocket-na-TCP most (samo Python standardna biblioteka, ograničenja veza, brzine i veličine, provjera podrijetla), stranica "Probajte urMQTT uživo" s MQTT klijentom u običnom JavaScriptu, runbook i model prijetnji. Testirano na loopbacku i u QEMU-u (18 scenarija, 26 testova mosta). Nije testirano: stvarni VPS, DNS, Caddy, kontejnerska slika. Treba odvojen VPS i izričito odobrenje za javno izlaganje.
Uz to je na sajtu objavljena i ponuda, Unikernel (15 jezika, bez cijena): prilagodba vašeg servisa tako da radi kao unikernel (procjena izvedivosti, prilagodba, pilot, podrška), s urMQTT-om kao referencom. Tekst izričito navodi ograničenja današnje osnove: IPv4, TCP samo kao poslužitelj, KVM, Rust prirodan, C samo kao izolirani radnik.
Izdanje v0.3.0
Tag v0.3.0 postavljen je 3.11. na commit f1a7fbc, a workflow za izdanje je uspio. Izdanje je označeno kao pre-release i sadrži razvojni aarch64 kernel, ISO i sliku, predložak ISO (kerneli za aarch64 i x86_64 i prazna konfiguracija, što urmqtt-config pack --iso i stranica graditelja pretvaraju u vašu sliku), CycloneDX SBOM, SHA256SUMS i njegov minisign potpis te SLSA atestacije podrijetla. Provjereno nakon objave, u praznom direktoriju: potpis SHA256SUMS vrijedi, svih pet datoteka se poklapa s kontrolnim zbrojevima, obje atestacije (kernel i ISO s CycloneDX predikatom) prolaze, SBOM ima 221 komponentu, a predložak se konfigurira (pack --iso ponovno upisuje šest kopija loadera). Ključ za potpis je i dalje probni ključ.
Redoslijed je bio važan i vrijedi ga zapisati: stranica graditelja ažurirana je prije oznake (zna format 4 i sve starije predloške), tek je onda zrcalo na aldo.tech preuzelo izdanje. Zrcalo ga je navelo kao potpisano i provjereno (ok, format konfiguracije 4, sha256 predloška jednak onom u SHA256SUMS), a živa stranica ga nudi kao zadano izdanje i u pregledniku napravi ISO za oko 20 sekundi. Bilješke izdanja kažu i ono što rijetko piše: CI na GitHubu nije zelen (vidi dolje), podrška samo za zadnju verziju (v0.2.1 i starije prestaju), i da je reviziju napravio sam projekt.
Na sajtu smo usput zamalo napravili vlastitu štetu: skripta za objavu stranice (publish.sh) radi rsync --delete, a zrcalo izdanja pripada drugom korisniku, pa je svaka objava završavala greškom 23 (i tako bi pokvarila gumb "Objavi" u blog adminu). Popravljeno izuzećem za /urmqtt/releases/. Sličan nalaz: deploy.sh s --delete briše stranicu graditelja s generatora, pa se mora vratiti prije objave.
Što još nije dokazano
- CI na GitHubu je crven. Ne samo danas: zadnje pokretanje potpunog CI-ja prije oznake imalo je sedam padlih poslova, a pokretanja unatrag do 31.10. su uglavnom crvena. Padali su: lint (shellcheck upozorenja u skriptama), TLS interop (svi wolfSSL slučajevi), lanac opskrbe (popis iznimki
vet-exemptions.mdnije ažuran na Linuxu), fuzz smoke (u tom poslu nedostaju razvojni certifikati), sanitizeri (LeakSanitizer javlja curenje u testovima), testovi na Linux aarch64 (jedan test alata pada s "Broken pipe") i jedan Kani dokaz za kolo vremena (timer wheel) ne prolazi. Nismo još razvrstali što je od toga okolina, a što stvarni nedostatak; dok to ne učinimo, ne tvrdimo da je izdanje prošlo CI. - Ključ za potpis je probni, a slike izdanja razvojne s probnim certifikatima (osim predloška).
- Prijevodi stranica i ovog teksta na ostalih 13 jezika napisao je AI i nisu ih provjerili izvorni govornici.
- Šifriranje diska nije izmjereno na x86_64 (ni na KVM-u ni na fizičkom hardveru), i nije izmjerena propusnost telemetrije niti vrijeme pokretanja sa šifriranjem. Na stranici graditelja ga nema (treba dijalog za ključ).
- Neovisne sigurnosne revizije nema. Opseg je napisan; revizora nemamo.
- Secure Boot je djelomičan (gore), bez fizičkog stroja.
- Bare metal, IOMMU, javni demo broker: nije gotovo ili nije objavljeno.
Što slijedi
- Razvrstati i popraviti CI (okolina prvo, pa Kani dokaz i test koji pada na aarch64 Linuxu), jer izdanje bez zelenog CI-ja ne smatramo gotovim.
- Odluka o D1 (zasebni VPS, DNS, odobrenje za javno izlaganje).
- Fuzzing PCI i virtio parsera i test zaključanih tablica stranica (H0-W9, H0-W10).
- Dijalog za ključ na stranici graditelja, pa šifriranje postaje dostupno bez naredbenog retka.
- Poslije izdanja: bare metal (X12 do X16) i IOMMU, redoslijedom koji ovisi o inventaru hardvera testnog PC-ja.
Kod je na github.com/zeljan-alduk/urMQTT; docs/PROGRESS.md je i dalje jedini izvor istine o tome što stvarno radi, docs/PERFORMANCE.md o tome što je izmjereno, a docs/AUDIT.md o tome što još nije dokazano. Pred-alfa, kao i prije; prijave grešaka i sigurnosne prijave po uputama u SECURITY.md dobrodošli su.
Komentari
Pravila komentiranja