Blog ·
Non privilegiato si misura, non si imposta
Ripristinare il backup di un container comincia dal leggere la configurazione dentro l’archivio, perché il valore sicuro di un flag non si manda alla cieca.
Di Provibr
Ripristinare un container su Proxmox è la stessa chiamata API che ne crea uno. Si fa POST verso l’endpoint dei container su un nodo, gli si passa l’archivio e si aggiunge il flag che trasforma la creazione in un ripristino. Il percorso della macchina virtuale non ha nemmeno quel flag: lì è l’archivio stesso a essere il ripristino. Quella simmetria è comoda e nasconde una trappola, perché uno dei campi di quella chiamata descrive una proprietà di sicurezza che dentro l’archivio ha già un valore.
Si mandi il valore sicuro e si rischia di rompere il container. Si ometta il campo e si rischia di infilare un container privilegiato in un parco che non dovrebbe averne. L’unica mossa corretta è leggere prima l’archivio, e poi agire su ciò che dice.
Due endpoint che si somigliano
Il percorso della macchina virtuale e quello del container differiscono in più punti di quanto il nome suggerisca:
| Macchina virtuale | Container | |
|---|---|---|
| Endpoint | POST /nodes/{node}/qemu | POST /nodes/{node}/lxc |
| L’archivio | archive=<volid> | ostemplate=<volid> più restore=1 |
| Il nome | name= | hostname= |
| Lo storage di destinazione deve accettare | images | rootdir |
| Tipo di attività nell’identificatore dell’attività | qmrestore | vzrestore |
Due di quelle righe è facile sbagliarle in un modo che produce un errore confuso. Proxmox chiama un archivio di container un template di sistema operativo, perché lo stesso parametro accetta entrambi, e per una macchina virtuale la presenza di archive è già di per sé il ripristino, quindi non esiste alcun flag separato. E images e rootdir sono due tipi di contenuto diversi: uno storage può offrire l’uno senza l’altro, quindi pretendere images ovunque rifiuta storage per container perfettamente validi, mentre non controllare affatto consegna al cliente una frase sulle directory dei container che per lui non significa nulla.
Quale dei due tipi sia viene stabilito una volta sola. Lo si legge dal nome del file di archivio nel momento in cui l’archivio viene elencato, lo si conserva insieme al punto di ripristino e lo si manda insieme all’istruzione; l’agente sceglie il proprio endpoint da quella parola e non ha alcun ripiego. Deve funzionare in questo verso, perché un 404 di Proxmox non distingue un guest dell’altro tipo da un guest che non esiste più. Provare l’altro endpoint dopo un 404 permetterebbe di indirizzare come container una macchina virtuale eliminata, e su un cluster in cui quel numero sia stato nel frattempo riassegnato verrebbe toccato il guest sbagliato.
Un flag non viene mai mandato, e si chiama force. Questo è il ripristino additivo: una macchina compare accanto all’originale e l’originale continua a girare. force è il flag con cui Proxmox sovrascrive un guest esistente, esiste su entrambi gli endpoint e appartiene all’altro tipo di ripristino, che ha un messaggio proprio, un permesso proprio e una conferma da digitare. Tenerlo fuori in modo permanente da questo percorso di codice costa meno che ricordarsi di non impostarlo.
Un flag viene sempre mandato, e si chiama unique. Senza di esso il guest ripristinato eredita gli indirizzi hardware delle schede di rete originali, e due macchine che rispondono allo stesso indirizzo sono un disservizio proprio sulla macchina che si stava attenti a non toccare.
Tre rifiuti prima che si scriva qualcosa
Ogni controllo preliminare esiste perché l’alternativa è una citazione di Proxmox su cui il cliente non può agire. L’archivio deve esserci ancora, perché la piattaforma lo sceglie da un indice che è per costruzione un’osservazione del passato. Il numero di destinazione deve essere libero, e libero deve voler dire libero sull’hypervisor anziché libero nella nostra amministrazione: la piattaforma assegna dentro l’intervallo che gestisce, quindi un guest creato a mano da qualcuno è del tutto reale e del tutto assente da ogni registro che teniamo. E lo storage di destinazione deve accettare il tipo di contenuto giusto.
Per un container ce n’è un quarto, ed è quello di cui parla questo articolo.
Tre cose che si possono fare con un archivio privilegiato, e due sono sbagliate in silenzio
I container non privilegiati sono l’impostazione sicura predefinita, e la piattaforma non ha modo di esprimere altro. Quindi, quando un archivio si rivela contenere un container privilegiato, l’agente ha esattamente tre opzioni.
La prima è mandare comunque il flag del non privilegiato. Proxmox applica allora una mappatura di identità sul filesystem ripristinato; la descrizione del parametro dice essa stessa che il campo non andrebbe modificato a mano. Il ripristino riesce, l’attività riferisce che è andato tutto bene, e il container non funziona.
La seconda è omettere il campo. Proxmox prende allora il valore dall’archivio, e un container privilegiato entra nel parco: uno che condivide il kernel dell’host con i privilegi di root, cioè proprio la cosa che la politica esiste per impedire.
La terza è rifiutare, con una frase che dice che cosa c’è nell’archivio. È quello che succede, e succede prima che si scriva qualunque cosa. Non viene mandata alcuna richiesta di creazione del container.
Leggere la configurazione che sta dentro l’archivio
La misura è una sola chiamata all’endpoint di estrazione per quell’archivio. Vale la pena conoscere tre dettagli.
La risposta è testo e non JSON. È il file di configurazione del container, una chiave e un valore per riga, esattamente come sta su disco, quindi serve un piccolo parser proprio accanto a quello che gestisce il JSON dell’API.
E l’assenza della chiave significa il contrario di ciò che di solito significa un’assenza. Proxmox scrive la riga del non privilegiato soltanto quando è vera. Una configurazione che non ce l’ha descrive un container privilegiato. Qualunque lettore che tratti «non trovato» come «ignoto, probabilmente va bene» lascia passare esattamente il caso per cui il controllo è stato costruito, quindi il confronto è generoso su ciò che accetta come vero e rifiuta tutto il resto.
Se la configurazione non si riesce a leggere affatto, anche il ripristino viene rifiutato, e la citazione di Proxmox torna indietro insieme al rifiuto. Di solito è un messaggio sui permessi, perché l’endpoint di estrazione vuole i diritti di backup sul guest e i diritti di allocazione sullo storage dei backup. Rifiutare qui ha un costo reale: un token API a cui manca un permesso non può ripristinare un container, e la ragione è un controllo anziché un limite. Resta comunque il lato giusto su cui sbagliare, perché la domanda aperta che ci sta dietro ha due risposte sbagliate in silenzio.
La stessa lettura risponde a una seconda domanda
La configurazione dentro l’archivio dice anche quali punti di mount ne facessero parte, e le regole di Proxmox in materia non sono quelle che la gente si aspetta:
| Che cosa dice la configurazione | Nell’archivio? | Perché |
|---|---|---|
| Il root filesystem | Sempre | Non ha alcuna opzione di backup; il campo non è nel suo schema |
| Un volume gestito con l’opzione di backup attiva | Sì | Abilitato esplicitamente |
| Un volume gestito senza di essa | No | L’opzione va attivata perché un punto di mount sia incluso |
| Una directory dell’host | No | Il contenuto dei bind mount point non viene salvato |
| Un dispositivo a blocchi dell’host | No | Lo stesso, per i punti di mount di dispositivo |
La terza riga è quella pericolosa. Sul disco di una macchina virtuale l’esclusione è l’eccezione e l’inclusione è l’impostazione predefinita; su un punto di mount di container è il contrario. Sbagliare il verso produce una tabella di copertura che dice «nel backup» della configurazione più diffusa che esista, il che è peggio che non avere alcuna tabella, perché è una promessa.
Le sezioni di snapshot nella configurazione vengono saltate. Un container con cinque snapshot contribuirebbe altrimenti con cinque versioni dello stesso root filesystem, ciascuna da un momento arbitrario.
Una sola chiamata serve entrambe le domande anziché due, perché due chiamate potrebbero descrivere due archivi diversi. E il verdetto viene calcolato sulla piattaforma, non dall’agente: l’agente riferisce le righe come le ha trovate, e non esiste alcun campo in cui potrebbe affermare una copertura completa nemmeno volendo. Dimostrabilmente fuori dal backup batte non misurato, quindi un container con un bind mount e una riga illeggibile è parziale anziché sconosciuto, e la riga illeggibile mantiene la propria parola nella tabella.
C’è un flag che dice se la configurazione sia stata letta o no, e non è decorazione. Un agente che non è riuscito a leggerla restituisce un elenco vuoto, e un elenco vuoto è indistinguibile da un container senza punti di mount, salvo che il secondo non esiste.
Quando nessuno può dire che cosa sia successo
Un ripristino che parte e poi perde la propria risposta, perché l’agente si è riavviato o la connessione è caduta, non deve essere indovinato. Le fonti di prova sono tre: l’indice degli archivi, la macchina che sarebbe dovuta comparire e lo stato dell’attività di Proxmox. Solo la terza può stabilire un fallimento; le altre due possono soltanto confermare un successo, perché un archivio assente e una macchina assente sono entrambi ugualmente compatibili con un ripristino ancora in corso.
Quel ragionamento avviene sulla piattaforma. L’interrogazione dell’attività da parte dell’agente riporta soltanto la citazione di Proxmox e non trae alcuna conclusione propria, perché per decidere a che cosa portino insieme le tre fonti servono l’esecuzione e l’intenzione da cui è partita, una accanto all’altra, e solo la piattaforma ha entrambe.
Non si ritenta all’infinito. C’è un contatore con un tetto di otto tentativi, e all’intervallo predefinito di sei ore questo risulta di circa due giorni. I tentativi in cui l’attività era dimostrabilmente ancora in corso non contano contro di esso, perché a un backup grande su una rete lenta è concesso di metterci una notte. Quando il contatore si esaurisce l’esecuzione diventa sconosciuta, e mai fallita. Fallita sarebbe un’affermazione che nessuno ha verificato, e completata affermerebbe un archivio che nessuno ha visto.
Che cosa metteremmo per iscritto
- Non mandare un valore per una proprietà che esiste già nell’input. La si legga, e poi la si confermi oppure si rifiuti.
- Scoprire che cosa significhi una chiave assente prima di scrivere il parser. Qui significa il valore non sicuro, che è il contrario della convenzione abituale.
- Rifiutare quando la misura non è disponibile, dicendo ad alta voce quanto costa, perché qualcuno ci sbatterà contro.
- Far rispondere a una sola lettura tutte le domande a cui può rispondere. Due chiamate possono descrivere due stati diversi.
- Mettere il giudizio dove sta il contesto. Un osservatore che non può esprimere un verdetto non può sbagliarlo.
- Dare a un ritentativo esaurito una parola terminale propria. Sconosciuto è onesto; fallito e completato sono entrambi affermazioni che nessuno ha fatto.
Per chiarezza su che cosa sia questo: i backup dei clienti stanno dietro un flag di rollout disattivato per impostazione predefinita, quindi niente di tutto questo è acquistabile oggi. E il limite importante sulle prove: nessun backup e nessun ripristino reale è stato eseguito contro un hypervisor dal vivo. Il comportamento dei parametri qui sopra è stato letto dalla sorgente e dalla documentazione di Proxmox stesso, e il codice è stato esercitato contro un server sostitutivo che risponde agli stessi endpoint. Anche la chiamata di estrazione non è stata provata contro un archivio su un server di backup dedicato, ma soltanto contro storage basato su file.
Dal lavoro sui backup di Provibr. Perché un archivio che nessuno ha ripristinato non sia ancora un backup è in un backup mai ripristinato non è un backup, che cosa garantiscano davvero le diverse modalità di backup è in le garanzie dietro a ciascuna modalità di vzdump, e perché reinstallare un container sia una distruzione e una ricostruzione è in reinstallare un container è una sostituzione.