Un server di laboratorio, tre ruoli di fiducia, un solo dominio di guasto
Un ingresso VPN, servizi privati e attività CI hanno bisogno di privilegi separati anche su un solo server. Separa i ruoli, isola le reti, mantieni il recupero da console.
Tutto funziona finché l’esecutore di build non ha bisogno dello stesso socket host usato dall’ospite VPN, e allora nessuno sa spiegare perché un’attività possa leggere le chiavi del gateway. Tutto è stato installato su una sola macchina perché ci stava, e a ogni ospite è stato dato un accesso ampio perché era il modo più rapido per farlo funzionare. Il risultato è un host la cui frontiera di fiducia è il cavo di alimentazione. Separare i ruoli costa meno lavoro di quanto sembri, e comincia dal dare loro un nome.
Tre ruoli che al momento condividono tutto
Un ingresso VPN, un insieme di servizi privati e un esecutore di build vogliono cose diverse. Il primo ha bisogno di una porta pubblica e di una rete stretta sul lato privato. Il secondo ha bisogno di archiviazione persistente e di segreti dell’applicazione. Il terzo esegue codice scritto da qualcun altro.
È proprio dare lo stesso accesso a tutti e tre che rende confuso il sintomo, perché poi ciascuno può influenzare gli altri. Un’attività di build che raggiunge il socket dell’hypervisor è un’attività che può fermare la VPN. Un ospite dei servizi con una rotta verso la rete di gestione può cambiare la configurazione dello switch. I privilegi sono il problema, e l’elenco dei processi è solo il punto in cui si vede. Dare un nome ai ruoli per primo dà a ogni privilegio un posto dove vivere.
Lo schizzo qui sotto è un punto di partenza per quella separazione, non una promessa di alta disponibilità. Trattalo come un’ipotesi da verificare, non come il progetto finito.
Separa tre ruoli, non tre processi
Tre ospiti su un host danno tre insiemi di privilegi e tre posizioni di rete. Non danno tre domini di guasto, e il progetto dovrebbe dirlo ad alta voce.
La separazione aiuta solo se una compromissione in un ospite non concede automaticamente gli altri, e questa è un’impostazione dell’hypervisor, non una convenzione di nomi. Chiamare gateway una VM non impedisce a un altro ospite di raggiungere l’host. Leggi la configurazione dello switch virtuale invece del nome host, poi conferma che ogni ospite stia dove pensi.
host
├─ vpn-gw 10.60.10.2/24 public 51820/udp only
├─ services 10.60.0.10/24 no route to the management network
└─ ci-runner 10.60.20.2/24 outbound only, no host socket
Scrivi la topologia e poi verificala con gli strumenti che la possiedono. Un ospite che compare sul bridge sbagliato è una constatazione, non un dettaglio. Correggi la rete prima di cambiare qualsiasi configurazione di servizio.
Lascia che l’ospite VPN apra solo le porte che possiede
Pubblica la porta del tunnel sul router e nient’altro. Le porte di amministrazione degli ospiti appartengono a un percorso di gestione, non a internet, e anche l’interfaccia dell’hypervisor appartiene lì.
La rete sul lato privato dovrebbe raggiungere i servizi che servono agli utenti remoti e fermarsi lì. L’autenticazione dell’applicazione resta attiva, perché un tunnel è trasporto e non un accesso. Mantieni la tabella di routing dell’ospite VPN abbastanza piccola da descriverla in una frase. Se non riesci a descriverla con quella semplicità, la frontiera si è già sfumata.
virsh net-list --all
virsh net-dumpxml lab-private
sudo nft list ruleset | grep -n -E '51820|10.60'
Qualunque cosa stia in ascolto sull’host e non serva alla gestione non dovrebbe ascoltare affatto. Questo include l’API dell’hypervisor, che è facile lasciare esposta su un’interfaccia pubblica.
Le attività di build hanno bisogno di un altro tipo di frontiera
Un’attività CI esegue comandi controllati dal repository, quindi la sua fiducia dipende da chi può pubblicare codice. Un esecutore shell esegue quei comandi come utente dell’esecutore sull’host, e questa è la frontiera più ampia disponibile.
Gli esecutori di container sono più stretti, e un container privilegiato non è tra quelli stretti. Mantieni l’esecutore nel proprio ospite con la propria portata di rete, e dagli solo l’accesso agli artefatti e al deployment che servono alle attività. I lavoratori usa e getta riducono lo stato conservato, ma non riducono la raggiungibilità né l’esposizione dei segreti. Entrambe hanno ancora bisogno di limiti espliciti.
L’ospite dei servizi possiede i dati, quindi il suo piano di backup è quello che deve funzionare. Mantieni repository, caricamenti e database nello stesso calendario. Prova un ripristino invece di leggere lo stato dell’attività.
Pianifica il guasto che tutti e tre condividono
Un host è un dominio di guasto. Un riavvio rimuove il tunnel e ogni servizio privato nello stesso istante, ed è proprio allora che una via d’ingresso conta di più.
Mantieni un accesso alla console che non dipenda dall’ospite VPN, che sia una console fisica, un’interfaccia di gestione fuori banda o una console del provider. Un’istantanea salvata sullo stesso disco non è una copia indipendente, e non lo è un backup che gira dentro l’ospite che protegge. Poi prova l’avvio a freddo come sequenza e non come lista: host, reti virtuali, percorso del tunnel e un’applicazione denominata sotto un account ordinario.
virsh list --all
systemctl is-active libvirtd
curl -sS -m 5 -o /dev/null -w '%{http_code}\n' https://10.60.0.10/
ss -ltnp | grep -E ':51820|:443'
L’ordine di avvio dei processi non dimostra nulla su rotte e DNS, quindi prova il percorso invece degli stati delle unità. Il percorso è ciò di cui un utente remoto ha davvero bisogno.