Blog ·
Da una risposta DHCP a un’installazione finita
Netboot, parte seconda. Dopo l’offerta: lo script di avvio, il file di risposte, la callback che nessuno scrive e quali vendor pubblicano un checksum.
Di Provibr
Far accettare a una macchina un’offerta di netboot è la parte di cui scrivono tutti. Ne abbiamo scritto anche noi, dopo aver perso una settimana sull’indirizzo di destinazione di una risposta proxy-DHCP.
La parte successiva non riceve quasi alcuna copertura, ed è lì che vivono i guasti veri. Una macchina che si avvia e poi resta ferma per sempre su una domanda è peggio di una che non si è mai avviata, perché sembra progresso. Questa è la seconda metà: che cosa deve essere vero fra l’arrivo dell’offerta e la comparsa di un prompt di login, e quali di quelle cose falliscono in silenzio.
Le cinque cose che vengono servite
Una volta che il client ha un’offerta e un file di avvio, un’installazione automatica è fatta di cinque artefatti. Nessuno di essi è facoltativo, e l’ultimo è quello che si tende a tralasciare.
| # | Che cosa | Servito da | Come fallisce |
|---|---|---|---|
| 1 | Script di avvio | HTTP | La macchina cade in una shell |
| 2 | Kernel | HTTP | L’avvio si interrompe |
| 3 | Initrd | HTTP | L’avvio si interrompe |
| 4 | File di risposte | HTTP | L’installatore pone una domanda e aspetta |
| 5 | Callback di completamento | Il sistema installato | Tutto resta appeso fino a una scadenza |
La colonna interessante è l’ultima. Le voci da 1 a 3 falliscono rumorosamente, sullo schermo, in un modo che qualcuno noterà. Le voci 4 e 5 falliscono come un’installazione che sembra in corso e non finisce mai.
Script di avvio e il problema del segnaposto
Lo script di avvio deve nominare URL assoluti, e non può conoscerli quando il profilo viene scritto: l’indirizzo di ciò che serve i file dipende dal segmento su cui la macchina si è accesa.
Il profilo contiene quindi un segnaposto che viene sostituito al momento del servizio. Il nostro è @@PROVIBR_BASE@@, e la sua forma è una scelta voluta anziché una preferenza di stile.
La forma ovvia sarebbe ${BASE}. È esattamente sbagliata, perché iPXE espande ${...} da sé. Una sostituzione che non avviene diventa una stringa vuota anziché un errore, e ci si ritrova a fissare
chain http:///vmlinuzchiedendosi che cosa non vada nel proprio server web. Un delimitatore che il boot loader non riconosce trasforma un fallimento silenzioso della sostituzione in un fallimento visibile.
Le tre righe di comando del kernel che contano, una volta tolto il marchio:
# Ubuntu (subiquity)
kernel <kernel> initrd=<initrd> ip=dhcp autoinstall ds=nocloud;s=<dir>/ --- || goto failed
# Debian (preseed)
kernel <kernel> initrd=<initrd> auto=true priority=critical ip=dhcp url=<file> DEBIAN_FRONTEND=text --- quiet || goto failed
# RHEL family (Anaconda)
kernel <kernel> initrd=<initrd> ip=dhcp inst.repo=<tree> inst.ks=<file> inst.text || goto failedTre installatori, tre vocabolari completamente diversi per la stessa idea, e l’argomento che porta il file di risposte si scrive in modo diverso in ciascuno. ds= prende una directory; url= e inst.ks= prendono un file. Confonderli dà un installatore che si avvia e poi pone la prima domanda.
Le quattro chiavi di autoinstall che decidono se arriva in fondo
L’autoinstall di Ubuntu merita un paragrafo a sé, perché su un segmento isolato quattro chiavi separano un’installazione che si completa da una che non ci riesce, e nessuna di esse riguarda dischi o utenti:
interactive-sections: []: senza di essa subiquity si ferma comunque a chiedere qualcosa.refresh-installer.update: false: senza di essa va prima a cercare un installatore più recente.apt.fallback: offline-install: senza di essa aspetta l’archivio prima di rinunciare.drivers.install: false: stessa classe di problema, stesso esito.
Il guasto è sempre lo stesso: una schermata che sembra occupata. Non c’è alcun errore, perché dal punto di vista dell’installatore non è andato storto nulla.
C’è un flag corrispondente per il supporto. L’initrd netboot di Ubuntu scompatta un supporto di installazione in una tmpfs e installa da lì; se non gli si dice dove sia quel supporto, va su internet a cercarne uno. Su un segmento senza uplink, è la differenza fra funzionare e quattro minuti di nulla seguiti da un prompt busybox.
La callback che nessuno scrive
La voce 5 dell’elenco è una sola riga in uno script di post-installazione che preleva un URL, ed è il pezzo che manca più spesso in una soluzione fatta in casa.
Senza di essa non esiste alcun evento che significhi finito. L’installatore riavvia, la macchina si accende e il sistema che ha avviato l’installazione non ne sa nulla, quindi aspetta la scadenza che ha e poi segnala un timeout su una macchina che si è installata perfettamente.
Due dettagli rendono la cosa meno fastidiosa di quanto sembri. Primo, spedisca l’hook dentro il template anziché documentarlo, così chi scrive un proprio script di post-installazione non deve sapere che esiste. Secondo, preveda un hook per il fallimento oltre a quello per il successo: un’installazione che fallisce alle 04:00 e lo dice è un’esperienza di assistenza diversa da una che ammutolisce.
E tenga due orologi, non uno. Il comando che avvia un’installazione deve concludersi non appena i listener sono attivi e la sessione è accettata, non quando l’installazione finisce; il nostro ha una scadenza di 90 secondi per quello, contro un budget di installazione di un’ora imposto da entrambe le parti. Una sola scadenza per entrambe le cose significa o un lavoro che resta fermo nell’interfaccia per un’ora senza fare nulla, oppure un’installazione uccisa perché ha impiegato il tempo che le installazioni impiegano.
Quali vendor pubblicano un checksum, misurato
Se la macchina preleva un kernel e un initrd via HTTP da un mirror, qualcosa deve verificare ciò che è tornato indietro. Il modo per farlo è fissare uno sha256 nel profilo e controllarlo dopo il download, nel punto che esegue il prelievo.
Funziona solo se il vendor ne pubblica uno. Avevamo dato per scontato che la risposta fosse «in gran parte no», l’avevamo messo per iscritto, e si è rivelato sbagliato in tre casi su quattro. Ciò che viene pubblicato davvero, misurato il 2026-09-02:
| Vendor | Kernel + initrd netboot | Dove |
|---|---|---|
| Debian | Sì | SHA256SUMS accanto a installer-amd64/current/images/ |
| Famiglia RHEL | Sì | [checksums] nel .treeinfo accanto all’albero |
| Ubuntu (ISO live server) | Sì | SHA256SUMS accanto alla release |
| Ubuntu (kernel + initrd netboot) | No | Nessun file di checksum nella directory netboot, e il tarball non compare nemmeno nelle somme della release |
Il costo di quell’assunzione sbagliata non è stato un disservizio; è stato peggio, in modo silenzioso. Asset che si sarebbero potuti fissare erano contrassegnati come non verificabili, e i profili si dichiaravano non installabili mentre il vendor pubblicava la risposta in bella vista. Quattro artefatti su cinque nei primi due profili.
Il comportamento nel caso in cui un checksum davvero non esista merita di essere progettato apposta. Il nostro lascia il pin vuoto, contrassegna l’asset come in attesa, e il profilo si dichiara non installabile finché un amministratore non incolla un hash. È deliberatamente più rumoroso che scaricarlo comunque. Un kernel non verificato prelevato via HTTP da un mirror è il punto di attacco di maggior valore in un sistema di provisioning, e «non siamo riusciti a verificarlo, quindi l’abbiamo fatto lo stesso» non è una politica.
Che cosa può voler dire «mantenuto»
Distribuiamo dieci profili di base: Ubuntu Server 24.04 e 26.04, Debian 12 e 13, Rocky Linux 9 e 10, AlmaLinux 9 e 10, CentOS Stream 10, Fedora Server 44.
Lo stato di supporto associato a ciascuno è calcolato, non digitato. Un profilo può dirsi mantenuto solo quando ha una esecuzione verificata su entrambi i percorsi firmware, BIOS e UEFI. Uno solo dei due dà «anteprima». Nessuno dei due dà «non mantenuto».
Farne una regola anziché un campo vale la piccola quantità di codice che costa. Un’etichetta scritta a mano vale esattamente quanto la cura di chi l’ha scritta, e questa è un’etichetta sulla cui base un cliente affida una macchina.
Il che significa essere chiari su dove ci lascia oggi. Ubuntu Server 26.04 e Debian 13 si sono installati da soli, in modo del tutto automatico, su un segmento isolato e su una macchina BIOS. Il percorso UEFI è stato riparato nella stessa tornata, e ripararlo non equivale ad averlo eseguito. Nessun profilo di serie è quindi maintained al momento in cui scriviamo, e i profili della famiglia RHEL non sono mai stati eseguiti su una macchina reale: sono stati scritti a partire dalla documentazione del vendor e confrontati con file di origine reali, il che è un’affermazione più debole di quanto sembri da fuori.
A parte questo, offrire il netboot come modo per creare una macchina nuova è disattivato per impostazione predefinita nella nostra piattaforma, in attesa di una cosa sola: una macchina reale che si installi così, su firmware reale. Il percorso che esiste e viene usato è la reinstallazione su una macchina esistente. Un flag di funzionalità attivo perché il codice è scritto, anziché perché si è osservato che la cosa funziona, produce un pulsante d’ordine che si arena a metà strada al primo cliente vero.
Piccole cose che costano un pomeriggio ciascuna
- Convalidi i percorsi dei file serviti. Nessuna barra iniziale, nessun
.., nessuna barra rovesciata, una sola classe di caratteri. Ciò che serve i file sta dentro la rete di qualcuno, con un filesystem dietro. - Oscuri la riga di audit. Registri quale file è stato servito e se portava un segreto, mai il contenuto. Chi ha bisogno del contenuto può rigenerare il profilo.
- Non lasci che il file di risposte sia una seconda fonte di verità sul supporto. L’argomento del kernel e il file di risposte nominano entrambi la provenienza dei pacchetti; derivarli entrambi da una sola impostazione è l’unico modo perché restino uguali.
- Dia per scontato che nessuno stia guardando la console. Ognuna delle scelte qui sopra è una scelta su che cosa succede quando l’installazione è al sesto minuto su otto e nella stanza non c’è nessuno.
Questa è la seconda parte del testo su perché il netboot UEFI fallisce dove il BIOS funziona, e nasce dalla costruzione dell’installazione via rete dentro Provibr. I dettagli sono nella documentazione netboot.