urMQTT su un vero VPS ARM64: senza schermo, senza generatore hardware di numeri casuali e con un handshake troppo lento
Cosa è successo dopo il testo su v0.3.0: la prima esecuzione su un vero server aarch64 con KVM (installazione sul suo disco, un'interruzione di corrente, cifre del disco), una voce Diagnostica che riferisce attraverso la rete perché la console è invisibile, cosa ha trovato sul generatore di numeri casuali, un assembly P-256 per ARM64 che ha reso gli handshake completi circa 1,5 volte più frequenti, e gli errori che abbiamo fatto lungo la strada.

Il testo precedente, urMQTT v0.3.0, si chiudeva con quattro punti sotto «Cosa viene dopo»: una decisione sul broker demo pubblico (D1), il fuzzing dei parser PCI e virtio e un test per le tabelle delle pagine bloccate, una finestra per la chiave nella pagina del builder, e bare metal. Nessuno dei quattro è stato fatto in questo giro, e lo diciamo per primo. Quello che abbiamo fatto è ciò che mancava da sempre: abbiamo eseguito urMQTT su un vero server ARM64 con KVM, e quasi tutto ciò che segue viene da quella macchina. Il primo punto della vecchia lista, la CI, è stato messo in ordine (il testo precedente e le note di rilascio che la davano per rossa sono stati corretti)
L'avvertenza iniziale resta la stessa: il progetto è pre-alpha, i numeri misurano codice, test e determinati esperimenti su una macchina, non la maturità in produzione. Dove una cifra non è stata misurata sulla macchina di destinazione, lo diciamo.
Com'è andata nel tempo
Dall'ultimo testo (3 novembre, 02:43) a oggi (4 novembre, 03:18) main ha 29 nuovi commit, 244 in tutto. Come prima, il progetto è costruito con Claude Code (i commit portano Co-Authored-By: Claude). I numeri sono calcolati con il metodo degli ultimi due testi (tutti i file .rs fuori da vendor/ e fuzz/, le funzioni #[test], i titoli ## ADR- in docs/DECISIONS.md); il metodo sul vecchio commit dà gli stessi numeri di allora.
| Momento | Commit | Ora | Righe di Rust | Test | ADR | Commit |
|---|---|---|---|---|---|---|
| ultimo testo | f1a7fbc | 3 nov., 02:43 | 115.148 | 962 | 133 | 215 |
| oggi | efddf6e | 4 nov., 03:18 | 117.084 | 978 | 134 | 244 |
Crescita: +1.936 righe, +16 test, +1 ADR. Di nuovo poco codice nuovo; l'assembly P-256 stesso (1.509 righe) è vendorizzato e non rientra nel conteggio delle righe.
Una seconda macchina vera
Finora il progetto aveva un solo server vero: un VPS Contabo con un AMD EPYC (x86_64, KVM), usato per il confronto delle prestazioni con Mosquitto. ARM64 era girato solo in QEMU, con accelerazione hardware su un Mac e senza in CI. La nuova macchina è un netcup VPS 500 ARM G12.5: un core Neoverse N1 a circa 3 GHz, 2 vCPU, 3,9 GiB di RAM, un disco virtio da 64 GiB, QEMU/KVM con firmware UEFI, una scheda di rete virtio. Tutto è stato pilotato attraverso l'API REST del fornitore con un client a riga di comando: carichiamo l'ISO, la colleghiamo come CD virtuale, cambiamo il dispositivo di avvio, spegniamo e riaccendiamo.
Cosa abbiamo fatto, in ordine:
- Prima uno snapshot del sistema originale. Con UEFI non è possibile uno snapshot online, quindi il server è stato spento ed è stato fatto uno snapshot offline.
- Installazione. L'installer dell'ISO ha scritto l'immagine sul disco (conto alla rovescia di 30 secondi compreso, circa 40 secondi in tutto) e ha spento la macchina da solo, come previsto.
- L'avvio dal disco installato ha richiesto circa 35 secondi fino a un handshake TLS.
- Un messaggio retained è sopravvissuto a un'interruzione di corrente brusca. L'abbiamo pubblicato, tolto l'alimentazione attraverso l'API, riavviato e riletto.
- Il disco installato, misurato dalla voce Diagnostica (solo letture; il test di scrittura è stato saltato perché il layout installato non lascia spazio non allocato, ed è voluto): lettura sequenziale 332 MiB/s, letture casuali da 4 KiB 22.147 al secondo con p50 10,4 µs, p90 28,3 µs, p99 671 µs, massimo 9,9 ms (2.000 letture).
Cinque punti non fanno una suite di test: è una macchina, una sola esecuzione di ciascuno, e il percorso di installazione è stato provato una volta sul disco vero. Ciò che mostra è che il driver virtio-blk del kernel, l'installer e l'avvio funzionano su un vero guest cloud e non solo in un emulatore da laboratorio.
Senza schermo, quindi la diagnostica passa dalla rete
La voce Diagnostica di v0.3.0 disegna le sue cifre sul framebuffer. Su questo server la console del fornitore dice «Display output is not active»: la macchina virtuale ha una scheda grafica virtio per cui non abbiamo un driver, e la porta seriale non è esposta. Le cifre esistevano e nessuno poteva leggerle.
La soluzione è una parola di avvio, diag-serve: la macchina esegue le stesse misure, poi si avvia come un normale broker live e pubblica il rapporto come un unico documento JSON nel messaggio retained $SYS/diag/report, che può leggere solo un certificato operatore. Un piccolo strumento, urmqtt-diag-png, disegna il cruscotto da quel JSON con lo stesso codice che il kernel usa sullo schermo (lo stesso layout e lo stesso font 8x8), quindi l'immagine è lo schermo che la macchina avrebbe mostrato:

Lungo la strada sono state aggiunte allo schermo due cose che ci mancavano: la frequenza della CPU (il contatore di cicli PMU rispetto alla base dei tempi: da 2,95 a 2,98 GHz qui; dove si può leggere solo un timer a frequenza fissa, dice «non misurabile» invece di stampare la frequenza del timer come se fosse quella della CPU) e un riquadro per il generatore di numeri casuali.
Di cosa è fatto il generatore di numeri casuali
Il riquadro indica da dove è stato inizializzato il generatore e verifica byte nuovi di ogni sorgente hardware. Su questa macchina dice:
- virtio-rng: nessun dispositivo.
RNDR(l'istruzione propria della CPU): non disponibile su questa CPU. - Il generatore di numeri casuali EFI del loader: 8 valori su 8 diversi da zero.
- Il ripiego sul jitter dei tempi della CPU (ADR-038): 1.024 campioni di 64 passi attraverso il contatore di cicli PMU, 3 bit stimati per campione, 768 bit accreditati (un quarto della stima, per scelta), test di integrità superati.
- L'uscita del generatore, un test di plausibilità su 4.096 byte: 49,6 % di uni, la sequenza più lunga di 16 bit. È un controllo che nulla sia bloccato o sbilanciato, non una certificazione statistica.
Quindi il generatore su questo server poggia sul ripiego a jitter e su ciò che il firmware ha consegnato. È esattamente ciò che descrive l'ADR-038, ed è l'inizializzazione più debole che il kernel accetta. Non la chiamiamo difettosa: i bit accreditati sono sopra la soglia che il kernel richiede (256), e lo stimatore è prudente. Non possiamo nemmeno dimostrare che sia buona quanto una sorgente hardware. Se un'immagine di produzione debba rifiutare l'avvio senza una sorgente hardware, a meno che l'operatore non lo accetti esplicitamente, è una questione aperta per il proprietario del progetto (è nella roadmap, più sotto). In pratica è una questione anche per il fornitore: un dispositivo hardware di numeri casuali per la macchina virtuale la risolverebbe.
Un handshake troppo lento
La voce Diagnostica ha mostrato anche quanto costa sul server un handshake completo con TLS reciproco: 1,08 ms. Quasi tutto è aritmetica a chiave pubblica: due verifiche ECDSA P-256 (il certificato del client e la prova del client), circa 508 µs ciascuna, X25519 176 µs, una firma ECDSA 141 µs. Su x86_64 l'avevamo risolto prima (ADR-130, l'assembly OpenSSL/Intel: P-256 da tre a quattro volte più veloce); su ARM64 girava ancora il codice portabile di RustCrypto.
Abbiamo fatto lo stesso per ARM64 (ADR-255). L'assembly è l'nistz256 ARMv8 di OpenSSL (Apache-2.0), preso da ring 0.17.14 e vendorizzato con la sua provenienza e ogni modifica meccanica annotata. Cosa è contato nei dettagli:
- Solo interi. L'assembly non usa registri NEON. Le due funzioni di selezione dalla tabella che upstream usa con NEON le abbiamo eliminate e abbiamo scritto in Rust scansioni mascherate a tempo costante (costano al massimo 1,4 µs su una firma da 12 µs).
- Protezione dei salti. Il kernel è compilato con BTI e autenticazione dei puntatori. Ciascuno degli 8 punti d'ingresso esportati ha un landing pad, verificato nel disassemblato del kernel, e i sei che salvano il link register lo firmano.
- Test, perché questa è crittografia nella base di cui ci fidiamo. Tutta la suite x86_64 ora gira anche su aarch64 Linux: aritmetica del campo e dello scalare contro un riferimento scolastico, l'intera tabella del generatore (2.368 voci) contro RustCrypto, firme identiche bit per bit a quelle di RustCrypto, verdetti di verifica, ECDH con ingressi fuori curva e zero, e i test con risposte note che girano a ogni avvio attraverso i percorsi di produzione. In una macchina virtuale a quattro core sotto una tempesta di interrupt sono stati eseguiti 59.526 round con risposte note con 0 discrepanze.
- Un test di temporizzazione con un controllo, e un segnale che era il nostro stesso test. Una moltiplicazione volutamente non a tempo costante ottiene |t| da 482 a 1.755, quindi il test riesce a vedere una perdita a questa scala. Su un core Apple in un container ECDH ha ottenuto da 0,2 a 2,1, la firma da 0,5 a 1,9, la generazione di chiavi da 0,8 a 5,5. Sul core server arm64 del runner CI, la firma ECDSA ha ottenuto |t| = 8,6, 9,1 e 8,3 in tre tentativi, sopra il nostro limite di 7, mentre ECDH (3,9) e la generazione di chiavi (1,9) sono passati sulla stessa macchina. Un risultato che si ripete non è rumore, quindi lo abbiamo annotato come rilievo aperto (A255-W1) e, per qualche ora, alzato a 12 il limite per quel solo caso, perché la build potesse passare. Poi abbiamo trovato la causa, ed era il banco di prova: il caso firmava un solo messaggio fisso, per cui la chiave sparsa produceva ogni volta la stessa firma e le chiavi casuali ogni volta una diversa, e la codifica DER di una firma ripetuta è più veloce (|t| da 49 a 57 per la sola codifica). Con un messaggio casuale per ogni misura in entrambe le classi il caso ottiene 1,4 e 1,8 sul runner (una macchina virtuale Neoverse N2), la sola generazione del nonce 2,7 e 1,1, e il limite è tornato a 7. Una cosa resta aperta (A255-W2): il flag del processore che rende indipendente dai dati il tempo del moltiplicatore sui core Arm (DIT) non è impostato da nessuna parte, né dal kernel né attorno alle chiamate lato host. Nessuna misura mostra una differenza, ma la macchina virtuale del runner non espone la funzione, quindi nemmeno il test poteva mostrarla.
Cosa ha guadagnato
Prima il microbenchmark sulla macchina dove l'abbiamo sviluppato, un Apple M4 Pro (non la CPU di destinazione, quindi i rapporti sul server possono differire), con lo stesso codice che chiama l'handshake, prima e dopo:
| Operazione | prima (µs) | dopo (µs) | rapporto |
|---|---|---|---|
| generazione di chiavi | 30,8 | 7,0 | 4,4x |
| ECDH P-256 | 135,2 | 25,0 | 5,4x |
| firma ECDSA | 34,7 | 11,6 | 3,0x |
| verifica ECDSA (chiave sconosciuta) | 147,4 | 29,1 | 5,1x |
| verifica CA (emittente registrato) | 54,2 | 9,6 | 5,6x |
| X25519 (codice invariato) | 20,4 | 20,1 | 1,0x |
| stima dell'handshake (somma dei precedenti) | 292 | 104 | 2,8x |
Poi la prova vera: lo stesso generatore di carico, lo stesso metodo del testo precedente sulle prestazioni (handshake TLS 1.3 completo con certificato client, senza ripresa, MQTT CONNECT, il server chiude per primo), eseguito da una macchina a 9 ms dal server, contro lo stesso VPS netcup, prima e dopo la modifica. L'immagine del benchmark ha i limiti dell'handshake allentati, come nel confronto su x86_64; tre ripetizioni, la cifra è la mediana.
| Thread | prima (conn/s) | dopo (conn/s) | rapporto | errori |
|---|---|---|---|---|
| 32 | 848 (408-855) | 1.317 (957-1.317) | 1,55x | 0 |
| 64 | 827 (815-839) | 1.398 (1.352-1.416) | 1,69x | 0 |
| 128 | 778 (714-788) | 1.316 (1.313-1.383) | 1,69x | 0 |
| Parte TLS di una connessione, p50, 32 thread | 18,5 ms | 13 ms |
Gli handshake completi al secondo sono aumentati di circa 1,55 a 1,7 volte, non delle tre volte che darebbe la sola aritmetica. Il motivo è semplice e vale la pena dirlo: abbiamo accelerato P-256, mentre X25519 (176 µs), lo stack TCP/IP, il livello dei record e MQTT sono invariati, quindi la frazione che resta pesa di più (legge di Amdahl). Il prossimo candidato è X25519.
Per orientarsi accanto al server x86_64 del testo precedente. Questi numeri non sono confrontabili come in una gara: CPU diverse, un numero diverso di core (4 contro 2), una diversa distanza dal generatore (15 ms contro 9 ms) e in entrambi i casi un host di carico condiviso che durante le esecuzioni ha perso dal 5 al 60 % della propria CPU. La colonna confrontabile è il costo di CPU del server per handshake:
| Contabo, x86_64 EPYC (asm) | netcup, ARM64 N1 prima | netcup, ARM64 N1 dopo | |
|---|---|---|---|
| CPU del server per handshake | 0,26 ms (la console del broker) | 1,08 ms (Diagnostica) | 0,55 ms (Diagnostica, una sola esecuzione, vedi sotto) |
| handshake/s, 32 thread | 169 (bench) | 848 | 1.317 |
| handshake/s, 64 thread | 278 (bench) | 827 | 1.398 |
| handshake/s, 128 thread | 413 (bench) | 778 | 1.316 |
Lo stesso giorno la voce Diagnostica è girata di nuovo su questa macchina, con l'assembly (in una build che contiene anche il driver grafico descritto nell'aggiornamento in fondo): il costo di CPU del server per un handshake con TLS reciproco è stato di 553 µs contro 1,08 ms prima, 1,95 volte in meno. La verifica ECDSA ha richiesto 192 µs (508 µs prima), la firma 78 µs (141 µs), e X25519 209 µs (176 µs; quel codice è invariato, e la frequenza della CPU in questa esecuzione era di 2,58 GHz contro da 2,95 a 2,98 GHz nelle precedenti, per cui le singole cifre su questa macchina condivisa variano di decine per cento). È una sola esecuzione, su un host condiviso. Che il numero di handshake al secondo sia salito da 1,55 a 1,7 volte mentre il costo proprio del server per handshake è sceso di 1,95 volte è coerente: il resto di una connessione è lo stack, il generatore e la rete. La variabilità tra esecuzioni si vede nei file grezzi: ogni serie di tre ha un valore anomalo (408 prima e 957 dopo a 32 thread), quando l'host di carico ha perso gran parte della sua CPU a favore di altre macchine virtuali. A 128 thread, su entrambe le macchine è il generatore, non il server, a essere al limite.
Il resto della suite su questa macchina
La stessa suite del server x86_64 (throughput, latenza, fan-out, connessioni inattive) è girata per intero sulla nuova build. Per queste righe non c'è un «prima»: l'esecuzione è stata interrotta per costruire l'assembly, quindi sono lo stato dopo la modifica, e l'assembly non tocca questi percorsi, tranne gli handshake che aprono le connessioni. Un fatto spiega gran parte di ciò che segue: questa macchina virtuale ha due vCPU. Il core 0 gestisce la scheda di rete, lo smistamento e tutto il lavoro MQTT e TLS, il core 1 è il core del database ed è stato occupato al 2-3 % per tutto il tempo. Il server x86_64 aveva quattro vCPU e due core di lavoro. Tutto ciò che segue è quindi prestazione del broker su un solo core.
| Connessioni inattive | 5.000 | 10.000 | 20.000 |
|---|---|---|---|
| connesse | 5.000 su 5.000 in 4,3 s | 10.000 in 9,3 s | 20.000 in 21,2 s |
| crescita dell'heap del kernel per connessione | 0,34 KB | 0,41 KB | 0,43 KB |
| errori | 0 | 0 | 0 |
(Da 0,50 a 0,66 KB per connessione sul server x86_64 al commit H7. La cifra dell'heap è $SYS/broker/memory/used meno il suo valore prima della prima connessione.)
| Latenza a ritmo modesto (8 coppie x 100 messaggi/s, 64 B) | p50 | p99 |
|---|---|---|
| QoS 0 | 4,0 ms | 6,9 ms |
| QoS 1 | 4,1 ms | 7,0 ms |
| Fan-out (1 publisher, 64 B) | consegnati al secondo | p50 | p99 |
|---|---|---|---|
| 100 sottoscrittori, QoS 0, 50 msg/s | 5.000 (tutti) | 5,9 ms | 8,6 ms |
| 100 sottoscrittori, QoS 1, 50 msg/s | 5.000 (tutti) | 6,3 ms | 10,0 ms |
| 1.000 sottoscrittori, QoS 0, 10 msg/s | 8.805 su 10.000 | 207 ms | 5,4 s |
| Flood, 16 coppie, publisher alla massima velocità che il broker accetta | pubblicati/s | consegnati/s | p50 / p99 | connessioni perse |
|---|---|---|---|---|
| QoS 0, 64 B | 231.317 | 85.140 | 1,36 s / 2,8 s | 0 |
| QoS 0, 1 KiB | 62.110 | 25.506 | 336 ms / 873 ms | 0 |
| QoS 1, 64 B | 107.349 | 55.965 | 21 ms / 29 ms | 0 |
| QoS 1, 1 KiB | 51.369 | 25.054 | 47 ms / 60 ms | 0 |
| QoS 1, 64 B, 64 coppie | 130.462 | 69.411 | 69 ms / 87 ms | 0 |
Come leggerlo. La latenza a ritmo modesto è il contributo del broker più un viaggio fino al generatore: il generatore è più vicino a questo server di quello del confronto su x86_64, quindi le due colonne di latenza non sono confrontabili. Il flood QoS 0 mostra il quadro solito di un singolo core sovraccarico: il broker accetta 231.000 messaggi al secondo e ne consegna 85.000 (il resto viene scartato dalle regole di back-pressure, ed è a questo che servono) con latenze di secondi, e nessuna connessione è andata persa; sul server x86_64 lo stesso tipo di flood costava diverse connessioni per esecuzione al watchdog da 20 ms (da allora l'ADR-221 ha cambiato il modo in cui quel budget viene contato). La riga dei 1.000 sottoscrittori ha lo stesso carattere di prima, code di secondi, in parte dovute al generatore stesso. Le cifre al commit H7 sul server x86_64 sono nel testo precedente; non le mettiamo nella stessa tabella di queste, perché il codice tra le due misure è cambiato nel percorso dei dati (da ADR-221 a ADR-224) e le macchine differiscono in tutto.
Errori che abbiamo trovato noi stessi
- L'immagine diceva «nessun disco trovato» e il disco era nel rapporto. La prima versione di
urmqtt-diag-pngnon leggeva affatto il campodisks. Il proprietario del progetto ha guardato l'immagine, ha detto che sembrava sbagliata, e aveva ragione. Corretto, e ora un test rilegge un disco dal JSON. - Abbiamo dichiarato finito l'installer troppo presto. Lo stato del server è passato a «spento» due volte: una alla fine del ciclo di alimentazione e una quando l'installer ha finito. Il nostro script ha preso la prima per la seconda, ha staccato l'ISO e ha avviato il vecchio sistema, che ha riportato il vecchio numero di versione e ci ha fatto cercare un difetto nell'installer che non c'era. Ora lo script aspetta prima un avvio nuovo.
- La cronologia dell'avanzamento è andata persa. Un'analisi per la roadmap ha scoperto che il commit della milestone S6 aveva sostituito
docs/PROGRESS.md(1.763 righe, milestone da M0 a S5) con la propria voce di 26 righe. Per giorni non se n'è accorto nessuno, benché il README chiami questo file «la fonte di verità». È stato ripristinato dalla cronologia. - La CI è fallita dopo l'assembly, in due punti. Tre test smoke avevano scritta dentro la dimensione dell'immagine di avvio («10 MiB»), cresciuta a 11 MiB con il kernel più grande; ora non presumono più la dimensione. L'altro fallimento era il test di temporizzazione descritto sopra: la prima volta sembrava rumore su un runner condiviso, quindi abbiamo fatto ripetere il test tre volte; è fallito in tutti e tre i tentativi, ed è così che abbiamo capito che era un segnale, e il segnale si è rivelato essere il nostro stesso test (firmava ogni volta lo stesso messaggio). Abbiamo alzato quel solo limite per qualche ora, lo abbiamo detto apertamente, e lo abbiamo riabbassato una volta corretta la causa.
- La documentazione conta tre parole di avvio e il codice ne ha quattro (
diag-serve), e la tabella delle milestone nel README mostra ancora come pianificato il lavoro dell'ultima settimana. Entrambe sono nella roadmap come punti da correggere.
Una roadmap, con domande
Per la prima volta c'è una roadmap: circa 40 punti in tre fasi (v0.4 rafforzamento e operatività; protocollo e rete; scale-out, più cloud e bare metal), ciascuno con un motivo tratto da un documento, uno sforzo, un rischio e un modo di verificarlo, 16 domande a cui può rispondere solo il proprietario del progetto, e un elenco di ciò che è fuori ambito. Alcune cose le dice senza giri di parole: è supportato solo MQTT 3.1.1, una pubblicazione QoS 2 chiude la connessione, non c'è IPv6 (il fornitore assegna un /64), né WebSocket né connessioni in uscita dal broker, e non ci sono driver per i dispositivi di rete e di archiviazione dei grandi cloud.
Cosa non è ancora dimostrato
- Il costo di CPU per handshake con l'assembly è stato misurato sul core Neoverse una sola volta (553 µs contro 1,08 ms prima, a una frequenza più bassa, su un host condiviso); il fattore da 1,55 a 1,7 viene dal numero di handshake completi al secondo, e quello da 3 a 5 da un core Apple. Il lato x86_64 del refactoring (il codice spostato in un file a sé) è stato controllato nei tipi e compilato, ma i suoi test unitari non sono stati eseguiti e il kernel x86_64 non è stato avviato dopo di esso: le suite x86_64 non sono nella CI, si eseguono a mano sul VPS Contabo, e per questa modifica non è ancora stato fatto.
- Il tempo costante: per la generazione di chiavi P-256, ECDH e la firma (la chiave e il nonce) il test statistico non ha trovato alcuna dipendenza da un segreto sparso. È girato su un core Apple e sul runner CI (una macchina virtuale Neoverse N2), non sul core Neoverse N1 di netcup, e mai con il flag DIT impostato su un core server (A255-W2, aperto: il kernel non lo imposta). Non è girato per X25519 né per Ed25519. Un test a scatola nera delimita solo ciò che riesce a risolvere alla dimensione del suo campione.
- Un server, un fornitore, un'installazione. Diversi risultati (installazione, un'interruzione di corrente) sono una singola esecuzione. Oltre agli handshake, le righe delle prestazioni non hanno un «prima», e una macchina con due vCPU dà al broker un solo core per i dati.
- Il generatore di numeri casuali su questo server è inizializzato dal jitter e dal firmware, come descritto sopra.
- La chiave di firma è ancora una chiave di prova, le immagini del rilascio sono build di sviluppo, e le traduzioni di questo testo sono state scritte da un'IA e non verificate da madrelingua.
- Nessun audit indipendente, nessun bare metal, nessuna IOMMU.
Cosa viene dopo
- X25519 in assembly per ARM64 (sono 176 µs dell'handshake rimanente), e la ripetizione della misura per handshake sul core Neoverse, più di una volta.
- Una decisione sul default del generatore di numeri casuali: rifiutare l'avvio senza una sorgente hardware a meno che l'operatore non dica altrimenti, oppure tenere il ripiego a jitter e dirlo a chiare lettere.
- La vecchia lista: una decisione su D1, il fuzzing dei parser PCI e virtio, la finestra per la chiave nella pagina del builder.
- Dalla roadmap, per il prossimo rilascio: una vera chiave di firma e una PKI di produzione, MQTT 5.0 e QoS 2.
Il codice è su github.com/zeljan-alduk/urMQTT; docs/PROGRESS.md resta la fonte di verità su ciò che funziona, docs/PERFORMANCE.md su ciò che è stato misurato (la sezione 10 ha le cifre dell'assembly), docs/AUDIT.md su ciò che non è dimostrato. Pre-alpha, come prima; segnalazioni di bug e segnalazioni di sicurezza secondo le istruzioni in SECURITY.md sono benvenute.
Aggiornamento, 4 novembre, più tardi lo stesso giorno. Quattro cose dalle ore successive alla pubblicazione di questo testo.
- La cifra mancante. La voce Diagnostica è girata di nuovo sul server netcup con l'assembly e ha dato il numero che manca nella prima tabella: 553 µs di CPU del server per handshake con TLS reciproco contro 1,08 ms prima (dettagli sotto quella tabella).
- Il segnale della firma era il nostro test. La causa del segnale di temporizzazione descritto sopra è stata trovata e corretta nel banco di prova (firmava ogni volta lo stesso messaggio); il limite è tornato a 7. Resta aperto che il flag DIT non è impostato da nessuna parte (A255-W2).
-
Il problema dello schermo è risolto, ed è su
main. Abbiamo scritto un driver per la scheda grafica virtio (solo 2D, ADR-256), attivato da una chiave di configurazione incorporata nell'immagine (console.virtio_gpu,offper impostazione predefinita,autoeonrichiedono il formato di configurazione 5). Conon, la scheda Screen della console del fornitore mostra la dashboard Diagnostica in diretta sul server netcup, la stessa immagine di sopra, compresa l'ultima schermata con «press any key». Il suo fuzz target ha trovato un vero bug nella prima versione: una risposta di informazioni sul display più corta di quattro byte rispetto alla specifica veniva accettata come valida (A256-W1); un dispositivo reale invia sempre la risposta intera, quindi il server non era colpito, e il parser ora rifiuta quella corta. Ciò che non abbiamo verificato: che il display scuro derivasse davvero dal firmware che resetta la scheda quando passa il controllo al kernel (probabile, non confermato), l'interazione con Secure Boot e x86_64 su una macchina reale (è girato solo sotto emulazione). Un limite noto (A256-W2): l'impostazioneautonon sa distinguere un framebuffer emulato vivo accanto a una scheda virtio da uno morto, quindi in quella sola configurazione sceglie la scheda virtio. Il driver aggiunge al kernel un parser delle risposte dell'hypervisor, ed è per questo che è disattivato se la configurazione non lo chiede. -
Il rilascio. Tutto questo è in v0.4.0, pubblicato la sera stessa come pre-release (rilascio su GitHub). La pagina del builder su questo sito lo offre per primo; richiede il formato di configurazione 6, e i modelli più vecchi (da v0.2.0 a v0.3.0) rifiutano le nuove chiavi con un messaggio che dice di usare un rilascio più recente. I limiti di v0.3.0 restano: la chiave di firma è ancora una chiave di prova, e le immagini sono build di sviluppo con certificati di prova. La CI sul commit del rilascio è diventata verde solo dopo che un job è stato rieseguito (un test client dentro uno scenario smoke è andato in timeout una volta ed è passato la seconda). Ciò che abbiamo verificato sui file pubblicati: la firma minisign, i checksum, le attestazioni di build del kernel e dell'SBOM, e il modello con le nuove chiavi (v0.4.0 le accetta, v0.3.0 le rifiuta). Il driver per i nuovi dispositivi virtio (console, balloon, input, vsock) è nel rilascio, ma è disattivato per impostazione predefinita e non è girato su un server reale.
Commenti
Regole dei commenti