[{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/ansible/","section":"Tags","summary":"","title":"Ansible","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/categories/architecture/","section":"Categories","summary":"","title":"Architecture","type":"categories"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/architecture/","section":"Tags","summary":"","title":"Architecture","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/backup/","section":"Tags","summary":"","title":"Backup","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/crisp/","section":"Tags","summary":"","title":"Crisp","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/devops/","section":"Tags","summary":"","title":"Devops","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/categories/devops/","section":"Categories","summary":"","title":"DevOps","type":"categories"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/disaster-recovery/","section":"Tags","summary":"","title":"Disaster Recovery","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/hetzner/","section":"Tags","summary":"","title":"Hetzner","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/categories/infrastructure/","section":"Categories","summary":"","title":"Infrastructure","type":"categories"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/infrastructure/","section":"Tags","summary":"","title":"Infrastructure","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/podman/","section":"Tags","summary":"","title":"Podman","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/","section":"Roberto Tazzoli","summary":"","title":"Roberto Tazzoli","type":"page"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/s3/","section":"Tags","summary":"","title":"S3","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/tailscale/","section":"Tags","summary":"","title":"Tailscale","type":"tags"},{"content":" Terraforming the Cloud: Provisioning e configurazione di Vault su Hetzner via Terraform e Ansible # Ci sono sessioni infrastrutturali in cui il valore principale non è il numero di file modificati, ma la conferma che il metodo di lavoro sta funzionando davvero. Questa è una di quelle.\nNegli ultimi giorni avevo separato in modo molto netto due fasi del runtime Vault su Hetzner. La prima, hetzner-vault-local-lifecycle (C1), aveva l\u0026rsquo;obiettivo di dimostrare che il nodo poteva esistere come entità locale coerente: TLS, storage Raft, bootstrap rigoroso, unseal automatico e contratti di identità chiari. La seconda, hetzner-vault-s3-backup-recovery (C2), doveva aggiungere durabilità remota e recovery: snapshot periodici, puntatori coerenti su S3, logica di confronto, riparazione della durabilità remota e, soprattutto, percorso di restore quando il nodo locale non esiste più ma la verità crittografica nel controller e i backup remoti sono ancora sani.\nA livello di cronologia, sembrano due lavori separati. A livello reale, però, sono stati un unico percorso. Per circa tre giorni ho lavorato quasi esclusivamente di progettazione: review, raffinamento, chiarimento di contratti, definizione di nomenclature, decision matrix, responsabilità fra Ansible e shell helper, comportamento in caso di stato ambiguo, flusso di bootstrap, ruolo di TazPod, struttura dei receipt, limiti del restore. Quando poi è arrivato il momento di implementare, il lavoro duro era già stato fatto. La parte di esecuzione si è compressa in poche ore e, soprattutto, si è svolta con una fluidità molto diversa da quella tipica dei lavori di questo tipo.\nQuesto non significa che non ci siano stati problemi. Ce ne sono stati, e alcuni anche istruttivi. Ma il tipo di problema è cambiato. Non mi sono trovato a rimettere in discussione l\u0026rsquo;architettura nel mezzo della sessione. Mi sono trovato invece davanti a problemi di integrazione, di dettagli operativi, di comportamento reale di tool come Podman, systemd, Tailscale e Vault. È una differenza enorme. Quando il design è solido, anche l\u0026rsquo;imprevisto smette di essere caos e diventa semplicemente un\u0026rsquo;anomalia da isolare e correggere.\nIn questo articolo racconto l\u0026rsquo;intero passaggio: dal lifecycle locale fino al backup remoto su S3, con la verifica live della rotazione degli snapshot, la prova che lo stato remoto può essere inizializzato e riparato a partire dalla verità locale, il test reale del caso “unchanged snapshot”, i cicli distruttivi di restore e, soprattutto, la chiusura completa della matrice C2. Alla fine del lavoro non è rimasto un ramo “quasi pronto”: la VM è coerente, Vault è attivo, TazPod è coerente, S3 è coerente, il timer di backup è vivo e l\u0026rsquo;intero set di scenari progettati per C2 è stato eseguito e portato a verde.\nIl punto di partenza: una C1 già credibile, non un prototipo fragile # Il primo elemento importante da capire è che C2 non è stata costruita sopra una base improvvisata. Il lavoro su C1 aveva già eliminato gran parte dell\u0026rsquo;entropia iniziale.\nIl nodo Vault su Hetzner non era più un semplice container “che parte”. Era già un runtime con una propria identità ben definita. Il nodo host era lushycorp-vm.ts.tazlab.net, il servizio TLS di Vault era lushycorp-api.ts.tazlab.net, i path persistenti erano stabili, la configurazione TLS era chiara, il bootstrap produceva un vault_lineage_id, il receipt locale raccontava l\u0026rsquo;identità del cluster e il meccanismo di unseal automatico aveva già una forma precisa.\nQuesta distinzione è fondamentale anche da un punto di vista metodologico. Molti problemi di backup e restore nascono perché si tenta di progettare la recovery remota quando il sistema locale non è ancora definito rigorosamente. In quel caso, il layer remoto eredita ambiguità già presenti nel layer locale e le amplifica. Qui è successo l\u0026rsquo;opposto: il lavoro fatto su C1 ha ridotto i gradi di libertà. Quando ho iniziato C2, non dovevo più decidere “cosa sia” il nodo Vault. Dovevo solo decidere come estendere in modo rigoroso un\u0026rsquo;identità già definita.\nQui vale la pena chiarire subito un termine che nel resto dell\u0026rsquo;articolo uso spesso: lineage. Con vault_lineage_id intendo l\u0026rsquo;identità stabile di una specifica storia di vita del Vault, cioè la “linea genealogica” di quell\u0026rsquo;istanza: nasce al primo bootstrap, viene conservata nei receipt locali e negli artifact canonici in TazPod, e serve a distinguere un vero restore della stessa istanza da una nuova inizializzazione che produce invece una nuova identità. In pratica, se ricreo il nodo ma sto davvero riportando in vita lo stesso Vault, la lineage resta la stessa. Se invece faccio un fresh init e nasce un Vault nuovo, nasce anche una lineage nuova.\nPer questo la divisione in fasi si è rivelata così utile. foundation, local lifecycle, remote durability non sono stati solo nomi di comodo. Sono stati veri confini diagnostici. Se qualcosa si rompeva nella durabilità remota, sapevo già che non stavo discutendo TLS, Tailscale di base, Podman runtime elementare o bootstrap locale. Questo riduce drasticamente il rumore quando si leggono i log e si devono prendere decisioni rapide.\nIl vero valore dei tre giorni di design # La parte più interessante di questa sessione, almeno per me, non è stata tanto la scrittura del codice Ansible o dei helper script. È stato constatare in modo molto concreto che i tre giorni di design avevano davvero trasformato la sessione di implementazione.\nIl punto non è semplicemente che “si è andati più veloci”. La velocità, in infrastruttura, da sola non dice molto. Si può andare veloci anche nella direzione sbagliata. Il punto è che la sessione si è svolta senza le tipiche rotture di continuità che capitano quando si programma l\u0026rsquo;architettura mentre si scrive il codice. Non ho dovuto fermarmi a metà per chiedermi se il bucket dovesse contenere l\u0026rsquo;ultimo snapshot globale o l\u0026rsquo;ultimo snapshot per lineage. Non ho dovuto ridefinire cosa fosse un restore “lecito”. Non ho dovuto decidere in corsa se l\u0026rsquo;admin token andasse ricreato sempre o solo in certi casi. Tutte queste scelte erano già state esplicitate.\nQuesto ha avuto un effetto molto pratico: quando emergeva un problema, il problema era confinato. Se la service unit di backup non passava le variabili giuste, la correzione era locale. Se il path degli snapshot non era montato nel container, la correzione era locale. Se due snapshot logicamente identici avevano hash binari diversi, il problema non diventava improvvisamente una crisi della strategia di backup; diventava un affinamento del contratto di confronto. Questa differenza fra “problema confinato” e “problema sistemico” è il motivo per cui considero questa sessione un successo.\nIn termini più didattici: la progettazione preventiva non elimina i bug, ma trasforma il tipo di bug che incontri. Riduce il rischio di bug architetturali, cioè quelli che obbligano a cambiare modello mentale a metà lavoro. Quello che resta sono i bug di integrazione, di comportamento reale, di interfaccia fra componenti. Sono comunque fastidiosi, ma molto più trattabili.\nC2 in pratica: cosa doveva fare davvero # La seconda fase del progetto non doveva limitarsi a “salvare file su S3”. Una formulazione così semplice sarebbe stata pericolosamente incompleta. Il vero obiettivo era introdurre durabilità remota coerente senza confondere il concetto di backup con quello di identità.\nUn Vault locale coerente produce una certa verità crittografica: chiavi di unseal, token amministrativi, lineage, stato Raft. Un backup remoto utile non è semplicemente un blob di dati; è un artefatto che deve poter essere riconnesso in modo affidabile a quella stessa identità. Da qui nasce il contratto dei pointer e del metadata. Non basta caricare uno snapshot. Bisogna sapere quale lineage rappresenta, quale slot è attivo, a quale hash corrisponde, e quale sia il candidato corretto da usare in fase di restore.\nPer questo ho implementato tre livelli distinti nel bucket:\nun global pointer (vault/raft-snapshots/latest.json) che indica il lineage attivo; un lineage-local pointer (vault/raft-snapshots/\u0026lt;vault_lineage_id\u0026gt;/latest.json) che indica il candidato attuale di restore per quella lineage; due slot remoti (slot-a e slot-b) che permettono una rotazione semplice e leggibile. Questa struttura è stata una scelta importante anche per la leggibilità operativa. In fase di incident response, un sistema elegante ma opaco è spesso peggiore di un sistema leggermente più verboso ma trasparente. Qui volevo che un operatore, leggendo gli oggetti in S3 o i log locali, potesse capire quale fosse lo stato attivo senza dover “indovinare” a partire da convenzioni implicite.\nL\u0026rsquo;implementazione del runtime C2 # L\u0026rsquo;implementazione materiale si è distribuita su una superficie abbastanza ampia, ma molto ordinata. Ho aggiunto un playbook dedicato (vault-s3-backup-recovery.yml), nuovi file task nell\u0026rsquo;Ansible role condiviso, due helper shell dedicati per backup e restore, e nuove unità systemd per il timer orario e per il restore esplicito.\nUn aspetto importante del design era la divisione di responsabilità tra Ansible e helper shell. Gli helper non dovevano “decidere” il comportamento del sistema. Dovevano eseguire meccanicamente operazioni ristrette: salvare uno snapshot, calcolare hash, leggere o scrivere oggetti S3, eseguire il primitive di restore quando già autorizzato. La visibilità delle scelte — restore sì/no, failure sì/no, lineage selezionata, necessità di recreation dell\u0026rsquo;admin token — doveva rimanere nei task Ansible. Questo non è solo un vezzo stilistico. È una scelta che migliora auditabilità e debugging. Una macchina a stati che vive in task separati è molto più leggibile di una shell script che ingloba tutto e restituisce un generico codice di uscita.\nSul nodo host sono comparsi anche nuovi punti fissi operativi:\n/etc/lushycorp-vault/s3.env per le credenziali S3 root-only; /etc/lushycorp-vault/remote-restore.env per il restore request contract; /etc/lushycorp-vault/snapshot-backup-token.txt per il token limitato al backup; /var/log/lushycorp-vault/vault-snapshot-backup.log; /var/log/lushycorp-vault/vault-remote-restore.log. Questo dettaglio dei path è meno banale di quanto sembri. In sessioni lunghe o distribuite su più giorni, la differenza fra un sistema “osservabile” e uno che costringe a intuire lo stato dai sintomi secondari è enorme. Qui ogni fase importante ha un suo log noto prima dell\u0026rsquo;avvio. Quando qualcosa non andava, non ero costretto a ricostruire ex post dove potesse essere fallito. Potevo leggerlo direttamente.\nI primi problemi: buoni problemi, non problemi architetturali # Il primo inciampo reale non ha riguardato Vault, ma il nodo operatore. La prima esecuzione C2 è fallita in fase di validazione Tailscale perché il sistema si aspettava il socket standard di tailscaled, mentre l\u0026rsquo;operatore locale stava usando una istanza userspace con socket dedicato in /tmp/tailscaled-operator.sock.\nIl punto interessante non è tanto il fix — rilanciare create.sh con TAILSCALE_SOCKET=/tmp/tailscaled-operator.sock — quanto il fatto che il problema sia stato immediatamente leggibile e confinato. Il phase log dedicato alla validazione Tailscale mostrava chiaramente il fallimento del path locale. Non c\u0026rsquo;è stato nessun effetto domino ambiguo su Terraform, Ansible o Vault. Questo è esattamente il tipo di comportamento che ci si aspetta da un\u0026rsquo;orchestrazione ben separata in fasi.\nSubito dopo sono emersi altri due problemi tipici da integrazione:\nla service unit del backup non passava ancora tutte le variabili operative necessarie (S3_BUCKET, S3_PREFIX, ecc.); il path degli snapshot esisteva sull\u0026rsquo;host ma non era montato nel container Vault. Entrambi sono stati risolti senza dover cambiare il modello. Ho corretto i template systemd per passare esplicitamente l\u0026rsquo;environment richiesto e ho aggiunto il mount della snapshot directory nella service unit del container. Questo è il tipo di lavoro che in una sessione poco preparata rischia di innescare dubbi più ampi (“forse il design del backup è sbagliato”). Qui invece era chiaro fin da subito che si trattava di un difetto locale di wiring.\nIl backup iniziale verso S3: prima prova reale della fase C2 # Una volta corretta la parte di wiring, il primo backup reale ha fatto quello che mi aspettavo da C2: ha trattato il layer remoto come autoritativamente ricostruibile a partire da una verità locale sana.\nQuesto punto merita una spiegazione. Nel modello che avevo definito, un S3 “vuoto” o “incoerente” non deve bloccare un Vault locale già coerente. Se il nodo locale è sano e TazPod è sano, il layer remoto non è la sorgente primaria di verità: è la durabilità secondaria. Di conseguenza, il backup successivo deve poter inizializzare o riparare il contenuto remoto senza trasformare un problema di backup in un blocco totale del runtime.\nEd è esattamente ciò che è successo. Il primo backup riuscito ha:\nclassificato il remote state; scritto snapshot e metadata su S3; creato il global pointer; creato il lineage-local pointer; impostato il primo slot attivo. Questa non è una vittoria puramente “meccanica”. È la prova che la distinzione fra verità locale e durabilità remota era stata modellata correttamente. Se il design fosse stato più confuso, il sistema avrebbe potuto tentare restore assurdi, bloccare il nodo per prudenza eccessiva, o scrivere oggetti remoti privi di contesto sufficiente per un futuro rebuild.\nGenerare uno stato distinguibile: il marker marker-A # Per evitare test troppo astratti, ho voluto introdurre uno stato applicativo chiaramente riconoscibile dentro Vault. Ho quindi scritto un marker nel KV store, con identificatore marker-A e scenario baseline-before-matrix.\nPerché è importante? Perché i test di backup e restore non devono fermarsi al livello infrastrutturale. Sapere che Vault è “up” o “unsealed” non basta. In un sistema di segreti, la vera domanda è: quali dati contiene esattamente questa istanza? Se dopo un rebuild il sistema torna up ma ha perso o cambiato i dati, il test è fallito anche se systemd è felice.\nQuesto marker ha avuto due usi molto concreti:\nha reso visibile la differenza tra snapshot di stati diversi; ha fornito un riferimento da rileggere dopo i cicli distruttivi. È un piccolo dettaglio, ma rappresenta bene il tipo di approccio che preferisco nelle validazioni: evitare test puramente sintattici e introdurre almeno un segnale funzionale leggibile che permetta di dire “questo è davvero lo stesso Vault logico che mi aspettavo di recuperare”.\nLa scoperta più interessante: due snapshot uguali logicamente, ma diversi come file # Il momento tecnicamente più istruttivo della sessione è arrivato quando ho verificato il comportamento del caso “unchanged snapshot”. Il contratto iniziale prevedeva una logica intuitiva: se l\u0026rsquo;hash del file snapshot corrente è uguale a quello dell\u0026rsquo;ultimo snapshot remoto, l\u0026rsquo;upload può essere saltato.\nSulla carta è ragionevole. Nella pratica, si è rivelato falso.\nHo eseguito un controllo di determinismo salvando due snapshot consecutivi senza modificare lo stato logico di Vault. Mi aspettavo file identici. Invece ho ottenuto:\nstesso contenuto logico rilevato da vault operator raft snapshot inspect; stesso indice Raft; hash file diversi. Questa è una differenza importantissima. Significa che il file binario di snapshot incorpora abbastanza variabilità da non poter essere usato come criterio affidabile per dire “lo stato logico è rimasto uguale”. Se avessi lasciato il sistema così, ogni run avrebbe continuato a caricare snapshot nuovi anche in assenza di vere modifiche.\nLa correzione che ho introdotto è stata semplice nel concetto ma molto importante nel risultato: ho separato integrità del file e equivalenza logica.\nsnapshot_sha256 continua a descrivere il file preciso caricato su S3; snapshot_compare_fingerprint viene calcolato a partire dall\u0026rsquo;output di vault operator raft snapshot inspect ed è usato per capire se lo stato logico è cambiato davvero. Dopo questo cambiamento, il test che prima avrebbe prodotto un falso positivo di “changed snapshot” ha finalmente restituito il comportamento corretto: upload-skipped. Per me questo è uno dei punti più riusciti dell\u0026rsquo;intera sessione, perché è un esempio perfetto di come un test reale possa migliorare il design senza distruggerlo. Il modello generale non era sbagliato. Aveva solo bisogno di un confronto più adatto alla semantica reale di Vault.\nIl punto di svolta finale: chiudere davvero il restore T1 + H0 + S1 # Dopo aver validato il percorso di backup, sono passato alla parte più delicata: il caso T1 + H0 + S1, cioè TazPod coerente, host locale vuoto, S3 coerente. In termini pratici: il nodo viene distrutto, ma il controller possiede ancora il set canonico di bootstrap e S3 possiede un candidate di restore coerente. Questo è il cuore del disaster recovery per la fase C2.\nQuando ho scritto la prima versione di questo articolo, quel ramo non era ancora chiuso fino in fondo. I test distruttivi avevano già dimostrato che il restore veniva selezionato correttamente, che il lineage veniva risolto nel modo giusto e che il sistema arrivava molto avanti nella ricostruzione. Restavano però due difetti reali che impedivano di dichiarare la matrice verde.\nIl primo era un problema di classificazione del remote state. In alcune condizioni di oggetto mancante su S3, il codice non conservava correttamente la distinzione tra empty e incoherent. Il risultato era sottile ma importante: un lineage-local pointer assente poteva essere trattato nel ramo sbagliato. Il fix è stato piccolo come modifica di shell, ma grande come conseguenza operativa: ho corretto la cattura dell\u0026rsquo;exit code e reso affidabile la lettura dei 404 di S3 sia nel path di restore sia in quello di backup.\nIl secondo era il problema veramente decisivo: dopo il restore il nodo non ricostruiva ancora in modo completamente autonomo il proprio local-unseal path host-side. In pratica, Vault poteva essere riportato fino allo stato corretto, ma le due unseal share locali non venivano sempre reidratate e il servizio di unseal oneshot poteva concludersi troppo presto durante la finestra in cui il container non era ancora nel punto giusto del bootstrap post-restore.\nQui il lavoro utile non è stato “aggiungere retry a caso”, ma rispettare il contratto C1/C2 già definito:\nil restore C2 ora reidrata esplicitamente sul nodo host unseal-share-1 e unseal-share-2 a partire dal set canonico conservato in TazPod; la logica di vault-local-unseal.sh ora distingue meglio il caso in cui Vault non è ancora inizializzato ma il materiale di unseal esiste già localmente, evitando di dichiarare successo troppo presto; il playbook di convergenza non si limita più a confidare nel solo oneshot systemd: rilancia in modo esplicito l\u0026rsquo;helper di local unseal dopo il restore, così il check finale di stato avviene davvero dopo la ricostruzione del path di unseal. Dopo questi fix, il ramo T1 + H0 + S1 è passato fino in fondo. Il nodo viene distrutto, ricreato, Vault viene ripristinato dal candidate corretto su S3, il receipt locale viene aggiornato, le unseal share host-side tornano presenti, il local-unseal riprende correttamente e il Vault finale torna initialized=true e sealed=false senza riconciliazione manuale.\nIl segnale più importante, comunque, è rimasto lo stesso: dopo la parte distruttiva e il restore completo, il contenuto logico atteso era ancora lì. Non stavo ottenendo un Vault “vivo ma nuovo”; stavo davvero recuperando l\u0026rsquo;istanza logica che volevo riportare in linea.\nDalla mezza vittoria alla matrice completa verde # La differenza tra una sessione promettente e una sessione chiusa sta tutta qui: a un certo punto smetti di dire “il modello sembra giusto” e inizi a poter dire “la matrice progettata è passata davvero”. È esattamente quello che è successo nel passaggio finale di C2.\nDopo il primo blocco di implementazione e i primi test live, avevo già prove forti su backup, pointer, repair e confronto semantico degli snapshot. Il lavoro finale ha trasformato quelle prove parziali in un set completo di scenari eseguiti uno per uno.\nIn pratica sono stati chiusi tutti i casi progettati per T7.\nPer leggere rapidamente la matrice: T indica lo stato del set canonico in TazPod, H lo stato del nodo host/Vault locale, S lo stato del layer remoto su S3. Il suffisso 0 significa empty, 1 significa coherent, 2 significa incoherent. Quindi T0 = TazPod vuoto, T1 = TazPod coerente, T2 = TazPod incoerente; H0 = host/Vault locale vuoto, H1 = host/Vault locale coerente, H2 = host/Vault locale incoerente; S0 = S3 vuoto, S1 = S3 coerente, S2 = S3 incoerente.\nT0 + H0 + S0 -\u0026gt; fresh init consentito; T0 + H0 + S1 -\u0026gt; hard fail perché manca l\u0026rsquo;anchor canonico in TazPod; T1 + H0 + S1 -\u0026gt; restore riuscito durante create.sh; T1 + H0 + S0 -\u0026gt; hard fail, nessun fake restore; T1 + H0 + S2 -\u0026gt; hard fail; T1 + H1 + S0 -\u0026gt; il backup inizializza correttamente il layer remoto; T1 + H1 + S2 -\u0026gt; il backup ripara correttamente il layer remoto da verità locale coerente; unchanged run -\u0026gt; upload-skipped reale; first valid backup into remote-empty lineage -\u0026gt; scrittura su slot-a + lineage-local pointer; changed run on coherent lineage -\u0026gt; switch sullo slot inattivo; pointer mancante con slot ancora presenti -\u0026gt; restore hard-fail e repair successivo tramite backup; metadata mismatch -\u0026gt; hard fail esplicito; TazPod incoerente -\u0026gt; hard fail; host locale incoerente -\u0026gt; hard fail. Questo passaggio è importante anche concettualmente. Finché una matrice resta parzialmente aperta, il sistema è ancora “promettente”. Quando invece hai coperto anche i casi brutti — pointer assente, metadata corrotti, lineage mismatch, stato locale incoerente — il sistema smette di essere solo convincente in demo e inizia a diventare credibile in esercizio.\nIl risultato più bello: gli imprevisti finali hanno confermato il design, non l\u0026rsquo;hanno demolito # Paradossalmente, i problemi emersi nell\u0026rsquo;ultima parte sono la prova migliore che i giorni di design sono serviti davvero.\nSe il modello fosse stato fragile, questi ultimi test avrebbero costretto a rimettere mano alla strategia generale: magari cambiare la struttura dei pointer, cambiare il rapporto fra TazPod e S3, o riscrivere la semantica dei casi empty/incoherent. Invece non è successo. I problemi si sono rivelati esattamente del tipo che speravo di incontrare in una sessione ben preparata: problemi locali, leggibili, confinati.\nun bug nel capture dell\u0026rsquo;exit code di shell; un problema preciso nella ricostruzione del materiale host-side dopo restore; un timing troppo ottimistico nel local-unseal post-restore. Sono problemi veri, ma non sono problemi architetturali. E questa, per me, è la differenza tra una sessione caotica e una sessione ingegnerizzabile.\nLo stato finale lasciato volutamente sano e coerente # Alla fine del lavoro non ho lasciato dietro di me una macchina “abbastanza buona per smettere di testare”. Ho riconciliato esplicitamente l\u0026rsquo;ambiente fino a uno stato finale pulito, coerente e riusabile.\nLo stato conclusivo è questo:\nVM Hetzner attiva e raggiungibile; lushycorp-vault.service attivo; vault-local-unseal.service attivo; Vault inizializzato e unsealed; TazPod coerente con il set canonico di artifact; S3 coerente con global pointer e lineage-local pointer validi; timer di backup attivo; log principali presenti e consultabili; lineage canonica finale riallineata su d91c4d14-30a6-4518-b162-d1c1a1b9c069. C\u0026rsquo;è un dettaglio che considero importante raccontare apertamente: durante i test di fresh init è stata generata anche una nuova lineage temporanea. Sarebbe stato facile considerarla “rumore di laboratorio” e ignorarla. Invece il lavoro serio è proprio quello di non lasciare rumore in giro. Alla fine della sessione quella lineage temporanea non è stata lasciata come stato operativo: il runtime finale è stato riportato in coerenza con la lineage canonica originale, sia su host che in TazPod e S3.\nQuesta scelta è intenzionale. In un contesto che vuole comportarsi in modo sempre più enterprise, anche la fine della sessione è parte del lavoro. Non basta dimostrare che il test passa. Bisogna lasciare il sistema in una condizione comprensibile e operabile dalla sessione successiva.\nRiflessioni post-lab, adesso che C2 è davvero chiusa # Se dovessi riassumere questa tappa in una sola frase, oggi la formulerei così: la progettazione ha spostato la difficoltà da “capire cosa costruire” a “chiudere con precisione gli ultimi dettagli reali fino a far passare tutta la matrice”.\nÈ esattamente il tipo di risultato che volevo ottenere con CRISP. Non perché il coding debba diventare banale, ma perché il coding dovrebbe essere l\u0026rsquo;ultima fase di una catena di decisioni già mature. In questa sessione il risultato si è visto in modo molto concreto: pochi problemi veramente imprevisti, tutti leggibili dai log, quasi tutti confinati, nessun crollo dell\u0026rsquo;impianto architetturale e nessuna sorpresa davvero distruttiva emersa dal nulla.\nLa differenza rispetto alla prima bozza di questo articolo è che ora non devo più fermarmi a dire “la recovery non è ancora completamente chiusa”. Posso dire qualcosa di più forte e più utile: il backup remoto è reale, il confronto degli snapshot è stato corretto sulla base del comportamento effettivo di Vault, il restore distruttivo è stato chiuso, i casi di hard-fail sono stati verificati, il runtime resta coerente fra TazPod, host e S3 e la fase C2 può essere considerata conclusa.\nQuesto, per una piattaforma di segreti su un singolo nodo Hetzner costruita con Podman, systemd, Tailscale, Ansible e S3, è un risultato molto significativo. Non perché sia “perfetto” in senso assoluto, ma perché ha raggiunto quel punto raro in cui design, implementazione, test distruttivi e stato finale operativo raccontano finalmente la stessa storia.\nProgettare senza fretta non ha eliminato il lavoro. Lo ha reso proporzionato. E, soprattutto, ha reso l\u0026rsquo;implementazione abbastanza lineare da far sembrare naturale qualcosa che, senza quei giorni di design, sarebbe probabilmente degenerato in molte più ore di debugging caotico.\nPer questa fase, è un ottimo posto dove fermarsi: con un Vault vivo, una durabilità remota credibile, una recovery davvero chiusa, una matrice completa portata a verde e la conferma che il tempo speso a pensare prima del codice continua a essere l\u0026rsquo;investimento più redditizio dell\u0026rsquo;intero laboratorio.\n","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/posts/terraforming-the-cloud-provisioning-configuring-vault-hetzner-terraform-ansible/","section":"Posts","summary":"","title":"Terraforming the Cloud: Provisioning e configurazione di Vault su Hetzner via Terraform e Ansible","type":"posts"},{"content":"","date":"12 aprile 2026","externalUrl":null,"permalink":"/it/tags/vault/","section":"Tags","summary":"","title":"Vault","type":"tags"},{"content":"","date":"10 aprile 2026","externalUrl":null,"permalink":"/it/tags/agents/","section":"Tags","summary":"","title":"Agents","type":"tags"},{"content":"","date":"10 aprile 2026","externalUrl":null,"permalink":"/it/tags/ai/","section":"Tags","summary":"","title":"Ai","type":"tags"},{"content":"","date":"10 aprile 2026","externalUrl":null,"permalink":"/it/categories/ai/","section":"Categories","summary":"","title":"AI","type":"categories"},{"content":"","date":"10 aprile 2026","externalUrl":null,"permalink":"/it/tags/context-management/","section":"Tags","summary":"","title":"Context-Management","type":"tags"},{"content":" Memoria ricorsiva, contesto compatto: il tassello che mancava per lavorare bene con gli agenti AI # Il problema dopo aver risolto il problema # Qualche settimana fa avevo scritto un articolo su AGENTS.ctx, il sistema di contesti che uso per lavorare con gli agenti AI senza dover rispiegare tutto da capo a ogni nuova sessione. L\u0026rsquo;idea di base era semplice: invece di aprire una chat vuota e reiniettare ogni volta regole, struttura del progetto, convenzioni operative e stato generale del lavoro, ho organizzato tutto in contesti caricabili on demand. L\u0026rsquo;agente non cambia: cambia il contesto che gli faccio leggere.\nQuella soluzione ha funzionato davvero bene. Non in teoria, ma nel lavoro di tutti i giorni. Apro il contesto tazpod, e l\u0026rsquo;agente sa subito come è fatta la CLI, quali sono i path critici, come si spinge su GitHub, dove stanno i rischi operativi. Apro blog-writer, e l\u0026rsquo;agente sa già come pianificare un articolo, come passare alla scrittura e quando fermarsi per la review. Apro crisp, e il sistema cambia ritmo: non si implementa, si ricerca e si progetta.\nIl punto però è che, una volta risolto il bootstrap operativo, è emerso un secondo problema. Più sottile del primo, ma altrettanto importante. I contesti spiegano come lavorare in un certo dominio. Non sempre spiegano dove siamo arrivati oggi, cosa è successo nelle ultime sessioni, quali debiti sono ancora aperti, qual è la verità corrente del sistema. Per un lavoro che dura settimane o mesi, questa distinzione conta moltissimo.\nIn altre parole: i contesti mi avevano dato il telaio. Mi mancava ancora una memoria attiva, continuamente aggiornata, che fosse abbastanza compatta da stare sempre a portata di mano ma abbastanza ricca da permettere a un agente di ripartire subito dal punto giusto.\nPerché non bastava “avere più memoria” # Quando si lavora con LLM e coding agent, la prima reazione davanti ai problemi di continuità è spesso istintiva: conservare più roba possibile. Tenere transcript più lunghi, più note, più file, più log, più riassunti. In apparenza sembra una buona idea. In pratica, quasi mai lo è.\nIl problema non è solo quantitativo. È strutturale. Se la memoria attiva cresce senza controllo, prima o poi smette di essere uno strumento e diventa rumore. Un agente che deve leggere troppo materiale prima di diventare operativo non è davvero allineato: è semplicemente sommerso. Il costo di bootstrap torna a salire, solo in una forma diversa. Non sto più rispiegando le cose manualmente, ma sto obbligando il sistema a digerire un blocco sempre più grande di contesto eterogeneo.\nQuesto è lo stesso problema che avevo già cercato di evitare progettando AGENTS.ctx: non caricare tutto, caricare solo quello che serve. La stessa filosofia andava estesa anche alla memoria.\nQuindi la domanda non era: come do più memoria all\u0026rsquo;agente? La domanda corretta era: come costruisco una memoria che resti operativa, leggibile, densa, e che non degradi man mano che il progetto va avanti?\nIl passo successivo: una memoria attiva, ricorsiva, ma sempre piccola # La soluzione che ho costruito si chiama semplicemente memory, ed è diventata il livello attivo della continuità operativa dentro AGENTS.ctx. Non è un database esterno. Non è un sistema opaco. Non è una funzione magica del modello. È ancora una volta una struttura di file semplice, versionata su Git, leggibile da qualsiasi agente che sappia leggere del Markdown.\nMa la differenza rispetto ai contesti statici è fondamentale: memory non è solo un insieme di regole. È una memoria viva che cambia nel tempo e che tiene traccia del presente in modo disciplinato.\nIl modello attuale è organizzato così:\nsystem-state.md debts.md past-summary.md chronicle.md past/ scripts/archive-memory.sh A prima vista può sembrare solo un\u0026rsquo;ulteriore cartella di documentazione. In realtà è un sistema di ruoli molto preciso, e la precisione dei ruoli è esattamente ciò che lo rende utile.\nIl ruolo dei file: non un accumulo, ma una gerarchia # La prima decisione importante è stata evitare il file unico onnivoro. Se tutto finisce nello stesso documento, la distinzione tra stato corrente, cronologia, debiti tecnici e riassunto storico si rompe nel giro di poche sessioni. All\u0026rsquo;inizio può sembrare comodo. Dopo qualche giorno diventa ingestibile.\nPer questo ogni file ha una responsabilità stretta.\nsystem-state.md: la verità di oggi # Questo file serve a rispondere a una sola domanda: cosa è vero adesso? Non cosa era vero tre giorni fa, non cosa si è deciso in una discussione di progetto, non il dettaglio completo del troubleshooting. Solo la dottrina operativa corrente.\nQui ci stanno le cose che, se apro un agente nuovo, voglio che sappia subito senza dover scavare:\nqual è il modello attuale di TazPod, quali sono i caveat importanti, come funziona in questo momento la pipeline CI, qual è il ruolo dei vari layer del sistema, quali componenti sono considerati fonte di verità. Il punto di system-state.md non è raccontare il passato. È condensare il presente.\ndebts.md: ciò che non è risolto # A un certo punto ho separato anche il debito tecnico in un file dedicato. Questa è stata una correzione importante, nata dall\u0026rsquo;uso reale. All\u0026rsquo;inizio è molto facile mescolare i problemi aperti dentro la cronologia o dentro lo stato. Ma sono due cose diverse.\nUn debito tecnico non è solo un evento del passato. È una tensione ancora attiva nel sistema. Va tenuto visibile in forma strutturata. Per questo debts.md è diventato il registro canonico di tutto quello che è aperto, in corso, rinviato o da chiudere in futuro.\nIn pratica, quando riapro una sessione, non voglio solo sapere cosa è stato fatto. Voglio vedere subito cosa ancora manca, dove sono i rischi, quali problemi non posso dimenticare.\nchronicle.md: la continuità causale recente # Questo è il diario attivo del ciclo corrente. Non è un riassunto storico compresso, e non è la dottrina. È la parte narrativa che tiene insieme causa ed effetto delle ultime sessioni.\nQui vive la risposta alla domanda: come siamo arrivati dove siamo?\nQuesta distinzione è importante. Lo stato da solo non basta. Se apro un agente e gli dico che oggi il sistema funziona in un certo modo, spesso manca il perché. E senza il perché diventa molto più facile rompere qualcosa nella sessione successiva.\nLa cronologia serve esattamente a questo: preservare la catena recente di eventi, problemi, correzioni e conseguenze.\npast-summary.md: la compressione del passato utile # Questo è forse il pezzo più interessante dell\u0026rsquo;intero sistema. Perché il problema non è solo tenere una memoria recente. È farlo senza perdere il passato e senza caricarlo tutto ogni volta.\npast-summary.md è il livello compresso della storia più vecchia. Non entra nei dettagli di tutto. Non deve farlo. Il suo compito è fornire orientamento storico ad alta densità: grandi decisioni, svolte architetturali, motivazioni che ancora contano oggi.\nÈ la memoria lunga, ma ancora pronta all\u0026rsquo;uso.\npast/: l\u0026rsquo;archivio ricorsivo # Ed ecco il punto che per me rende il sistema davvero interessante. Il passato non viene cancellato. Viene archiviato ricorsivamente.\nQuando il ciclo attivo supera una certa soglia, il sistema non si limita a “fare cleanup”. Sposta i file root dentro past/, conserva la struttura, e poi rigenera una nuova root attiva pulita. Se in futuro serve andare più indietro, si può scavare a strati.\nQuesta è una feature molto importante del modello. Quello che ho subito in mano è solo quello che mi serve ora. Ma se ho bisogno di ricostruire cosa è successo in modo più profondo, posso farlo andando a ritroso un livello alla volta, senza trasformare la root attiva in un archivio infinito.\nPerché la ricorsione qui non è un vezzo teorico # Dire “ricorsivo” può sembrare un tecnicismo elegante usato per darsi un tono. In realtà qui è una scelta molto concreta.\nIn una memoria lineare tradizionale ci sono due esiti comuni:\no il file cresce senza controllo, oppure si taglia brutalmente il passato e si perde continuità. Io volevo evitare entrambe le cose. La ricorsione mi permette di conservare tutto senza dover tenere tutto caricato nello stesso piano operativo. È una differenza sottile ma decisiva.\nIl livello attivo resta compatto. Il passato non sparisce. Semplicemente viene spostato in un livello storico meno immediato. Se mi serve, lo riapro. Se non mi serve, non inquina la lettura corrente.\nQuesto approccio ha anche un effetto molto pratico sul modo di lavorare: quando riapro un agente, questo non deve attraversare settimane di storia per capire cosa fare. Legge la root attiva, e solo se il problema lo richiede va a cercare più indietro.\nÈ memoria stratificata, non memoria gonfiata.\nL\u0026rsquo;archive model: tenere la memoria viva senza farla esplodere # Per rendere questo modello sostenibile non bastava dividere i file. Serviva anche un meccanismo disciplinato per evitare che la root attiva crescesse senza fine. Per questo ho introdotto un archive flow esplicito, gestito da scripts/archive-memory.sh.\nLa logica è questa:\nquando la cronologia attiva supera una soglia, oppure quando si chiude una tappa importante, oppure quando decido di forzare l\u0026rsquo;archivio, il ciclo corrente viene archiviato, la memoria root viene rigenerata, e il lavoro riparte da una base compatta. Questa parte era importante non solo come idea, ma come implementazione reale. Non mi interessava avere un modello elegante su carta. Volevo verificare che, nel momento in cui la memoria andava davvero archiviata, il comportamento fosse pulito: file spostati correttamente, summary rigenerato, cronologia nuova minimale, nessun log inutile che sporca il workspace.\nPer questo ho anche eseguito un forced archive reale, non una semplice review teorica. È stato un passaggio importante, perché ha trasformato il design in un comportamento verificato.\nIl sistema non è nato perfetto, ed è un bene # Una delle cose che considero più interessanti di questo lavoro è che il sistema non è emerso in una sola soluzione “geniale”. È stato corretto mano a mano che lo usavo.\nLa prima versione del modello era già utile, ma aveva ancora confini troppo morbidi. Alcune responsabilità erano mescolate. Alcune informazioni storiche finivano nel posto sbagliato. Il rischio era quello classico dei sistemi documentali: partire ordinati e tornare gradualmente al caos.\nLe correzioni più importanti sono arrivate proprio da questo uso reale:\nil debito tecnico è stato separato in debts.md, la cronologia è stata ripulita fino a diventare davvero cronologia, lo stato è stato ristretto a “truth of today”, past-summary.md è diventato un artefatto di primo livello, il contract di archive è stato reso più preciso, la validazione è stata fatta con un ciclo completo, non solo con buon senso. Questo per me è un segnale di salute del sistema. Un workflow utile non è quello che sembra perfetto al primo sketch. È quello che regge il contatto con l\u0026rsquo;uso reale e sopravvive alle revisioni senza perdere coerenza.\nMemory e Mnemosyne: due memorie, due usi diversi # Qui c\u0026rsquo;è una distinzione che per me vale la pena esplicitare molto bene, perché all\u0026rsquo;esterno può sembrare ridondanza.\nHo già Mnemosyne, che uso come memoria storica e retrieval semantico. Allora perché costruire anche memory?\nPerché servono due cose diverse in due momenti diversi.\nMnemosyne: cercare per significato # Mnemosyne è utile quando non ricordo esattamente quando è successa una cosa, ma ricordo di cosa trattava. Per esempio:\n“quando abbiamo già visto un problema simile con la CI?” “avevamo già discusso quella quota Gemini?” “in quale sessione era emerso quel trade-off su Hetzner?” Questa è ricerca semantica. È potentissima, ma non sostituisce il contesto attivo.\nMemory: sapere dove siamo ora # memory serve invece quando apro il terminale e voglio ripartire subito. Non voglio cercare semanticamente nel passato. Voglio sapere:\ncosa è vero oggi, cosa è ancora aperto, cosa è successo di recente, qual è la base attiva da cui continuare. È memoria cronologica e operativa. Non è retrieval. È continuità di lavoro.\nPer questo considero memory e Mnemosyne complementari, non concorrenti. Una serve a riprendere. L\u0026rsquo;altra serve a ritrovare.\nCosa cambia davvero nel lavoro quotidiano # La parte più interessante non è l\u0026rsquo;architettura in sé, ma il cambiamento di ritmo che produce.\nQuando un workflow del genere funziona, succede una cosa molto semplice da descrivere e molto difficile da ottenere: riapro un agente, e questo sa già dove eravamo. Non in modo vago. Non nel senso “forse ricorda qualcosa”. Sa davvero qual è il punto del percorso.\nQuesto riduce enormemente il costo di riapertura delle sessioni. E riduce anche un\u0026rsquo;altra forma di attrito che all\u0026rsquo;inizio avevo sottovalutato: l\u0026rsquo;energia mentale spesa per ricostruire il contesto. Ogni volta che devo fermarmi a riassumere a mano lo stato di un progetto, sto sprecando una parte della mia attenzione su un compito amministrativo invece che sul problema tecnico reale.\nCon memory, quella ricostruzione è già lì. E soprattutto è lì in una forma abbastanza piccola da restare utile.\nLa direzione futura: semplificare ancora # C\u0026rsquo;è anche una conseguenza architetturale che sto vedendo più chiaramente proprio adesso che il sistema ha iniziato a funzionare bene: il modello finale dovrebbe probabilmente essere ancora più semplice.\nIl mio obiettivo di lungo periodo è avere sostanzialmente due soli layer:\nmemory come memoria cronologica, attuale, ricorsiva, Mnemosyne come memoria semantica e di ricerca. Il resto, progressivamente, dovrebbe convergere lì dentro. Non perché la storia intermedia non sia stata utile, ma perché un modello che si lascia spiegare in due frasi è quasi sempre più robusto di uno che ha troppi strati transitori.\nQuesto non significa buttare via il passato. Significa assorbirlo in una struttura più chiara.\nRiflessioni post-lab # Se dovessi riassumere il senso di questo lavoro in una frase, direi così: dopo aver costruito i contesti, mi serviva un modo per non ripartire mai davvero da zero. La memoria ricorsiva è stata quel tassello.\nNon volevo una memoria infinita. Volevo una memoria sempre pronta. Non volevo un accumulo di note. Volevo un sistema che separasse chiaramente stato, debiti, cronologia e passato compresso. Non volevo perdere la storia. Volevo poterla scavare solo quando serve.\nIl risultato, almeno fin qui, è molto convincente. I contesti continuano a fare il loro lavoro: dare regole, struttura, indirizzamento e specializzazione operativa. La memoria aggiunge la continuità temporale che mancava. Mnemosyne resta il livello di retrieval semantico, utile quando serve cercare nel passato per significato.\nSono tre problemi diversi, ma due stanno ormai convergendo in una forma più pulita: una memoria attiva, ricorsiva, a grandezza controllata, e una memoria semantica per la ricerca storica.\nPer il mio modo di lavorare, da solo, con agenti diversi e progetti lunghi, questo non è un dettaglio organizzativo. È un cambio di qualità del workflow. Apro il terminale, riapro l\u0026rsquo;agente, e il sistema sa già da dove ripartire. Non tutto. Solo quello che serve. Ed è esattamente questo il punto.\n","date":"10 aprile 2026","externalUrl":null,"permalink":"/it/posts/recursive-memory-compact-context-ai-agents/","section":"Posts","summary":"","title":"Memoria ricorsiva, contesto compatto: il tassello che mancava per lavorare bene con gli agenti AI","type":"posts"},{"content":"","date":"10 aprile 2026","externalUrl":null,"permalink":"/it/tags/memory/","section":"Tags","summary":"","title":"Memory","type":"tags"},{"content":"","date":"10 aprile 2026","externalUrl":null,"permalink":"/it/categories/productivity/","section":"Categories","summary":"","title":"Productivity","type":"categories"},{"content":"","date":"10 aprile 2026","externalUrl":null,"permalink":"/it/tags/productivity/","section":"Tags","summary":"","title":"Productivity","type":"tags"},{"content":"","date":"10 aprile 2026","externalUrl":null,"permalink":"/it/tags/workflow/","section":"Tags","summary":"","title":"Workflow","type":"tags"},{"content":"","date":"9 aprile 2026","externalUrl":null,"permalink":"/it/tags/automation/","section":"Tags","summary":"","title":"Automation","type":"tags"},{"content":"","date":"9 aprile 2026","externalUrl":null,"permalink":"/it/tags/llm/","section":"Tags","summary":"","title":"Llm","type":"tags"},{"content":"","date":"9 aprile 2026","externalUrl":null,"permalink":"/it/tags/terraform/","section":"Tags","summary":"","title":"Terraform","type":"tags"},{"content":" Una sessione infrastrutturale più tranquilla del solito: quando la progettazione riduce il caos # Obiettivo della sessione # Questa tappa del progetto aveva un obiettivo molto preciso: chiudere il passo foundation della pipeline Hetzner, cioè arrivare a una macchina runtime capace di nascere da golden image, essere bootstrapata via SSH pubblico, entrare correttamente in Tailscale, spostare il piano operativo sul canale privato e dimostrare di convergere in modo ripetibile anche al secondo run.\nDetto in forma più concreta, il target era il progetto hetzner-tailscale-foundation: non ancora Vault, non ancora lifecycle completo del servizio applicativo, ma il primo vero strato operativo su cui tutto il resto potrà appoggiarsi. Se questa base non è pulita, ogni fase successiva diventa rumorosa: quando qualcosa si rompe non capisco più se il problema è nel provisioning, nella rete, nei segreti, nel runtime, oppure nel servizio finale. Chiudere bene la foundation significa togliere ambiguità al resto del viaggio.\nLa cosa interessante è che questa sessione è stata relativamente tranquilla. Non perfetta, ma ordinata. Ci sono stati un paio di problemi reali, anche istruttivi, ma non è stata una maratona di caos. Ed è proprio questo il punto che voglio fissare: non è andata bene perché il problema fosse banale. È andata bene perché il lavoro più difficile era già stato fatto prima dell’implementazione.\nLa parte invisibile che ha reso visibile la fluidità # Se guardassi solo l’ultima esecuzione potrei raccontarla così: ho lanciato la build, ho corretto alcuni dettagli di integrazione reale, ho validato il passaggio su Tailscale, ho verificato il rerun idempotente e ho chiuso con destroy.sh. Sarebbe un racconto corretto, ma incompleto. Il punto decisivo è che questa build non è nata da un “fammi questa infrastruttura” lanciato a un LLM sperando che tutto si componga da solo in modo elegante.\nPrima di arrivare qui c’erano già state ore di discussione, ridefinizione del problema, chiarimento dei vincoli, review dei comportamenti reali di TazPod, correzione delle assunzioni sbagliate e soprattutto una scelta fondamentale: spezzare il progetto in parti più piccole. Prima la golden image, poi la foundation, solo dopo la convergenza Vault. Questa scomposizione ha abbassato drasticamente il numero di variabili attive in ogni fase.\nÈ qui che la metodologia CRISP e il passaggio successivo in crisp-build hanno avuto valore reale. Non tanto come etichetta metodologica, ma come disciplina. Ho usato un contesto per progettare, discutere, correggere il piano e fissare i contratti. Solo dopo ho aperto il cantiere implementativo. Il beneficio pratico è stato enorme: quando qualcosa non tornava, la deviazione era leggibile. Non dovevo indagare un blob di provisioning+runtime+rete+Vault tutto insieme, ma un singolo gradino del sistema.\nPerché dividere in sottoprogetti cambia davvero il risultato # Questa è probabilmente la lezione più forte della sessione. Se avessi cercato di fare nello stesso momento golden image, foundation, bootstrap Tailscale, segreti e primo lifecycle di Vault, avrei ottenuto un classico effetto domino. Ogni errore avrebbe sporcato tutti i livelli sovrastanti, rendendo il troubleshooting ambiguo. Una VM che non risponde avrebbe potuto significare immagine difettosa, ACL sbagliata, playbook non idempotente, token di bootstrap errato, policy Tailscale incoerente o semplice rete locale instabile.\nSeparando invece il percorso in più passi, ho ottenuto l’opposto. La golden image era già stata chiusa e validata come gate autonomo. Questo significava che durante la foundation potevo trattare il runtime di base come affidabile, e concentrarmi solo su provisioning, bootstrap di rete e convergenza operativa. In termini di ingegneria è una riduzione fortissima della superficie di diagnosi. Non è solo “project management”: è riduzione concreta dell’entropia tecnica.\nÈ la differenza tra lanciare un’operazione con dieci ipotesi aperte e lanciare un’operazione dove sette ipotesi sono già state chiuse prima. Quando poi compaiono problemi reali, come è successo qui, la loro natura è molto più leggibile. E infatti è esattamente quello che è successo.\nL’implementazione vera: creare una foundation pulita e verificabile # Il lavoro implementativo si è concentrato nel nuovo workspace sotto ephemeral-castle/runtimes/lushycorp-vault/hetzner/. Ho costruito lì tutti i pezzi necessari per la foundation:\nlayer Terraform per VM, firewall bootstrap e output locali, baseline Ansible per la verifica runtime, ruolo Ansible per il bootstrap Tailscale, create.sh e destroy.sh con log separati per fase, helper script per inventory generation e validazione dei tag, sorgente di verità esplicita per la golden image approvata. Questa scelta ha un significato preciso: il progetto non doveva dipendere da memoria implicita o da ID ricordati a voce. Se l’immagine approvata è lushycorp-vault-base-20260404-v4 con ID 373384231, quella informazione deve vivere in un file consumato davvero dagli script, non in una nota mentale o in una frase persa in un documento di design.\nIl cuore del provisioning foundation è stato volutamente semplice. Terraform crea una VM da golden image approvata, apre solo il minimo necessario nel cloud firewall per la fase A e genera l’inventory pubblico da usare per il primo bootstrap. Ansible entra via SSH pubblico, verifica il modello utente, installa o controlla i componenti necessari e porta il nodo dentro Tailscale. A quel punto il sistema deve essere in grado di spostarsi sul piano privato e continuare a operare lì.\nUn esempio molto rappresentativo del livello Terraform è questo:\nresource \u0026#34;hcloud_server\u0026#34; \u0026#34;foundation\u0026#34; { name = var.server_name server_type = var.server_type image = var.image_id location = var.location ssh_keys = [var.ssh_key_name] firewall_ids = [ hcloud_firewall.foundation_bootstrap.id, ] public_net { ipv4_enabled = true ipv6_enabled = true } labels = merge(local.foundation_labels, { image_name = var.image_name image_id = var.image_id }) } Qui si vede bene il senso del passo: nessuna fantasia infrastrutturale, nessun layer opaco, solo il minimo indispensabile per generare una macchina coerente e tracciabile, con output utili ai passi successivi.\nI problemi che sono emersi erano problemi “buoni” # Il primo punto interessante è che i problemi emersi non hanno mai messo in discussione l’architettura complessiva. Questo non significa che fossero banali. Significa che erano problemi di integrazione reale, non segnali di un progetto sbagliato.\nIl primo errore serio è apparso al momento del tailscale up sulla VM runtime. La macchina era stata creata correttamente, l’accesso iniziale via SSH funzionava, il daemon Tailscale si installava, ma il join falliva con un messaggio molto preciso: i tag richiesti non erano validi o non consentiti. Questo è esattamente il tipo di problema che una build live deve far emergere. Il design diceva correttamente che il nodo doveva entrare con tag:tazlab-vault e tag:vault-api. La realtà del control plane Tailscale diceva invece che l’OAuth client bootstrap non aveva ancora il modello di ownership giusto per assegnarli.\nQuesta diagnosi è stata importante perché ha mostrato la qualità del piano: non ho dovuto ripensare l’intera foundation, ho dovuto correggere un contratto reale tra bootstrap client e policy tailnet. Ho aggiornato l’ACL source of truth e la definizione dell’OAuth client in ephemeral-castle/tailscale/, applicando direttamente il fix sul tailnet reale. È un dettaglio apparentemente piccolo, ma dice molto: il progetto aveva bisogno di un allineamento del control plane, non di una riscrittura della pipeline.\nIl secondo problema: il nodo era online, ma SSH su Tailscale non passava # Dopo aver corretto il problema dei tag, il runtime entrava effettivamente in Tailscale e mostrava i tag attesi. Sembrava tutto risolto. Invece il passaggio successivo — Ansible via Tailscale — continuava a fallire. Questo è stato il punto più istruttivo dell’intera sessione, perché sulla carta il nodo era sano:\ntailscale ping rispondeva, il nodo compariva nel tailnet, i tag erano corretti, sshd era attivo, l’interfaccia tailscale0 aveva il suo IP. Eppure l’SSH al 100.x andava in timeout.\nQui si è vista di nuovo la differenza tra caos e indagine leggibile. Il fatto che il peer fosse vivo ma il trasporto applicativo no mi diceva che non ero davanti a un fallimento globale di Tailscale. C’erano due possibilità: una ACL incompleta sul piano di controllo, oppure una particolarità del lato operatore. In realtà erano entrambe vere.\nDa un lato mancava esplicitamente la porta 22 nel path ACL tag:tazpod -\u0026gt; tag:tazlab-vault. Questo era un errore reale di policy e andava corretto sulla tailnet. Dall’altro lato c’era un aspetto ancora più interessante: nel mio ambiente operatore locale Tailscale gira in userspace-networking, quindi tailscale ping può funzionare perfettamente anche se il sistema host non ha routing kernel diretto verso gli indirizzi 100.x.\nQuesta distinzione è molto importante. Un uso superficiale degli strumenti avrebbe portato facilmente alla conclusione sbagliata: “Tailscale è su, quindi SSH al 100.x dovrebbe funzionare”. Invece no. In userspace mode il mesh è sano, ma il percorso TCP dal sistema host può comunque richiedere un bridge esplicito.\nLa correzione finale è stata piccola, ma molto istruttiva # La soluzione non è stata forzare il sistema locale a comportarsi come un nodo con routing kernel pieno, ma adattare il transport switch al contesto reale. Ho quindi cambiato la generazione dell’inventory Tailscale in modo che usasse tailscale nc come ProxyCommand SSH. In questo modo Ansible non dipende più dal fatto che il mio host locale sappia raggiungere direttamente il 100.x a livello di stack di rete tradizionale: usa il canale userspace offerto dal daemon Tailscale locale.\nÈ un fix piccolo, ma dal punto di vista del design è eccellente, perché rende il sistema più robusto rispetto all’ambiente operatore reale. Non sto scrivendo una foundation che funziona solo nel laboratorio ideale; sto chiudendo una foundation che funziona anche nel contesto concreto in cui oggi la sto usando.\nLa parte chiave dell’inventory generato è diventata questa:\n[foundation_tailscale] foundation-node ansible_host=100.83.183.124 ansible_user=admin ansible_ssh_private_key_file=/home/tazpod/secrets/ssh/lushycorp-vault/id_ed25519 ansible_ssh_common_args=\u0026#39;-o ProxyCommand=\u0026#34;tailscale nc %h %p\u0026#34; -o StrictHostKeyChecking=accept-new\u0026#39; Questa riga racconta una lezione più ampia: i sistemi reali non falliscono sempre sui concetti grandi. Spesso falliscono nei punti di contatto tra un progetto ben pensato e un ambiente operativo con caratteristiche specifiche. La differenza la fa la capacità di leggere il problema senza generalizzare troppo in fretta.\nIl risultato finale: create, rerun, destroy # Una volta corretti questi dettagli, il progetto ha chiuso il suo obiettivo esattamente come previsto. Il create.sh è arrivato fino in fondo. La VM è nata dalla golden image, ha superato il bootstrap pubblico, è entrata nel tailnet come lushycorp-vault-foundation, ha mostrato i tag corretti, ha risposto sul path Tailscale e ha eseguito il baseline check via Ansible sul canale privato. Anche la verifica podman --version è passata senza sorprese.\nAncora più importante, il rerun ha confermato la sanità del piano. Terraform è andato in no-op, Tailscale non ha richiesto mutazioni inutili, e il sistema ha mostrato il comportamento che mi aspettavo da una foundation ben progettata: non solo “funziona una volta”, ma converge quando lo rilancio.\nInfine ho eseguito anche destroy.sh e ho verificato il cleanup locale. Questo passaggio per me è essenziale. Un progetto infrastructure-as-code non è davvero chiuso quando crea una macchina: è chiuso quando sa anche rimuoverla in modo pulito, lasciando il workspace leggibile e pronto al ciclo successivo. È lì che si vede se la pipeline è solo una demo o un processo riapribile.\nLa lezione sugli LLM è la parte più importante del post # Tutto questo conferma in modo molto concreto una cosa che avevo già intuito e scritto in altri momenti: gli LLM sono potenti, ma il risultato non dipende solo dalla loro capacità generativa. Dipende enormemente da come vengono guidati.\nSe l’approccio è “fammi questa infrastruttura” e poi attendo che emerga un sistema ben fatto da una richiesta generica, il rischio è altissimo. Posso ottenere output plausibili, ma fragili, poco coerenti con il contesto reale, o costruiti su assunzioni non verificate. Un modello linguistico può produrre una quantità impressionante di materiale utile, ma non sostituisce automaticamente il lavoro di chiarificazione del problema.\nQuello che questa sessione mostra è quasi l’opposto: quando l’operatore sa cosa sta costruendo, sa spezzare il problema, sa individuare i veri vincoli e usa l’LLM dentro una struttura disciplinata, il moltiplicatore cambia scala. Non è più un generatore di testo che prova a improvvisare un’infrastruttura. Diventa un acceleratore della capacità ingegneristica di chi lo sta guidando.\nLa formula più onesta che mi porto via è questa: più chi usa l’LLM conosce il dominio, più il moltiplicatore si alza. Se la comprensione è debole, il modello amplifica ambiguità. Se la comprensione è forte, il modello amplifica velocità, ampiezza di esplorazione e qualità dell’implementazione.\nRiflessioni post-lab # Questa tappa non è memorabile perché “non ci sono stati problemi”. I problemi ci sono stati, ed è giusto così. È memorabile perché i problemi erano del tipo giusto: piccoli, reali, leggibili e correggibili senza demolire il progetto. Questo è il segnale migliore possibile per una foundation.\nLa cosa più soddisfacente, in questa fase, non è aver portato su una VM su Hetzner con Tailscale. È aver verificato che la combinazione di progettazione preventiva, scomposizione del lavoro e uso guidato di un LLM produce un’esecuzione molto più fluida di quella che avrei ottenuto con un approccio più impulsivo o monolitico.\nIn fondo è proprio questo il senso di questa sessione: meno improvvisazione durante la build, più intelligenza prima della build. E quando questo accade, anche una tappa infrastrutturale complessa può diventare, finalmente, una sessione più tranquilla del solito.\n","date":"9 aprile 2026","externalUrl":null,"permalink":"/it/posts/infrastructure-session-when-design-reduces-chaos/","section":"Posts","summary":"","title":"Una sessione infrastrutturale più tranquilla del solito: quando la progettazione riduce il caos","type":"posts"},{"content":" Obiettivo della sessione # L’obiettivo era semplice da descrivere ma non banale da chiudere bene: arrivare a una golden image runtime stabile e riusabile su Hetzner, pronta per essere consumata nella fase successiva della foundation.\nIn pratica volevo eliminare bootstrap pesanti a runtime e spostare il lavoro in build-time: preparare una VM builder, configurarla, validarla, congelarla in snapshot, poi verificare che da quello snapshot nascano macchine coerenti e prevedibili.\nA livello di metodo ho imposto una regola operativa chiara: non fermarmi alla “prima volta che sembra funzionare”, ma chiudere il ciclo completo fino a una versione finale verificata su istanze fresche. Questo ha portato a più iterazioni (v1 → v4), ma è stato il passaggio necessario per trasformare un risultato locale in un artefatto affidabile.\nPerché una golden image prima della foundation # Quando si costruisce una foundation infrastrutturale, mescolare provisioning, installazione pacchetti, hardening e bootstrap applicativo nello stesso momento crea un effetto domino difficile da diagnosticare. Se qualcosa fallisce, non è mai immediato capire se il problema è:\nnello strato di rete, nello strato di accesso, nello strato runtime, o in una race condition durante il bootstrap. La golden image separa le responsabilità:\nBuild-time: preparo il runtime base una volta sola, in modo ripetibile. Deploy-time: istanzio e convergo rete/foundation con meno variabili in gioco. Questo approccio riduce la superficie di errore e rende più leggibile il troubleshooting. Non è solo una scelta “elegante”: è una scelta pratica quando vuoi consegnare una pipeline che regge anche le sessioni successive, non solo la demo del momento.\nIl profilo builder: scelta economica ma sufficiente # Un vincolo esplicito della sessione era usare il profilo più conveniente possibile, purché adeguato:\ncx23 4 GB RAM 40 GB SSD shared CPU La scelta è stata mantenuta per tutte le iterazioni finali. Questo è importante perché evita di costruire una pipeline che funziona solo su tagli più costosi e poi degrada quando la riporti su profili realistici.\nIn altre parole, ho voluto verificare il comportamento nel perimetro economico reale del progetto, non in un ambiente “comodo”.\nPrimo uso reale di Ansible nel mio flusso # Questa è stata la prima volta in cui ho usato Ansible in modo centrale nel mio processo, non come tool secondario. La differenza operativa è stata netta: passare da azioni manuali a una configurazione dichiarativa ripetibile.\nIl playbook ha coperto la baseline runtime con:\npacchetti necessari al runtime, modello utenti coerente (admin operativo, vault non interattivo), hardening SSH (password disabilitata), configurazioni di sistema minime ma deterministiche. Esempio del cuore del playbook usato in build:\n- name: Configure Hetzner runtime golden image baseline hosts: builder become: true vars: runtime_packages: - podman - python3 - curl - jq - ca-certificates - gnupg - apt-transport-https tasks: - name: Update apt cache ansible.builtin.apt: update_cache: true cache_valid_time: 3600 - name: Install runtime baseline packages ansible.builtin.apt: name: \u0026#34;{{ runtime_packages }}\u0026#34; state: present - name: Ensure admin user exists ansible.builtin.user: name: admin shell: /bin/bash groups: sudo append: true create_home: true state: present - name: Ensure vault service user exists (no login) ansible.builtin.user: name: vault shell: /usr/sbin/nologin create_home: false system: true state: present Il valore pratico non è stato “Ansible in sé”, ma il fatto che ogni correzione entrava nel playbook e non restava workaround manuale dimenticato nella sessione successiva.\nIl ciclo di build e validazione # Il flusso operativo completo è stato:\ncreate builder VM, apply baseline con Ansible, validazioni tecniche, poweroff builder, snapshot, test su VM nuova da snapshot, cleanup risorse transitorie. Questo ciclo è stato ripetuto più volte fino a rimuovere tutte le incoerenze rilevate tra “builder funziona” e “istanza fresca funziona davvero”.\nPerché più snapshot (v1, v2, v3, v4) # È il punto più importante della sessione: la stabilità vera non si misura sul nodo che hai appena configurato, ma su una macchina nuova nata dall’artefatto.\nOgni iterazione ha eliminato un difetto pratico emerso solo al momento del re-test su istanza fresca. Alla fine, invece di tenere una catena di snapshot “quasi buoni”, ho scelto una politica più pulita:\npromuovere solo la versione finale valida, eliminare le versioni intermedie, fissare un ID unico di handoff. Il difetto più concreto: comportamento diverso tra utenti # Una parte della stabilizzazione è stata garantire che i comandi risultassero disponibili non solo in root ma anche nell’utente operativo.\nQuesto tipo di problema è tipico nelle pipeline immagine: installazioni apparentemente corrette ma legate a path utente o contesti shell differenti. La soluzione finale è stata rendere la pubblicazione del binario esplicita in un path di sistema (/usr/local/bin), così da uniformare la visibilità per root/admin nelle istanze create da snapshot.\nLa lezione qui è diretta: quando validi una golden image, non basta verificare “comando presente”. Devi verificare presenza + esecuzione + utente target.\nLa parte che sembrava un bug immagine ma non lo era # In una fase avanzata ho visto test fallire con VM dichiarate running. Il sospetto iniziale può facilmente andare verso snapshot corrotto o bootstrap incompleto. In realtà il problema era diverso: connettività locale instabile nella rete di lavoro (hotspot), in particolare sul percorso IPv6 in alcune finestre temporali.\nQuesto ha un impatto pratico enorme sul debugging: puoi perdere ore a cambiare immagine quando il problema è nel path client→server.\nPer evitare falsi positivi ho separato i due piani:\nqualità artefatto immagine, affidabilità del canale di test. Da qui la decisione operativa finale per la pipeline test: default robusto IPv4 in contesti mobili, IPv6-only come modalità esplicita quando la rete locale è confermata.\nHardening del test harness # Per chiudere bene la sessione non mi sono limitato a “test passato una volta”. Ho consolidato anche gli strumenti operativi.\nHo strutturato tre script principali:\ngenerazione inventory dinamico, build golden image end-to-end, test immagine con validazioni e cleanup. Esempio di intent del test script (in forma sintetica):\n./scripts/test-image.sh \\ --image-id 373384231 \\ --server-name lv-img-script-test-ipv4-final Il punto chiave è che il test non si limita al ping SSH. Verifica esplicitamente i binari attesi su entrambi gli utenti operativi, e chiude il ciclo cancellando la VM di test al termine.\nRisultato finale # Artefatto finale promosso:\nSnapshot name: lushycorp-vault-base-20260404-v4 Image ID: 373384231 Criteri soddisfatti:\nbaseline runtime applicata in modo ripetibile, validazione su istanza fresca, comportamento coerente per utente root/admin, test harness stabile con cleanup esplicito, nessuna VM lasciata attiva a fine run. Cosa mi porto via da questa tappa # Questa sessione non è stata solo “costruire un’immagine”. È stata una tappa di maturazione del processo.\nLe cose che hanno fatto davvero la differenza:\nSeparare build-time e deploy-time per ridurre il rumore diagnostico. Usare Ansible come fonte di verità della configurazione, non come supporto occasionale. Validare sempre su istanze nuove, non fermarsi alla macchina builder. Distinguere bug infrastrutturale da bug di rete locale prima di cambiare artefatto. Chiudere con cleanup rigoroso per non inquinare il ciclo successivo. Dal punto di vista operativo, la pipeline della golden image è ora in uno stato utilizzabile: non perfetta in astratto, ma sufficientemente deterministica per diventare input affidabile della fase foundation.\nEd è esattamente il tipo di risultato che cercavo: meno “script che funziona oggi”, più “processo che posso riaprire domani senza ricominciare da zero”.\n","date":"7 aprile 2026","externalUrl":null,"permalink":"/it/posts/hetzner-runtime-golden-image-final-path/","section":"Posts","summary":"","title":"Golden Image runtime su Hetzner: il percorso fino alla versione finale","type":"posts"},{"content":"","date":"7 aprile 2026","externalUrl":null,"permalink":"/it/tags/golden-image/","section":"Tags","summary":"","title":"Golden-Image","type":"tags"},{"content":"","date":"7 aprile 2026","externalUrl":null,"permalink":"/it/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"7 aprile 2026","externalUrl":null,"permalink":"/it/tags/testing/","section":"Tags","summary":"","title":"Testing","type":"tags"},{"content":" LushyCorp Vault su Hetzner: scelte architetturali guidate dalla sicurezza # Questo articolo non racconta l’implementazione. Racconta la progettazione: come ho definito il cuore del progetto LushyCorp Vault su Hetzner prima di spezzarlo in sottoprogetti esecutivi.\nL’obiettivo era uno solo: costruire un runtime Vault che potesse nascere, morire e rinascere senza perdere sicurezza, senza dipendere da passaggi manuali fragili e senza introdurre segreti “di comodo” in posti sbagliati.\n1) The Current State: il problema reale da risolvere # Il punto di partenza non era “mi serve una VM con Vault”. Quello è semplice. Il problema vero era questo:\ncome avvio una macchina nuova in cloud, come la configuro senza password, come inietto i prerequisiti di rete privata, come inizializzo Vault la prima volta, come lo riapro in modo deterministico le volte successive, e come faccio tutto questo senza lasciare segreti in image, user-data o repository. In altre parole: non stavo progettando un server, stavo progettando un ciclo di vita sicuro.\nSe questa parte viene progettata male, tutto il resto (rotation, governance, private connectivity con il cluster, ecc.) nasce già su fondamenta deboli.\n2) The “Why”: perché queste scelte (e non altre) # Nessun segreto nell’immagine # L’immagine base doveva contenere solo software e configurazioni neutre. Nessun token, nessuna chiave di bootstrap, nessuna credenziale cloud.\nMotivo: un’immagine è fatta per essere clonata. Se ci metti un segreto, quel segreto diventa automaticamente moltiplicabile e difficilmente revocabile in modo ordinato.\nNiente cloud-init/user-data per passare chiavi # Ho scartato il pattern “passo tutto in user-data” perché non è coerente con il modello di sicurezza cercato. I metadata cloud non sono il posto dove voglio far transitare credenziali sensibili.\nSe un domani devo fare audit o incident response, devo poter dire con certezza: i segreti non sono mai passati dai metadata del provider.\nAccesso iniziale solo SSH a chiave, mai password # La VM nasce con una sola porta aperta, SSH, e solo con autenticazione a chiave già registrata su Hetzner. Nessun accesso password, nessun bootstrap interattivo fragile.\nQuesto riduce due superfici contemporaneamente:\nattacchi opportunistici su password, dipendenza da passaggi manuali non ripetibili. La svolta: da mega script SH a Ansible (il vero momento di chiarezza) # Una parte fondamentale della progettazione è stata proprio questa: all\u0026rsquo;inizio stavo disegnando tutto con un unico script SH molto complesso. L\u0026rsquo;idea era far fare allo script ogni passaggio: entrare in SSH, controllare stati, applicare configurazioni, iniettare chiavi, gestire branch first-run/re-run, validare output e fare cleanup.\nSulla carta sembrava fattibile. Nella pratica stavo costruendo a mano un orchestratore idempotente, con logica condizionale, retry, gestione errori, ordine delle dipendenze e tracciabilità delle azioni. A un certo punto la domanda è diventata inevitabile: \u0026ldquo;ma non è esattamente il caso ideale per usare Ansible?\u0026rdquo;\nLa risposta è stata sì, senza ambiguità: stavamo di fatto scrivendo un mini-Ansible in Bash. Ed è stato il momento in cui ho capito davvero a cosa serve Ansible nel mondo reale: non per \u0026ldquo;fare comandi remoti\u0026rdquo;, ma per dare forma dichiarativa, ripetibile e verificabile alla convergenza di una macchina.\nPer me è stato un passaggio importante anche sul piano professionale: conoscevo Ansible da tempo in teoria, ma non avevo mai avuto un caso in cui fosse così chiaramente il tool giusto. In questo progetto la sua utilità è stata evidente perché:\nil flusso richiede idempotenza (first-run e re-run devono convergere, non divergere), la sicurezza impone una configurazione deterministica (niente passaggi manuali opachi), serviva tracciare \u0026ldquo;cosa viene applicato, quando e in quale ordine\u0026rdquo;. In più, il modello dichiarativo di Ansible è coerente con il resto dello stack che uso: stessa mentalità di Kubernetes e stessa disciplina di tracciabilità tipica dei flussi GitOps. Non è Kubernetes, ma parla la stessa lingua operativa: stato desiderato, convergenza, verificabilità.\nIl ruolo di Ansible nel progetto è quindi preciso:\nconfigurare l\u0026rsquo;ambiente host in modo coerente, iniettare i materiali necessari (es. chiavi/config Tailscale) nel punto corretto del ciclo, mantenere separato il bootstrap iniziale dalla convergenza successiva, ridurre drasticamente il rischio di drift dovuto a script SH cresciuti oltre soglia. Senza questa convergenza dichiarativa, la sicurezza resterebbe legata alla memoria della sessione precedente. Con Ansible, invece, diventa parte del sistema.\nPerché non affidarsi a un Key Manager esterno (es. AWS KMS) in questa fase # La discussione più importante è stata questa: “usiamo un key manager esterno e risolviamo”.\nSulla carta è elegante. In pratica, nel mio scenario, per autenticare una macchina fuori dal loro perimetro avrei comunque bisogno di materiale di autenticazione locale (segreti/credenziali) da conservare sulla macchina stessa.\nQuindi il punto di rischio non sparisce: si sposta.\nConservare localmente credenziali per autenticarsi a KMS, oppure conservare localmente materiale necessario al bootstrap in un contenitore cifrato, in questo contesto hanno un profilo di rischio molto vicino, se non gestisci il primo caso con un ecosistema completo enterprise che qui non è disponibile.\nDa qui la scelta pragmatica e controllabile: niente dipendenza forzata da key manager esterno in questa fase, ma ciclo deterministico con artefatti cifrati e percorso di recupero esplicito.\n3) The Target Architecture: il progetto completo, prima dello split esecutivo # Prima di dividerlo in più sottoprogetti, il progetto era pensato come un’unica pipeline logica end-to-end.\nStep A — Golden image runtime (solo base tecnica) # Creo un’immagine base con i software necessari preinstallati. L’immagine viene testata. Nessun segreto dentro l’immagine. Questa è la base di fiducia: una macchina che nasce già pronta a convergere, ma ancora “neutra” dal punto di vista dei segreti.\nStep B — Istanza con sola porta SSH # Istanzio la VM da golden image. Porta aperta: solo 22. Accesso: solo chiave SSH registrata su Hetzner. Niente password, niente shell bootstrap via cloud metadata.\nStep C — Convergenza via Ansible, iniezione controllata # Una volta dentro via SSH, Ansible prepara il runtime:\nconfigura sistema e ambiente, inietta i materiali necessari per Tailscale, prepara il terreno per il passaggio al canale privato. Step D — Switch del piano di gestione su Tailscale # Dopo la convergenza iniziale:\nla VM entra nella rete Tailscale, la gestione passa al canale privato, in prospettiva si chiude SSH pubblico (sia lato internet sia cloud firewall), da quel momento l’operatività è privata. Questo è il passaggio chiave: SSH pubblico è solo ponte iniziale, non canale permanente.\nStep E — Fase Vault: primo avvio vs riavvio # Qui il design è esplicitamente biforcato.\nPrimo avvio (bootstrap) # Vault viene inizializzato, vengono generate le chiavi necessarie (unseal/root metadata), gli artefatti vengono salvati nel percorso segreti cifrato su S3. Avvii successivi (re-instanziazione) # gli artefatti esistono già, non si re-inizializza Vault, si recupera lo stato e si riapre il runtime in modo deterministico. Questo evita il rischio più pericoloso: “re-init accidentale” con perdita di continuità operativa.\nStep F — Integrazione privata con il cluster # Una volta stabilizzato il runtime su rete privata, anche il cluster entra nello stesso dominio di comunicazione privata.\nÈ qui che il progetto esprime il suo valore finale:\ngestione segreti, sincronizzazione, rotazione, avvengono su rete privata, non su esposizione pubblica.\n4) Blueprint operativo (script previsti dal design) # Questo è il blueprint finale. La prima idea era un unico script SH monolitico; dopo la svolta su Ansible, il progetto è stato ripensato in una pipeline dove gli script orchestrano le fasi e Ansible gestisce la convergenza configurativa.\n# 1) build immagine base sicura (no secrets) create-runtime-golden-image.sh # 2) istanzia da golden image con SSH iniziale create-runtime-instance.sh # 3) convergenza host + iniezione materiali rete privata converge-runtime-with-ansible.sh # 4) switch gestione su Tailscale e chiusura progressiva SSH pubblico switch-to-tailscale-management.sh # 5) bootstrap Vault first-run (init + salvataggio artefatti cifrati) vault-first-init.sh # 6) path di riapertura su re-instanziazione (no re-init) vault-recover-from-secrets.sh # 7) cleanup controllato risorse runtime destroy-runtime.sh La distinzione importante non è il nome degli script, ma la responsabilità di ciascun blocco. Ogni script deve fare una sola cosa critica, con log chiari e output verificabili.\n5) Perché questa parte viene prima dell’implementazione a sottoprogetti # Solo dopo aver definito questo flusso completo ho scelto di dividere l’implementazione in fasi separate. Lo split non nasce per “complicare la governance”, nasce per mantenere intatto il disegno di sicurezza durante l’esecuzione.\nQuindi il punto non è il numero di sottoprogetti. Il punto è che il progetto, nel suo cuore, resta questo:\nbase immagine pulita, bootstrap controllato, convergenza dichiarativa via Ansible, passaggio a gestione privata via Tailscale, lifecycle Vault first-run/re-run deterministic, niente segreti in posti sbagliati. Future Outlook: cosa sblocca davvero questa architettura # Quando questo disegno è rispettato, ottengo tre proprietà strategiche:\nRipetibilità operativa\nposso ricreare runtime senza reinventare la procedura. Riduzione del rischio strutturale\ni segreti non transitano su canali impropri, l’esposizione pubblica non è la modalità operativa permanente. Continuità del lifecycle Vault\nil primo avvio e le riaperture successive sono percorsi distinti e controllati. Questa è la parte davvero importante del progetto: non “mettere su Vault”, ma costruire un sistema che rimane sicuro anche quando lo rifai da zero.\n","date":"4 aprile 2026","externalUrl":null,"permalink":"/it/posts/lushycorp-vault-hetzner-security-architecture/","section":"Posts","summary":"","title":"LushyCorp Vault su Hetzner: scelte architetturali guidate dalla sicurezza","type":"posts"},{"content":"","date":"4 aprile 2026","externalUrl":null,"permalink":"/it/categories/security/","section":"Categories","summary":"","title":"Security","type":"categories"},{"content":"","date":"4 aprile 2026","externalUrl":null,"permalink":"/it/tags/security/","section":"Tags","summary":"","title":"Security","type":"tags"},{"content":" L\u0026rsquo;illusione del \u0026ldquo;Gratis\u0026rdquo; e la Ricerca della Stabilità # L\u0026rsquo;obiettivo iniziale era semplice e ambizioso: un cluster privato di HashiCorp Vault sulle risorse \u0026ldquo;Always Free\u0026rdquo; di Oracle Cloud a Torino. 4 vCPU ARM, 24 GB di RAM e 200 GB di storage, tutto gratis. Un paradiso per un home lab professionale.\nDopo 24 ore di battaglia, ho dovuto fare i conti con la cruda realtà: quando si tratta di servizi critici, il \u0026ldquo;gratis\u0026rdquo; può costare molto caro in termini di tempo e affidabilità.\nIl nome Lushy Corp è nato da un typo — stavo scrivendo \u0026ldquo;HashiCorp Vault Container\u0026rdquo; e l\u0026rsquo;agente AI ha letto \u0026ldquo;LushyCorp\u0026rdquo;. Da quel momento, è diventato il nome in codice del nostro vault.\nLa Saga OCI: Una Battaglia contro i Mulini a Vento # Il Muro della Capacità # Il primo ostacolo è arrivato prima ancora di poter fare qualsiasi cosa. Le istanze Ampere (ARM64) nel datacenter eu-turin-1 di OCI sono estremamente popolari: offrono prestazioni eccellenti in un tier gratuito. Il problema è che la domanda supera di gran lunga l\u0026rsquo;offerta.\nHo dovuto implementare un loop aggressivo nel mio script di provisioning, un create.sh che tentava continuamente di creare istanze, spesso per ore. Il messaggio Out of host capacity è diventato il mio compagno costante. Oracle semplicemente non aveva risorse disponibili nel momento in cui le richiedevo.\nQuesto ha evidenziato un problema concettuale: se devo \u0026ldquo;lottare\u0026rdquo; per ottenere una risorsa gratuita, quel tempo ha un costo. E se quel tempo viene speso su un servizio critico 24/7, il rischio operativo diventa inaccettabile.\nIl Bug dell\u0026rsquo;Architettura: Istanze che Girano ma non Partono # Dopo aver finalmente \u0026ldquo;accaparrato\u0026rdquo; un\u0026rsquo;istanza Ampere, ho incontrato un problema più subdolo. L\u0026rsquo;istanza raggiungeva lo stato RUNNING senza errori, ma Talos Linux non si avviava. Nessun output sulla console, solo un\u0026rsquo;istanza che girava nel vuoto.\nL\u0026rsquo;investigazione ha richiesto ore. Alla fine, la causa è emersa: l\u0026rsquo;immagine ARM64 importata era stata registrata con i metadati di architettura su None invece di ARM64. OCI accettava l\u0026rsquo;istanza, ma al boot il firmware UEFI non riconosceva l\u0026rsquo;architettura e si bloccava silenziosamente.\nLa soluzione è stata rimportare l\u0026rsquo;immagine tramite OCI CLI, specificando esplicitamente ARM64:\noci compute image import \\ --compartment-id $COMPARTMENT_ID \\ --image-id $IMAGE_ID \\ --source-image-type QCOW2 \\ --launch-mode PARAVIRTUALIZED \\ --architecture ARM64 Una lezione importante: su OCI, i metadati dell\u0026rsquo;immagine devono essere corretti al momento dell\u0026rsquo;importazione. Non possono essere modificati dopo.\nTerragrunt e Problemi di Orchestrazione # Quando Terragrunt ha iniziato a bloccarsi su problemi di caching e credenziali, ho dovuto bypassarlo completamente, passando a comandi OCI CLI diretti. Inoltre, OCI impiegava un tempo anomalo ad assegnare gli IP privati alle VNIC, richiedendo multipli reset hardware per forzare la sincronizzazione.\nIl sentimento prevalente non era soddisfazione: era la consapevolezza che stavo costruendo su fondamenta instabili.\nIl Colpo Fatale: La Politica di Spegnimento # Il momento decisivo è arrivato quando ho realizzato che le politiche di OCI \u0026ldquo;Always Free\u0026rdquo; consentono lo spegnimento delle istanze ritenute \u0026ldquo;inattive\u0026rdquo;. Per un servizio come Vault, che deve essere sempre disponibile, questo è un rischio inaccettabile.\nImmaginate la scena: è notte, un\u0026rsquo;applicazione Kubernetes ha bisogno di accedere a un segreto per ruotare un certificato, ma il Vault è stato spento da Oracle perché \u0026ldquo;inattivo\u0026rdquo;. Il certificato scade, l\u0026rsquo;app va in errore, e voi state dormendo. È esattamente il tipo di fallimento silenzioso che un sistema di gestione dei segreti deve prevenire a tutti i costi.\nHo deciso che la stabilità operativa di Lushy Corp valeva più di qualche euro risparmiato.\nIl Pivot AWS: Il Problema delle Istanze Spot # Prima di arrivare a Hetzner, ho fatto un\u0026rsquo;escursione nell\u0026rsquo;ecosistema AWS. Ho progettato un\u0026rsquo;architettura basata su Fargate + EFS + Tailscale + KMS Auto-Unseal, con l\u0026rsquo;intento di mantenere un costo stimato di circa 4€/mese.\nNon è stata la complessità dell\u0026rsquo;infrastruttura a fermarmi. Al contrario, configurare quell\u0026rsquo;ambiente è stato uno stimolo tecnico interessante, un\u0026rsquo;ottima occasione per imparare e approfondire componenti avanzati di AWS. Il problema reale, emersi fatti i conti, era il compromesso tra costi e affidabilità.\nPer rientrare in quel budget così basso, avrei dovuto usare istanze Fargate Spot. Tuttavia, le istanze Spot introducono lo stesso identico problema da cui stavo scappando su OCI: se AWS necessita di potenza computazionale, ti spegne la macchina. Tornavamo al punto di partenza, un rischio inaccettabile per un Vault.\nPer avere un\u0026rsquo;architettura realmente solida e funzionante per bene (usando istanze On-Demand classiche), la spesa sarebbe salita a più del doppio di quanto preventivato all\u0026rsquo;inizio. Per un progetto nato per imparare, mettermi alla prova e testare tecnologie nel mio home lab (dove un cluster Vault è di per sé già una sovrastruttura \u0026ldquo;esagerata\u0026rdquo; per i dati che contiene), mi sembrava semplicemente una spesa ingiustificata.\nLa Scelta Finale: Hetzner e la Bellezza della Semplicità # La scelta è caduta su un VPS dedicato su Hetzner. Questa decisione offre il bilanciamento perfetto per un home lab professionale.\n1. Versatilità: Una VM Linux non è solo per Vault. Può ospitare altri microservizi, un reverse proxy, strumenti di monitoring. Il costo fisso di 4-5€/mese si distribuisce su più servizi nel tempo.\n2. Semplicità Operativa: Con una VM pura, ho il controllo completo e diretto su ogni componente. Niente servizi gestiti opachi, solo pura amministrazione di sistema Linux.\n3. Costo Prevedibile: 4-5€/mese garantiti 24/7. È il prezzo della pace mentale, senza i rischi delle istanze Spot e senza sorprese in bolletta.\nIl Setup: Podman e Tailscale Nativo # Invece di soluzioni iper-impacchettate, sto pensando a un approccio più \u0026ldquo;grezzo\u0026rdquo; e formativo. Installerò Tailscale direttamente nel sistema operativo della macchina, garantendo la connettività sicura a livello di host in modo pulito.\nPer quanto riguarda i container, ho deciso di approfittarne per usare Podman al posto del classico Docker. Non perché Docker non vada bene (anzi, Podman non mi darà necessariamente feature in più per questo use case base), ma puramente per il gusto di provarlo e imparare a usarlo in un contesto reale. Su questo strato Podman farò girare il container di Vault. Essendo un VPS pubblico, in futuro, mi farà comodo avere questa infrastruttura già pronta per tirar su facilmente altri servizi.\nConclusione: Fallire per Costruire Meglio # Ho \u0026ldquo;nuclearizzato\u0026rdquo; il compartment OCI, cancellato il progetto AWS Fargate, ma non sono stati fallimenti. Sono state tappe necessarie di un percorso che ha portato a un\u0026rsquo;architettura più solida, pragmatica e consapevole.\nOgni pivot ha insegnato qualcosa:\nOCI: Il \u0026ldquo;gratis\u0026rdquo; ha costi nascosti enormi in termini di affidabilità e tempo perso. AWS Fargate: Le architetture serverless \u0026ldquo;economiche\u0026rdquo; via Spot non sono adatte a servizi always-on critici per infrastrutture lab. Hetzner: La semplicità di una VM classica è una virtù. L\u0026rsquo;era di Lushy Corp inizia ora, su una solida fondazione Linux, pronta a gestire i segreti.\nProssima tappa: Provisioning.\n","date":"30 marzo 2026","externalUrl":null,"permalink":"/it/posts/cloud-free-reality-lushy-corp-hetzner-pivot/","section":"Posts","summary":"","title":"Cloud Free e la Cruda Realtà: Il Pivot di Lushy Corp verso Hetzner","type":"posts"},{"content":"","date":"30 marzo 2026","externalUrl":null,"permalink":"/it/tags/homelab/","section":"Tags","summary":"","title":"Homelab","type":"tags"},{"content":"","date":"30 marzo 2026","externalUrl":null,"permalink":"/it/tags/oci/","section":"Tags","summary":"","title":"OCI","type":"tags"},{"content":"","date":"30 marzo 2026","externalUrl":null,"permalink":"/it/tags/vps/","section":"Tags","summary":"","title":"VPS","type":"tags"},{"content":"","date":"24 marzo 2026","externalUrl":null,"permalink":"/it/tags/infrastructure-as-code/","section":"Tags","summary":"","title":"Infrastructure-as-Code","type":"tags"},{"content":"","date":"24 marzo 2026","externalUrl":null,"permalink":"/it/tags/networking/","section":"Tags","summary":"","title":"Networking","type":"tags"},{"content":"","date":"24 marzo 2026","externalUrl":null,"permalink":"/it/tags/oauth/","section":"Tags","summary":"","title":"OAuth","type":"tags"},{"content":" Tailscale: La Spina Dorsale Sicura della Rinascita del TazLab # Introduzione: Il Tessuto Connettivo tra i Due Mondi # Nel percorso di ricostruzione del TazLab che ho descritto nei precedenti articoli, siamo arrivati a un punto critico. Abbiamo un piano per far rinascere l\u0026rsquo;infrastruttura da un singolo bucket S3 e abbiamo blindato le credenziali di bootstrap eliminandole dal disco grazie a TazPod e AWS SSO. Ma c\u0026rsquo;è un elemento che mancava ancora: il \u0026ldquo;filo invisibile\u0026rdquo; che permette a questi componenti di parlarsi in modo sicuro, privato e agnostico rispetto al provider cloud.\nL\u0026rsquo;obiettivo di oggi non era solo \u0026ldquo;attivare una VPN\u0026rdquo;. L\u0026rsquo;obiettivo era progettare e implementare la fondazione di rete del TazLab come una risorsa Infrastructure-as-Code (IaC) pura. Niente configurazioni manuali nella console di Tailscale, niente chiavi di autenticazione temporanee che scadono dopo 90 giorni costringendomi a interventi manuali. Ho cercato una soluzione che fosse eterna, dichiarativa e integrata nel ciclo di vita effimero dei miei cluster.\nIl Problema delle Pre-auth Key: Un Debito Tecnico Annunciato # Il modo standard di aggiungere nodi a una Tailnet è usare le Pre-auth Keys. Sono comode per un setup rapido, ma presentano tre problemi fondamentali per un\u0026rsquo;infrastruttura che punta all\u0026rsquo;automazione totale:\nScadenza: Anche se impostate alla durata massima, scadono. Questo significa che se il mio cluster deve scalare o rinascere dopo sei mesi, il bootstrap fallirà perché la chiave iniettata nel codice o nei segreti non è più valida. Gestione Manuale: Generare una nuova chiave richiede un\u0026rsquo;azione umana nella UI di Tailscale. È l\u0026rsquo;opposto del principio \u0026ldquo;Bootstrap from Zero\u0026rdquo; che sto perseguendo. Mancanza di Tracciabilità IaC: Non puoi definire una Pre-auth Key in Terraform in modo che venga ricreata automaticamente senza intervento esterno se non tramite giri tortuosi. La soluzione architetturale corretta è l\u0026rsquo;uso di un Client OAuth. Un Client OAuth di Tailscale non è una chiave, ma un\u0026rsquo;identità che può generare chiavi di autenticazione al volo. Non scade mai (a meno di revoca esplicita) e può essere gestito programmaticamente. Questo è il componente che ho deciso di mettere al centro della rete del TazLab.\nLa Fase IaC: Ephemeral-Castle Si Espande # Ho iniziato creando una nuova directory nel repository delle configurazioni infrastrutturali: ephemeral-castle/tailscale/. Qui ho depositato il codice Terraform che governa l\u0026rsquo;intera rete.\nIl Cuore Declarativo: acl.json # Invece di scrivere le policy di accesso direttamente nell\u0026rsquo;HCL di Terraform, ho scelto di mantenere un file acl.json separato. Questa scelta non è estetica: le ACL di Tailscale sono un JSON complesso e avere un file dedicato permette di validarlo indipendentemente e di leggerlo con estrema chiarezza.\nLa filosofia applicata è lo Zero Trust basato sui Tag. Nessun nodo ha accesso alla rete perché è \u0026ldquo;nella LAN\u0026rdquo;. L\u0026rsquo;accesso è concesso solo se il nodo possiede un tag specifico. Ho definito cinque tag fondamentali:\ntag:tazlab-vault: I nodi del cluster Vault su Oracle Cloud. tag:tazlab-k8s: I nodi del cluster K8s principale su Proxmox/AWS. tag:vault-api: L\u0026rsquo;identità specifica del proxy Vault. tag:tazlab-db: L\u0026rsquo;identità specifica del proxy database. tag:tazpod: La mia workstation di amministrazione. Il principio del Least Privilege è applicato rigorosamente: il cluster K8s può parlare con il Vault solo sulla porta 8200, e solo tramite il tag del proxy. I nodi non si vedono tra loro a livello di OS, vedono solo i servizi necessari.\n{ \u0026#34;tagOwners\u0026#34;: { \u0026#34;tag:tazlab-vault\u0026#34;: [\u0026#34;roberto.tazzoli@gmail.com\u0026#34;], \u0026#34;tag:tazlab-k8s\u0026#34;: [\u0026#34;roberto.tazzoli@gmail.com\u0026#34;], \u0026#34;tag:vault-api\u0026#34;: [\u0026#34;roberto.tazzoli@gmail.com\u0026#34;], \u0026#34;tag:tazlab-db\u0026#34;: [\u0026#34;roberto.tazzoli@gmail.com\u0026#34;], \u0026#34;tag:tazpod\u0026#34;: [\u0026#34;roberto.tazzoli@gmail.com\u0026#34;] }, \u0026#34;acls\u0026#34;: [ { \u0026#34;action\u0026#34;: \u0026#34;accept\u0026#34;, \u0026#34;src\u0026#34;: [\u0026#34;tag:tazlab-vault\u0026#34;], \u0026#34;dst\u0026#34;: [\u0026#34;tag:tazlab-vault:8201\u0026#34;] }, { \u0026#34;action\u0026#34;: \u0026#34;accept\u0026#34;, \u0026#34;src\u0026#34;: [\u0026#34;tag:tazlab-k8s\u0026#34;], \u0026#34;dst\u0026#34;: [\u0026#34;tag:vault-api:8200\u0026#34;] }, { \u0026#34;action\u0026#34;: \u0026#34;accept\u0026#34;, \u0026#34;src\u0026#34;: [\u0026#34;tag:tazpod\u0026#34;], \u0026#34;dst\u0026#34;: [\u0026#34;tag:tazlab-vault:6443,50000\u0026#34;, \u0026#34;tag:tazlab-k8s:6443,50000\u0026#34;] } ] } Durante l\u0026rsquo;implementazione, ho riscontrato un errore di validazione interessante: Terraform restituiva Error: ACL validation failed: json: unknown field \u0026quot;comment\u0026quot;. Questo è un classico esempio di discrepanza tra la UI (che permette commenti inline nelle ACL) e l\u0026rsquo;API JSON pura, che non li accetta. Ho dovuto ripulire il file acl.json da ogni commento per permettere a Terraform di applicarlo con successo.\nLa Scoperta (The \u0026ldquo;Aha!\u0026rdquo; Moment): Terraform e il Client OAuth # Inizialmente, il mio piano prevedeva l\u0026rsquo;uso di curl all\u0026rsquo;interno di uno script di bootstrap per creare il Client OAuth, poiché molte guide datate suggerivano che il provider Terraform di Tailscale non supportasse ancora questa risorsa.\nHo iniziato a scrivere lo script setup.sh usando curl, ma continuavo a ricevere errori 404 page not found. Ho provato a debuggare l\u0026rsquo;URL, a cambiare il formato (usando - per il tailnet name, o il Tailnet ID completo), ma senza successo. Il troubleshooting stava diventando frustrante.\nInvece di insistere sull\u0026rsquo;errore, ho deciso di fare un passo indietro e analizzare i sorgenti del provider Terraform tailscale/tailscale ~\u0026gt; 0.17. È stata la svolta: ho scoperto che la risorsa tailscale_oauth_client esiste ed è perfettamente funzionante.\nHo cancellato lo script curl e ho riscritto tutto in Terraform:\n# OAuth client per bootstrap (genera pre-auth keys) resource \u0026#34;tailscale_oauth_client\u0026#34; \u0026#34;bootstrap\u0026#34; { description = \u0026#34;tazlab-bootstrap\u0026#34; scopes = [\u0026#34;auth_keys\u0026#34;, \u0026#34;devices\u0026#34;] tags = [\u0026#34;tag:tazpod\u0026#34;] } Questa scoperta ha cambiato radicalmente la qualità del lavoro. Ora l\u0026rsquo;identità che genera le chiavi di rete è una risorsa gestita, tracciata nel terraform.tfstate e ricreabile con un comando. L\u0026rsquo;idempotenza non è più un desiderio, ma una realtà tecnica.\nIl Problema dei TagOwners # Un altro ostacolo si è presentato subito dopo: requested tags [tag:tazpod] are invalid or not permitted (400). Per creare un Client OAuth che possa assegnare un tag, l\u0026rsquo;utente (o la chiave API) che esegue l\u0026rsquo;operazione deve essere esplicitamente dichiarato come \u0026ldquo;proprietario\u0026rdquo; di quel tag nella sezione tagOwners delle ACL. Ho dovuto aggiornare acl.json includendo la mia email per ogni tag prima che Terraform potesse creare con successo il client OAuth. È un dettaglio di sicurezza fondamentale: Tailscale impedisce che un\u0026rsquo;identità compromessa possa creare nuovi client con tag arbitrari a cui non ha accesso.\nIntegrazione con TazPod: Chiudere il Cerchio della Sicurezza # Una volta generato il Client OAuth tramite Terraform, il problema è diventato: dove salviamo il client_id e il client_secret? Non possono stare nel repository git (ovviamente) e non volevo salvarli in un file locale insicuro.\nHo utilizzato il Vault RAM di TazPod. Ho aggiornato lo script di orchestrazione setup.sh affinché, dopo l\u0026rsquo;esecuzione di Terraform, estragga automaticamente i segreti dagli output:\n# Estraggo le credenziali da Terraform OAUTH_CLIENT_ID=$(terraform output -raw oauth_client_id) OAUTH_CLIENT_SECRET=$(terraform output -raw oauth_client_secret) # Le salvo nel vault RAM di TazPod echo \u0026#34;$OAUTH_CLIENT_ID\u0026#34; \u0026gt; ~/secrets/tailscale-oauth-client-id echo \u0026#34;$OAUTH_CLIENT_SECRET\u0026#34; \u0026gt; ~/secrets/tailscale-oauth-client-secret # Sincronizzo con S3 (cd /workspace \u0026amp;\u0026amp; tazpod save \u0026amp;\u0026amp; tazpod push vault) Ora, il ciclo di rinascita è completo anche per la rete. Quando eseguo tazpod unlock, i segreti necessari per connettersi alla Tailnet vengono montati in memoria. Qualsiasi nuovo cluster o istanza TazPod potrà usare queste credenziali per unirsi alla rete in meno di un secondo.\nVerifica Empirica: Il Test Live # La teoria è bella, ma i sistemi devono funzionare. Ho eseguito un test live installando Tailscale direttamente nel container tazpod-lab (che ancora non lo includeva). Questa mancanza è stata il trigger per un aggiornamento immediato della layer hierarchy di TazPod: Tailscale deve essere parte del DNA dell\u0026rsquo;immagine base.\nDopo aver avviato il demone tailscaled in modalità userspace (necessaria perché il container non ha i permessi per creare interfacce tun sul kernel dell\u0026rsquo;host), ho tentato la connessione usando le credenziali appena salvate nel vault:\nID=$(cat ~/secrets/tailscale-oauth-client-id) SECRET=$(cat ~/secrets/tailscale-oauth-client-secret) sudo tailscale up \\ --client-id=\u0026#34;$ID\u0026#34; \\ --client-secret=\u0026#34;$SECRET\u0026#34; \\ --hostname=tazpod-lab \\ --advertise-tags=tag:tazpod \\ --reset Il risultato è stato istantaneo: active login: tazpod-lab.magellanic-gondola.ts.net IP: 100.73.57.110\nIl nodo è apparso nella rete, correttamente taggato come tag:tazpod, con la scadenza delle chiavi disabilitata automaticamente dal sistema (comportamento standard di Tailscale per i nodi taggati).\nRiflessioni Post-Lab: Cosa Abbiamo Imparato # Questa sessione ha consolidato la fondazione di rete del TazLab in tre modi:\nIndipendenza dal Provider: Non importa se un cluster gira su OCI, AWS o nel mio salotto. Se ha l\u0026rsquo;estensione Tailscale e il Client OAuth, è parte della rete privata del TazLab istantaneamente. Manutenibilità Zero: Passando dai Client OAuth gestiti via IaC, ho eliminato il rischio di fallimenti dovuti a scadenze di chiavi. La rete è ora un\u0026rsquo;entità \u0026ldquo;viva\u0026rdquo; che si autogestisce. Sicurezza Integrata: La catena di fiducia che parte da AWS SSO e passa per il Vault RAM di TazPod ora protegge anche l\u0026rsquo;accesso alla rete. Il prossimo passo della roadmap è il provisioning del cluster tazlab-vault su Oracle Cloud. Grazie al lavoro di oggi, quel cluster nascerà già parlando privatamente con il resto del mio mondo, senza che io debba mai esporre la sua porta 8200 al traffico pubblico di internet.\nLa rete c\u0026rsquo;è. Il castello effimero ha ora le sue mura invisibili.\n","date":"24 marzo 2026","externalUrl":null,"permalink":"/it/posts/tailscale-secure-backbone-tazlab-rebirth/","section":"Posts","summary":"","title":"Tailscale: La Spina Dorsale Sicura della Rinascita del TazLab","type":"posts"},{"content":"","date":"24 marzo 2026","externalUrl":null,"permalink":"/it/tags/tazpod/","section":"Tags","summary":"","title":"Tazpod","type":"tags"},{"content":"","date":"24 marzo 2026","externalUrl":null,"permalink":"/it/tags/zero-trust/","section":"Tags","summary":"","title":"Zero Trust","type":"tags"},{"content":"","date":"22 marzo 2026","externalUrl":null,"permalink":"/it/tags/aws/","section":"Tags","summary":"","title":"Aws","type":"tags"},{"content":"","date":"22 marzo 2026","externalUrl":null,"permalink":"/it/tags/ci-cd/","section":"Tags","summary":"","title":"Ci-Cd","type":"tags"},{"content":"","date":"22 marzo 2026","externalUrl":null,"permalink":"/it/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":"","date":"22 marzo 2026","externalUrl":null,"permalink":"/it/tags/github-actions/","section":"Tags","summary":"","title":"Github-Actions","type":"tags"},{"content":"","date":"22 marzo 2026","externalUrl":null,"permalink":"/it/tags/golang/","section":"Tags","summary":"","title":"Golang","type":"tags"},{"content":"","date":"22 marzo 2026","externalUrl":null,"permalink":"/it/tags/iam-identity-center/","section":"Tags","summary":"","title":"Iam-Identity-Center","type":"tags"},{"content":"","date":"22 marzo 2026","externalUrl":null,"permalink":"/it/tags/secrets-management/","section":"Tags","summary":"","title":"Secrets Management","type":"tags"},{"content":"","date":"22 marzo 2026","externalUrl":null,"permalink":"/it/tags/sso/","section":"Tags","summary":"","title":"Sso","type":"tags"},{"content":" Zero Credenziali sul Disco: Riscrivere TazPod con AWS IAM Identity Center # Introduzione: Il Problema che Non Riuscivo a Risolvere # Nel precedente articolo su questo progetto ho descritto la visione architetturale: rimpiazzare Infisical con AWS IAM Identity Center come ancora del bootstrap, eliminare ogni credenziale statica dall\u0026rsquo;immagine Docker di TazPod, e rendere l\u0026rsquo;intero ciclo di rinascita riproducibile da una macchina vuota con solo un bucket S3, una passphrase e un dispositivo MFA.\nQuella era la progettazione. Questo articolo racconta l\u0026rsquo;implementazione — quattro ore di lavoro che hanno prodotto TazPod 0.3.12, undici versioni di build, sei bug distinti scoperti esclusivamente durante il test live sul sistema reale, e un CI/CD pipeline ricostruito iterativamente.\nFase 1: La Rimozione Chirurgica di Infisical # Il punto di partenza era cmd/tazpod/main.go — 613 righe, circa un terzo delle quali dedicate esclusivamente all\u0026rsquo;integrazione Infisical. La tentazione in questi casi è fare una rimozione graduale, lasciando rami di compatibilità o wrapper deprecati. Ho resistito deliberatamente a quella tentazione.\nIl principio che ho applicato si chiama Design Integrity: il codice deve dire la verità su quello che fa il sistema. Ogni riga di codice Infisical lasciata compilabile — anche commentata, anche con un deprecation warning — è una menzogna raccontata al prossimo lettore. La rimozione deve essere totale o non è una rimozione.\nHo eliminato: le struct SecretMapping e SecretsConfig, la variabile globale secCfg, le costanti SecretsYAML e EnvFile, le funzioni pullSecrets(), login() (versione Infisical), runInfisical(), runCmd(), checkInfisicalLogin(), loadEnclaveEnv(), resolveSecret(), e il metodo isMounted() locale (duplicato di utils.IsMounted). Sono sparite anche le dipendenze bytes e strings dagli import, rimaste orfane.\nIl risultato è stato un file di 250 righe invece di 613. Il compilatore ha confermato la pulizia al primo tentativo.\nLa stessa operazione in internal/vault/vault.go è stata più delicata. Le costanti Infisical (InfisicalLocalHome, InfisicalKeyringLocal, InfisicalVaultDir, InfisicalKeyringVault) erano usate da setupBindAuth() e da Lock(). Le ho rimpiazzate con le equivalenti AWS:\nconst ( AwsLocalHome = \u0026#34;/home/tazpod/.aws\u0026#34; AwsVaultDir = MountPath + \u0026#34;/.aws\u0026#34; PassCache = MountPath + \u0026#34;/.vault_pass\u0026#34; ) La funzione setupBindAuth() ora crea un bind mount dalla directory AWS nella RAM tmpfs verso ~/.aws nel container. Il meccanismo è identico a quello che usava per Infisical — un bind mount che rende la directory RAM indistinguibile da una directory normale per qualsiasi processo, incluso l\u0026rsquo;AWS CLI e il Go SDK.\nFase 2: Il Symlink ~/.aws — Due Implementazioni Prima di Quella Giusta # La prima implementazione del symlink per la configurazione AWS è stata un errore di granularità. Ho scritto in SetupIdentity() (vault.go) il codice per symlinkare il file ~/.aws/config verso /workspace/.tazpod/aws/config. Era sbagliato per tre ragioni: symlinkavo un file invece di una directory, usavo il nome aws senza il punto iniziale (incoerente con il pattern degli altri tool), e l\u0026rsquo;avevo messo in Go invece che in .bashrc.\nIl pattern corretto già esisteva nel .bashrc per quattro altri strumenti: .pi, .omp, .gemini, .claude. Ogni directory di tool viene symlinkava dalla workspace verso la home: ~/.pi → /workspace/.tazpod/.pi, e così via. La logica è nel .bashrc perché viene eseguita ad ogni shell, garantendo la ricreazione dei symlink anche dopo un lock che smonta il tmpfs.\nPer ~/.aws c\u0026rsquo;era però una complessità aggiuntiva che gli altri tool non avevano: quando il vault è sbloccato, setupBindAuth() esegue rm -rf ~/.aws e lo rimpiazza con un bind mount dalla RAM. Se il loop generico del .bashrc girasse in una nuova shell con il vault già aperto, distruggerebbe il bind mount attivo.\nLa soluzione è stata un guard esplicito con mountpoint -q:\n# AWS config: symlink ~/.aws -\u0026gt; /workspace/.tazpod/.aws # Skip if already bind-mounted from the vault enclave (vault unlocked) if ! mountpoint -q \u0026#34;$HOME/.aws\u0026#34; 2\u0026gt;/dev/null; then mkdir -p /workspace/.tazpod/.aws if [ ! -L \u0026#34;$HOME/.aws\u0026#34; ] || [ \u0026#34;$(readlink \u0026#34;$HOME/.aws\u0026#34;)\u0026#34; != \u0026#34;/workspace/.tazpod/.aws\u0026#34; ]; then rm -rf \u0026#34;$HOME/.aws\u0026#34; \u0026amp;\u0026amp; ln -sf /workspace/.tazpod/.aws \u0026#34;$HOME/.aws\u0026#34; fi fi Se ~/.aws è un mountpoint (vault sbloccato) il blocco viene saltato. Se non lo è (vault bloccato, o prima apertura), il symlink viene creato o ricreato. Il bind mount del vault e il symlink della workspace coesistono senza conflitti, servendo due stati operativi distinti.\nFase 3: Il Bug del Go AWS SDK con i Profili SSO # La funzione NewS3Client nel package utils accettava solo il nome del bucket. Ho aggiunto un secondo parametro per il profilo SSO:\nfunc NewS3Client(bucket, profile string) (*S3Client, error) { opts := []func(*config.LoadOptions) error{ config.WithRegion(DefaultRegion), } if profile != \u0026#34;\u0026#34; \u0026amp;\u0026amp; os.Getenv(\u0026#34;AWS_ACCESS_KEY_ID\u0026#34;) == \u0026#34;\u0026#34; { opts = append(opts, config.WithSharedConfigProfile(profile)) } cfg, err := config.LoadDefaultConfig(context.TODO(), opts...) ... } La condizione os.Getenv(\u0026quot;AWS_ACCESS_KEY_ID\u0026quot;) == \u0026quot;\u0026quot; non è ovvia e merita una spiegazione. Durante il testing ho scoperto che passare WithSharedConfigProfile al Go AWS SDK causa un hang di 30+ secondi quando AWS_ACCESS_KEY_ID è già nell\u0026rsquo;ambiente. Il SDK cerca comunque di caricare la configurazione del profilo SSO — incluso un tentativo di contattare l\u0026rsquo;endpoint SSO per validare o rinfrescare i token — indipendentemente dal fatto che le credenziali statiche siano già disponibili.\nLa credential chain del Go SDK v2 dà priorità alle variabili d\u0026rsquo;ambiente rispetto alle credenziali del profilo. Ma il caricamento della configurazione del profilo (regione, endpoint, parametri SSO) avviene comunque se WithSharedConfigProfile viene passato. Saltare il profilo quando le env var sono presenti è la soluzione corretta: le credenziali statiche hanno già tutto ciò che serve.\nQuesto bug non si manifesta mai in ambiente di produzione — dove non ci sono credenziali statiche e il profilo SSO è l\u0026rsquo;unica fonte — ma è critico per il testing e per le situazioni di fallback.\nFase 4: AWS IAM Identity Center — Setup Guidato # Il setup di IAM Identity Center è stato interattivo: l\u0026rsquo;ho fatto in collaborazione, passaggio per passaggio dalla AWS Console. I punti non ovvi che vale la pena documentare:\nLa regione è us-east-1, non eu-central-1. Anche se ho configurato IAM Identity Center dalla console di eu-central-1, il portale SSO viene creato in us-east-1. L\u0026rsquo;URL del portale — https://ssoins-7223c4f9117b4c94.portal.us-east-1.app.aws — contiene esplicitamente la regione. Configurare sso_region = eu-central-1 nel profilo AWS ha prodotto InvalidRequestException: Couldn't find Identity Center Instance. La correzione è stata immediata una volta identificata la causa.\nIl permission set TazLabBootstrap segue il Principle of Least Privilege. La policy inline permette solo le tre operazioni strettamente necessarie, sul singolo bucket e sul singolo prefisso:\n{ \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [{ \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Action\u0026#34;: [\u0026#34;s3:GetObject\u0026#34;, \u0026#34;s3:PutObject\u0026#34;, \u0026#34;s3:ListBucket\u0026#34;], \u0026#34;Resource\u0026#34;: [ \u0026#34;arn:aws:s3:::tazlab-storage\u0026#34;, \u0026#34;arn:aws:s3:::tazlab-storage/tazpod/vault/*\u0026#34; ] }] } Nessun accesso ad altri bucket. Nessuna operazione di gestione. Se questo profilo venisse compromesso, l\u0026rsquo;attaccante potrebbe solo scaricare o sovrascrivere il file vault.tar.aes — che è cifrato con AES-256-GCM e inutile senza la passphrase.\nIl file di configurazione persistente vive in /workspace/.tazpod/.aws/config, tracciato nella workspace ma non nel vault cifrato — perché non contiene segreti:\n[profile tazlab-bootstrap] sso_start_url = https://ssoins-7223c4f9117b4c94.portal.us-east-1.app.aws sso_account_id = 468971461088 sso_role_name = TazLabBootstrap sso_region = us-east-1 region = eu-central-1 Fase 5: Il CI/CD Pipeline — Sette Iterazioni # Il workflow GitHub Actions esistente era semplice: buildava il CLI Go (senza iniettare la versione) e buildava sempre tutte e quattro le immagini Docker ad ogni push su master. Ho ricostruito tutto in sette commit iterativi, ognuno che risolveva un problema specifico.\nIterazione 1: versione nel binario. Il comando di build non usava -ldflags, producendo sempre un binario con Version = \u0026quot;dev\u0026quot;. Corretto a:\nGOOS=linux GOARCH=amd64 go build -ldflags \u0026#34;-X main.Version=${VERSION}\u0026#34; -o tazpod cmd/tazpod/main.go Iterazione 2: pubblicazione automatica della release. Aggiunto uno step con gh release create che pubblica il binario compilato come asset GitHub. Questo rende scripts/install.sh funzionante senza intervento manuale.\nIterazione 3: build selettiva. Le immagini Docker non devono essere rebuildate ad ogni commit. Ho aggiunto un check che analizza git diff --name-only HEAD~1 HEAD:\nSe cambiano cmd/, internal/, o VERSION → build CLI + release Se cambiano .tazpod/Dockerfile* o dotfiles/ → build Docker Iterazione 4: permessi GitHub Token. Il step gh release create falliva con HTTP 403. La causa: GITHUB_TOKEN ha permessi limitati per default nei workflow. Soluzione:\npermissions: contents: write Iterazione 5: il binario non è in git. Con bin/tazpod (15MB) tracciato da git, ogni push richiedeva 30-35 secondi di upload HTTPS. Rimosso con git rm --cached bin/tazpod, aggiunto bin/ al .gitignore. I push successivi: meno di 1 secondo.\nIterazione 6: il build CLI deve girare sempre. Con la build condizionale, quando solo i Dockerfile cambiavano il binario non veniva compilato. Ma Dockerfile.base contiene COPY tazpod /home/tazpod/.local/bin/tazpod — senza il file nel build context, la Docker build fallisce. Il Setup Go e il Build CLI step non hanno condizioni: girano sempre. Solo Publish GitHub Release è condizionato.\nIterazione 7: GHA Docker cache. Aggiunto cache-from e cache-to con type=gha e uno scope per layer (tazpod-base, tazpod-aws, tazpod-k8s, tazpod-ai). La prima build popola la cache; le successive riusano i layer invariati. Su una modifica a Dockerfile.ai (il layer finale), i tre layer precedenti vengono recuperati dalla cache in secondi.\nFase 6: Il Metodo di Autenticazione Git — 30 Secondi vs 1 Secondo # Durante il lavoro sul CI/CD ho identificato che ogni git push impiegava 30-35 secondi sistematicamente, causando timeout degli strumenti. La causa era il metodo di autenticazione usato fino a quel momento:\n# SBAGLIATO git -c http.extraheader=\u0026#34;Authorization: Basic $(echo -n x-access-token:${TOKEN} | base64)\u0026#34; push Il metodo http.extraheader con Base64 aggiunge overhead al protocollo di negoziazione HTTP di git — una fase di handshake che con GitHub risulta significativamente più lenta rispetto al metodo nativo.\nIl metodo corretto usa un credential helper inline che implementa il protocollo credential standard di git:\n# CORRETTO git -c credential.helper=\u0026#34;!f() { echo \u0026#39;username=x-access-token\u0026#39;; echo \\\u0026#34;password=${TOKEN}\\\u0026#34;; }; f\u0026#34; push origin master La differenza misurata: 30-35 secondi contro 0.8-1.2 secondi. Il benchmark è stato effettuato su commit identici dello stesso repository. Il metodo corretto usa il protocollo che GitHub si aspetta nativamente, senza layer di codifica aggiuntivi.\nFase 7: I Sei Bug del Test Live # È questa la parte che differenzia un\u0026rsquo;implementazione progettata a tavolino da una verificata su un sistema reale. Tutti e sei i bug erano invisibili in fase di sviluppo — nessuno era individuabile senza eseguire il flusso completo su una macchina host reale.\nBug 1: loadConfigs() non chiamata nel path senza argomenti. In main(), loadConfigs() veniva invocata solo dopo il controllo degli argomenti. Quando tazpod veniva eseguito senza argomenti, smartEntry() leggeva cfg ancora al valore zero. Risultato: ❌ container_name mancante in config.yaml. Fix: loadConfigs() come prima istruzione di smartEntry().\nBug 2: path del vault hardcodato. vault.VaultFile è costante a /workspace/.tazpod/vault/vault.tar.aes — il path assoluto valido dentro il container, dove il progetto è sempre montato su /workspace. Sull\u0026rsquo;host, il progetto può stare ovunque. Fix: filepath.Join(cwd, \u0026quot;.tazpod/vault/vault.tar.aes\u0026quot;) relativo alla working directory corrente dell\u0026rsquo;host.\nBug 3: unlock chiede la password sudo all\u0026rsquo;utente host. vault.Unlock() esegue sudo mount -t tmpfs per creare il tmpfs in RAM. Dentro il container, l\u0026rsquo;utente tazpod ha NOPASSWD sudo. Sull\u0026rsquo;host, l\u0026rsquo;utente non ha quel privilegio. La separazione architettuale corretta: login e pull vault sull\u0026rsquo;host (dove c\u0026rsquo;è il browser per SSO), unlock dentro il container (dove ci sono i permessi sudo). Implementato con execInContainer(), un helper che esegue comandi interattivi via docker exec -it.\nBug 4: aws CLI non trovata nel bootstrap. docker exec bash -c \u0026quot;...\u0026quot; apre una shell non-interattiva che non sourcia .bashrc. Il symlink ~/.aws non viene creato, la configurazione AWS non è trovata. Fix: passare -e AWS_CONFIG_FILE=/workspace/.tazpod/.aws/config esplicitamente a docker exec, bypassando completamente il symlink.\nBug 5: la sequenza non si interrompe su errore. tazpod login usciva con codice 0 anche in caso di errore — main() non propagava il codice di uscita dei subcomandi falliti. Il \u0026amp;\u0026amp; nella catena shell non fermava l\u0026rsquo;esecuzione. Fix: os.Exit(1) nei path di errore di login() e pullVault().\nBug 6: la passphrase corrotta dal buffer TTY. Con bash -c \u0026quot;tazpod login \u0026amp;\u0026amp; tazpod pull vault \u0026amp;\u0026amp; tazpod unlock\u0026quot;, i tre comandi condividono lo stesso TTY. Durante il flusso SSO — mentre il browser è aperto, l\u0026rsquo;utente naviga e inserisce il codice MFA — i keystroke vengono bufferizzati nel TTY. Quando arriva il momento di leggere la passphrase vault con term.ReadPassword, il buffer TTY contiene già caratteri che vengono letti come parte della passphrase. Il risultato è ❌ WRONG PASSWORD con la passphrase corretta. Fix: ogni passo (login, pull, unlock) gira in un execInContainer separato, con il proprio TTY pulito. execInContainer ritorna bool per interrompere la sequenza in caso di fallimento.\nQuesti sei bug, risolti in sequenza nelle versioni da 0.3.5 a 0.3.12, descrivono in modo preciso la differenza tra ambiente di sviluppo (container, cwd prevedibile, TTY controllato) e ambiente di produzione (host reale, utente diverso, sessioni terminale con I/O non deterministico).\nRiflessioni: Cosa Cambia con Zero Credenziali sul Disco # Il risultato finale è un binario che, eseguito su un host con solo Docker installato, gestisce autonomamente l\u0026rsquo;intero flusso di bootstrap: verifica la presenza di un progetto inizializzato, porta su il container se necessario, e — se non c\u0026rsquo;è un vault locale — guida l\u0026rsquo;utente attraverso aws sso login, il download da S3, e la decrifratura in RAM.\nIl tutto senza che nessuna credenziale AWS statica tocchi mai il disco dell\u0026rsquo;host.\nL\u0026rsquo;immagine Docker che gira nel container (tazzo/tazpod-aws:latest) contiene l\u0026rsquo;AWS CLI — ma non credenziali. La configurazione SSO in /workspace/.tazpod/.aws/config contiene l\u0026rsquo;URL del portale e il nome del ruolo — ma non token, non chiavi, non segreti. Il vault cifrato su S3 contiene tutto il resto — ma è inutile senza la passphrase che vive solo in testa.\nL\u0026rsquo;architettura ha ora tre caratteristiche che prima non aveva: è verificabile (puoi ispezionare ogni file e non trovare credenziali), è riproducibile (la sequenza tazpod → SSO → pull → unlock funziona da qualsiasi host con Docker), ed è resiliente al furto (rubare il laptop dà accesso all\u0026rsquo;immagine Docker e al file di configurazione SSO pubblico, non ai segreti).\nIl prossimo passo — che chiude il ciclo descritto nell\u0026rsquo;articolo precedente — è il provisioning di tazlab-vault su Oracle Cloud e la migrazione dei segreti applicativi da Infisical a HashiCorp Vault CE. Ma questa è un\u0026rsquo;altra sessione.\n","date":"22 marzo 2026","externalUrl":null,"permalink":"/it/posts/tazpod-zero-credentials-aws-sso/","section":"Posts","summary":"","title":"Zero Credenziali sul Disco: Riscrivere TazPod con AWS IAM Identity Center","type":"posts"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/bootstrap/","section":"Tags","summary":"","title":"Bootstrap","type":"tags"},{"content":" The Current State: Il Piano Aveva un Buco # Nel precedente articolo su questa roadmap ho descritto il motivo per cui il TazLab sta migrando da Infisical a HashiCorp Vault CE, e la direzione verso cui si sta muovendo: segreti dinamici, rotazione automatica, un secondo cluster su Oracle Cloud. Il \u0026ldquo;cosa\u0026rdquo; e il \u0026ldquo;perché\u0026rdquo; erano chiari.\nQuello che mancava era il \u0026ldquo;come sopravvive il sistema quando tutto sparisce\u0026rdquo;.\nLa domanda che non riuscivo a togliermi dalla testa era questa: se domani mattina Proxmox, Oracle Cloud e il mio computer bruciassero contemporaneamente, cosa mi resterebbe? Un bucket S3, una passphrase in testa, e un dispositivo MFA fisico. Basta. Da questi tre elementi deve ripartire tutto — e non in maniera eroica e manuale, ma in modo sistematico, il più automatico possibile.\nQuesta è la sessione di progettazione in cui abbiamo risolto esattamente quel problema.\nThe \u0026ldquo;Why\u0026rdquo;: Non Basta Migrare, Bisogna Rinascere # La migrazione da Infisical a Vault non è solo una questione di vendor. È l\u0026rsquo;occasione per riprogettare il bootstrap da zero — il momento in cui l\u0026rsquo;intera filosofia del castello effimero viene messa alla prova.\nUn\u0026rsquo;infrastruttura è davvero effimera solo se puoi distruggerla e ricostruirla senza paura. E puoi farlo senza paura solo se hai risposto onestamente a questa domanda: cosa deve esistere fuori dai cluster per renderli ricostruibili?\nLa risposta ha una forma precisa. Non una serie infinita di segreti sparsi, non una dipendenza da un servizio esterno sempre attivo. Tre anchor, tutti sullo stesso bucket S3:\nS3: tazlab-storage/ ├── tazpod/vault.tar.aes ← segreti di bootstrap (AES-256-GCM, passphrase) ├── vault/vault-latest.snap ← snapshot Raft di Vault (tutti i segreti app) └── pgbackrest/ ← backup PostgreSQL (Mnemosyne, dati tazlab-k8s) Il primo contiene il minimo indispensabile per far partire tutto prima che esistano cluster. Il secondo è la memoria di Vault — tutti i segreti delle applicazioni, aggiornata automaticamente ogni giorno. Il terzo è il database: i dati di Mnemosyne, le configurazioni, lo storico. Nessuno dei tre ha senso senza gli altri due. Insieme, sono tutto ciò che serve per ricominciare.\nThe Target Architecture: Quattro Decisioni Difficili # La progettazione di questo ciclo ha richiesto di sciogliere quattro nodi che, in superficie, sembravano semplici.\nIl Problema del Bootstrap: Chi Viene Prima dell\u0026rsquo;Uovo? # L\u0026rsquo;immagine Docker di tazpod è pubblica. Non può contenere credenziali. Ma per scaricare vault.tar.aes da S3 ho bisogno di credenziali AWS. E le credenziali AWS le ho nel vault. E il vault è su S3.\nLa soluzione non è tecnica — è architetturale. Ho usato AWS IAM Identity Center (il servizio SSO di AWS): un flusso di autenticazione interattivo dove inserisci email, password e codice MFA, e ricevi credenziali temporanee valide 8 ore. Il file di configurazione AWS che va nell\u0026rsquo;immagine contiene solo l\u0026rsquo;URL del portale SSO e il nome del ruolo — nessun segreto, pubblicabile senza problemi.\ndocker run tazzo/tazpod-ai │ ▼ aws sso login --profile tazlab-bootstrap │ → email + password + MFA fisico ▼ aws s3 cp s3://tazlab-storage/tazpod/vault.tar.aes ... │ ▼ tazpod unlock ← passphrase (solo in testa) │ ▼ secrets/ aperta — da qui parte tutto il resto La passphrase vive solo nella mia testa. Il dispositivo MFA è fisico. Senza entrambi, il bucket S3 è un archivio cifrato inutile.\nL\u0026rsquo;Unseal di Vault: Professionale Non Significa Costoso # Vault parte sempre in stato \u0026ldquo;sealed\u0026rdquo; — non risponde finché non gli viene fornita la chiave per decifrare la sua master key. Nella mia testa il problema sembrava richiedere un KMS esterno: AWS KMS (1$/mese), OCI KMS (gratuito per chiavi software), qualcosa di sempre disponibile.\nMa c\u0026rsquo;era una soluzione più pulita che non richiedeva nessuna dipendenza esterna. Le chiavi di unseal di Vault (algoritmo di Shamir: 3 chiavi, servono 3 su 5 per aprire) vengono generate una volta sola all\u0026rsquo;inizializzazione. Le salvo in secrets/. Al bootstrap, create.sh le usa direttamente:\nvault operator unseal $(cat /home/tazpod/secrets/vault-unseal-key-1) vault operator unseal $(cat /home/tazpod/secrets/vault-unseal-key-2) vault operator unseal $(cat /home/tazpod/secrets/vault-unseal-key-3) È completamente automatico dal punto di vista dello script, perché l\u0026rsquo;interazione umana era già avvenuta: passphrase + MFA all\u0026rsquo;inizio del bootstrap hanno già aperto secrets/. Da lì in poi, nessun intervento richiesto.\nOCI KMS rimane come opzione per ambienti di simulazione dove il ciclo manuale è scomodo.\nLa Rete: Tailscale Come Estensione del Sistema Operativo # ESO su tazlab-k8s deve poter raggiungere Vault su tazlab-vault (OCI) dal primo momento in cui viene deployato. Vault non può stare su un endpoint pubblico senza motivo.\nLa soluzione è Tailscale — ma non come pod Kubernetes. Come estensione del sistema operativo Talos. Esiste siderolabs/tailscale come extension ufficiale: viene baked nell\u0026rsquo;immagine alla Talos Image Factory e si avvia come servizio di sistema, prima che Kubernetes esista.\nNodo OCI si avvia │ ▼ Talos OS → Tailscale extension → nodo nella tailnet ← prima di K8s │ ▼ Kubernetes bootstrap → cluster healthy │ ▼ Terragrunt deploya Vault → ESO connette Vault via tailnet ✓ La auth key di Tailscale (reusable, con tag tag:tazlab-node) vive in secrets/ e viene iniettata nella machine config durante il provisioning. Il nodo rientra automaticamente nella stessa rete ad ogni rebuild, con lo stesso nome DNS.\nLa stessa estensione va su tazlab-k8s. I due cluster comunicano privatamente, senza esporre nulla su internet.\ntazlab-vault: Minimal by Design # L\u0026rsquo;ultima decisione è stata forse la più semplice una volta formulata correttamente: tazlab-vault non ha bisogno di Flux.\nFlux ha senso quando gestisci molte applicazioni che cambiano continuamente e vuoi che il cluster si auto-riconcili. tazlab-vault ha una sola responsabilità: far girare Vault. Per deployare una sola applicazione, Flux è un livello di complessità che non guadagna nulla. Gli upgrade di Vault devono essere deliberati, testati, e mai automatici.\nLa scelta è Terragrunt con il Helm provider — esattamente il pattern già usato in ephemeral-castle per ESO, MetalLB e Longhorn. La struttura dei layer:\nsecrets → platform → vault Niente engine (ESO), niente gitops (Flux). Vault usa Raft integrated storage su hostPath — non ha bisogno di Longhorn perché i dati persistenti vengono comunque ripristinati dallo snapshot S3 ad ogni rebuild.\nPhased Approach: Sette Fasi Verso la Rinascita Completa # Il lavoro è organizzato in fasi sequenziali, ognuna stabile prima di passare alla successiva.\nFase A — Prerequisiti: configurare AWS IAM Identity Center, creare l\u0026rsquo;utente SSO con MFA, generare la Tailscale reusable auth key. Zero impatto sui cluster esistenti.\nFase B — tazlab-vault minimal: nuovo schematic Talos con l\u0026rsquo;estensione Tailscale, risolvere il blocker degli IP riservati OCI (da tazlab-vault-init), completare il bootstrap Talos, deployare Vault CE via Terragrunt, prima inizializzazione e salvataggio delle unseal keys in vault.tar.aes.\nFase C — Vault configuration: abilitare KV v2, configurare il Kubernetes auth method per ESO, migrare tutti i segreti da Infisical a Vault KV.\nFase D — Migrazione tazlab-k8s: aggiornare la Talos image con l\u0026rsquo;estensione Tailscale (upgrade rolling, non rebuild), sostituire il ClusterSecretStore da Infisical a Vault, aggiornare tutti gli ExternalSecret con i nuovi path KV.\nFase E — tazpod Vault integration: rimuovere la logica Infisical da main.go, implementare tazpod pull via Vault CLI, aggiornare tazpod vpn per usare Tailscale al posto del WireGuard custom mai testato.\nFase F — Decommission Infisical: verificare che zero componenti usino ancora Infisical, rimuovere provider e riferimenti da tutti i repo, cancellare i segreti dall\u0026rsquo;account Infisical.\nFase G — Make repos public: audit del git history con trufflehog, verifica dei .gitignore, rendere pubblici tazpod, tazlab-k8s e ephemeral-castle.\nFuture Outlook: La Prova Finale # C\u0026rsquo;è un test che non mente: riesci a rendere pubblici i tuoi repo senza paura?\nSe la risposta è sì, hai davvero raggiunto zero-secrets-in-git. Non come principio dichiarato, ma come realtà verificabile. Chiunque può aprire il codice, vedere come funziona tutto, e non trovare nessuna credenziale, nessun token, nessun segreto. La sicurezza non dipende dall\u0026rsquo;oscurità.\nIl ciclo di rinascita completo diventa quindi questa sequenza, eseguibile da chiunque che abbia accesso ai tre elementi giusti:\nMacchina vuota + bucket S3 (sempre disponibile) + passphrase (in testa) + dispositivo MFA (in tasca) ────────────────────────────── = infrastruttura completa, operativa, \u0026lt; 30 minuti Il TazLab non ha un indirizzo fisso. Ha solo un Bucket S3 da cui rinasce. +++\n","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/posts/bootstrap-from-zero-vault-s3-rebirth/","section":"Posts","summary":"","title":"Bootstrap from Zero: Ricostruire Tutto da un Singolo Bucket S3","type":"posts"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/hashicorp-vault/","section":"Tags","summary":"","title":"HashiCorp Vault","type":"tags"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/infisical/","section":"Tags","summary":"","title":"Infisical","type":"tags"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/kubernetes/","section":"Tags","summary":"","title":"Kubernetes","type":"tags"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/oracle-cloud/","section":"Tags","summary":"","title":"Oracle Cloud","type":"tags"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/talos-os/","section":"Tags","summary":"","title":"Talos OS","type":"tags"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/terragrunt/","section":"Tags","summary":"","title":"Terragrunt","type":"tags"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/arm64/","section":"Tags","summary":"","title":"Arm64","type":"tags"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/iac/","section":"Tags","summary":"","title":"Iac","type":"tags"},{"content":"","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/tags/talos-linux/","section":"Tags","summary":"","title":"Talos-Linux","type":"tags"},{"content":" Terraforming the Cloud: My First IaC on OCI # Introduzione: Quando l\u0026rsquo;Infrastruttura Diventa Reale # Per anni ho letto di \u0026ldquo;Infrastructure as Code\u0026rdquo; (IaC). Ho studiato i principi, ho visto i tutorial, e ho persino implementato soluzioni locali che si avvicinavano al concetto. Ma c\u0026rsquo;è una differenza fondamentale tra definire una macchina virtuale sul proprio server Proxmox in cantina e definire un\u0026rsquo;infrastruttura completa su un cloud provider pubblico come Oracle Cloud Infrastructure (OCI). La prima è un esercizio controllato; la seconda è la realtà.\nOggi ho colmato quel divario. L\u0026rsquo;obiettivo non era banale: non volevo un \u0026ldquo;Hello World\u0026rdquo; con una singola istanza Linux. Volevo replicare l\u0026rsquo;architettura robusta ed efimera del mio cluster locale (tazlab-k8s) su OCI, sfruttando il piano Always Free e l\u0026rsquo;architettura ARM64 (Ampere A1) per costruire la base del nuovo cluster tazlab-vault. Questo cluster ospiterà in futuro un\u0026rsquo;installazione enterprise di HashiCorp Vault, quindi la \u0026ldquo;serietà\u0026rdquo; del progetto imponeva un rigore tecnico assoluto fin dal primo giorno.\nQuesta non è la storia di un successo immediato. È la cronaca di un pomeriggio passato a combattere con scaffolding non testati, peculiarità delle immagini cloud, e il paradosso dei certificati TLS in ambienti NAT. È la storia di come due macchine virtuali che si accendono possano rappresentare una vittoria tecnica significativa.\n1. Il Contesto e la Scelta Tecnologica # Perché OCI? E perché Terraform?\nLa scelta di Oracle Cloud è puramente pragmatica: il loro piano Always Free offre risorse ARM64 incredibilmente generose (4 OCPU e 24 GB di RAM), perfette per un cluster Kubernetes a due nodi (Control Plane + Worker) senza costi ricorrenti.\nLa scelta dello stack tecnologico segue la filosofia del Ephemeral Castle, il framework che ho sviluppato internamente:\nTerraform: Per il provisioning delle risorse base (VCN, Subnet, Istanze). Terragrunt: Per mantenere il codice DRY e gestire le dipendenze tra i layer (rete vs compute). Talos Linux: Un sistema operativo immutabile, minimale e sicuro per Kubernetes. Talos non ha SSH, non ha shell, ed è gestito interamente via API. Questo forza un approccio IaC puro: non puoi \u0026ldquo;entrare e fixare\u0026rdquo; una configurazione sbagliata; devi distruggere e ricreare. Avevo preparato uno scaffolding iniziale dei file Terraform una settimana fa, ma non era mai stato eseguito (\u0026ldquo;sgrossato\u0026rdquo; ma non testato). Oggi era il giorno della verità.\n2. Phase 1: SDD e Preparazione dell\u0026rsquo;Account # Prima di scrivere una sola riga di codice o lanciare un comando, ho attivato il mio processo di Spec-Driven Development (SDD). Invece di lanciarmi a capofitto nell\u0026rsquo;esecuzione, ho definito quattro artefatti:\nConstitution: Regole immutabili (niente segreti nel codice, logging obbligatorio, stack definito). Spec: Cosa dobbiamo costruire oggi? (Account OCI, CLI, VM, Script di lifecycle). Plan: Come lo facciamo? (Importazione immagine custom, fix dei moduli, test end-to-end). Tasks: 28 micro-task per tracciare il progresso. Questo approccio, che potrebbe sembrare burocratico per un progetto personale, si è rivelato salvifico quando la complessità tecnica è esplosa nelle fasi successive.\nIl Primo Contatto con OCI # L\u0026rsquo;account OCI era vuoto. Zero. Ho dovuto navigare la console per creare il primo Compartment (tazlab-vault), generare le API Key per l\u0026rsquo;accesso programmatico e configurare la OCI CLI sulla mia workstation.\nUn dettaglio critico è stato determinare l\u0026rsquo;Availability Domain (AD) corretto. A differenza di AWS o GCP che usano zone come eu-central-1a, OCI usa identificatori specifici per tenancy, come GRGU:EU-TURIN-1-AD-1. Hardcodare questi valori è un errore; ho dovuto estrarli dinamicamente o salvarli come segreti nel mio vault locale (gestito da Infisical).\n3. Il Dilemma dell\u0026rsquo;Immagine Talos # Qui ho incontrato il primo vero ostacolo architetturale. Il mio scaffolding originale prevedeva di usare un\u0026rsquo;immagine standard Oracle Linux 8 e poi installare Talos sopra di essa usando uno script cloud-init.\nSulla carta, funziona. Nella pratica, è fragile. Trasforma un\u0026rsquo;operazione atomica (boot dell\u0026rsquo;OS) in un processo a due stadi prono a errori di rete e dipendenze. Inoltre, il template cloud-init che avevo scritto era solo un placeholder non funzionale.\nLa Decisione: Ho deciso di abbandonare l\u0026rsquo;approccio ibrido e usare un\u0026rsquo;immagine Talos nativa. Talos fornisce un \u0026ldquo;Image Factory\u0026rdquo; che permette di generare immagini disco personalizzate. Ho usato lo stesso schematic ID del mio cluster locale (e187c9b9...) che include moduli kernel specifici (iscsi_tcp, nbd) per il supporto allo storage distribuito Longhorn.\nL\u0026rsquo;Odissea dell\u0026rsquo;Importazione # Importare un\u0026rsquo;immagine custom su OCI non è banale come incollare un URL.\nTentativo 1: Incollare l\u0026rsquo;URL della Factory nella console OCI. Risultato: Errore. OCI accetta URL solo dal proprio Object Storage. Tentativo 2: Scaricare l\u0026rsquo;immagine, caricarla su un Bucket OCI, importare. Risultato: Errore Shape VM.Standard.A1.Flex is not valid for image. OCI aveva rilevato l\u0026rsquo;immagine come x86 perché non avevo specificato l\u0026rsquo;architettura. La console web non permetteva di selezionare \u0026ldquo;ARM64\u0026rdquo; per immagini custom importate in quel modo. La Soluzione (The Hard Way): Ho dovuto seguire la procedura ufficiale \u0026ldquo;Bring Your Own Image\u0026rdquo; di Talos per Oracle Cloud, che è sorprendentemente manuale:\nScaricare l\u0026rsquo;immagine raw compressa (.raw.xz). Decomprimerla e convertirla in formato QCOW2 (qemu-img convert). Creare un file image_metadata.json specifico per dire a OCI \u0026ldquo;Ehi, questa è un\u0026rsquo;immagine ARM64 UEFI compatibile con VM.Standard.A1.Flex\u0026rdquo;. Impacchettare tutto in un archivio .oci (tarball di qcow2 + json). Caricare questo pacchetto di 90MB sul Bucket e importare da lì. Solo così OCI ha riconosciuto l\u0026rsquo;immagine come valida per le istanze Ampere A1. È stato un promemoria brutale che il cloud non è magico; è solo computer di qualcun altro con regole molto rigide.\n4. Terraforming: Debugging dello Scaffolding # Con l\u0026rsquo;immagine pronta, ho lanciato terragrunt plan. Il risultato è stato un muro di errori rosso. Il codice scritto una settimana fa e mai testato mostrava tutti i suoi limiti.\n1. Funzioni Inesistenti # Avevo usato get_terragrunt_config() nei file figlio, una funzione che non esiste. Terragrunt richiede di includere la configurazione radice e poi leggere i valori tramite read_terragrunt_config(). Ho dovuto riscrivere la logica di passaggio delle variabili tra i layer engine (rete) e platform (compute).\n2. Conflitti di Provider # Ogni modulo dichiarava i suoi required_providers, ma anche il file radice generava un versions.tf. Risultato: Terraform andava in panico per definizioni duplicate. Ho dovuto ripulire i moduli, lasciando che fosse Terragrunt a iniettare le dipendenze corrette.\n3. La \u0026ldquo;Tag Tax\u0026rdquo; # OCI è schizzinoso sui tag. Il mio codice usava tags = { ... }, ma il provider OCI distingue tra freeform_tags (chiave-valore liberi) e defined_tags (tassonomie enterprise). Ho dovuto refattorizzare ogni singola risorsa per usare freeform_tags. Inoltre, ho scoperto che i tag sono case-insensitive nelle chiavi, causando conflitti di merge quando cercavo di sovrascrivere Layer con layer.\n4. DNS Label Limits # Un errore banale ma fastidioso: dns_label per le subnet ha un limite di 15 caratteri. La mia stringa tazlab-vault-public-subnet generava tazlabvaultpublicsubnet (23 caratteri), bloccando il provisioning della VCN. Una semplice substr() ha risolto, ma mi ha ricordato di controllare sempre i limiti dei provider.\nDopo due ore di ciclo fix-plan-repeat, ho finalmente visto il messaggio più bello del mondo: Plan: 12 to add, 0 to change, 0 to destroy.\n5. \u0026ldquo;They\u0026rsquo;re Alive!\u0026rdquo; (e il problema di rete nascosto) # Ho lanciato lo script create.sh. Terraform ha creato la VCN, le subnet, le Security List e infine le due istanze Compute. In meno di 3 minuti, avevo due IP pubblici.\nMa il cluster non rispondeva. Il comando talosctl version andava in timeout.\nL\u0026rsquo;Indagine: Ho usato nc (netcat) per testare la porta 50000 (API di Talos). Connection refused. Era strano. Le mie Network Security Group (NSG) permettevano esplicitamente il traffico sulla porta 50000. Ho scavato nella configurazione della VCN e ho trovato il colpevole: la Default Security List. In OCI, ogni subnet ha una Security List di default che viene applicata in aggiunta alle NSG. Questa lista permetteva solo SSH (porta 22). Anche se la mia NSG diceva \u0026ldquo;lascia passare tutto\u0026rdquo;, la Security List diceva \u0026ldquo;blocca tutto tranne SSH\u0026rdquo;. È un modello di sicurezza \u0026ldquo;defense in depth\u0026rdquo; che mi ha colto di sorpresa.\nHo aperto la Security List e la situazione è cambiata istantaneamente: Connection refused è diventato tls: certificate required. Il server rispondeva!\n6. Il Paradosso TLS e la Configurazione di Macchina # A questo punto, le macchine erano accese, Talos era avviato, ma non potevo fare il bootstrap del cluster. Perché?\nPerché Talos, essendo sicuro by-default, usa mTLS (Mutual TLS) per ogni comunicazione. Il certificato del server viene generato al primo avvio basandosi sulla configurazione della macchina. La configurazione, generata da Terraform, impostava come cluster_endpoint l\u0026rsquo;indirizzo IP privato della VM (10.0.1.100), l\u0026rsquo;unico noto al momento del plan.\nIo, però, stavo cercando di connettermi dall\u0026rsquo;esterno tramite l\u0026rsquo;IP pubblico (92.x.x.x). Risultato: Il client talosctl si connetteva all\u0026rsquo;IP pubblico, il server presentava un certificato valido solo per 10.0.1.100, e il client rifiutava la connessione per mismatch del nome.\nIl vicolo cieco:\nNon potevo rigenerare il certificato senza accedere alla macchina. Non potevo accedere alla macchina senza un certificato valido. Non potevo usare l\u0026rsquo;IP privato perché non ho (ancora) una VPN site-to-site con OCI. Ho tentato di usare degli IP Pubblici Riservati, iniettandoli nella configurazione prima della creazione delle istanze. Ho modificato Terraform per aggiungere questi IP ai certSANs (Subject Alternative Names) del certificato. Sfortunatamente, Terraform su OCI non permette facilmente di assegnare un IP pubblico riservato durante la creazione dell\u0026rsquo;istanza in un solo passaggio atomico; richiede una risorsa separata. Le istanze nascevano comunque con IP effimeri diversi da quelli che avevo messo nel certificato.\nConclusioni: Un Successo Parziale è Pur Sempre un Successo # Alla fine della sessione, ho dovuto accettare una vittoria parziale. Le macchine sono su. Talos è installato e configurato. L\u0026rsquo;infrastruttura è definita come codice. Lo script destroy.sh, che ho dovuto riscrivere per gestire correttamente la pulizia delle risorse orfane (istanze terminate che mantenevano i dischi boot occupati), funziona perfettamente, permettendomi di azzerare i costi con un comando.\nHo raggiunto l\u0026rsquo;obiettivo della \u0026ldquo;Terraformazione\u0026rdquo;: ho trasformato un\u0026rsquo;intenzione (un cluster) in realtà (risorse cloud) usando solo codice. Il problema del bootstrap TLS è un classico problema di \u0026ldquo;Day 2\u0026rdquo; anticipato al \u0026ldquo;Day 0\u0026rdquo;. La soluzione per la Fase 2 è chiara: associare correttamente gli IP Riservati alle interfacce di rete (VNIC) o stabilire un tunnel sicuro per operare sull\u0026rsquo;IP privato.\nMa per oggi, vedere quelle due righe RUNNING nella console di Oracle, sapendo che non ho cliccato nessun bottone per crearle, è una soddisfazione immensa. È la conferma che lo studio teorico si è trasformato in competenza pratica. L\u0026rsquo;infrastruttura, finalmente, è diventata reale.\n","date":"20 marzo 2026","externalUrl":null,"permalink":"/it/posts/terraforming-the-cloud-iac-oci/","section":"Posts","summary":"","title":"Terraforming the Cloud: My First IaC on OCI","type":"posts"},{"content":"","date":"18 marzo 2026","externalUrl":null,"permalink":"/it/tags/agenti-ai/","section":"Tags","summary":"","title":"Agenti AI","type":"tags"},{"content":"","date":"18 marzo 2026","externalUrl":null,"permalink":"/tags/ai-agents/","section":"Tags","summary":"","title":"AI Agents","type":"tags"},{"content":"","date":"18 marzo 2026","externalUrl":null,"permalink":"/it/tags/cloud/","section":"Tags","summary":"","title":"Cloud","type":"tags"},{"content":" La Tesi: Potenti, ma Solo se Sai Dove Mandarli # Gli agenti AI sono lo strumento più trasformativo che ho aggiunto al mio workflow negli ultimi anni. In due mesi di lavoro serale ho prodotto quello che avrei impiegato due anni a costruire da solo. Eppure, ogni volta che vedo qualcuno su YouTube che dice \u0026ldquo;guarda, gli ho chiesto di farmi un\u0026rsquo;app e l\u0026rsquo;ha fatta\u0026rdquo;, mi viene un mezzo sorriso amaro.\nPerché c\u0026rsquo;è una differenza abissale tra fare una cosa e farla bene. E quella differenza, al momento, passa ancora interamente dall\u0026rsquo;ingegnere.\nQuesta non è una cronaca entusiasta sull\u0026rsquo;AI che cambierà il mondo. È il resoconto di chi lavora ogni giorno con questi strumenti su Kubernetes, cluster cloud, pipeline GitOps e gestione dei segreti — e ha imparato a proprie spese dove si può dare fiducia a questi agenti e dove, invece, bisogna tenerli al guinzaglio.\nContextual Analysis: Un Panorama in Movimento Rapido # Quando ho iniziato a sperimentare con gli agenti AI sull\u0026rsquo;infrastruttura, il mio punto di partenza era semplice: volevo capire se potevano aiutarmi a fare cose che già sapevo fare, ma più velocemente. Non cercavo magia. Cercavo efficienza.\nIl primo problema che ho incontrato è stato il vincolo di piattaforma. Gemini CLI, Cloud Code, gli strumenti nativi dei grandi provider: ognuno ha il suo mondo, le sue regole, i suoi aggiornamenti che cambiano interfacce senza avvisare. Un giorno lavori in un certo modo, il giorno dopo qualcuno ha deciso che si fa diversamente. E tu devi seguire.\nLa svolta è stata scoprire pi.dev, la piattaforma su cui è costruito questo stesso ambiente di lavoro. È un agente minimale, paragonabile a VI tra gli editor: c\u0026rsquo;è poco di default, ma è configurabile in maniera estrema e con una semplicità disarmante. Puoi dirgli direttamente \u0026ldquo;creami questa estensione, aggiungi questo comportamento\u0026rdquo; e costruirti il tuo strumento su misura. Soprattutto, non ti lega a nessun provider specifico.\nQuesto mi ha aperto le porte a OpenRouter, che è essenzialmente uno sportello unico per tutti i modelli linguistici esistenti. Da lì ho cominciato a esplorare seriamente cosa offrono i vari provider, con un occhio costante ai costi — perché sono un privato, e un abbonamento da €200 al mese non è una voce di bilancio sostenibile.\nIl Confronto sul Campo # Ho testato molti modelli nel contesto specifico del lavoro su cluster, container e cloud. Il verdetto non è quello che mi aspettavo.\nClaude (Cloud Code) è eccellente per i lavori di progettazione complessa. Ragiona bene, fa le scelte giuste, capisce le architetture. Il problema è il costo: con Opus esaurisco le quote in un\u0026rsquo;ora. Con Sonnet si va un po\u0026rsquo; più avanti, ma non molto. Ottimo per lavori chirurgici e critici, insostenibile come strumento quotidiano.\nGemini Flash 3.0 mi ha sorpreso più di una volta. In almeno due occasioni, su problemi reali di configurazione Kubernetes che Sonnet non riusciva a sbloccare, il Flash li ha risolti al primo tentativo. Non è una regola, ma è abbastanza frequente da essere significativo. Ha prezzi ragionevoli e nel mio campo si comporta bene. C\u0026rsquo;è un asterisco importante, però: Gemini CLI usato tramite pi.dev diventa quasi inutilizzabile per problemi di rate limiting — 50 secondi di attesa tra una chiamata e l\u0026rsquo;altra, poi va in errore. La soluzione è usarlo attraverso il suo terminale nativo, dove funziona correttamente.\nMinimax M2.5 è stato una delusione. Ne parlano bene in giro, ma nel mio campo specifico — configurazione cluster, Kubernetes, infrastruttura cloud — sbagliava e dimenticava troppe cose.\nGrok 4.1 fast non è male per il prezzo che ha. Si perde su lavori lunghi, ma su compiti circoscritti è utilizzabile.\nStepfun (modello gratuito): velocissimo, ma produce fiumi di log intermedi perché è un modello a ragionamento esteso. Nella pratica, è quasi inutilizzabile su lavori di configurazione.\nGLM-5 (Zhipu AI) è la sorpresa positiva del lotto. Prezzi comparabili a Gemini Flash, si comporta bene su Kubernetes e configurazioni cloud, porta avanti il lavoro con disciplina e si corregge quando sbaglia. È uno di quei modelli che tengo sempre come riserva quando finisco le quote.\nHunter Alpha (openrouter/hunter-alpha) merita un discorso a parte. È un modello gratuito di autore ignoto — probabilmente una nuova versione di DeepSeek o qualcosa di simile, ma non è chiaro. Lo sto usando con soddisfazione crescente: è bravo a correggersi, gestisce bene i lavori complessi, e per ora è gratuito. Un po\u0026rsquo; lento — probabilmente la conseguenza del free tier — ma i risultati sono ottimi.\nIl pattern che emerge è chiaro: il modello migliore dipende dal task, non dalla marca. E avere una piattaforma agnostica come pi.dev, che mi permette di passare da uno all\u0026rsquo;altro in pochi secondi senza cambiare workflow, vale più di qualsiasi singolo modello.\nDeep Dive: Dove gli Agenti Eccellono e Dove Cedono # 1. Il Problema del Contesto e della Memoria # Il valore reale di un agente AI nell\u0026rsquo;infrastruttura non sta nella capacità di scrivere YAML. Sta nella capacità di tenere in testa un contesto complesso e applicarlo in modo coerente. Kubernetes è un universo: c\u0026rsquo;è Linux alla base, poi Docker, poi Kubernetes stesso, poi networking, poi sicurezza, poi le specificità di ogni cloud provider. Ogni livello ha la sua sintassi, i suoi strumenti, i suoi flag.\nPer anni il mio problema non è stato non sapere cosa volevo. Ho sempre saputo cosa volevo ottenere. Il problema era dovermi rileggere la documentazione ogni volta che cambiavo libreria o provider, perché ogni ecosistema ha le sue idiosincrasie. Terraform su Oracle non è Terraform su AWS — stessa logica, comandi e configurazioni diverse.\nCon gli agenti, questo problema scompare quasi completamente. Io descrivo il risultato, loro scrivono il codice. Il mio tempo si sposta dalla memorizzazione mnemonica dei comandi alla definizione precisa di cosa voglio ottenere.\nHo costruito per questo una soluzione di gestione del contesto personalizzata — di cui ho scritto altrove — che mi permette di aprire un terminale e ritrovarmi in 20 secondi con il contesto del progetto già carico: cosa è stato fatto, dove siamo arrivati, quali problemi abbiamo incontrato. Posso cambiare agente, cambiare progetto, e il nuovo agente sa esattamente da dove riprendere. I contesti sono tenuti piccoli e densi — solo le informazioni pertinenti al momento — il che riduce il rischio di perdita di coerenza.\n2. Il Rischio dell\u0026rsquo;Autonomia: Cosa Succede Quando li Lasci Andare # Questo è il punto su cui voglio essere più diretto, perché è quello su cui si fa più confusione.\nHo provato più volte a lasciare gli agenti lavorare in autonomia su compiti complessi. Il risultato è quasi invariabilmente lo stesso: portano il compito a termine, ma le scelte che fanno lungo il percorso sono spesso sbagliate. Non sbagliate nel senso che non funzionano — a volte funzionano benissimo. Sbagliate nel senso che non rispettano i vincoli architetturali che avevo in testa, prendono scorciatoie che creano debito tecnico, costruiscono soluzioni fragili che non si possono estendere.\nIl caso più emblematico che ho vissuto: lavoro su un cluster gestito con filosofia GitOps, quindi tutto finisce su Git. In più di un\u0026rsquo;occasione, un agente lasciato andare ha committato credenziali all\u0026rsquo;interno di un ConfigMap o direttamente in un file YAML. Ha visto un problema, ha cercato la soluzione più rapida, e quella soluzione era sbagliata da un punto di vista di sicurezza. Non perché non sapesse che i segreti non si committano — se glielo chiedi, te lo sa spiegare perfettamente. Ma nel flusso del lavoro autonomo, la pressione di \u0026ldquo;chiudere il task\u0026rdquo; ha prevalso sul rispetto delle regole.\nQuesto mi ha insegnato una cosa: gli agenti AI conoscono le best practice, ma non le sentono come un vincolo imprescindibile quando operano in autonomia. Le applicano quando vengono esplicitamente istruiti a farlo, o quando c\u0026rsquo;è qualcuno che verifica.\n3. Lo Spec-Driven Development: La Risposta Strutturale # Non sono l\u0026rsquo;unico ad aver notato questo problema. È nato un intero movimento attorno a quello che viene chiamato Spec-Driven Development: si progetta prima il sistema in dettaglio, si documentano le scelte architetturali e i vincoli, e solo dopo si lascia eseguire l\u0026rsquo;agente su quella specifica.\nLo uso, e funziona. Ma c\u0026rsquo;è una condizione necessaria che spesso viene omessa nelle presentazioni entusiaste: per scrivere una specifica buona, devi sapere cosa stai specificando. Non puoi descrivere con precisione un\u0026rsquo;architettura di sicurezza per Kubernetes se non hai una conoscenza solida di RBAC, dei secrets engine, delle network policy. L\u0026rsquo;agente seguirà le tue specifiche alla lettera — ma se le specifiche sono vaghe o sbagliate, il risultato sarà vago o sbagliato.\nIl metodo funziona perché sposta il lavoro intellettuale dove deve stare: nella testa dell\u0026rsquo;ingegnere, nella fase di progettazione. E l\u0026rsquo;agente diventa l\u0026rsquo;esecutore di un piano preciso, non il progettista.\n4. Il Debugging e il Carico Cognitivo # Una delle trasformazioni più concrete che ho vissuto riguarda il debugging. Passavo ore — a volte notti — a cambiare flag, ricompilare, testare, leggere stack trace, cercare su forum. Era la parte più frustrante del lavoro. Ed era anche, va detto, la parte più formativa.\nGli agenti lo fanno in parallelo e non si stancano. Quando c\u0026rsquo;è un problema che non riescono a risolvere, continuano a iterare fino a trovare la soluzione. E poi me la spiegano — non solo cosa hanno fatto, ma perché hanno scelto quell\u0026rsquo;approccio. Questo è il valore formativo che non mi aspettavo: non solo smetto di fare debugging manuale, ma imparo dai ragionamenti che l\u0026rsquo;agente esplicita.\nHo preso l\u0026rsquo;abitudine di non usarli come oracoli silenziosi. Chiedo sempre spiegazioni, discuto le scelte, a volte li metto in discussione. Alla fine di ogni sessione significativa mi faccio fare un resoconto: cosa è stato fatto, quali problemi si sono presentati, come sono stati risolti, quali scelte architetturali sono state prese. In quel momento, rileggendo, mi rendo conto di cose che non avevo notato mentre lavoravamo — scelte che non avrei fatto, vincoli che sono stati aggirati invece di rispettati.\nThe Human Element: Il Nuovo Ruolo dell\u0026rsquo;Ingegnere # C\u0026rsquo;è una metafora che uso spesso tra me e me: l\u0026rsquo;agente AI è come qualcuno che sa scrivere codice ma ha bisogno di essere portato al pascolo. Sa cosa fare, ha le competenze tecniche, ma se gli lasci il volante va dove trova l\u0026rsquo;erba più vicina, non dove serve andare. Il tuo lavoro è indicare la direzione, verificare il percorso, e correggere quando devia.\nQuesto cambia profondamente cosa significa essere un ingegnere. Scrivo meno codice. Penso molto di più ad alto livello. Mi concentro su architettura, vincoli, trade-off. Sono diventato più bravo a descrivere sistemi in modo preciso — perché se la descrizione è imprecisa, l\u0026rsquo;agente produce qualcosa di impreciso.\nE sto imparando di più, non di meno. Perché la discussione con un agente informato su un argomento che non conosci bene è uno dei modi più efficaci per approfondirlo che io abbia mai trovato. Non è un motore di ricerca, non è documentazione: è un interlocutore che può rispondere alle domande specifiche del tuo contesto specifico. Sono passato da una conoscenza superficiale di Kubernetes a una comprensione profonda di come funziona la gestione dei segreti, il RBAC, la rotazione delle credenziali — non perché abbia letto un libro, ma perché ho trascorso ore a discuterne nel contesto del mio cluster, del mio caso d\u0026rsquo;uso, dei miei errori.\nFinal Synthesis: Raccomandazioni per Chi Lavora sul Serio # La narrativa dominante sull\u0026rsquo;AI ha un difetto: tende a presentare questi strumenti come livellatori. Come se chiunque, con il prompt giusto, potesse costruire le stesse cose. Non è così — almeno non nel lavoro serio su infrastruttura complessa.\nIl mio osservazione, dopo mesi di utilizzo quotidiano, è che l\u0026rsquo;AI è un moltiplicatore, non un sostituto. E la grandezza del moltiplicatore dipende dalla competenza di partenza. Per chi ha una base solida, questi strumenti sono trasformativi — 100x, forse 1000x su certi tipi di lavoro. Per chi non ce l\u0026rsquo;ha, il beneficio è reale ma molto più limitato.\nQuesto non significa che siano inutili per chi sta imparando. Significa che per imparare davvero bisogna usarli in modo attivo: discutere, chiedere spiegazioni, mettere in discussione le scelte, costruire la comprensione invece di accettare il risultato. La montagna dell\u0026rsquo;expertise è ancora alta. Questi strumenti la rendono più scalabile, non più bassa.\nPer chi vuole usarli seriamente sull\u0026rsquo;infrastruttura, alcune lezioni che ho imparato a caro prezzo:\nNon lasciare mai il volante. Soprattutto su un cluster che ospita servizi reali. Monitoring, analisi, proposta di soluzioni — tutto benissimo. Ma l\u0026rsquo;approvazione di ogni modifica deve passare da una persona che capisce le conseguenze.\nProgettare prima, eseguire poi. Uno Spec-Driven Development robusto è la differenza tra un agente che porta avanti il tuo progetto e uno che costruisce qualcosa che dovrai buttare via. Ma questo richiede che tu sappia già come si fa la cosa, almeno nei suoi aspetti fondamentali.\nVerificare sempre l\u0026rsquo;output critico. Specialmente in un contesto GitOps: ogni commit che non hai scritto tu va letto. Gli agenti non hanno la percezione del rischio che ha chi ha passato notti a ripristinare un cluster rotto.\nUsare la piattaforma giusta, non il modello giusto. Un ambiente agnostico che ti permette di cambiare modello senza cambiare workflow vale più di qualsiasi singolo modello. I modelli cambiano continuamente — la settimana scorsa il migliore era uno, adesso è un altro. Il workflow deve rimanere stabile.\nIl programmatore non è morto. Si è evoluto in qualcosa di più vicino a un progettista di sistemi che ha a disposizione un team di sviluppatori instancabili, veloci, ben informati — ma che hanno bisogno di sapere esattamente dove devono andare.\n","date":"18 marzo 2026","externalUrl":null,"permalink":"/it/posts/man-in-the-loop-ai-agents-infrastructure/","section":"Posts","summary":"","title":"Man in the Loop: Riflessioni sull'Uso degli Agenti AI per Costruire Infrastrutture","type":"posts"},{"content":"","date":"18 marzo 2026","externalUrl":null,"permalink":"/it/tags/openrouter/","section":"Tags","summary":"","title":"OpenRouter","type":"tags"},{"content":"","date":"18 marzo 2026","externalUrl":null,"permalink":"/it/tags/pi.dev/","section":"Tags","summary":"","title":"Pi.dev","type":"tags"},{"content":"","date":"17 marzo 2026","externalUrl":null,"permalink":"/it/tags/gitops/","section":"Tags","summary":"","title":"GitOps","type":"tags"},{"content":" The Current State: Un Cluster Solido, Ma con un Tallone d\u0026rsquo;Achille # Il TazLab oggi è un\u0026rsquo;infrastruttura che funziona. Ho un cluster Kubernetes su Proxmox con Talos OS, una pipeline GitOps gestita da Flux, metriche raccolte da Prometheus e visualizzate su Grafana, e etcd cifrato a riposo. Da fuori, è un setup che ispira fiducia.\nMa guardando dall\u0026rsquo;interno, c\u0026rsquo;è un problema che mi tiene sveglio.\nLa gestione dei segreti è affidata a Infisical nel suo piano gratuito. Funziona: sincronizza i segreti su Kubernetes tramite l\u0026rsquo;External Secrets Operator, i pod li usano, la vita va avanti. Tuttavia, il piano gratuito di Infisical impone un limite che non riesco più ad accettare: non supporta la rotazione automatica dei segreti.\nI segreti non ruotano. Le credenziali del database sono statiche. Se una chiave viene compromessa, l\u0026rsquo;intervento è manuale.\nThe \u0026ldquo;Why\u0026rdquo;: Quando l\u0026rsquo;AI Mette a Nudo il Problema # Il punto di svolta non è arrivato da un incidente, ma da una riflessione. Ho iniziato a usare con regolarità tool di intelligenza artificiale nel mio workflow — Gemini CLI, Cloud Code, e altri agenti con accesso alla shell e al filesystem. Questi strumenti sono potenti, ma hanno un\u0026rsquo;abitudine fastidiosa: loggare tutto. Prompt, output, contesto di sessione. Potenzialmente, anche frammenti di segreti che compaiono nelle risposte dei comandi.\nIn quel momento ho capito che il mio modello di sicurezza dei segreti era fragile per definizione. Non perché Infisical faccia un cattivo lavoro, ma perché segreti statici e longevi sono intrinsecamente vulnerabili. Un segreto che non ruota mai è una bomba a orologeria.\nLa risposta professionale a questo problema ha un nome preciso: segreti dinamici e rotazione automatica delle chiavi.\nThe Target Architecture: Vault Come Centro di Gravità # La scelta è ricaduta su HashiCorp Vault Community Edition, installato come pod all\u0026rsquo;interno del cluster stesso. È una scelta deliberatamente ambiziosa — probabilmente overkill per un home lab — ma è esattamente il tipo di overkill che voglio. Vault è lo standard de facto del settore per la gestione dei segreti in ambienti enterprise. Impararlo qui, nel mio laboratorio, significa portare competenze reali nel mondo reale.\nIl modello che voglio implementare funziona così:\nVault genera i segreti dinamicamente e gestisce la loro scadenza e rotazione. External Secrets Operator intercetta i cambiamenti e sincronizza i nuovi segreti su Kubernetes come Secret nativi. Reloader rileva le modifiche nei Secret e nei ConfigMap e attiva automaticamente il reload dei pod coinvolti. Il risultato: nessuna credenziale statica, nessun intervento manuale, nessuna finestra di esposizione indefinita.\nIl Nuovo Nodo: Oracle Cloud Always Free # Per ospitare Vault in modo robusto e separato dall\u0026rsquo;infrastruttura principale, sto aggiungendo un secondo cluster al TazLab. La piattaforma scelta è Oracle Cloud Infrastructure, che offre un livello Always Free generoso e stabile:\nControl Plane: VM con 8 GB di RAM Worker: VM con 16 GB di RAM OS: Talos OS, come sul cluster locale — coerenza operativa al primo posto Questo cluster Oracle diventerà il nodo di sicurezza del TazLab: ospiterà Vault, sarà raggiungibile dalla VPN, e non dipenderà dall\u0026rsquo;hardware fisico di casa.\nTailscale: La Colla che Tiene Insieme Tutto # Il tassello più critico di questa architettura non è Vault — è la VPN mesh.\nPer capire perché, bisogna capire come funzionano i segreti dinamici di Vault per PostgreSQL. Quando un\u0026rsquo;applicazione richiede credenziali al database, Vault non restituisce una password salvata da qualche parte: crea un utente PostgreSQL al momento, con una scadenza definita, e lo elimina quando il lease scade. Per farlo, Vault ha bisogno di accesso diretto al database con privilegi di amministratore.\nSe Vault è su Oracle Cloud e PostgreSQL è sul cluster Proxmox di casa, serve un canale sicuro e permanente tra i due. È qui che entra Tailscale: una soluzione VPN mesh moderna, zero-config, basata su WireGuard. Ogni nodo della rete — cluster locale, cluster Oracle, workstation — diventa parte della stessa rete privata, indipendentemente da dove si trova fisicamente.\nLa VPN non è un dettaglio implementativo. È la precondizione che rende possibile l\u0026rsquo;intera architettura.\nPhased Approach: Le Tappe del Percorso # Il lavoro si articola in fasi sequenziali, ognuna delle quali deve essere stabile prima di procedere alla successiva.\nFase 1 — VPN Mesh Configurare Tailscale tra il cluster Proxmox locale e il nuovo cluster Oracle Cloud. Verificare la connettività bidirezionale. Nessun Vault, nessun segreto dinamico finché questa base non è solida.\nFase 2 — Nuovo Cluster Oracle Provisioning del cluster Talos su Oracle Cloud tramite Terragrunt. Integrazione con il repo GitOps esistente. Il cluster deve essere gestito da Flux esattamente come il cluster locale.\nFase 3 — HashiCorp Vault Deploy di Vault sul cluster Oracle. Configurazione del PKI engine, del secrets engine per PostgreSQL, delle policy di accesso. Migrazione progressiva dei segreti da Infisical a Vault.\nFase 4 — Integrazione ESO + Reloader Configurare External Secrets Operator su entrambi i cluster per leggere da Vault. Integrare Reloader per il reload automatico dei pod. Testare l\u0026rsquo;intero ciclo: rotazione → sync → reload.\nFuture Outlook: Il Cluster Effimero Diventa Realtà # Questa roadmap non è solo una lista di strumenti da installare. È il passo che trasforma il TazLab da infrastruttura solida a infrastruttura veramente effimera.\nL\u0026rsquo;obiettivo finale è un cluster che puoi distruggere e ricreare a piacimento, su qualsiasi cloud provider, in qualsiasi momento. Il processo di bootstrap sarà completamente automatizzato: il nuovo cluster si connette alla mesh Tailscale, ottiene i certificati automaticamente, raggiunge Vault e recupera i propri segreti, ripristina i dati dall\u0026rsquo;S3 bucket. Nessun intervento manuale.\nAWS oggi, Google Cloud domani, Oracle dopodomani. La piattaforma diventa irrilevante.\nQuesto è il \u0026ldquo;Terraforming the Cloud\u0026rdquo; nella sua forma più compiuta: non terraformare un singolo cloud, ma rendere il proprio ecosistema indipendente da tutti.\nIl TazLab non ha un indirizzo fisso. Ha solo un punto di ripartenza.\n","date":"17 marzo 2026","externalUrl":null,"permalink":"/it/posts/tazlab-roadmap-hashicorp-vault-oracle-cloud/","section":"Posts","summary":"","title":"TazLab Roadmap: HashiCorp Vault e Oracle Cloud","type":"posts"},{"content":"","date":"15 marzo 2026","externalUrl":null,"permalink":"/it/tags/flux/","section":"Tags","summary":"","title":"Flux","type":"tags"},{"content":"","date":"15 marzo 2026","externalUrl":null,"permalink":"/it/tags/sdd/","section":"Tags","summary":"","title":"Sdd","type":"tags"},{"content":" Gli ultimi sviluppi del TazLab # Questo è uno di quegli articoli che si scrivono quando le cose vanno bene. Nessun incidente da riportare, nessuna rebuild del cluster alle due di notte, nessun Deployment che rifiuta di partire per ragioni incomprensibili. Il lab gira. La pipeline funziona. Ho avuto il tempo di pensare ai processi invece di combattere i problemi.\nNelle ultime sessioni sono successe due cose concrete che vale la pena documentare: ho implementato un sistema di Spec-Driven Development come puro contesto Markdown, e ho usato quel sistema per sistemare un problema al DAG di Flux che resisteva da settimane. Il risultato è stato più pulito di quanto mi aspettassi.\nSpec-Driven Development come contesto: zero codice, solo regole # L\u0026rsquo;articolo precedente sull\u0026rsquo;AGENTS.ctx descriveva l\u0026rsquo;idea di base del sistema di gestione dei contesti: ogni dominio operativo ha il suo CONTEXT.md, caricato su richiesta, con le regole già scritte. L\u0026rsquo;agente non cambia — il contesto sì.\nLa domanda naturale che mi sono posto subito dopo è stata: posso applicare lo stesso principio al processo di sviluppo stesso? Non come tool esterno, non come sistema a sé stante — come un altro contesto da aprire quando ne ho bisogno.\nLa risposta è sì, e ci ho messo circa mezza giornata.\nSDD (Spec-Driven Development) è oggi un file AGENTS.ctx/sdd/CONTEXT.md con un workflow in quattro fasi e un insieme di regole che l\u0026rsquo;agente segue quando apre il contesto. Nessun codice. Nessuna dipendenza. Solo Markdown versionato su Git.\nLe quattro fasi # Il workflow che ho definito è deliberatamente lineare, con gate espliciti tra ogni fase. L\u0026rsquo;agente non può passare alla fase successiva senza approvazione.\nPhase 1 — Constitution. Il documento fondazionale del progetto. Definisce le fondamenta immutabili: linguaggio, framework, convenzioni di naming, vincoli, librerie proibite. Una volta approvata, la constitution non cambia senza approvazione esplicita. È il documento a cui si torna quando durante l\u0026rsquo;implementazione emerge un dubbio su \u0026ldquo;ma avevamo detto di fare così?\u0026rdquo;.\nPhase 2 — Specification. Definisce cosa costruire in dettaglio logico completo. Non l\u0026rsquo;implementazione — la logica. Input attesi, output desiderati, comportamento per ogni caso. Edge case e gestione degli errori. I criteri di accettazione: quando il lavoro è considerato finito? Questo documento è la fonte di verità. Se durante l\u0026rsquo;implementazione qualcosa non quadra, si torna qui.\nPhase 3 — Plan. Definisce come costruirlo tecnicamente. Quali file modificare, quali creare. Scelte architetturali e il loro razionale. Dipendenze e ordine di esecuzione. Il piano viene proposto dall\u0026rsquo;agente sulla base di constitution + spec, e richiede approvazione prima di procedere.\nPhase 4 — Tasks. La spec viene decomposta in una checklist di micro-task atomici, ognuno segnato come [ ] o [x]. Ogni task è un\u0026rsquo;azione discreta e completabile. Durante l\u0026rsquo;implementazione, il file tasks è il GPS: si apre, si vede il prossimo step pendente, si esegue, si marca completato.\nIl project inventory # Ogni progetto SDD vive in AGENTS.ctx/sdd/assets/\u0026lt;project-name\u0026gt;/ con i suoi quattro file. Il contesto mantiene una tabella di inventario in CONTEXT.md che si aggiorna quando un progetto viene creato o completato. Quando apro il contesto vedo subito lo stato di tutto: cosa è in progress, cosa è bloccato, cosa è completato con le note rilevanti.\nQuesto ha un effetto pratico importante: ogni sessione successiva non parte da zero. L\u0026rsquo;agente legge l\u0026rsquo;inventario, identifica il progetto in progress, carica il tasks.md, e continua dal prossimo step pendente. Il warm-up è quasi inesistente.\nLa struttura è la stessa che posso passare a Gemini o qualsiasi altro agente: basta far leggere il AGENTS.ctx/CONTEXT.md principale, che spiega dove trovare i contesti disponibili, e l\u0026rsquo;agente è immediatamente orientato senza ulteriori spiegazioni.\nIl primo test reale: flux-dag-fix-v2 # La teoria costa poco. Il primo test reale dell\u0026rsquo;SDD è arrivato subito, con un problema che portavo dietro da settimane: il DAG delle kustomization Flux sul cluster TazLab non si comportava come previsto.\nIl contesto del problema # Il cluster TazLab è gestito interamente via GitOps con Flux. Ogni risorsa Kubernetes è definita in Git, Flux riconcilia continuamente lo stato del repository con lo stato del cluster. Le kustomization — i raggruppamenti logici di risorse — hanno dipendenze dichiarate esplicitamente tramite dependsOn, e possono avere wait: true che forza Flux ad aspettare che tutte le risorse della kustomization siano pronte prima di procedere con le dipendenti.\nIl problema aveva già un\u0026rsquo;analisi precisa alle spalle: un documento strutturato con tabella dei problemi identificati nel DAG, diagramma del grafo obiettivo, sezioni dettagliate per ogni fix, matrice dei rischi e riepilogo delle 15 modifiche da applicare. Non era documentazione approssimativa — era un piano tecnico completo.\nLa difficoltà era diversa: il piano era pensato come un\u0026rsquo;unica soluzione da applicare tutta insieme. Tutte le modifiche, un commit, poi verifica finale. Senza una sequenza di passi isolati, senza un gate di verifica tra una modifica e la successiva, senza la possibilità di isolare esattamente quale cambiamento avesse introdotto un problema se qualcosa fosse andato storto.\nL\u0026rsquo;import nell\u0026rsquo;SDD # Ho usato il piano esistente come punto di partenza per creare il progetto SDD. L\u0026rsquo;analisi tecnica era già fatta — quello che mancava era la struttura di esecuzione. Le fasi hanno fatto il loro lavoro:\nLa constitution ha fissato un vincolo fondamentale che nelle sessioni precedenti non avevo mai esplicitato formalmente: un cambiamento alla volta, ognuno verificato con un ciclo completo di destroy+create prima di procedere al successivo. Sembra ovvio, ma senza che sia scritto da qualche parte è facile cedere alla tentazione di raggruppare più fix in un unico commit \u0026ldquo;per risparmiare tempo\u0026rdquo; — che è esattamente quello che aveva portato alla situazione confusa di partenza.\nLa specification mi ha costretto a enunciare le cause radice reali, non i sintomi. I sintomi erano kustomization bloccate in NotReady, dipendenze che non si sbloccavano, pod che non partivano nel giusto ordine. Le cause erano distinte e separate, e richiedevano correzioni separate.\nIl plan ha decomposto il lavoro in 14 step isolati, ognuno con la sua verifica. Non 14 commit in sequenza cieca — 14 step ognuno con un ciclo destroy+create e un insieme preciso di condizioni da verificare prima di considerarlo completato.\nIl file tasks è diventato la checklist operativa. Ogni sessione: apri tasks.md, vedi il prossimo step pendente, eseguilo, marcalo completato, chiudi. La sessione successiva: riapri, continua.\nLa causa radice # Una volta strutturato il problema correttamente, la causa principale è emersa chiaramente.\nLa kustomization infrastructure-operators-core raggruppava due categorie di risorse fondamentalmente diverse:\nController leggeri: cert-manager, traefik, Reloader, Dex, OAuth2-proxy, cloudflare-ddns. Chart Helm relativamente veloci, install in 1-2 minuti. Chart pesanti: kube-prometheus-stack e postgres-operator. Il primo in particolare ha un\u0026rsquo;installazione che può richiedere 10-15 minuti su hardware lento. Il problema con wait: true su questa kustomization era strutturale: Flux aspetta che tutte le risorse della kustomization siano in stato Ready prima di sbloccare le dipendenti. Con kube-prometheus-stack dentro operators-core, aggiungere wait: true significava bloccare l\u0026rsquo;intero grafo per 15 minuti ogni volta. Tutte le kustomization dipendenti — infrastructure-bridge, infrastructure-instances, apps-static, apps-data — restavano ferme ad aspettare che Prometheus finisse di installarsi.\nQuesto è un errore di progettazione del DAG, non un errore di configurazione. Avevo mescolato risorse con tempi di convergenza radicalmente diversi nello stesso nodo del grafo, e poi cercato di mettere un gate su quel nodo. Il gate era corretto in principio — wait: true su operators-core garantisce che cert-manager sia pronto prima che i certificati vengano richiesti — ma impossibile nella pratica finché il nodo conteneva chart pesanti.\nLa correzione # La correzione era separare i concern. Ho rimosso ../monitoring e ../postgres-operator dalla kustomization infrastructure/operators/core/kustomization.yaml, lasciandoli nelle loro kustomization dedicate (infrastructure-monitoring e infrastructure-operators-data) che già esistevano e già gestivano il loro ciclo di vita in modo autonomo.\n# infrastructure/operators/core/kustomization.yaml — dopo la correzione resources: - ../cert-manager - ../traefik - ../reloader - ../dex - ../auth - ../cloudflare-ddns # kube-prometheus-stack rimosso → gestito da infrastructure-monitoring # postgres-operator rimosso → gestito da infrastructure-operators-data Con questa modifica, operators-core conteneva solo chart leggeri. L\u0026rsquo;installazione completa richiedeva 2-3 minuti. wait: true è diventato sicuro da abilitare: il gate garantisce che cert-manager, traefik e gli altri controller fondamentali siano operativi prima che le kustomization dipendenti inizino a creare risorse che li richiedono.\nIl ciclo finale destroy+create ha dichiarato il blog online in 8 minuti e 20 secondi — il critical path leggero funzionava esattamente come previsto. Il database PostgreSQL, con il restore da S3 in background, e i servizi dipendenti (Mnemosyne MCP, pgAdmin) hanno completato intorno ai 12-13 minuti. Tempi nella norma: il restore non è sul critical path del blog, avviene in parallelo mentre i pod upstream già servono traffico.\nCosa ha cambiato l\u0026rsquo;SDD in questa sessione # Sarei disonesto se dicessi che senza SDD il problema sarebbe stato irrisolvibile. Probabilmente lo avrei risolto comunque. Ma con più tentativi, più commit disordinati, e quasi certamente avrei introdotto regressioni lungo la strada.\nQuello che SDD ha cambiato è la modalità di lavoro: invece di procedere per tentativi locali — \u0026ldquo;proviamo a togliere questa dipendenza e vediamo cosa succede\u0026rdquo; — ho dovuto prima enunciare formalmente cosa stava andando storto e perché, poi progettare una sequenza di correzioni verificabili, poi eseguirle una alla volta con conferma esplicita tra ognuna.\nQuesta disciplina ha un costo in termini di tempo iniziale. Ha un beneficio enorme in termini di chiarezza: quando sei al decimo step di quattordici e qualcosa non si comporta come previsto, sai esattamente cosa hai già verificato, cosa hai escluso, e dove cercare.\nIl ritmo del lavoro con i contesti # C\u0026rsquo;è un effetto collaterale del sistema dei contesti che non avevo previsto quando l\u0026rsquo;ho progettato, e che si è rivelato più prezioso di quanto pensassi: il ritmo di lavoro è cambiato.\nPrima del sistema dei contesti, ogni sessione aveva un costo di bootstrap non trascurabile. Riaprire una sessione significava rispiegare dove eravamo, qual era lo stato del progetto, quali erano le regole da seguire. Con progetti complessi, questo poteva richiedere diversi scambi di messaggi prima di essere operativi.\nOggi il pattern è diventato: apro il terminale, carico il contesto, sono operativi in pochi secondi. Il contesto porta con sé le regole, lo stato del progetto, il prossimo step da eseguire. Chiudo il terminale, riapro, sono di nuovo esattamente dove ero.\nQuesto ha cambiato anche il modo in cui penso alle nuove funzionalità. Quando voglio aggiungere una nuova capacità al mio workflow, non penso più \u0026ldquo;ho bisogno di un nuovo agente specializzato\u0026rdquo;. Penso \u0026ldquo;ho bisogno di un nuovo contesto con le regole giuste\u0026rdquo;. Scrivo il CONTEXT.md, definisco il comportamento atteso, e ogni agente che lo legge si comporterà coerentemente.\nIl vantaggio in termini di portabilità è reale. Passare a Gemini da Claude non richiede di reimpostare nulla: basta far leggere il AGENTS.ctx/CONTEXT.md principale, che spiega la struttura del sistema, dove trovare i contesti disponibili e le regole generali. L\u0026rsquo;agente è immediatamente orientato. Non c\u0026rsquo;è lock-in su nessun tool specifico.\nRiflessioni # Questa tappa del percorso ha confermato qualcosa che intuivo ma non avevo ancora sperimentato direttamente: la struttura del processo ha un impatto sulla qualità dell\u0026rsquo;output tanto quanto le capacità tecniche.\nIl problema del DAG di Flux non era difficile una volta enunciato correttamente. La difficoltà era nell\u0026rsquo;enunciarlo correttamente dopo settimane di tentativi disorganizzati che avevano accumulato rumore. SDD non ha aggiunto capacità tecniche — ha aggiunto il framework per usare quelle capacità in modo ordinato.\nC\u0026rsquo;è un\u0026rsquo;altra cosa che vale la pena notare: il sistema è volutamente semplice. Non c\u0026rsquo;è un tool da installare, non c\u0026rsquo;è un database da configurare, non c\u0026rsquo;è un server da mantenere. Sono file Markdown in una cartella Git. Questa semplicità non è una limitazione — è una scelta progettuale consapevole. Un sistema che dipende da pochi strumenti ubiqui è un sistema che sopravvive ai cambiamenti dell\u0026rsquo;ecosistema e funziona su qualsiasi macchina, con qualsiasi agente.\nIl prossimo passo naturale è usare questo stesso sistema per i progetti futuri, raccogliendo nel tempo un inventario di spec, piani e task completati che documenta non solo cosa è stato costruito, ma perché è stato costruito così.\n","date":"15 marzo 2026","externalUrl":null,"permalink":"/it/posts/sdd-context-dag-fix-first-shot/","section":"Posts","summary":"","title":"SDD in mezza giornata: un contesto con delle regole, e il DAG del cluster sistemato al primo colpo","type":"posts"},{"content":"","date":"14 marzo 2026","externalUrl":null,"permalink":"/it/tags/mcp/","section":"Tags","summary":"","title":"Mcp","type":"tags"},{"content":"","date":"14 marzo 2026","externalUrl":null,"permalink":"/it/tags/mnemosyne/","section":"Tags","summary":"","title":"Mnemosyne","type":"tags"},{"content":" Il punto di arrivo # Oggi ho migrato Mnemosyne dal protocollo SSE deprecato a Streamable HTTP. Ma questo non è un articolo su una migrazione tecnica. È un articolo su cosa significa quando il tuo cluster Kubernetes diventa noioso - nel senso buono del termine.\nHo fatto il commit, aspettato due minuti, e il nuovo pod era in esecuzione con la nuova configurazione. Nessun intervento manuale, nessun kubectl apply, nessun panico. Flux ha rilevato il cambiamento nel repository Git, l\u0026rsquo;ImagePolicy ha puntato alla nuova immagine buildata dalla GitHub Action, e il Deployment è stato aggiornato.\nQuesta non è una configurazione che ho fatto stamattina. È il risultato di mesi di iterazioni, di rebuild completi del cluster, di pipeline CI/CD che fallivano e venivano riparate, di ImagePolicy che non riconoscevano i tag corretti. Ma oggi, finalmente, funziona.\nLa migrazione come caso di studio # Mnemosyne è il server MCP che gestisce la mia memoria semantica. Espone tool per l\u0026rsquo;ingestione, la ricerca e la gestione di ricordi tecnici, usando PostgreSQL con pgvector per la similarità semantica. Fino a ieri usava il protocollo SSE (Server-Sent Events) per comunicare con i client MCP.\nIl problema: il client oh-my-pi non gestiva correttamente il protocollo SSE. Richiedeva che il client mantenesse una connessione GET persistente su /sse mentre inviava richieste POST su /message. Ma oh-my-pi trattava SSE come un semplice HTTP POST, senza il listener in background.\nLa soluzione non era fixare il client, ma migrare al nuovo standard: Streamable HTTP. Questo protocollo usa un singolo endpoint POST (/mcp) che restituisce una risposta SSE quando necessario. Niente session management complesso, niente listener separati.\nLa migrazione è stata semplice:\nAggiornato mcp-go dalla v0.44.0 alla v0.45.0 Sostituito NewSSEServer() con NewStreamableHTTPServer() Cambiato l\u0026rsquo;endpoint da /sse + /message a /mcp Aggiornato MCP_TRANSPORT da \u0026quot;sse\u0026quot; a \u0026quot;http\u0026quot; nel Deployment Quattro modifiche minime. Il codice si è compilato al primo tentativo. Ho fatto commit, push, e il cluster ha fatto il resto.\nLa pipeline GitOps che funziona # La nostra pipeline CI/CD è deliberatamente semplice:\nCommit → GitHub Action → Build immagine → Push su registry → Flux riconcilia → Deploy Non abbiamo stage multipli, non abbiamo approval gate, non abbiamo deployment su ambienti separati. È un laboratorio casalingo, non una enterprise. Ma questa semplicità è una feature, non un limite.\nQuando faccio commit su mnemosyne-mcp-server, la GitHub Action:\nFa checkout del codice Builda l\u0026rsquo;immagine Docker con un tag basato sul numero di run e sul commit SHA completo Pusha su Docker Hub come tazzo/mnemosyne-mcp:mcp-\u0026lt;run_number\u0026gt;-\u0026lt;full_sha\u0026gt; Nel frattempo, nel cluster:\nFlux ha un ImageRepository che monitora Docker Hub Un ImagePolicy seleziona l\u0026rsquo;immagine più recente Il Deployment ha un commento {\u0026quot;$imagepolicy\u0026quot;: \u0026quot;flux-system:mnemosyne-mcp\u0026quot;} che Flux usa per l\u0026rsquo;auto-update Quando rileva una nuova immagine, aggiorna il Deployment Kubernetes fa rollout del nuovo pod Tempo totale: 2-3 minuti dalla push al pod in esecuzione.\nIl sistema dei contesti AGENTS.ctx # Ma la parte più interessante non è la pipeline. È come ho strutturato le procedure operative.\nHo creato un sistema di contesti in AGENTS.ctx/ che definisce regole, workflow e memoria per ogni tipologia di attività. Ogni contesto ha:\nUn file CONTEXT.md che descrive il progetto e le sue regole File asset con prompt specifici, template, o risorse Un inventory di progetti, stati, e debito tecnico Quando apro un contesto, l\u0026rsquo;agent che uso diventa immediatamente specializzato. Per esempio:\nblog-writer: Definisce un workflow a 5 fasi (Planning → Writing → Review → Translation → Publish) con regole per lo stile, la formattazione, e la pubblicazione GitOps mnemosyne-mcp-server: Documenta il server MCP, la struttura del codice, le variabili d\u0026rsquo;ambiente, e le procedure di build/deploy tazlab-k8s: Descrive il cluster Kubernetes, le risorse Flux, e come interagire con esso Questo articolo è il secondo che scrivo usando il contesto blog-writer. Il processo è diventato quasi automatico: apro il contesto, decido i punti chiave, l\u0026rsquo;agent scrive, io revisiono. Niente più iterazioni infinite con prompt generici. Le regole sono già lì, pronte.\nLa visione: procedure automatiche per Mnemosyne # Il prossimo passo è creare un contesto per l\u0026rsquo;ingestione dei ricordi in Mnemosyne.\nAttualmente, quando voglio salvare un ricordo tecnico, devo:\nFormattare il contenuto Chiamare manualmente il tool ingest_memory Verificare che sia stato salvato correttamente Con un contesto dedicato, questo diventerà automatico. L\u0026rsquo;agent saprà:\nQuale formato usare per i ricordi Come strutturare il contenuto per la ricerca semantica Quando salvare (es. alla fine di una sessione di lavoro) Come verificare l\u0026rsquo;avvenuto salvataggio Basta aprire il contesto e dire \u0026ldquo;salva quello che abbiamo fatto oggi\u0026rdquo;. Tutto il resto è gestito dalle regole.\nIl paradigma multi-agent ridisegnato # Per molto tempo, il paradigma prevalente per l\u0026rsquo;automazione con LLM è stato \u0026ldquo;usa agenti specializzati diversi per compiti diversi\u0026rdquo;. Un agent per il codice, uno per la scrittura, uno per i dati.\nCon il sistema dei contesti, questo ragionamento si ribalta — ma in modo più sottile di quanto sembri.\nPer come lavoro oggi, da solo, la configurazione ottimale è un agent generico + N contesti caricati on-demand. Quando apro il contesto blog-writer, l\u0026rsquo;agent sa già come strutturare un articolo, quali regole seguire, come pubblicarlo. Quando apro mnemosyne-mcp-server, conosce la struttura del codice, le variabili d\u0026rsquo;ambiente, la pipeline CI/CD. L\u0026rsquo;agent non cambia — cambia il contesto.\nMa lo stesso sistema scala in orizzontale. In futuro, potrei deployare più agenti separati direttamente sul cluster Kubernetes — ognuno con il proprio contesto già caricato come ConfigMap o montato come volume. Un agente responsabile della manutenzione del cluster, uno dedicato all\u0026rsquo;ingestione delle memorie in Mnemosyne, uno che monitora i deploy Flux. Ognuno autonomo, ognuno specializzato, ognuno con una cartella di contesti che copre le situazioni operative che potrebbe incontrare.\nIl punto è che i contesti sono portabili e componibili. Non sono legati a un singolo agent. Sono unità di conoscenza operativa che possono essere distribuite, montate, combinate. Oggi le uso in modo interattivo. Domani potrebbero essere la base di un sistema di automazione autonoma.\nQuesto riduce la complessità di gestione:\nUn solo formato di conoscenza operativa (Markdown strutturato) Contesti versionabili su Git, aggiornabili centralmente Stessa struttura per uso interattivo e deployment autonomo È come avere una libreria di procedure operative che funziona sia quando le sfoglio io, sia quando le legge un agente in esecuzione su un pod.\nIl cluster in salute # Tornando all\u0026rsquo;inizio: il cluster è stabile. Questo non significa che non ci siano problemi - ci sono sempre. Ma significa che i problemi sono gestibili, e le procedure sono ripetibili.\nQuando ho dovuto migrare Mnemosyne a Streamable HTTP, non ho dovuto:\nRicostruire l\u0026rsquo;ambiente di sviluppo Configurare manualmente le variabili d\u0026rsquo;ambiente Fare debugging della pipeline CI/CD Imparare da capo come funziona Flux Ho semplicemente:\nAperto il contesto mnemosyne-mcp-server Fatto le modifiche al codice Commit e push Il resto è successo da solo. Questo è il risultato di aver documentato, iterato, e costruito procedure solide nel tempo.\nLa pipeline del futuro # La nostra pipeline oggi è semplice. In futuro diventerà più ricca:\nTest automatizzati: Ogni PR triggera test prima del merge Ambienti di staging: Deploy su un ambiente separato prima della produzione Rollback automatici: Se i health check falliscono, rollback alla versione precedente Notifiche: Slack o email quando un deploy completa o fallisce Ma la base c\u0026rsquo;è, ed è robusta. Ogni nuova feature sarà un\u0026rsquo;estensione, non una rifondazione. È questo il vantaggio di aver costruito bene le fondamenta.\nCosa abbiamo imparato # Questa \u0026ldquo;tappa\u0026rdquo; del viaggio mi ha confermato che:\nLa GitOps non è solo teoria: Quando funziona, ti dimentichi che esiste. Fai commit, e il codice arriva in produzione. I contesti cambiano il modo di lavorare: Nel mio caso, lavorando da solo, un agent generico + contesti ben definiti è risultato più comodo e gestibile di tanti agenti separati. Non è una legge universale, ma per questo flusso di lavoro funziona bene. La documentazione è codice: I file CONTEXT.md sono vivi. Vengono aggiornati, versionati, e usati ogni giorno. La semplicità vince: Una pipeline con 3 passaggi che funziona è meglio di una con 10 che non sai come configurare. Il cluster è maturo. Non \u0026ldquo;completo\u0026rdquo; - non lo sarà mai. Ma maturo abbastanza da permettermi di lavorare su cose interessanti invece di spegnere incendi.\n","date":"14 marzo 2026","externalUrl":null,"permalink":"/it/posts/mature-cluster-gitops-agent-contexts-mnemosyne/","section":"Posts","summary":"","title":"Un cluster maturo: deploy automatici, contesti per agenti e la migrazione MCP di Mnemosyne","type":"posts"},{"content":" Il Problema: Amnesia di Sessione # Ogni volta che riavvio un terminale e apro una nuova sessione con un agente AI, mi trovo di fronte allo stesso problema: devo rispiegare tutto da capo. Dove siamo, cosa stiamo facendo, quali sono le regole del progetto, quali problemi abbiamo già risolto.\nÈ un ciclo frustrante. L\u0026rsquo;agente non ricorda nulla della sessione precedente. Devo reiniettare il contesto manualmente, oppure sperare che il sistema abbia qualche meccanismo di persistenza — ma spesso questi meccanismi sono opachi, inefficienti, o semplicemente non esistono.\nIl problema diventa ancora più evidente quando si lavora su più progetti paralleli. Ogni progetto ha le sue convenzioni, la sua struttura, le sue regole non scritte. Caricare tutto nella stessa sessione non è solo inefficiente: è controproducente.\nIl Limite del Context Window # I modelli linguistici hanno un limite fisico: il context window. Quando la sessione inizia a riempirsi, le performance degradano. Intorno al 50% della capacità, la qualità delle risposte peggiora visibilmente. Il modello \u0026ldquo;dimentica\u0026rdquo; le istruzioni iniziali, perde coerenza, ripete informazioni.\nQuesto è il context bloat: troppe informazioni non pertinenti caricate nella stessa sessione. La soluzione non è avere più memoria, ma avere memoria selettiva.\nDue Tool, Due Scopi # Prima di arrivare alla soluzione, è importante distinguere due problemi diversi:\nMnemosyne è un MCP server che ho costruito per la memoria a lungo termine. Registra cosa ho fatto, quali problemi ho incontrato, come li ho risolti. È un archivio consultabile: quando un problema si ripresenta, cerco nei ricordi e trovo la soluzione applicata in passato. È utile per il troubleshooting, per la documentazione automatica, per costruire una knowledge base personale.\nAGENTS.ctx risponde a un problema diverso: il contesto attivo. Non voglio ricordare cosa ho fatto tre mesi fa — voglio che l\u0026rsquo;agente sappia ora dove siamo, cosa stiamo facendo, quali regole seguire. E voglio che lo sappia senza che io debba ripetere tutto ogni volta.\nMnemosyne è il diario storico. AGENTS.ctx è il brief operativo.\nL\u0026rsquo;Architettura: Indirezione e Caricamento Selettivo # L\u0026rsquo;idea centrale di AGENTS.ctx è semplice: non caricare tutto, caricare solo il necessario.\nLa struttura si basa su tre livelli:\nLivello 0: AGENTS.md (Entry Point) # Nella directory di lavoro (/workspace), un file AGENTS.md contiene le istruzioni base per l\u0026rsquo;agente. Dice cosa fare all\u0026rsquo;avvio, dove trovare i contesti, come gestirli.\nQuesto file è leggero, pochi paragrafi. Il suo compito è indicare la strada, non trasportare il carico.\nLivello 1: AGENTS.ctx/CONTEXT.md (Base Context) # Nella cartella AGENTS.ctx/, un file CONTEXT.md contiene il contesto base: la lista dei contesti disponibili, le regole generali che si applicano a tutti i progetti, la struttura delle cartelle.\nQuesto file viene caricato automaticamente all\u0026rsquo;avvio. È il \u0026ldquo;sistema operativo\u0026rdquo; dei contesti: fornisce l\u0026rsquo;indirizzario e le regole fondamentali.\nLivello 2: Contesti Specifici # Ogni contesto ha la sua sottocartella. Possono essere:\nProgetti: tazpod/, ephemeral-castle/, tazlab-k8s/ Workflow generici: blog-writer/, plans/ — per attività ripetibili Utilità: contesti che caricano solo regole, si usano e si chiudono Quando dico \u0026ldquo;lavora nel contesto X\u0026rdquo;, l\u0026rsquo;agente carica solo quel file. Niente di più, niente di meno. Finito il lavoro, chiudo la sessione e riparto pulito, pronto per un altro contesto.\nContesti Composti # Alcuni lavori richiedono più contesti contemporaneamente. Ad esempio, \u0026ldquo;cluster\u0026rdquo; è un contesto composto che carica sia ephemeral-castle (l\u0026rsquo;infrastruttura Proxmox/Talos) sia tazlab-k8s (le configurazioni Kubernetes). L\u0026rsquo;agente legge entrambi i file e unisce le regole.\nQuesto permette di lavorare su sistemi complessi senza dover duplicare informazioni.\nAgent-Agnostic by Design # Una scelta deliberata: tutto è basato su file di testo in cartelle semplici. Niente database, niente formati proprietari, niente lock-in.\nQuesto significa che posso usare qualsiasi agente: Gemini CLI, Claude Code, pi.dev. Basta che l\u0026rsquo;agente sappia leggere un file di testo e seguire istruzioni.\nLa portabilità è fondamentale. Non voglio che il mio workflow dipenda da uno strumento specifico. Se domani scopro un agente migliore, voglio poterlo adottare senza ricostruire tutto il sistema.\nIspirazione e Attribuzione # L\u0026rsquo;idea non è mia. L\u0026rsquo;ho vista in questo video, che mostra un approccio simile per gestire contesti con gli agenti AI. Ho adattato il concetto al mio workflow, aggiungendo la struttura a livelli, i contesti composti, e l\u0026rsquo;integrazione con il mio sistema esistente.\nCome Funziona in Pratica # La sequenza di avvio è:\nL\u0026rsquo;agente legge /workspace/AGENTS.md Segue l\u0026rsquo;istruzione: \u0026ldquo;leggi AGENTS.ctx/CONTEXT.md\u0026rdquo; Il base context lista i contesti disponibili Quando dico \u0026ldquo;contesto X\u0026rdquo;, l\u0026rsquo;agente legge AGENTS.ctx/X/CONTEXT.md Struttura di un Contesto # Ogni contesto può contenere:\nCONTEXT.md: le istruzioni principali scripts/: script di interazione (deploy, test, utility) docs/: documentazione aggiuntiva assets/: file di configurazione, template, risorse La struttura è flessibile. L\u0026rsquo;importante è che CONTEXT.md spieghi cosa c\u0026rsquo;è e come usarlo.\nEsempio: Contesto tazpod # TazPod è una CLI Go per gestire un ambiente di sviluppo nomade e secrets-aware. Fornisce:\nUn vault AES-256-GCM in RAM per i secrets (si monta con tazpod unlock, si azera con lock) Container Docker con tutto il toolchain (kubectl, terraform, helm, neovim, ecc.) Sync automatico dell\u0026rsquo;identità su S3 per portabilità Integrazione con Infisical per secrets management Il contesto tazpod/CONTEXT.md spiega all\u0026rsquo;agente l\u0026rsquo;architettura a tre livelli (host CLI, tmpfs enclave, container), i comandi principali, i path hardcoded, e le procedure custom (come il push GitHub con token).\nQuando lavoro su tazpod, l\u0026rsquo;agente ha subito il quadro completo: non devo spiegare cos\u0026rsquo;è il vault, come funziona l\u0026rsquo;enclave, o dove stanno i file. Il contesto è compatto e focalizzato.\nTrade-offs e Lezioni Imparate # Cosa Funziona Bene # Caricamento esplicito: so esattamente cosa viene caricato Separazione netta: ogni progetto ha il suo spazio Zero magia: niente auto-discovery che carica cose inaspettate Portabilità: funziona con qualsiasi agente Cosa Potrebbe Migliorare # Gestione manuale: devo aggiornare le tabelle quando aggiungo contesti Niente inferenza: l\u0026rsquo;agente non indovina il contesto, deve essere esplicito Overhead iniziale: richiede un po\u0026rsquo; di setup Il trade-off principale è tra comodità e controllo. Ho scelto il controllo.\nConclusione: Contesto Compatto, Performance Migliori # AGENTS.ctx risolve un problema pratico: evitare di ripetere le stesse cose ogni volta che apro una sessione. La soluzione non è più memoria, ma memoria organizzata.\nIndirezione, caricamento selettivo, contesti separati. L\u0026rsquo;agente ha solo il necessario per il lavoro corrente. Niente bloat, niente degradazione.\nE quando cambio agente, il sistema viene con me.\n","date":"13 marzo 2026","externalUrl":null,"permalink":"/it/posts/ai-context-management-agents-ctx/","section":"Posts","summary":"","title":"AGENTS.ctx: Gestione Contesti per Agenti AI Senza Rispiegare Tutto da Capo","type":"posts"},{"content":"","date":"9 marzo 2026","externalUrl":null,"permalink":"/it/tags/cloud-native/","section":"Tags","summary":"","title":"Cloud-Native","type":"tags"},{"content":"","date":"9 marzo 2026","externalUrl":null,"permalink":"/it/tags/developer-tools/","section":"Tags","summary":"","title":"Developer Tools","type":"tags"},{"content":" Introduzione: La Frustrazione dei Walled Garden # Quando si lavora su un\u0026rsquo;infrastruttura complessa come TazLab — un ecosistema nomadico basato su Talos Kubernetes, GitOps con Flux CD, e una superficie di attacco ridotta tramite Zero Trust — la necessità di automazione intelligente diventa rapidamente critica. Non parlo di automazione procedurale (quella è risolta con Terragrunt e Ansible), ma di assistenza cognitiva: un agente AI capace di leggere il contesto del progetto, ragionare sulle dipendenze, suggerire refactoring e debuggare problemi complessi di orchestrazione.\nInizialmente, ho cercato di risolvere questo problema affidandomi agli strumenti mainstream: Gemini CLI di Google e Cloud Code. Entrambi promettevano integrazione nativa con le API di Gemini e un workflow fluido. Tuttavia, dopo settimane di utilizzo intensivo, mi sono scontrato con limiti strutturali che rendevano impossibile adattarli ai requisiti di TazLab.\nQuesta analisi documenta il mio percorso verso Pi.Dev (pi-coding-agent), uno strumento minimale ma radicalmente configurabile che ho adottato come base per costruire agenti specializzati. Il confronto non è accademico: riflette esigenze concrete emerse dalla gestione di un laboratorio home lab in produzione.\nFase 1: I Limiti delle Soluzioni \u0026ldquo;Convenience-First\u0026rdquo; # Gemini CLI: Potenza Limitata da Scelte Architetturali # Gemini CLI è lo strumento ufficiale di Google per interagire con i modelli Gemini via linea di comando. La mia prima impressione era positiva: supporta multi-modalità (testo, immagini, video), gestisce sessioni persistenti e integra il protocollo Model Context Protocol (MCP) per estendere le capacità tramite server esterni.\nDeep-Dive Concettuale: Model Context Protocol (MCP) Il Model Context Protocol è un protocollo JSON-RPC che permette agli agenti AI di invocare \u0026ldquo;tool\u0026rdquo; esterni (funzioni esposte da server remoti o locali). Ad esempio, un server MCP può fornire strumenti per interrogare un database Postgres, cercare in una knowledge base vettoriale o leggere metriche da Prometheus. Il protocollo supporta due modalità di trasporto: Stdio (comunicazione tra processi sulla stessa macchina tramite stdin/stdout) e SSE (Server-Sent Events su HTTP, per integrazioni distribuite).\nIl problema con Gemini CLI emerge quando si vuole fare più di quanto Google abbia previsto. Ecco i limiti che ho incontrato:\nLentezza cronica: Anche con il piano Pro, Gemini CLI è notevolmente lento. Le risposte arrivano con latenze significative — a volte decine di secondi per query che richiedono context reading del cluster. In un workflow di debugging iterativo, dove interroghi l\u0026rsquo;agente più volte per affinare la diagnosi, questa lentezza diventa un freno tangibile alla produttività.\nEstensibilità rigida via MCP: Sebbene Gemini CLI supporti MCP, la configurazione è limitata a un file JSON (settings.json) che specifica quali server esterni invocare. Non è possibile iniettare logica custom direttamente nel loop dell\u0026rsquo;agente senza passare per un server MCP separato. Questo significa che se volevo un agente che, ad esempio, leggesse automaticamente i log di Flux CD dal cluster Kubernetes prima di rispondere a una domanda, dovevo costruire un server MCP dedicato per esporre quel tool — per ogni singola funzionalità.\nNessun controllo sul prompt di sistema: Gemini CLI usa un prompt di sistema hard-coded. Non è possibile modificarlo per istruire l\u0026rsquo;agente su convenzioni specifiche del progetto (ad esempio, \u0026ldquo;Quando scrivi manifesti Kubernetes, usa sempre Kustomize invece di Helm\u0026rdquo; o \u0026ldquo;Per ogni commit, aggiungi una git note con il timestamp\u0026rdquo;). Questo limita drasticamente la specializzazione.\nCloud Code: Velocità al Costo del Quota # Cloud Code è lo strumento successivo che ho provato — da terminale, non come estensione VS Code che non rientra nel mio workflow. La differenza rispetto a Gemini CLI è immediata: le risposte sono notevolmente più veloci. Per chi lavora su infrastrutture come TazLab, dove ogni query implica leggere log di controller, stato di Flux e output di kubectl, la velocità di risposta non è un dettaglio cosmetic.\nIl problema è la sostenibilità del piano. Con il piano Pro, una sessione di debugging Kubernetes — leggere log di kustomize-controller, validare manifest, iterare su un problema di Flux — è sufficiente per esaurire il quota. Fatico a superare le due ore di lavoro intensivo prima di ritrovarmi bloccato.\nPerché Cloud Code non era sostenibile per TazLab:\nQuota insostenibile per workload Kubernetes: Il piano Pro si esaurisce rapidamente su task che richiedono context intensivo. Una singola sessione di Flux debugging consuma abbastanza da bloccarti il resto della giornata. Non è un caso limite: è la norma per chi lavora su infrastrutture complesse.\nNessuna possibilità di scripting: Non posso invocare Cloud Code da uno script Bash per automatizzare task ripetitivi. È un\u0026rsquo;interfaccia conversazionale chiusa, non componibile in pipeline.\nVendor Lock-In: L\u0026rsquo;intero ecosistema spinge verso i servizi Google Cloud. Questa filosofia è opposta a quella di TazLab, dove la sovranità digitale è un principio fondamentale. Non voglio che la mia capacità di lavorare dipenda dalla disponibilità — o dalla generosità del piano — di un servizio cloud esterno.\nFase 2: La Scoperta di Pi.Dev — Filosofia Unix per gli Agenti AI # Dopo settimane di frustrazione, ho iniziato a cercare alternative che soddisfacessero questi requisiti:\nEstensibilità radicale: Capacità di modificare ogni aspetto del comportamento dell\u0026rsquo;agente. Modularità: Supporto per più agenti specializzati, ciascuno con il proprio prompt di sistema e set di tool. Multi-modello: Possibilità di usare modelli diversi (Anthropic Claude, Google Gemini, OpenAI, Ollama) a seconda del task. Determinante è il supporto nativo a OpenRouter, che permette di accedere a praticamente qualsiasi modello disponibile sul mercato con un\u0026rsquo;unica API key. Uno degli esperimenti in programma è un benchmark sistematico dei modelli di punta su contesti Kubernetes — per capire quali offrono il miglior rapporto qualità/costo per task come debugging di Flux, analisi di manifest e generazione di configurazioni. Scripting-friendly: Utilizzabile sia interattivamente che in pipeline automatiche. Minimale: Nessuna dipendenza da IDE o framework pesanti. Scavando tra progetti open source e sperimentazioni della community, sono arrivato a Pi.Dev (pi-coding-agent). Il confronto che faccio spesso per descriverlo è: Pi.Dev sta a Gemini CLI/Cloud Code come Neovim sta a Visual Studio Code. È minimale, configurabile fino ai minimi dettagli, e richiede investimento iniziale per padroneggiarlo, ma ripaga con flessibilità totale.\nVale la pena aggiungere un dato di contesto: OpenClaw, il coding agent che negli ultimi mesi ha raccolto notevole attenzione nella comunità degli sviluppatori, è costruito proprio su Pi.Dev. Non è un dettaglio marginale — significa che il framework che uso come base ha già dimostrato di reggere sotto carichi e ambizioni di produzione reali.\nAnatomia di Pi.Dev: Architettura Component-Based # Pi.Dev è scritto in TypeScript e distribuito come pacchetto npm. L\u0026rsquo;architettura è basata su tre concetti fondamentali:\nAgent: Un\u0026rsquo;istanza AI con un prompt di sistema specifico, un modello associato, e un set di tool disponibili. Skill: Moduli riutilizzabili che aggiungono capacità contestuali (es. \u0026ldquo;quando l\u0026rsquo;utente chiede di lavorare su Kubernetes, carica le istruzioni dal file KUBERNETES.md\u0026rdquo;). Extension: Funzioni custom (tool) che l\u0026rsquo;agente può invocare, scritte in TypeScript e integrate tramite un\u0026rsquo;interfaccia semplice. Deep-Dive Concettuale: Agent vs Assistant vs Tool È importante distinguere i livelli di astrazione. Un Assistant (come Gemini o Claude) è il modello sottostante, fornito da un provider (Google, Anthropic). Un Agent è una configurazione specifica di quell\u0026rsquo;assistant, con un prompt di sistema e un set di tool. Ad esempio, posso avere un agent chiamato \u0026ldquo;k8s-debugger\u0026rdquo; che usa il modello claude-sonnet-4, con prompt di sistema che lo istruisce a leggere sempre i log di Flux prima di rispondere, e con accesso a tool custom per interrogare Prometheus. Un Tool è una funzione che l\u0026rsquo;agent può invocare. Pi.Dev permette di definire tool sia come estensioni (codice locale) che come skill (bundle predefiniti di prompt + tool).\nLa differenza chiave è l\u0026rsquo;approccio filosofico. Gemini CLI e Cloud Code sono prodotti finiti — strumenti progettati per un caso d\u0026rsquo;uso mainstream e poi sigillati. Pi.Dev è un toolkit — fornisce i mattoni (gestione conversazioni, invocazione modelli, protocollo MCP) e lascia che l\u0026rsquo;utente costruisca la propria architettura di agenti.\nFase 3: Casi d\u0026rsquo;Uso — Agenti Specializzati per l\u0026rsquo;Ecosistema Kubernetes # Una volta compreso il potenziale di Pi.Dev, ho iniziato a mappare i casi d\u0026rsquo;uso concreti per TazLab. Eccone due che sto esplorando:\nCaso 1: L\u0026rsquo;Agente \u0026ldquo;Blog Writer\u0026rdquo; (Questo Articolo) # Il primo agente che ho configurato è quello che sta scrivendo questo articolo. Il suo prompt di sistema (CLAUDE.md nel repository) gli istruisce a:\nLeggere la documentazione esistente nel blog (~/kubernetes/blog-src/content/posts/) per capire lo stile. Seguire un template strutturato (Introduzione → Fasi → Riflessioni). Espandere ogni concetto tecnico con paragrafi \u0026ldquo;Deep-Dive\u0026rdquo;. Usare un tono professionale in prima persona singolare. Questo agente usa il modello claude-sonnet-4 di Anthropic perché eccelle nella scrittura tecnica lunga e strutturata. Quando gli chiedo di scrivere un articolo, legge autonomamente esempi esistenti, identifica i tag appropriati, e genera un file Markdown completo con frontmatter TOML.\nPerché questo non sarebbe possibile con Gemini CLI: Con Gemini CLI, avrei dovuto:\nCreare un server MCP che espone un tool \u0026ldquo;read_blog_posts\u0026rdquo;. Lanciare il server in background. Configurare Gemini CLI per connettersi al server. Scrivere manualmente il prompt di sistema ogni volta, perché non posso salvarlo nella configurazione. Parsare l\u0026rsquo;output testuale e salvarlo manualmente. Con Pi.Dev, tutto questo è configurato una volta nel file dell\u0026rsquo;agente, e ogni invocazione è automatica.\nCaso 2: L\u0026rsquo;Agente \u0026ldquo;K8s Watchdog\u0026rdquo; — Sorveglianza Proattiva del Cluster # Il secondo caso d\u0026rsquo;uso è il più ambizioso: un pod con una versione minimale di Pi.Dev deployato dentro il cluster Kubernetes, che agisce da watchdog generale su tutti i componenti critici dell\u0026rsquo;infrastruttura.\nL\u0026rsquo;architettura è un CronJob Kubernetes con intervallo configurabile — probabilmente tra i dieci e i trenta minuti. Ad ogni esecuzione, l\u0026rsquo;agente interroga il cluster su più fronti usando il client in-cluster con un ServiceAccount dal RBAC stretto: sola lettura su risorse, log ed eventi.\nPerimetro di monitoraggio:\nGitOps (Flux): stato di HelmRelease, Kustomization, GitRepository. Rileva riconciliazioni fallite, stalled o in ritardo rispetto alla revisione più recente. Storage (Longhorn): salute dei volumi, stato delle repliche, backup recenti. Identifica volumi in stato degraded o senza snapshot nell\u0026rsquo;intervallo atteso. Database: pod dei database critici (Postgres/CrunchyPostgres e altri stateful set). Verifica che siano Running, senza restart anomali, con liveness probe che risponde. Pod generale: qualsiasi pod in CrashLoopBackOff, OOMKilled, ImagePullBackOff, o con restart count sopra una soglia configurabile. Flusso operativo:\nNominal: Se tutto è sano, produce un report sintetico e termina. Anomalia rilevata: Passa in modalità investigativa — legge gli eventi Kubernetes correlati, i log del componente in errore, lo stato delle risorse dipendenti. Causa elevata: Se un pod si riavvia troppo spesso, correla con OOMKilled events, memory limit, log dell\u0026rsquo;applicazione. Se un volume Longhorn è degraded, verifica lo stato dei nodi e delle repliche. Report strutturato: Diagnosi probabile, lista ordinata di opzioni da verificare manualmente, soluzioni concrete da valutare — senza applicare nulla autonomamente. La distinzione è deliberata: l\u0026rsquo;agente ha visibilità completa ma zero potere esecutivo. L\u0026rsquo;obiettivo non è creare un sistema autonomo che possa peggiorare una situazione già critica, ma ridurre il triaging da \u0026ldquo;leggo tutto io\u0026rdquo; a \u0026ldquo;leggo il report e decido\u0026rdquo;.\nModello previsto: un modello economico via OpenRouter — il perimetro di analisi è ampio ma strutturato, e la frequenza di esecuzione rende il costo per token un vincolo non negoziabile.\nRiflessioni Architetturali: Verso un\u0026rsquo;Infrastruttura \u0026ldquo;Agent-Aware\u0026rdquo; # L\u0026rsquo;adozione di Pi.Dev sta cambiando il modo in cui penso all\u0026rsquo;architettura di TazLab. Tradizionalmente, l\u0026rsquo;automazione era separata in due categorie:\nAutomazione procedurale (script Bash, Terragrunt, Ansible) — task ripetibili, deterministici. Intervento umano (debugging, decisioni architetturali, refactoring) — task che richiedono ragionamento. Con agenti AI configurabili, emerge una terza categoria: automazione cognitiva. Task che richiedono ragionamento ma che possono essere delegati a un agente con il giusto contesto.\nIl Problema della Trust Boundary # Tuttavia, questo introduce una sfida di sicurezza critica. Un agente AI in-cluster con accesso a kubectl e alle API del cluster ha potenzialmente il potere di distruggere l\u0026rsquo;intera infrastruttura. Come gestisco questa trust boundary?\nApprocci che sto esplorando:\nRBAC Stretto: L\u0026rsquo;agente in-cluster gira con un ServiceAccount Kubernetes con permessi limitati. Ad esempio, può leggere metriche e log, ma non può cancellare risorse o modificare ConfigMap critiche.\nAudit Trail Completo: Ogni azione dell\u0026rsquo;agente viene loggata in modo immutabile (Loki + S3 backup). Se l\u0026rsquo;agente compie un\u0026rsquo;azione distruttiva, posso ricostruire la catena di eventi.\nHuman-in-the-Loop per Azioni Critiche: L\u0026rsquo;agente può proporre modifiche (es. \u0026ldquo;Ecco una PR per scalare lo storage Longhorn\u0026rdquo;), ma l\u0026rsquo;applicazione richiede approvazione umana via GitOps.\nSandbox Environments: Prima di deployare un agente in produzione, lo testo in un cluster di staging (cluster \u0026ldquo;Green\u0026rdquo; di TazLab, non ancora documentato).\nIl Pattern \u0026ldquo;Agent-as-Operator\u0026rdquo; # Un Operator Kubernetes tradizionale (scritto in Go con controller-runtime) riconcilia uno stato desiderato dichiarato in CRD. L\u0026rsquo;idea di un \u0026ldquo;Agent-as-Operator\u0026rdquo; è diversa: l\u0026rsquo;agente non riconcilia uno stato dichiarato, ma risponde a eventi e prende decisioni contestuali.\nEsempio concreto:\nOperator tradizionale: \u0026ldquo;Se il PVC supera l'80% di utilizzo, aumenta la dimensione a X GB (valore hard-coded).\u0026rdquo; Agent-as-Operator: \u0026ldquo;Se il PVC supera l'80%, analizza i pattern di crescita degli ultimi 7 giorni, verifica il budget di storage disponibile, consulta i log di backup per garantire recovery, e proponi un piano di scaling ottimale.\u0026rdquo; Questo pattern non sostituisce gli Operator tradizionali (che sono più efficienti per task deterministici), ma li complementa per scenari che richiedono flessibilità.\nFase 4: Cosa Manca — Gap e Direzioni Future # Nonostante l\u0026rsquo;entusiasmo (controllato) per Pi.Dev, ci sono gap evidenti che sto affrontando:\nGap 1: Costo per Token e Multi-Model Orchestration # Usare modelli diversi per task diversi è potente, ma introduce complessità di budgeting. Claude Sonnet è costoso (circa $3 per milione di token di input), mentre Gemini Flash è quasi gratuito. Devo costruire logica per:\nRouting intelligente: task semplici → modello economico, task complessi → modello capace. Monitoring dei costi: dashboard che traccia quanti token consumo per agente/task. Pi.Dev non fornisce questo out-of-the-box. Sto esplorando l\u0026rsquo;integrazione con tool come LangSmith o costruzione di una dashboard custom con Prometheus + Grafana.\nGap 2: Testing e Validazione degli Agenti # Come testo che un agente funzioni correttamente? Con codice tradizionale, scrivo unit test. Con un agente AI, il comportamento è probabilistico. Sto sperimentando con:\nGolden Test: Eseguo l\u0026rsquo;agente su problemi noti (es. \u0026ldquo;Debugga questo errore di Flux che so essere causato da un YAML malformato\u0026rdquo;) e verifico che l\u0026rsquo;output contenga le keyword giuste. Regression Test: Ogni volta che l\u0026rsquo;agente risolve un problema, salvo input/output come test case. Se cambio il prompt di sistema, ri-eseguo i test per verificare che i comportamenti desiderati non siano regressi. Gap 3: Persistent State per Agenti In-Cluster # Un agente in un pod Kubernetes è per definizione effimero. Se il pod crasha, perde la memoria della conversazione. Per agenti long-running, devo implementare persistent state. Opzioni:\nDatabase esterno (Postgres): Salvo la cronologia delle conversazioni e il contesto. Kubernetes ConfigMap: Per stato leggero (configurazioni, task queue). Conclusione: Una Scelta di Sovranità Tecnica # L\u0026rsquo;adozione di Pi.Dev rispetto a Gemini CLI o Cloud Code non è stata guidata da fanatismo per l\u0026rsquo;open source o da avversione a Google. È stata una scelta pragmatica basata sui requisiti architetturali di TazLab:\nEstensibilità: Ho bisogno di agenti che si comportino esattamente come voglio, non come un product manager di BigTech ha deciso. Multi-Modello: Voglio scegliere il modello ottimale per ogni task, non essere vincolato a un ecosistema. Integrazione Deep: Gli agenti devono vivere dentro il mio ecosistema (TazPod, Kubernetes, Mnemosyne), non in un walled garden cloud. Sovranità: Voglio capire e controllare ogni aspetto del sistema, dal prompt di sistema al protocollo di comunicazione. Pi.Dev, con la sua filosofia minimale e configurabile, soddisfa questi requisiti. È il Neovim degli AI coding agent: ha una curva di apprendimento ripida, richiede investimento iniziale, ma ripaga con controllo totale.\nMentre scrivo questo articolo (tramite l\u0026rsquo;agente \u0026ldquo;blog-writer\u0026rdquo; basato su Pi.Dev), sto ancora imparando. La documentazione è frammentata, alcune feature sono sperimentali, e ci sono edge case da risolvere. Ma è proprio questo il punto: ho la possibilità di risolverli. Con Gemini CLI, se una feature non esiste, posso solo aprire una issue su GitHub e sperare. Con Pi.Dev, posso aprire il codice, capire come funziona, e contribuire la patch.\nQuesto è il tipo di empowerment tecnico che cercavo quando ho iniziato il progetto TazLab. L\u0026rsquo;aggiunta di Pi.Dev all\u0026rsquo;arsenale rappresenta un ulteriore passo verso un ecosistema veramente sovrano, dove ogni componente — dall\u0026rsquo;OS (Talos) al vault (TazPod) alla memoria (Mnemosyne) agli agenti (Pi.Dev) — è controllabile, ispezionabile e modificabile.\nNei prossimi articoli, documenterò l\u0026rsquo;implementazione concreta del \u0026ldquo;K8s Watchdog\u0026rdquo; e i primi risultati del benchmark comparativo tra modelli su task Kubernetes. Se questa analisi comparativa ti ha incuriosito, ti invito a esplorare Pi.Dev e a considerare se la filosofia \u0026ldquo;tool minimale ma radicalmente configurabile\u0026rdquo; si adatta al tuo workflow.\nNota per i lettori: Questo articolo è stato scritto da un agente Pi.Dev configurato come \u0026ldquo;Home Lab Blogger\u0026rdquo;. L\u0026rsquo;ironia non è casuale — è una dimostrazione pratica della tesi dell\u0026rsquo;articolo. L\u0026rsquo;agente ha letto autonomamente articoli precedenti del blog, identificato il formato corretto, generato il frontmatter TOML, e prodotto questo testo seguendo le regole di stile definite nel suo prompt di sistema. Il processo è stato: pi --agent blog-writer --task \u0026quot;Scrivi analisi comparativa su Pi.Dev vs Gemini CLI/Cloud Code\u0026quot;. Tempo di generazione: ~3 minuti. Costo: ~$0.15 (Claude Sonnet 4, ~50k token output). +++\n","date":"9 marzo 2026","externalUrl":null,"permalink":"/it/posts/pi-dev-agent-architecture-comparative/","section":"Posts","summary":"","title":"Pi.Dev: Architettura di Agenti Minimali per l'Ecosistema Cloud-Native","type":"posts"},{"content":" Enterprise Monitoring in a Home Lab: La strada (in salita) verso Grafana e Prometheus Stateless # Introduzione: Oltre il Monitoring \u0026ldquo;Out of the Box\u0026rdquo; # In un Homelab che ambisce a essere qualcosa di più di un semplice insieme di container, il monitoraggio non può essere un elemento accessorio. Dopo aver stabilizzato il mio cluster Talos Linux su Proxmox e aver consolidato lo storage distribuito con Longhorn, ho sentito la necessità di una visibilità granulare. Non mi servivano solo grafici; mi serviva un\u0026rsquo;infrastruttura di osservabilità che seguisse gli stessi principi di resilienza e immutabilità del resto del cluster.\nMolti tutorial suggeriscono di installare kube-prometheus-stack con i valori di default: Grafana che salva i dati su un database SQLite locale e Prometheus che scrive su un volume temporaneo. Questa soluzione, per quanto rapida, è antitetica alla mia visione di \u0026ldquo;Enterprise Homelab\u0026rdquo;. Se un nodo fallisce e il Pod di Grafana viene rischedulato altrove senza un volume persistente, perderei ogni dashboard creata manualmente, ogni utente e ogni configurazione. Ho deciso quindi di intraprendere la strada più complessa: un\u0026rsquo;architettura Stateless per Grafana e una Persistenza Duratura per Prometheus, orchestrata interamente via GitOps con FluxCD.\nLa Strategia Architetturale: Perché lo \u0026ldquo;Stateless\u0026rdquo;? # Il concetto di \u0026ldquo;applicazione stateless\u0026rdquo; è fondamentale nelle architetture cloud-native moderne. Per Grafana, questo significa che il binario dell\u0026rsquo;applicazione non deve contenere alcuno stato vitale. Ho deciso di utilizzare il cluster PostgreSQL esistente (tazlab-db), gestito dal CrunchyData Postgres Operator, come backend per i metadati di Grafana.\nIl Ragionamento: SQLite vs PostgreSQL # Perché prendersi il disturbo di configurare un database esterno? In un\u0026rsquo;installazione standard, Grafana utilizza SQLite, un database a file singolo. Sebbene eccellente per semplicità, SQLite in Kubernetes richiede un PersistentVolumeClaim (PVC) dedicato. Se il PVC si corrompe o se ci sono problemi di lock del file durante una migrazione di nodo (comune con i volumi RWO), Grafana non parte. Utilizzando PostgreSQL, sposto la responsabilità della persistenza su un sistema che ho già reso resiliente (con backup S3 via pgBackRest e alta affidabilità). Questo mi permette di trattare i Pod di Grafana come sacrificabili: posso distruggerli e ricrearli in qualsiasi momento, sapendo che i dati sono al sicuro nel database centrale.\nLa scelta di Prometheus su Longhorn # Per Prometheus, la situazione è diversa. Prometheus è intrinsecamente \u0026ldquo;stateful\u0026rdquo; a causa del suo database a serie temporali (TSDB). Sebbene esistano soluzioni come Thanos o Cortex per renderlo stateless, per il mio attuale volume di dati sarebbe stato un overkill inutile. Ho optato per un approccio pragmatico: un volume da 10GB su Longhorn con una policy di retention di 15 giorni. Questo garantisce che i dati storici sopravvivano ai riavvii dei Pod, mentre la replica distribuita di Longhorn mi protegge dai guasti hardware dei nodi fisici Proxmox.\nL\u0026rsquo;Implementazione: Configurazione e GitOps # L\u0026rsquo;intero stack è definito tramite un HelmRelease di FluxCD. Questo mi permette di gestire la configurazione in modo dichiarativo nel repository tazlab-k8s.\nIl cuore della configurazione (Snippet Tecnico) # Ecco come ho dichiarato l\u0026rsquo;integrazione con PostgreSQL e la gestione del networking:\nspec: values: grafana: enabled: true grafana.ini: database: type: postgres host: tazlab-db-primary.tazlab-db.svc.cluster.local:5432 name: grafana user: grafana env: GF_DATABASE_TYPE: postgres GF_DATABASE_HOST: tazlab-db-primary.tazlab-db.svc.cluster.local:5432 GF_DATABASE_NAME: grafana GF_DATABASE_USER: grafana envValueFrom: GF_DATABASE_PASSWORD: secretKeyRef: name: tazlab-db-pguser-grafana key: password service: type: LoadBalancer annotations: metallb.universe.tf/loadBalancerIPs: \u0026#34;192.168.1.240\u0026#34; metallb.universe.tf/allow-shared-ip: \u0026#34;tazlab-internal-dashboard\u0026#34; port: 8005 Questa configurazione utilizza le External Secrets (ESO) per iniettare la password del database, sincronizzandola direttamente da Infisical. È un passaggio critico per la sicurezza: nessuna password è scritta in chiaro nel codice Git.\nLa Cronaca dei Fallimenti: Un Percorso a Ostacoli # Nonostante la pianificazione, l\u0026rsquo;installazione è stata un \u0026ldquo;Trail of Failures\u0026rdquo; che ha richiesto ore di debugging profondo. Documentare questi errori è fondamentale, perché rappresentano la realtà del lavoro di un ingegnere DevOps.\n1. Il Fantasma di SQLite (The Silent Failure) # Dopo il primo deploy, ho notato dai log che Grafana tentava ancora di inizializzare un database SQLite in /var/lib/grafana/grafana.db. Nonostante avessi configurato la sezione database in grafana.ini, le impostazioni venivano ignorate.\nL\u0026rsquo;Investigazione: Ho eseguito un kubectl exec nel Pod per ispezionare il file di configurazione generato. Ho scoperto che, a causa del modo in cui la Helm Chart di Grafana processa i valori, alcune variabili inserite in grafana.ini non venivano propagate correttamente se non erano presenti anche come variabili d\u0026rsquo;ambiente. La Soluzione: Ho dovuto duplicare la configurazione sia nella sezione grafana.ini che nella sezione env. Solo allora Grafana ha \u0026ldquo;capito\u0026rdquo; di dover puntare a PostgreSQL. È un comportamento fastidioso delle chart complesse: la ridondanza a volte è l\u0026rsquo;unica via.\n2. Il Muro dei Permessi di Postgres 16 # Una volta risolto il problema della configurazione, il Pod di Grafana ha iniziato a crashare con un errore criptico: pq: permission denied for schema public.\nL\u0026rsquo;Investigazione: Sapevo che il database era attivo e che l\u0026rsquo;utente grafana esisteva. Tuttavia, PostgreSQL 16 ha introdotto cambiamenti restrittivi sui permessi dello schema public. Per impostazione predefinita, i nuovi utenti non hanno più il diritto di creare oggetti in quello schema. La Soluzione: Ho dovuto intervenire manualmente sul database con una sessione SQL:\nGRANT ALL ON SCHEMA public TO grafana; ALTER SCHEMA public OWNER TO grafana; Questo passaggio mi ha ricordato che, anche in un mondo automatizzato, la conoscenza profonda dei sistemi sottostanti (come il RBAC di un database) è insostituibile.\n3. Il Conflitto di Rete: La Porta 8004 # Il cluster utilizza MetalLB per esporre i servizi su un IP dedicato (192.168.1.240). Durante il deploy, il servizio di Grafana rimaneva in stato \u0026lt;pending\u0026gt;.\nL\u0026rsquo;Investigazione: Ho controllato gli eventi del servizio con kubectl describe svc. MetalLB segnalava un conflitto: \u0026ldquo;port 8004 is already occupied\u0026rdquo;. Un\u0026rsquo;analisi rapida della mia documentazione ha rivelato che mnemosyne-mcp stava già usando quella porta sullo stesso IP condiviso. La Soluzione: Ho spostato Grafana sulla porta 8005. Questo evidenzia l\u0026rsquo;importanza di un IP Address Management (IPAM) rigoroso anche in un ambiente di laboratorio, specialmente quando si usano annotazioni come allow-shared-ip.\n4. Il Silenzio del Node Exporter (Pod Security Standards) # Dopo l\u0026rsquo;installazione, le dashboard erano visibili ma\u0026hellip; vuote. Nessun dato dai nodi.\nL\u0026rsquo;Investigazione: Ho controllato il DaemonSet del node-exporter. Nessun Pod era stato creato. Il controller restituiva un errore di violazione delle Pod Security Policies: violates PodSecurity baseline:latest. Il node-exporter richiede accesso ai namespace dell\u0026rsquo;host (hostNetwork, hostPID) e ai hostPath per leggere le metriche hardware, comportamenti che Kubernetes ora blocca di default per sicurezza. La Soluzione: Ho dovuto \u0026ldquo;ammorbidire\u0026rdquo; il namespace monitoring etichettandolo come privileged:\napiVersion: v1 kind: Namespace metadata: name: monitoring labels: pod-security.kubernetes.io/enforce: privileged È un compromesso necessario: per monitorare l\u0026rsquo;hardware, il software deve poterlo \u0026ldquo;vedere\u0026rdquo;.\nGitOps per le Dashboard: Il Sidecar Magico # Un altro pilastro di questa installazione è l\u0026rsquo;automazione delle dashboard. Non voglio creare grafici a mano cliccando nell\u0026rsquo;interfaccia; voglio che le dashboard siano parte del codice.\nHo configurato il Grafana Sidecar, un processo leggero che gira accanto a Grafana e scansiona il cluster alla ricerca di ConfigMap con l\u0026rsquo;etichetta grafana_dashboard: \u0026quot;1\u0026quot;. Quando ne trova una, scarica il JSON della dashboard e lo inietta in Grafana. Questo trasforma il monitoraggio in un sistema puramente dichiarativo. Se domani dovessi reinstallare tutto da zero, le mie dashboard professionali (\u0026ldquo;Nodes Pro\u0026rdquo;, \u0026ldquo;Cluster Health\u0026rdquo;) apparirebbero automaticamente al primo avvio.\nRiflessioni Post-Lab: Cosa abbiamo imparato? # Questa \u0026ldquo;tappa\u0026rdquo; del viaggio nel TazLab è stata una delle più impegnative dal punto di vista del troubleshooting. Cosa significa questo setup per la stabilità a lungo termine?\nResilienza ai Guasti: Ora posso perdere un intero nodo o corrompere il namespace del monitoraggio senza perdere la storia del mio lavoro. Il database PostgreSQL è il mio \u0026ldquo;ancoraggio\u0026rdquo;. Standardizzazione: L\u0026rsquo;uso di privileged namespaces e porte specifiche su MetalLB è ora documentato e codificato, riducendo l\u0026rsquo;entropia del cluster. Scalabilità Mentale: Affrontare questi problemi mi ha costretto a scavare nelle specifiche di Postgres 16 e nei meccanismi interni di Kubernetes (PSA, MetalLB). È questa la vera crescita professionale. In conclusione, l\u0026rsquo;osservabilità non è solo \u0026ldquo;vedere dei grafici\u0026rdquo;. È costruire un sistema che sia affidabile quanto il sistema che deve monitorare.\n","date":"4 marzo 2026","externalUrl":null,"permalink":"/it/posts/enterprise-monitoring-grafana-prometheus-stateless/","section":"Posts","summary":"","title":"Enterprise Monitoring in a Home Lab: La strada (in salita) verso Grafana e Prometheus Stateless","type":"posts"},{"content":"","date":"4 marzo 2026","externalUrl":null,"permalink":"/it/tags/fluxcd/","section":"Tags","summary":"","title":"Fluxcd","type":"tags"},{"content":"","date":"4 marzo 2026","externalUrl":null,"permalink":"/it/tags/grafana/","section":"Tags","summary":"","title":"Grafana","type":"tags"},{"content":"","date":"4 marzo 2026","externalUrl":null,"permalink":"/it/tags/monitoring/","section":"Tags","summary":"","title":"Monitoring","type":"tags"},{"content":"","date":"4 marzo 2026","externalUrl":null,"permalink":"/it/tags/postgresql/","section":"Tags","summary":"","title":"Postgresql","type":"tags"},{"content":"","date":"4 marzo 2026","externalUrl":null,"permalink":"/it/tags/prometheus/","section":"Tags","summary":"","title":"Prometheus","type":"tags"},{"content":" Introduzione: Il Problema della Protezione dei Dashboard # Quando costruisci un\u0026rsquo;infrastruttura Kubernetes moderna, uno dei problemi più critici che emergi rapidamente è la gestione dell\u0026rsquo;accesso ai dashboard operazionali. Nel mio laboratorio TazLab—un cluster Talos Linux su Proxmox con stack completo GitOps — avevo già implementato Grafana per il monitoraggio, pgAdmin per la gestione del database PostgreSQL, e una dashboard informativa (Homepage) per la navigazione. Tutti questi componenti erano accessibili via Traefik Ingress, ma nessuno di essi era protetto da autenticazione. Chiunque potesse raggiungere https://grafana.tazlab.net dal mio laboratorio poteva accedere a dati sensibili di monitoraggio senza inserire credenziali.\nHo deciso che questa situazione violava il principio fondamentale di Zero Trust che guida l\u0026rsquo;intera architettura di Ephemeral Castle. L\u0026rsquo;obiettivo della giornata era dunque ambizioso: implementare un sistema di Single Sign-On (SSO) tramite Google OAuth, dove tutti le dashboard sarebbero state protette dietro un\u0026rsquo;unica porta d\u0026rsquo;ingresso di autenticazione. L\u0026rsquo;utente avrebbe dovuto fare login una sola volta con il proprio account Google, e poi tutti gli accessi successivi ai vari servizi sarebbero stati autorizzati automaticamente, senza ulteriori prompt di password.\nQuesta \u0026ldquo;tappa del viaggio\u0026rdquo; di TazLab rappresenta una svolta significativa: l\u0026rsquo;infrastruttura stava evolvendo da un ambiente semplicemente \u0026ldquo;funzionante\u0026rdquo; a un ambiente \u0026ldquo;enterprise-ready\u0026rdquo;, dove la sicurezza non era un\u0026rsquo;aggiunta ma un principio fondante.\nFase 1: L\u0026rsquo;Architettura OIDC e le Scelte Strategiche # Prima di scrivere il primo manifesto YAML, ho dovuto prendere una serie di decisioni architettoniche che avrebbero definito l\u0026rsquo;intero approccio. Non esisteva una sola strada corretta; ogni scelta implicava trade-off che avrebbero influenzato la stabilità a lungo termine del sistema.\nPerché DEX e Non Keycloak? Una Comparazione Consapevole # La scelta più critica è stata il provider OIDC. Gli standard nel panorama Kubernetes sono due: Keycloak e DEX. Keycloak è un ecosistema completo, estremamente flessibile, supportato da una comunità gigantesca, con un\u0026rsquo;interfaccia di amministrazione ricca e decine di connettori. DEX, invece, è uno strumento minimalista: un provider OIDC Kubernetes-native che legge la propria configurazione da file YAML, persiste i dati tramite CRD (Custom Resource Definition) di Kubernetes stesso, e non ha interfaccia web di amministrazione (tutto è dichiarativo).\nHo scelto DEX per una ragione fondamentale: l\u0026rsquo;allineamento filosofico con la mia infrastruttura. TazLab è costruita completamente attorno a Kubernetes come database dei fatti. Flux CD gestisce lo stato dichiarativo attraverso il controllo versione (Git). Tutti i segreti risiedono in Infisical e vengono sincronizzati tramite External Secrets Operator. Aggiungere Keycloak significava introdurre un nuovo strato di dati—un database separato con il suo ciclo di vita, i suoi backup, le sue dipendenze—che vivrebbe fuori dal paradigma dichiarativo. DEX, al contrario, sfrutta le CRD di Kubernetes per la persistenza: ogni token, ogni sessione di autenticazione, è un oggetto nativo di Kubernetes memorizzato in etcd. Questo significa che i backup automatici di etcd proteggono anche il sistema di autenticazione. Significa che la disaster recovery è coerente con il resto dell\u0026rsquo;infrastruttura.\nLa controindicazione di DEX è la mancanza di un\u0026rsquo;interfaccia web ricca. Se devo modificare il comportamento del provider (aggiungere un nuovo connettore, cambiare la configurazione), devo editare file YAML e committarli in Git, non cliccare in una UI. Inizialmente, questa limitazione sembrava restrittiva. Ma dopo aver implementato il sistema, mi sono reso conto che era un punto di forza: la tracciabilità. Ogni modifica a DEX è un commit Git con un autore, un timestamp, un motivo documentato in un PR. Non esiste \u0026ldquo;l\u0026rsquo;amministratore che ha clickato il pulsante sbagliato\u0026rdquo;.\noauth2-proxy Come Middleware Traefik: Il Pattern ForwardAuth # Una volta scelto DEX come OIDC provider, mi è stato necessario un proxy che intercettasse le richieste HTTP ai miei dashboard, verificasse se l\u0026rsquo;utente era già autenticato con Google, e se no, lo redirigesse al flusso di autenticazione. La soluzione standard nel mondo Kubernetes è oauth2-proxy.\noauth2-proxy è un reverse proxy specializzato nell\u0026rsquo;integrazione OAuth2. Viene tipicamente distribuito come pod in Kubernetes e configurato come un Middleware Traefik nel modello ForwardAuth. In questo pattern architetturale, quando una richiesta arriva a un Ingress Traefik protetto, Traefik non passa direttamente la richiesta all\u0026rsquo;applicazione backend. Invece, invia una richiesta di verifica al servizio oauth2-proxy, chiedendogli: \u0026ldquo;Questo cliente è autenticato?\u0026rdquo; Se oauth2-proxy risponde con HTTP 200, significa \u0026ldquo;sì, è valido\u0026rdquo;, e Traefik procede. Se risponde con 401, Traefik blocca la richiesta e redirige il client al servizio di login.\nDeep-Dive Concettuale: Il Pattern ForwardAuth di Traefik\nIl pattern ForwardAuth è un\u0026rsquo;implementazione del paradigma di \u0026ldquo;external authorization service\u0026rdquo; che viene comunemente usato anche in nginx (tramite auth_request). L\u0026rsquo;idea è elegante dal punto di vista architetturale: la decisione di autenticazione è delegata a un servizio specializzato, il quale rimane completamente disaccoppiato dall\u0026rsquo;applicazione vera. Questo significa che posso proteggere qualsiasi applicazione—Grafana, pgAdmin, una semplice pagina HTML—senza modificarne il codice. L\u0026rsquo;applicazione non ha nemmeno bisogno di \u0026ldquo;sapere\u0026rdquo; che c\u0026rsquo;è un proxy davanti. Dal suo punto di vista, arrivano richieste HTTP come sempre. La differenza è che Traefik ha già verificato l\u0026rsquo;autenticazione tramite il Middleware ForwardAuth, e passa all\u0026rsquo;app alcuni header aggiuntivi (come X-Auth-Request-User) che l\u0026rsquo;app può usare per riconoscere automaticamente l\u0026rsquo;utente loggato.\nQuesto pattern è particolarmente potente quando combinato con la possibilità di Traefik di passare header HTTP verso il servizio di verifica e raccogliere header di risposta. Nel caso di oauth2-proxy, il flusso diventa:\nClient richiede /dashboard su Grafana Traefik intercetta la richiesta e la invia a oauth2-proxy per verifica oauth2-proxy controlla se il client ha il cookie di sessione valido Se sì, risponde 200 e include negli header di risposta il nome utente (es. X-Auth-Request-User: roberto.tazzoli@gmail.com) Traefik passa la richiesta a Grafana, aggiungendo quegli header Grafana legge l\u0026rsquo;header e crea automaticamente una sessione per quell\u0026rsquo;utente Fase 2: L\u0026rsquo;Implementazione Iniziale (La Fiducia Nei Piani) # Con le decisioni architettoniche prese, ho proceduto all\u0026rsquo;implementazione. Ho deciso di strutturare il progetto seguendo le convenzioni già presenti in TazLab:\ninfrastructure/configs/dex/: ExternalSecrets che tirano i segreti Google da Infisical, e i file di configurazione di DEX infrastructure/instances/dex/: Deployment, Service, Ingress, RBAC per DEX infrastructure/auth/: Un nuovo layer dedicato a oauth2-proxy, middleware Traefik, e la configurazione di Flux infrastructure/operators/monitoring/: Aggiornamenti agli ingress di Grafana per applicare il middleware ForwardAuth Ho creato 19 file YAML in totale, circa 1500 righe di manifesti Kubernetes. Ogni componente era dichiarativo, versionato in Git, sincronizzabile da Flux. La teoria era solida. La pratica stava per insegnarmi lezioni umilianti.\nLa Struttura di DEX: CRD Storage e Connettori Google # La configurazione di DEX è un file YAML puro che specifica:\nL\u0026rsquo;issuer (l\u0026rsquo;URL dove DEX è accessibile, es. https://dex.tazlab.net) Lo storage backend (nel mio caso, CRD di Kubernetes) I \u0026ldquo;connettori\u0026rdquo; (i provider di identità, nel mio caso Google OAuth) I \u0026ldquo;static clients\u0026rdquo; (le applicazioni autorizzate a chiedere token, nel mio caso oauth2-proxy) Ecco un snippet semplificato di come ho strutturato il ConfigMap di DEX:\napiVersion: v1 kind: ConfigMap metadata: name: dex-config namespace: dex data: config.yaml: | issuer: https://dex.tazlab.net storage: type: kubernetes config: inCluster: true web: http: 0.0.0.0:5556 allowedOrigins: - https://dex.tazlab.net connectors: - type: google id: google name: Google config: clientID: $GOOGLE_CLIENT_ID clientSecret: $GOOGLE_CLIENT_SECRET redirectURI: https://dex.tazlab.net/callback staticClients: - id: oauth2-proxy secret: $OAUTH2_PROXY_CLIENT_SECRET redirectURIs: - https://auth.tazlab.net/oauth2/callback name: oauth2-proxy Perché External Secrets Operator e Non ConfigMap Diretto? # I segreti Google (clientID, clientSecret) non possono risiedere nel ConfigMap in plaintext—sarebbe una violazione basilare dei principi di sicurezza. Ho deciso di utilizzare External Secrets Operator (ESO) per sincronizzare i segreti da Infisical (la mia cassaforte centralizzata) e renderli disponibili come Kubernetes Secrets. Questo pattern è ormai consolidato in TazLab, quindi la scelta era naturale.\nHo creato un ExternalSecret che tirava DEX_GOOGLE_CLIENT_ID e DEX_GOOGLE_CLIENT_SECRET da Infisical:\napiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: dex-google-secrets namespace: dex spec: refreshInterval: 1h secretStoreRef: kind: ClusterSecretStore name: tazlab-secrets target: name: dex-google-secrets creationPolicy: Owner data: - secretKey: DEX_GOOGLE_CLIENT_ID remoteRef: key: DEX_GOOGLE_CLIENT_ID - secretKey: DEX_GOOGLE_CLIENT_SECRET remoteRef: key: DEX_GOOGLE_CLIENT_SECRET - secretKey: OAUTH2_PROXY_CLIENT_SECRET remoteRef: key: OAUTH2_PROXY_CLIENT_SECRET Il Deployment di DEX montava il Secret e lo iniettava come variabili di ambiente:\napiVersion: apps/v1 kind: Deployment metadata: name: dex namespace: dex spec: replicas: 1 template: spec: containers: - name: dex image: ghcr.io/dexidp/dex:v2.41.1 args: - dex - serve - /etc/dex/cfg/config.yaml env: - name: GOOGLE_CLIENT_ID valueFrom: secretKeyRef: name: dex-google-secrets key: DEX_GOOGLE_CLIENT_ID - name: GOOGLE_CLIENT_SECRET valueFrom: secretKeyRef: name: dex-google-secrets key: DEX_GOOGLE_CLIENT_SECRET Fase 3: Il Primo Errore - L\u0026rsquo;ADMIN_EMAIL Sparito # Dopo il primo git push, ho lanciato un flux reconcile source git flux-system e ho aspettato che Flux sincronizzasse tutto lo stato descritto nei miei manifesti.\nLa riconciliazione ha incontrato un errore inaspettato nella ClusterRoleBinding che doveva assegnare il ruolo tazlab-admin all\u0026rsquo;utente con email ${ADMIN_EMAIL}:\nClusterRoleBinding/tazlab-admin-binding dry-run failed (Invalid): ClusterRoleBinding [...] subjects[0].name: Required value Il campo subjects[0].name era vuoto. Ho controllato il manifesto:\napiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: tazlab-admin-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: tazlab-admin subjects: - kind: User name: ${ADMIN_EMAIL} La variabile ${ADMIN_EMAIL} non era stata sostituita. Ho verificato il ConfigMap cluster-vars nel namespace flux-system—quello dove Flux memorizza le variabili globali usate dai postBuild.substituteFrom:\n$ kubectl get cm cluster-vars -n flux-system -o jsonpath=\u0026#39;{.data}\u0026#39; {\u0026#34;domain\u0026#34;: \u0026#34;tazlab.net\u0026#34;, \u0026#34;cluster_name\u0026#34;: \u0026#34;tazlab-k8s\u0026#34;, \u0026#34;traefik_lb_ip\u0026#34;: \u0026#34;192.168.1.240\u0026#34;} Mancava ADMIN_EMAIL. Qui emergeva un insight architetturale cruciale: il ConfigMap cluster-vars non è gestito da GitOps, ma da Terraform. È creato durante il bootstrap del cluster dal modulo k8s-flux in ephemeral-castle. Non potevo aggiungerlo direttamente in un file YAML di GitOps, perché Flux non lo controllava. Dovevo modificare Terraform.\nHo aperto /workspace/ephemeral-castle/clusters/tazlab-k8s/modules/k8s-flux/main.tf e agigiunto il parametro admin_email:\nvariable \u0026#34;admin_email\u0026#34; { type = string description = \u0026#34;Email dell\u0026#39;admin TazLab — usata da Flux per RBAC e oauth2-proxy allowlist\u0026#34; } # Nel blocco che crea il ConfigMap: data = { domain = var.base_domain cluster_name = var.cluster_name traefik_lb_ip = var.traefik_lb_ip ADMIN_EMAIL = var.admin_email } Poi ho aggiornato clusters/tazlab-k8s/live/gitops/terragrunt.hcl per leggere l\u0026rsquo;email da Infisical e passarla a Terraform:\ninputs = { admin_email = data.infisical_secrets.github.secrets[\u0026#34;ADMIN_EMAIL\u0026#34;].value # ... altri parametri } Ho fatto il push di questi cambiamenti su Terraform, e poi un kubectl patch configmap cluster-vars -n flux-system --type merge -p '{\u0026quot;data\u0026quot;: {\u0026quot;ADMIN_EMAIL\u0026quot;: \u0026quot;roberto.tazzoli@gmail.com\u0026quot;}}' come patch di emergenza per accelerare il test.\nLezione appresa: Quando progetti un\u0026rsquo;infrastruttura con Terraform e GitOps, devi essere consapevole di quale strato \u0026ldquo;possiede\u0026rdquo; quale dato. Terraform crea il foglio bianco iniziale del cluster; GitOps mantiene lo stato dichiarativo dai manifesti. Se una configurazione è generata una volta durante il bootstrap e non cambierà spesso, appartiene a Terraform. Se cambia frequentemente e ha una storia di versioning, appartiene a GitOps. Mescolare i due livelli è il modo migliore per creare confusione operativa.\nFase 4: Il Problema DEX - La Variabile che Non Viene Espansa # Dopo aver risolto l\u0026rsquo;ADMIN_EMAIL, tutto il resto ha iniziato a riconciliare correttamente. I pod di DEX e oauth2-proxy sono partiti. Ho testato il flusso di login navigando a https://grafana.tazlab.net—Traefik mi ha redirigeto a DEX, che mi ha mostrato il pulsante \u0026ldquo;Log in with Google\u0026rdquo;. Ho cliccato, Google mi ha chiesto di autenticarmi\u0026hellip;\nE poi ho ricevuto un errore dal server Google:\nErrore 400: invalid_request flowName=GeneralOAuthFlow - Missing required parameter: client_id Google non stava ricevendo il client_id. Ho controllato i log di DEX per capire cosa stesse accadendo:\n[2026/02/28 08:14:23] [connector.go:123] provider.go: authenticating, error: invalid_request: Missing required parameter: client_id Il problema era silenzioso nel log di DEX. Ho deciso di fare un\u0026rsquo;indagine più profonda. Ho esaminato il config file che DEX stava leggendo dentro il pod:\n$ kubectl exec -it deployment/dex -n dex -- cat /etc/dex/cfg/config.yaml | grep -A 5 \u0026#34;connectors:\u0026#34; connectors: - type: google id: google name: Google config: clientID: \u0026#34;$GOOGLE_CLIENT_ID\u0026#34; Aha! La variabile $GOOGLE_CLIENT_ID era letterale nel file YAML. DEX non stava espandendo le variabili d\u0026rsquo;ambiente dentro il suo file di configurazione. Ho provato a leggere la documentazione di DEX per capire se supportasse la sostituzione di variabili\u0026hellip; e ho scoperto che DEX non fa nessuna espansione di variabili nel ifile di configurazione. DEX è un\u0026rsquo;applicazione Go che legge il file YAML una sola volta all\u0026rsquo;avvio, lo unmarshalla in una struttura dati Go, e lo usa così. Non c\u0026rsquo;è alcun post-processing.\nQuesto era un problema architetturale serio. Non potevo mettere i segreti direttamente nel ConfigMap in plaintext. Ma non potevo nemmeno usare le variabili d\u0026rsquo;ambiente come placeholder nei file YAML e aspettarmi che DEX le espandesse.\nHo considerato alcune soluzioni:\nSed wrapper: Un entrypoint che usa sed per sostituire le variabili nel file YAML prima di lanciare DEX Il flag secretEnv di DEX: DEX ha un campo speciale per il client secret che legge da una variabile d\u0026rsquo;ambiente ESO template engine: Usare External Secrets Operator v2 per renderizzare il file di configurazione completo con i valori veri Ho tentato inizialmente la soluzione #1 (sed wrapper). Ho creato un entrypoint shell:\n#!/bin/sh sed -e \u0026#34;s|\\$GOOGLE_CLIENT_ID|${GOOGLE_CLIENT_ID}|g\u0026#34; \\ -e \u0026#34;s|\\$GOOGLE_CLIENT_SECRET|${GOOGLE_CLIENT_SECRET}|g\u0026#34; \\ /etc/dex/cfg/config.yaml.template \u0026gt; /tmp/config.yaml exec dex serve /tmp/config.yaml Questo non ha funzionato, sed ha prodotto il file con valori vuoti (se le variabili d\u0026rsquo;ambiente non erano definite al momento dell\u0026rsquo;esecuzione), DEX crashava silenziosamente con un errore di parsing YAML.\nHo quindi provato la soluzione #2: usare il campo secretEnv di DEX per il client secret di oauth2-proxy. Nel file di configurazione, posso dire a DEX: \u0026ldquo;Per questo client, il secret non è nel file YAML, ma nella variabile d\u0026rsquo;ambiente\u0026rdquo;. Però questo funzionava solo per il secret del client statico, non per il clientSecret del connettore Google.\nHo deciso di implementare la soluzione #3: ESO template engine v2. Questo è un feature di External Secrets Operator che trasforma il Secret generato tramite un motore Go template. Creo un ExternalSecret che dice a ESO:\n\u0026ldquo;Vai in Infisical, prendi DEX_GOOGLE_CLIENT_ID e DEX_GOOGLE_CLIENT_SECRET, poi renderizza il file di configurazione completo di DEX usando questi valori dentro i template {{ .DEX_GOOGLE_CLIENT_ID }}\u0026rdquo;\napiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: dex-config-rendered namespace: dex spec: refreshInterval: 1h secretStoreRef: kind: ClusterSecretStore name: tazlab-secrets target: name: dex-rendered-config creationPolicy: Owner template: engineVersion: v2 data: config.yaml: | issuer: https://dex.tazlab.net storage: type: kubernetes config: inCluster: true connectors: - type: google id: google name: Google config: clientID: \u0026#34;{{ .DEX_GOOGLE_CLIENT_ID }}\u0026#34; clientSecret: \u0026#34;{{ .DEX_GOOGLE_CLIENT_SECRET }}\u0026#34; redirectURI: https://dex.tazlab.net/callback staticClients: - id: oauth2-proxy secretEnv: OAUTH2_PROXY_CLIENT_SECRET redirectURIs: - https://auth.tazlab.net/oauth2/callback name: oauth2-proxy data: - secretKey: DEX_GOOGLE_CLIENT_ID remoteRef: key: DEX_GOOGLE_CLIENT_ID - secretKey: DEX_GOOGLE_CLIENT_SECRET remoteRef: key: DEX_GOOGLE_CLIENT_SECRET Quando ESO ricrea questo ExternalSecret, passa i segreti dal block data al template engine, che sostituisce {{ .DEX_GOOGLE_CLIENT_ID }} con il valore vero, e genera un Secret con il file di configurazione completamente renderizzato, con i valori veri già dentro.\nHo aggiornato il Deployment di DEX per montare il Secret dex-rendered-config anziché il ConfigMap:\nspec: volumes: - name: config secret: secretName: dex-rendered-config items: - key: config.yaml path: config.yaml Dopo il deploy, ho verificato che il Secret contenesse i valori veri:\n$ kubectl get secret dex-rendered-config -n dex -o jsonpath=\u0026#39;{.data.config\\.yaml}\u0026#39; | base64 -d | grep clientID clientID: \u0026#34;502646366772-9165kme6a67a10m1s8imiv540ltoisp7.apps.googleusercontent.com\u0026#34; Perfetto. DEX stava leggendo il file di configurazione con i valori veri.\nFase 5: Il Redirect Che Non Funzionava # Dopo che DEX iniziò a lavorare correttamente con Google, il flusso di autenticazione proseguì. L\u0026rsquo;utente (me stesso) veniva redirigeto a Google, si autenticava, e poi\u0026hellip;\nFiniva su https://auth.tazlab.net/authenticated con un semplice messaggio: \u0026ldquo;Authenticated\u0026rdquo;. Non veniva redirigeto a Grafana. Dovevo rimettere manualmente https://grafana.tazlab.net nella barra degli indirizzi.\nIl problema era in oauth2-proxy. Quando riceveva il callback da Google, sapeva che l\u0026rsquo;utente era autenticato, ma non sapeva a quale URL ritornare. oauth2-proxy è uno strumento complesso con molte configurazioni, e il bug risiede nel modo in cui gestisce il tracking dell\u0026rsquo;URL di origine dopo il redirect.\nQuando Traefik chiama oauth2-proxy come middleware ForwardAuth, potrebbe non passare l\u0026rsquo;URL originale al servizio di autenticazione. Quindi oauth2-proxy non sa da dove è venuto il client. Aggiunsi il parametro --reverse-proxy=true:\nargs: - --provider=oidc - --oidc-issuer-url=https://dex.tazlab.net - --client-id=oauth2-proxy - --client-secret=$(OAUTH2_PROXY_CLIENT_SECRET) - --cookie-secret=$(OAUTH2_PROXY_COOKIE_SECRET) - --cookie-secure=true - --cookie-domain=.tazlab.net - --redirect-url=https://auth.tazlab.net/oauth2/callback - --upstream=static://200 - --http-address=:4180 - --reverse-proxy=true # \u0026lt;-- Nuovo - --set-xauthrequest=true - --authenticated-emails-file=/etc/oauth2-proxy/allowed-emails.txt Deep-Dive Concettuale: Il Flag --reverse-proxy in oauth2-proxy\nQuando oauth2-proxy è esposto direttamente al client (come in una configurazione reverse proxy tradizionale), riceve gli header HTTP standard: Host, User-Agent, ecc. Ma quando è dietro un reverse proxy come Traefik, il proxy intermedio aggiunge header \u0026ldquo;forwarded\u0026rdquo;: X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Uri. Questi header indicano al proxy downstream quale era la richiesta originale. Il flag --reverse-proxy=true dice a oauth2-proxy: \u0026ldquo;Leggi questi header per ricostruire l\u0026rsquo;URL originale del client\u0026rdquo;. Così, dopo il callback di Google, oauth2-proxy sa di ritornare non a se stesso (auth.tazlab.net), ma all\u0026rsquo;URL originale (grafana.tazlab.net).\nPurtroppo, questo non ha risolto completamente il problema. Ho realizzato che c\u0026rsquo;era un\u0026rsquo;ulteriore complessità: l\u0026rsquo;integrazione fra DEX, oauth2-proxy e Grafana stessa.\nFase 6: Configurare Grafana per Riconoscere l\u0026rsquo;Utente Autenticato # Anche dopo che oauth2-proxy ridirigeva correttamente il client a Grafana, Grafana chiedeva comunque le credenziali. La ragione è che Grafana non stava leggendo l\u0026rsquo;header X-Auth-Request-User che oauth2-proxy passava via Traefik Middleware.\nGrafana ha una sezione di configurazione dedicata ai \u0026ldquo;proxy auth\u0026rdquo;: quando abilitata, Grafana fiduciosamente legge un header HTTP (di default X-WEBAUTH-USER) e assume che l\u0026rsquo;utente fornito dall\u0026rsquo;header sia già autenticato. Questa è una feature di sicurezza comune negli ambienti aziendali dove c\u0026rsquo;è un SSO centralizzato.\nNel mio caso, dovevo dire a Grafana di abilitare questo modulo e di leggere da X-Auth-Request-User (l\u0026rsquo;header che oauth2-proxy genera). Ho modificato il HelmRelease di kube-prometheus-stack:\ngrafana: enabled: true grafana.ini: auth.proxy: enabled: true header_name: X-Auth-Request-User header_property: username auto_sign_up: true sync_ttl: 60 Con questa configurazione:\nenabled: true: Attiva il modulo header_name: X-Auth-Request-User: Leggi da questo header header_property: username: Il valore nell\u0026rsquo;header è il campo username (email, in questo caso) auto_sign_up: true: Se l\u0026rsquo;utente non esiste in Grafana, crealo automaticamente sulla prima login sync_ttl: 60: Ogni 60 secondi, sincronizza i dati dell\u0026rsquo;utente da Infisical (se integrato) Dopo questo cambio, Grafana ha riconosciuto automaticamente l\u0026rsquo;utente roberto.tazzoli@gmail.com e lo ha loggato senza chiedere password.\nFase 7: Il Crash di oauth2-proxy - L\u0026rsquo;Errore Silenzioso # Proprio quando credevo che tutto fosse stabile, ho aggiunto due parametri a oauth2-proxy che avevano il potenziale di migliorare il comportamento:\nargs: # ... parametri precedenti ... - --url=https://auth.tazlab.net - --auth-logging=true Dopo il push, i pod di oauth2-proxy entrarono in CrashLoopBackOff. I log del container mostravano:\nunknown flag: --url Avevo usato un flag che non esisteva nella versione v7.8.1 di oauth2-proxy che stavo usando. Ho controllato la documentazione e la lista dei flag supportati\u0026hellip; e il flag non c\u0026rsquo;era. Era possibile che fosse stato aggiunto in una versione più recente, ma la mia immagine era precedente.\nQuello che seguì fu una sequenza di problemi a cascata: Kubernetes continuava a cercare di far partire il pod con la vecchia configurazione cacheata. Flux è rimasto bloccato in uno stato di \u0026ldquo;Reconciliation in progress\u0026rdquo; per cinque minuti (il timeout dei health check). I pod in CrashLoopBackOff si riavviavano ogni 10 secondi, creando rumore nei log.\nHo riversionato i commit che avevano aggiunto quei flag e ho patchato manualmente il deployment nel cluster per rimuovere i parametri problematici:\nkubectl patch deployment oauth2-proxy -n auth --type json -p \u0026#39;[ { \u0026#34;op\u0026#34;: \u0026#34;replace\u0026#34;, \u0026#34;path\u0026#34;: \u0026#34;/spec/template/spec/containers/0/args\u0026#34;, \u0026#34;value\u0026#34;: [ \u0026#34;--provider=oidc\u0026#34;, \u0026#34;--oidc-issuer-url=https://dex.tazlab.net\u0026#34;, \u0026#34;--client-id=oauth2-proxy\u0026#34;, \u0026#34;--client-secret=$(OAUTH2_PROXY_CLIENT_SECRET)\u0026#34;, \u0026#34;--cookie-secret=$(OAUTH2_PROXY_COOKIE_SECRET)\u0026#34;, \u0026#34;--cookie-secure=true\u0026#34;, \u0026#34;--cookie-domain=.tazlab.net\u0026#34;, \u0026#34;--whitelist-domain=.tazlab.net\u0026#34;, \u0026#34;--redirect-url=https://auth.tazlab.net/oauth2/callback\u0026#34;, \u0026#34;--upstream=static://200\u0026#34;, \u0026#34;--http-address=:4180\u0026#34;, \u0026#34;--skip-provider-button=true\u0026#34;, \u0026#34;--set-xauthrequest=true\u0026#34;, \u0026#34;--reverse-proxy=true\u0026#34;, \u0026#34;--authenticated-emails-file=/etc/oauth2-proxy/allowed-emails.txt\u0026#34;, \u0026#34;--silence-ping-logging=true\u0026#34; ] } ]\u0026#39; Dopo alcuni minuti, un nuovo pod è partito con la configurazione corretta e il sistema si è stabilizzato.\nLezione critica: Quando scrivi parametri di configurazione per applicazioni che ottengono da immagini pubbliche, verifica sempre la documentazione della versione specifica che stai usando. Un flag potrebbe non esistere nella versione che stai usando, causando crash silenziosi. La soluzione è usare version pinning rigoroso e documentare quale versione supporta quali feature.\nFase 8: Flux Rimane Bloccato - Il Timeout dei Health Check # Quando il pod di oauth2-proxy crashava continuamente, Flux rimase bloccato in uno stato patologico. La kustomization infrastructure-auth non riusciva a completare la riconciliazione perché il health check attendeva che i pod diventassero ready. Ma i pod non diventavano mai ready a causa del crash.\nFlux ha un timeout di health check di 5 minuti. Dopo 5 minuti, marca la riconciliazione come fallita, ma rimane in uno stato di \u0026ldquo;Reconciliation in progress\u0026rdquo; aspettando il prossimo tentativo automatico (che è programmato per un\u0026rsquo;ora dopo, a meno che non lo forzi manualmente).\nHo dovuto forare il processo:\nHo revertito il commit che conteneva i flag problematici Ho forzato Flux a riconoscere il nuovo commit: flux reconcile source git flux-system Ho cancellato forzatamente tutti i pod vecchi: kubectl delete pods -n auth --all --grace-period=0 --force Ho patchato il deployment manualmente per far partire il pod con la configurazione corretta Ho aspettato che il pod stabilizzasse Flux ha riconosciuto infine che tutto era in ordine e ha completato la riconciliazione Riflessioni Finali: Cosa Abbiamo Costruito # Dopo questa \u0026ldquo;tappa del viaggio\u0026rdquo;, TazLab ha ora un sistema di autenticazione enterprise-ready che combina:\nDEX come provider OIDC Kubernetes-native, con CRD storage e integrazione Google OAuth oauth2-proxy come middleware Traefik, con ForwardAuth pattern per intercettazione trasparente External Secrets Operator con template engine per renderizzare la configurazione di DEX con i segreti veri da Infisical Kubernetes RBAC con ClusterRole e ClusterRoleBinding che legge l\u0026rsquo;email dell\u0026rsquo;admin da Flux Grafana configurato per auth.proxy, riconoscendo automaticamente gli utenti via header X-Auth-Request-User Il flusso completo funziona così:\nUtente navigua a https://grafana.tazlab.net Traefik ForwardAuth chiama oauth2-proxy oauth2-proxy vede che non c\u0026rsquo;è un cookie di sessione valido oauth2-proxy ridirige il client a https://dex.tazlab.net/auth DEX mostra il pulsante \u0026ldquo;Login with Google\u0026rdquo; Utente si autentica con Google Google redirige indietro a https://auth.tazlab.net/oauth2/callback oauth2-proxy elabora il callback, genera un cookie di sessione oauth2-proxy ridirige il client a https://grafana.tazlab.net (l\u0026rsquo;URL originale ricostruito dai header X-Forwarded-*) Traefik ForwardAuth chiama di nuovo oauth2-proxy, che rispondecon 200 e header X-Auth-Request-User: roberto.tazzoli@gmail.com Traefik passa la richiesta a Grafana, aggiungendo l\u0026rsquo;header Grafana legge l\u0026rsquo;header, crea automaticamente una sessione per quell\u0026rsquo;utente Grafana risponde con la dashboard L\u0026rsquo;intero sistema è dichiarativo, versionato in Git, recuperabile da backup di etcd, integrabile con Flux per la disaster recovery. Non c\u0026rsquo;è \u0026ldquo;stato esterno\u0026rdquo; che vive fuori Kubernetes. È la realizzazione concreta del principio Zero Trust che guida Ephemeral Castle.\nI problemi incontrati—la variabile non espansa, il flag inesistente, il timeout di Flux—sono stati tutti risolti grazie a un approccio sistematico di debugging: identificare il sintomo, costruire ipotesi, testare, iterare. E soprattutto, documentare il processo in modo che chi legga questo diario possa imparare dalle mie esperienze senza ripetere gli stessi errori.\nQuesto laboratorio è ora pronto per il prossimo capitolo della sua evoluzione: l\u0026rsquo;integrazione di nuovi provider di identità, l\u0026rsquo;implementazione di RBAC granulare, la sincronizzazione di attributi utente da directory aziendali. Ma per adesso, il sistema di autenticazione è stabile, sicuro, e pronto per la produzione.\n","date":"28 febbraio 2026","externalUrl":null,"permalink":"/it/posts/dex-oauth2-kubernetes-oidc-journey/","section":"Posts","summary":"","title":"Da Zero a OIDC: Il Diario di Viaggio dell'Autenticazione Zero Trust nel Nostro Cluster Kubernetes","type":"posts"},{"content":"","date":"28 febbraio 2026","externalUrl":null,"permalink":"/it/tags/dex/","section":"Tags","summary":"","title":"Dex","type":"tags"},{"content":"","date":"28 febbraio 2026","externalUrl":null,"permalink":"/it/tags/external-secrets/","section":"Tags","summary":"","title":"External-Secrets","type":"tags"},{"content":"","date":"28 febbraio 2026","externalUrl":null,"permalink":"/it/tags/oauth2/","section":"Tags","summary":"","title":"Oauth2","type":"tags"},{"content":"","date":"28 febbraio 2026","externalUrl":null,"permalink":"/it/tags/oidc/","section":"Tags","summary":"","title":"Oidc","type":"tags"},{"content":"","date":"28 febbraio 2026","externalUrl":null,"permalink":"/it/tags/traefik/","section":"Tags","summary":"","title":"Traefik","type":"tags"},{"content":"","date":"25 febbraio 2026","externalUrl":null,"permalink":"/it/categories/devsecops/","section":"Categories","summary":"","title":"DevSecOps","type":"categories"},{"content":" Phoenix Protocol V2: Sicurezza Enterprise, Parallelismo e il Traguardo degli 8 Minuti # Se il primo capitolo del Phoenix Protocol riguardava la validazione del dato e la sua immortalità attraverso il ripristino da S3, questa seconda tappa del viaggio nel Castello Effimero affronta una sfida ancora più ambiziosa: la perfezione del processo. Non basta che il cluster rinasca; deve farlo in modo deterministico, senza esitazioni umane e con un profilo di sicurezza che non ammette compromessi, nemmeno durante i pochi minuti in cui l\u0026rsquo;infrastruttura è \u0026ldquo;nuda\u0026rdquo; sotto il fuoco del bootstrap.\nOggi ho deciso di spingere il limite oltre la soglia psicologica dei dieci minuti. Per farlo, ho dovuto ripensare radicalmente il modo in cui il cluster \u0026ldquo;reclama\u0026rdquo; la propria identità e come i diversi layer si incastrano tra loro. Questo non è solo un esercizio di velocità, ma una ricerca di efficienza ingegneristica dove ogni secondo risparmiato è un\u0026rsquo;incertezza rimossa.\nIl Mindset: La Sicurezza come Cemento, non come Vernice # Spesso, nei progetti HomeLab o nelle infrastrutture in fase di sviluppo, si tende a \u0026ldquo;far funzionare le cose\u0026rdquo; e poi, solo in un secondo momento, a blindarle. Ho deciso che questo approccio è intrinsecamente fallace. In un\u0026rsquo;architettura Zero-Knowledge, la sicurezza deve essere il cemento delle fondamenta. Se un segreto tocca il disco durante il bootstrap, quel disco è compromesso per sempre nella mia visione.\nL\u0026rsquo;obiettivo della sessione è stato duplice: eliminare le dipendenze esterne instabili e garantire che nessun segreto \u0026ldquo;viaggi\u0026rdquo; in chiaro o risieda in modo persistente sull\u0026rsquo;host che orchestra la rinascita.\nFase 1: Lo Spostamento del \u0026ldquo;Root of Trust\u0026rdquo; (Addio GITHUB_TOKEN) # Uno dei rischi latenti nelle versioni precedenti era la presenza del GITHUB_TOKEN nelle variabili d\u0026rsquo;ambiente dell\u0026rsquo;host durante l\u0026rsquo;esecuzione di Terragrunt. Sebbene il token fosse iniettato in RAM, la sua esistenza nel guscio bash rappresentava un punto di attacco.\nIl Ragionamento: Perché l\u0026rsquo;Internalizzazione dei Segreti? # Ho deciso di spostare la responsabilità del recupero dell\u0026rsquo;identità all\u0026rsquo;interno del cluster stesso. Invece di \u0026ldquo;consegnare\u0026rdquo; il token a Flux CD durante l\u0026rsquo;installazione, ho configurato il sistema affinché sia il cluster, appena nato, a \u0026ldquo;reclamare\u0026rdquo; il proprio accesso al codice.\nL\u0026rsquo;alternativa sarebbe stata continuare a passare il token via variabile d\u0026rsquo;ambiente, ma questo avrebbe mantenuto il segreto esposto ai log di sistema dell\u0026rsquo;host e a potenziali dump della memoria dei processi figli. Usando l\u0026rsquo;External Secrets Operator (ESO) e una Machine Identity di Infisical, il cluster diventa autonomo.\nDeep-Dive: Machine Identity # Una Machine Identity è un\u0026rsquo;entità di sicurezza progettata per sistemi automatizzati. A differenza di un token generato da un utente umano, essa è legata a un ruolo specifico con permessi granulari (Least Privilege) e può essere revocata o ruotata senza impattare le utenze reali. È il cuore del modello \u0026ldquo;Trust no one, verify internal identity\u0026rdquo;.\nImplementazione Tecnica # Ho modificato il layer engine affinché prepari il terreno per Flux prima ancora che Flux venga installato. Il trucco risiede in un loop di attesa intelligente:\n# modules/k8s-engine/main.tf # 1. Creazione del namespace per Flux in anticipo resource \u0026#34;kubernetes_namespace_v1\u0026#34; \u0026#34;flux_system\u0026#34; { metadata { name = \u0026#34;flux-system\u0026#34; } } # 2. Iniezione della Machine Identity per Infisical resource \u0026#34;kubernetes_secret_v1\u0026#34; \u0026#34;infisical_machine_identity\u0026#34; { metadata { name = \u0026#34;infisical-machine-identity\u0026#34; namespace = kubernetes_namespace_v1.external_secrets.metadata[0].name } data = { clientId = var.infisical_client_id clientSecret = var.infisical_client_secret } } # 3. ExternalSecret che scarica il token GitHub resource \u0026#34;kubectl_manifest\u0026#34; \u0026#34;github_token_external_secret\u0026#34; { yaml_body = \u0026lt;\u0026lt;YAML apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: github-api-token namespace: flux-system spec: refreshInterval: 1h secretStoreRef: kind: ClusterSecretStore name: tazlab-secrets target: name: flux-system # Il nome che Flux si aspetta per il suo segreto di boot data: - secretKey: password remoteRef: key: GITHUB_TOKEN YAML depends_on = [helm_release.external_secrets] } # 4. Il \u0026#34;Gancio\u0026#34; di sincronizzazione resource \u0026#34;null_resource\u0026#34; \u0026#34;wait_for_github_token\u0026#34; { provisioner \u0026#34;local-exec\u0026#34; { command = \u0026#34;kubectl wait --for=condition=Ready externalsecret/github-api-token -n flux-system --timeout=60s\u0026#34; } depends_on = [kubectl_manifest.github_token_external_secret] } Fase 2: Segreti Effimeri e la Guerra ai Processi Zombie # Un problema tecnico ricorrente durante i test è stato il blocco dello script create.sh. Invocando ogni comando tramite infisical run, i processi Terragrunt diventavano spesso \u0026lt;defunct\u0026gt; (zombie).\nL\u0026rsquo;Investigazione: L\u0026rsquo;illusione dell\u0026rsquo;automazione esterna # Ho osservato che in sessioni non interattive, il wrapper della CLI di Infisical faticava a gestire correttamente i segnali di uscita dei processi figli. Il risultato era un bootstrap che si \u0026ldquo;congelava\u0026rdquo; senza produrre log, costringendomi a intervenire manualmente.\nHo deciso di eliminare il wrapper. La nuova strategia, battezzata Vault-Native, prevede l\u0026rsquo;estrazione dei segreti dal vault RAM del TazPod (/home/tazpod/secrets) una sola volta all\u0026rsquo;inizio dello script.\nIl Ragionamento: Perché i file in RAM? # I file in una directory montata come tmpfs (RAM) non toccano mai i piatti del disco. Sono protetti dalla cifratura del TazPod e spariscono istantaneamente allo spegnimento o allo smontaggio del vault. Questo mi permette di avere la velocità di un file locale con la sicurezza di un segreto cloud.\n# create.sh - Nuova logica di risoluzione resolve() { local var_name=$1 local vault_file=\u0026#34;/home/tazpod/secrets/${2:-$1}\u0026#34; if [[ -f \u0026#34;$vault_file\u0026#34; ]]; then export \u0026#34;$var_name\u0026#34;=$(cat \u0026#34;$vault_file\u0026#34; | tr -d \u0026#34;\u0026#39;\u0026#34; \u0026#34;) else # Fallback se il segreto è già in env ma punta a un file local val=\u0026#34;${!var_name}\u0026#34; [[ -f \u0026#34;$val\u0026#34; ]] \u0026amp;\u0026amp; export \u0026#34;$var_name\u0026#34;=$(cat \u0026#34;$val\u0026#34; | tr -d \u0026#34;\u0026#39;\u0026#34; \u0026#34;) fi } resolve \u0026#34;PROXMOX_TOKEN_ID\u0026#34; \u0026#34;proxmox-token-id\u0026#34; resolve \u0026#34;GITHUB_TOKEN\u0026#34; \u0026#34;github-token\u0026#34; Fase 3: Ingegneria del Parallelismo (Il \u0026ldquo;Turbo Flow\u0026rdquo;) # Il bootstrap sequenziale è il nemico della velocità. Nella versione V1, i layer nascevano uno dopo l\u0026rsquo;altro: secrets -\u0026gt; platform -\u0026gt; engine -\u0026gt; networking -\u0026gt; storage -\u0026gt; gitops.\nL\u0026rsquo;Analisi del Collo di Bottiglia # Ho notato che mentre MetalLB (Networking) negoziava gli IP, Flux (GitOps) e Longhorn (Storage) stavano semplicemente \u0026ldquo;guardando\u0026rdquo;. Non c\u0026rsquo;è un motivo tecnico per cui lo storage debba aspettare che il LoadBalancer sia pronto; entrambi hanno bisogno solo che l\u0026rsquo;API Server del cluster sia vivo.\nLa Soluzione: Parallelismo spinto # Ho slegato le dipendenze in Terragrunt e modificato l\u0026rsquo;orchestratore per lanciare i tre layer pesanti simultaneamente.\n# create.sh - Turbo Acceleration echo \u0026#34;🚀 [TURBO] Launching Networking, GitOps, and Storage in PARALLEL...\u0026#34; ( cd \u0026#34;$LIVE_DIR/networking\u0026#34; \u0026amp;\u0026amp; $TG apply --auto-approve ) \u0026amp; PID_NET=$! ( cd \u0026#34;$LIVE_DIR/gitops\u0026#34; \u0026amp;\u0026amp; $TG apply --auto-approve ) \u0026amp; PID_GITOPS=$! ( cd \u0026#34;$LIVE_DIR/storage\u0026#34; \u0026amp;\u0026amp; $TG apply --auto-approve ) \u0026amp; PID_STORAGE=$! wait $PID_NET $PID_GITOPS $PID_STORAGE Questo cambiamento ha ridotto il tempo di \u0026ldquo;ferro\u0026rdquo; di oltre il 30%. Ma la vera sfida era gestire il caos che questo parallelismo introduceva in Kubernetes.\nFase 4: La Trappola dei Percorsi Flux e la Scomposizione Granulare # Nel tentativo di rendere tutto più veloce, ho deciso di spezzare il monolite degli operatori di Flux. Invece di un unico blocco infrastructure-operators, ho creato tre unità: core (Traefik/Cert-Manager), data (Postgres) e namespaces.\nLo \u0026ldquo;Struggle\u0026rdquo;: Not a Directory # Dopo il push, Flux è andato in errore: kustomization.yaml: not a directory. L\u0026rsquo;analisi del fallimento è stata immediata: Kustomize richiede che ogni risorsa sia una directory contenente un indice. Spostando i file, avevo rotto i riferimenti relativi. Ho dovuto ricostruire la struttura ad albero:\ninfrastructure/operators/ ├── core/ │ └── kustomization.yaml (con ../cert-manager) ├── data/ │ └── kustomization.yaml (con ../postgres-operator) └── namespaces/ └── kustomization.yaml Questo mi ha insegnato che la velocità richiede ordine. La granularità non deve mai sacrificare la struttura logica del repository.\nFase 5: Resilienza Asincrona e il \u0026ldquo;Fast-Track\u0026rdquo; del Blog # L\u0026rsquo;ultimo ostacolo era il tempo di attesa delle applicazioni. Perché il Blog Hugo, una semplice immagine Nginx con file statici, doveva aspettare il restore di un database da 10GB?\nLa Soluzione: InitContainers e RBAC # Ho implementato una \u0026ldquo;corsia preferenziale\u0026rdquo;. Ho slegato il Blog (apps-static) da ogni dipendenza pesante. Per le app che invece hanno bisogno del database (Mnemosyne, PGAdmin), ho introdotto un InitContainer.\nDeep-Dive: InitContainers # Un InitContainer è un container specializzato che viene eseguito prima dei container dell\u0026rsquo;applicazione in un Pod. Deve completarsi con successo prima che il container principale possa partire. È lo strumento perfetto per gestire le dipendenze asincrone.\nInvece di far fallire il Pod con un CreateContainerConfigError (perché il segreto della password non esiste ancora), l\u0026rsquo;InitContainer interroga l\u0026rsquo;API di Kubernetes:\n# apps/base/mnemosyne-mcp/deployment.yaml initContainers: - name: wait-for-db-secret image: bitnami/kubectl:latest command: - /bin/sh - -c - | until kubectl get secret tazlab-db-pguser-mnemosyne; do echo \u0026#34;waiting for database user secret...\u0026#34; sleep 5 done Questo richiede un ServiceAccount con permessi minimi di lettura (get, list) sui segreti, configurato tramite un apposito file rbac.yaml. Il risultato è un cluster che \u0026ldquo;converge\u0026rdquo; in modo organico: le parti leggere salgono subito, le parti pesanti si auto-configurano non appena i dati sono pronti.\nRisultato Finale: 8 Minuti e 43 Secondi # La validazione finale ha prodotto una telemetria impressionante. Siamo passati dagli 11:38 ai 8:43 per avere il Blog online e sicuro.\nLayer Tempo Stato Secrets (RAM) 10s Ottimizzato Platform (Iron) 1m 53s Stabile Parallel Layers 1m 56s TURBO GitOps Fast-Track 1m 31s RECORD Totale: 8 minuti e 43 secondi.\nDopo altri 4 minuti, anche il database e l\u0026rsquo;MCP server erano pronti, completando l\u0026rsquo;intero stack in meno di 13 minuti totali, includendo il restore dei dati da S3.\nRiflessioni Post-Lab: La Bellezza del Determinismo # Questo setup non è solo \u0026ldquo;veloce\u0026rdquo;. È deterministico. La rimozione di wrapper instabili, la gestione intelligente delle attese e la scomposizione dei componenti hanno trasformato il bootstrap da una sequenza di speranze in un protocollo ingegneristico.\nCosa ho imparato oggi: # Meno è Meglio: Rimuovere tool intermedi (come Infisical CLI in esecuzione costante) riduce la superficie d\u0026rsquo;attacco e i punti di fallimento. L\u0026rsquo;Asincronia è Forza: Non costringere il cluster a essere un monolite. Lascia che ogni componente gestisca la propria pazienza. La Sicurezza accelera: Implementare pratiche enterprise (Machine Identity, RBAC, RAM Vault) ha reso lo script più pulito e, di conseguenza, più veloce da eseguire e facile da debuggare. L\u0026rsquo;infrastruttura di TazLab ha raggiunto una nuova soglia di maturità tecnica. Il protocollo di rinascita non è più soltanto un meccanismo di ripristino, ma un sistema ingegneristico ottimizzato per garantire resilienza, sicurezza e precisione assoluta in ogni fase del ciclo di vita del cluster.\nCronaca Tecnica a cura di Taz - HomeLab DevOps \u0026amp; Architect\n","date":"25 febbraio 2026","externalUrl":null,"permalink":"/it/posts/phoenix-protocol-v2-turbo-rebirth/","section":"Posts","summary":"","title":"Phoenix Protocol V2: Sicurezza Enterprise, Parallelismo e il Traguardo degli 8 Minuti","type":"posts"},{"content":"","date":"25 febbraio 2026","externalUrl":null,"permalink":"/it/tags/reliability/","section":"Tags","summary":"","title":"Reliability","type":"tags"},{"content":"","date":"22 febbraio 2026","externalUrl":null,"permalink":"/it/tags/go/","section":"Tags","summary":"","title":"Go","type":"tags"},{"content":" Introduzione: Il Paradosso dell\u0026rsquo;Effimero # In un ecosistema nomadico e \u0026ldquo;Zero Trust\u0026rdquo; come quello di TazLab, l\u0026rsquo;ambiente di sviluppo (TazPod) è per sua natura effimero. Alla chiusura del container, ogni traccia dell\u0026rsquo;attività svanisce, eccezion fatta per i dati salvati nel vault cifrato. Questa volatilità, sebbene eccellente per la sicurezza e la pulizia del sistema, introduce un problema fondamentale: l\u0026rsquo;amnesia dell\u0026rsquo;agente AI. Ogni nuova sessione è un foglio bianco, una tabula rasa in cui l\u0026rsquo;intelligenza artificiale non ha memoria delle decisioni architettoniche prese ieri, dei bug risolti con fatica o delle direzioni strategiche del progetto.\nHo deciso che TazLab doveva avere una memoria semantica a lungo termine, una \u0026ldquo;coscienza tecnica\u0026rdquo; residente nell\u0026rsquo;infrastruttura stessa. Questo progetto ha preso il nome di Mnemosyne. L\u0026rsquo;obiettivo della giornata era ambizioso: abbandonare i bridge Python instabili e implementare un server nativo basato sul Model Context Protocol (MCP), integrato direttamente nel Gemini CLI, per permettere all\u0026rsquo;AI di consultare il proprio passato tecnico in modo fluido e sovrano.\nFase 1: Il Miraggio del Cloud e il Ritorno alla Sovranità # Inizialmente, la mia strategia per Mnemosyne si basava su Google Cloud AlloyDB. L\u0026rsquo;idea di delegare la persistenza vettoriale a un servizio gestito \u0026ldquo;Enterprise\u0026rdquo; sembrava la mossa più sicura e performante. AlloyDB, con la sua estensione pgvector, offriva una potenza di calcolo enorme per le ricerche semantiche.\nDeep-Dive Concettuale: AlloyDB e pgvector AlloyDB è un database PostgreSQL-compatibile di Google Cloud, ottimizzato per carichi di lavoro intensivi. È un servizio VPC-native, il che significa che per ragioni di sicurezza non espone normalmente un IP pubblico, ma richiede una connessione privata all\u0026rsquo;interno del cloud di Google. pgvector è l\u0026rsquo;estensione che permette di memorizzare gli \u0026ldquo;embeddings\u0026rdquo; (vettori numerici che rappresentano il significato del testo) e di eseguire ricerche di similitudine tramite l\u0026rsquo;operatore di distanza del coseno (\u0026lt;=\u0026gt;).\nTuttavia, mi sono scontrato rapidamente con la realtà operativa. Per accedere ad AlloyDB dal TazPod in mobilità, ho dovuto configurare l\u0026rsquo;AlloyDB Auth Proxy, un binario che crea un tunnel sicuro verso GCP. All\u0026rsquo;interno di un container Docker, questo proxy creava processi zombie e soffriva di latenze imprevedibili. Inoltre, il firewall di GCP richiedeva lo sblocco dinamico degli IP tramite script (memory-gate), creando un attrito costante che tradiva la natura agile del laboratorio. Ogni volta che cambiavo connessione (passando dal Wi-Fi di casa alla rete mobile), la mia memoria semantica diventava irraggiungibile finché non aggiornavo manualmente le regole di rete.\nHo deciso quindi di cambiare rotta: la vera sovranità digitale richiede che i dati risiedano sul mio hardware. Ho migrato Mnemosyne su un\u0026rsquo;istanza PostgreSQL locale ospitata nel mio cluster Kubernetes (Proxmox/Talos), utilizzando il Postgres Operator per la gestione del ciclo di vita. Questa scelta non ha solo azzerato i costi cloud, ma ha reso la memoria parte integrante del \u0026ldquo;ferro\u0026rdquo; di TazLab, rendendola accessibile in modo trasparente tramite la VPN Wireguard integrata nel TazPod.\nFase 2: Genesi di un Server Nativo in Go # Per collegare il Gemini CLI al database Postgres, avevo bisogno di un ponte che parlasse il linguaggio MCP. Inizialmente usavo uno script Python che fungeva da bridge, ma la latenza di avvio dell\u0026rsquo;interprete e la fragilità delle dipendenze mi hanno spinto verso una soluzione più professionale: un server scritto in Go.\nHo scelto Go per la sua capacità di generare binari statici minuscoli, perfetti per le immagini Distroless di Google. Una immagine Distroless non contiene una shell o un package manager, riducendo drasticamente la superficie di attacco del pod in Kubernetes. Il server doveva essere ibrido per supportare due scenari d\u0026rsquo;uso:\nStdio Transport: Per lo sviluppo locale rapido, dove il CLI lancia il binario e comunica tramite standard input/output. SSE Transport (Server-Sent Events): Per la produzione, dove il server espone un endpoint HTTP nel cluster e il CLI si connette come client remoto tramite un LoadBalancer MetalLB. Deep-Dive Concettuale: Stdio vs SSE Il trasporto Stdio è il modo più semplice di far comunicare due processi sullo stesso host: i messaggi JSON-RPC passano per i file descriptor di sistema. È estremamente veloce ma limitato alla macchina locale. Il trasporto SSE, invece, è un protocollo unidirezionale su HTTP che permette al server di inviare \u0026ldquo;eventi\u0026rdquo; al client. Nel protocollo MCP, SSE viene usato per mantenere aperto un canale di risposta asincrono dal server verso l\u0026rsquo;AI, permettendo integrazioni multi-utente e distribuite.\nFase 3: The Trail of Failures (La sezione dei fallimenti) # Il passaggio a un server nativo non è stato privo di ostacoli. Anzi, mi sono scontrato con una serie di bug che hanno richiesto un\u0026rsquo;indagine quasi forense.\nIl Bug dell\u0026rsquo;Apice Mortale (Errore 400) # Dopo il primo deploy, ogni ricerca semantica restituiva un laconico embedding API returned status 400. Ho controllato i log del server, ma il corpo dell\u0026rsquo;errore di Google non veniva visualizzato. Ho sospettato di tutto: dal modello di embedding (gemini-embedding-001) al formato del JSON.\nDopo aver implementato un logging più aggressivo che catturava il corpo della risposta HTTP, ho scoperto l\u0026rsquo;assurda verità: il file dei segreti nel TazPod (/home/tazpod/secrets/gemini-api-key) conteneva la chiave racchiusa tra apici singoli ('AIzaSy...'). Questi apici erano stati inclusi per errore durante una operazione di copia-incolla. Le API di Google ricevevano l\u0026rsquo;apice come parte della chiave, invalidandola. Ho risolto pulendo fisicamente il file con sed e aggiungendo una funzione di sanificazione nel codice Go per rendere il server resiliente a errori umani simili:\n// Pulizia aggressiva della chiave (rimuove apici e spazi) apiKey = strings.Trim(strings.TrimSpace(apiKey), \u0026#34;\u0026#34;\u0026#39;\u0026#34;) Il Silenzio è d\u0026rsquo;Oro (Stdio Discovery Failure) # Un altro comportamento inaspettato si è verificato all\u0026rsquo;avvio del Gemini CLI. Nonostante il server fosse configurato correttamente nel file settings.json, il CLI riportava No tools found on the server.\nIndagando sui log di debug, ho capito che il protocollo Stdio è estremamente fragile: qualsiasi carattere stampato su stdout che non faccia parte del JSON-RPC rompe la comunicazione. Il mio server stampava dei log di benvenuto tramite fmt.Printf. Questi log sporcavano lo stream, facendo fallire il parser JSON del client Gemini CLI. Ho dovuto rendere il server totalmente silenzioso in modalità Stdio, reindirizzando ogni log diagnostico su stderr.\n// Prima (SBAGLIATO): fmt.Printf(\u0026#34;🚀 Server starting...\u0026#34;) // Dopo (CORRETTO): fmt.Fprintf(os.Stderr, \u0026#34;🚀 Server starting...\u0026#34;) Fase 4: Arrendersi agli Standard (Refactoring SDK) # Dopo ore passate a scrivere a mano la gestione dei messaggi JSON-RPC e dei canali SSE, ho dovuto ammettere un errore di orgoglio: reinventare il protocollo MCP da zero era complesso e incline ai bug di concorrenza. Ad esempio, il mio server perdeva messaggi se il client apriva più sessioni simultanee con lo stesso ID.\nHo deciso di rifattorizzare tutto usando l\u0026rsquo;SDK ufficiale della community: github.com/mark3labs/mcp-go. Questo ha significato riscrivere l\u0026rsquo;intero gestore dei tool, ma ha portato benefici immediati in termini di stabilità. L\u0026rsquo;SDK gestisce nativamente il \u0026ldquo;flushing\u0026rdquo; dei dati SSE, garantendo che i messaggi non rimangano bloccati nei buffer del server.\nTuttavia, anche qui la sfida non è mancata. Durante la build automatica su GitHub Actions, l\u0026rsquo;immagine prodotta continuava a mostrare i log del vecchio codice. Dopo aver controllato ogni riga, ho individuato un problema di Module Naming. Il modulo Go era denominato tazlab/mnemosyne-mcp-server, ma il repository reale su GitHub era github.com/tazzo/.... Go, durante la build in cloud, non riuscendo a risolvere i pacchetti interni come file locali, scaricava versioni vecchie del codice dai branch remoti invece di usare quelli appena committati. Ho corretto la struttura del modulo per allinearla al percorso GitHub reale, forzando una build pulita.\nFase 5: Il Deadlock GitOps (Quando Flux mente) # L\u0026rsquo;ultimo scoglio è stato il deploy nel cluster. Nonostante i commit fossero corretti e la build GHA fosse passata, il pod continuava a girare con la vecchia immagine v14. Flux CD riportava Applied revision, ma lo stato live del cluster era congelato.\nDeep-Dive Concettuale: GitOps e Flux CD La filosofia GitOps prevede che il repository Git sia l\u0026rsquo;unica \u0026ldquo;fonte di verità\u0026rdquo;. Flux CD monitora Git e applica i cambiamenti al cluster. Se però una risorsa fallisce la validazione di Kustomize, Flux si blocca per evitare di corrompere lo stato del cluster.\nHo indagato con flux get kustomizations e ho scoperto un Deadlock di Dipendenze. La kustomization apps (che gestisce Mnemosyne) era bloccata perché dipendeva da infrastructure-configs, che a sua volta era in errore a causa di uno YAML malformato. Involontariamente, avevo introdotto un errore di indentazione nel blocco env del manifesto di Mnemosyne durante un rebase Git concitato. Questo errore impediva al controller di Flux di generare i nuovi manifesti, lasciando in esecuzione la vecchia versione v14.\nHo risolto il deadlock riscrivendo il manifesto in modo pulito e forzando una riconciliazione a cascata di tutta la catena:\nexport KUBECONFIG=\u0026#34;/path/to/kubeconfig\u0026#34; # Sblocco della catena di dipendenze flux reconcile kustomization flux-system --with-source flux reconcile kustomization apps --with-source Fase 6: Stato Finale: \u0026ldquo;1 MCP Caricato\u0026rdquo; # Dopo aver risolto l\u0026rsquo;errore di indentazione e aver forzato Kubernetes a scaricare l\u0026rsquo;immagine fresca con la policy imagePullPolicy: Always, il momento della verità è arrivato.\nLanciando il comando gemini, il CLI ha mostrato finalmente la scritta: \u0026ldquo;1 MCP caricato\u0026rdquo;. Mnemosyne era vivo. Ho testato il tool list_memories e ho visto apparire i miei ricordi tecnici degli ultimi mesi, recuperati dal database Postgres locale tramite il protocollo SSE.\nSnippet finale del server MCP (Go SDK):\nfunc (s *Server) registerTools() { // Tool per la ricerca semantica retrieve := mcp.NewTool(\u0026#34;retrieve_memories\u0026#34;, mcp.WithDescription(\u0026#34;Search semantic memory\u0026#34;)) retrieve.InputSchema = mcp.ToolInputSchema{ Type: \u0026#34;object\u0026#34;, Properties: map[string]any{\u0026#34;query\u0026#34;: map[string]any{\u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;}}, Required: []string{\u0026#34;query\u0026#34;}, } s.mcp.AddTool(retrieve, s.handleRetrieve) } Riflessioni post-lab: Verso una Conoscenza Resiliente # Questa sessione di lavoro è stata una vera e propria maratona tecnica di oltre 4 ore. Ho imparato che la semplicità dell\u0026rsquo;architettura (tornare a Postgres locale) vince quasi sempre sulla complessità dei servizi gestiti nel cloud, specialmente in un contesto di laboratorio. Il passaggio all\u0026rsquo;SDK standard ha trasformato Mnemosyne da un esperimento fragile a un componente infrastrutturale solido.\nCosa significa questo per TazLab? Ora il mio ambiente di sviluppo non è più amnesico. L\u0026rsquo;agente AI può finalmente dire: \u0026ldquo;Ricordo come abbiamo configurato Longhorn tre settimane fa\u0026rdquo; o \u0026ldquo;Ecco perché abbiamo scelto quella specifica policy di MetalLB\u0026rdquo;. La memoria è sovrana, risiede sul mio hardware e parla un protocollo universale.\nCosa ho imparato in questa tappa: # L\u0026rsquo;importanza degli standard: Usare un SDK ufficiale (come quello di mark3labs) salva ore di debug sui dettagli dei protocolli come il flushing SSE e la gestione dei session ID. GitOps Vigilance: Non bisogna mai fidarsi di un \u0026ldquo;Reconciliation Succeeded\u0026rdquo; a livello globale se un componente a valle non risponde. Un errore di YAML silenzioso può congelare l\u0026rsquo;intero cluster. Sanificazione dei Segreti: Un singolo apice in un file di testo può essere più distruttivo di un bug logico complesso. La missione Mnemosyne continua. Il prossimo obiettivo sarà l\u0026rsquo;automazione della distillazione della conoscenza, affinché ogni sessione venga archiviata senza intervento umano, trasformando ogni riga di log in un fatto atomico per il futuro.\n","date":"22 febbraio 2026","externalUrl":null,"permalink":"/it/posts/mnemosyne-mcp-integration/","section":"Posts","summary":"","title":"Mnemosyne Rebirth: Cronaca di una Memoria Sovrana (e di come mi sono scontrato con il protocollo MCP)","type":"posts"},{"content":"","date":"10 febbraio 2026","externalUrl":null,"permalink":"/it/tags/longhorn/","section":"Tags","summary":"","title":"Longhorn","type":"tags"},{"content":" Phoenix Protocol: Validazione del Rebirth Zero-Touch e l\u0026rsquo;Inferno del PITR su S3 # Nell\u0026rsquo;architettura del Castello Effimero, la resilienza non è un\u0026rsquo;opzione, ma la condizione stessa di esistenza. Un\u0026rsquo;infrastruttura che può essere distrutta e ricreata in meno di dodici minuti è inutile se, al termine della rinascita, la sua memoria è svanita. Nelle ultime 48 ore, ho sottoposto il cluster TazLab a quello che ho battezzato Phoenix Protocol: un ciclo ossessivo di nuclear-wipe e create, mirato a validare l\u0026rsquo;immortalità del dato attraverso il ripristino automatico (Point-In-Time Recovery) da AWS S3.\nQuesta non è la storia di un successo immediato, ma la cronaca onesta di una guerra di logoramento contro gli automatismi dell\u0026rsquo;operatore Postgres di CrunchyData, le idiosincrasie dei percorsi degli oggetti S3 e la latenza fisica dello storage distribuito su hardware limitato.\nIl Mindset: L\u0026rsquo;Infrastruttura è Cenere, il Dato è Diamante # Ho deciso di adottare una filosofia radicale: l\u0026rsquo;intero stato del cluster (VM, configurazioni OS, volumi locali) deve essere considerato sacrificabile. L\u0026rsquo;unico elemento che deve sopravvivere al \u0026ldquo;fuoco nucleare\u0026rdquo; è il backup criptato su S3. Per testare questa visione, ho dovuto affrontare tre scogli tecnici principali:\nL\u0026rsquo;Orchestrazione Deterministica: Garantire che i layer di Terragrunt nascano nell\u0026rsquo;ordine corretto, gestendo le dipendenze tra lo storage di rete e le istanze database. L\u0026rsquo;Iniezione delle Credenziali S3: Risolvere il paradosso di un operatore che richiede chiavi di accesso per poter scaricare il manifesto di ripristino che contiene le istruzioni su come usare quelle stesse chiavi. La Latenza di Longhorn: Gestire il riaggancio dei volumi su nodi che, dopo un wipe totale, presentano residui di stato che confondono lo scheduler di Kubernetes. Fase 1: Lo Struggle dello Storage e il Paradosso di Longhorn # Il primo tentativo di rinascita si è scontrato con la realtà fisica del mio HomeLab (3 nodi Proxmox con circa 32GB di RAM totali). Longhorn, il motore di storage distribuito che ho scelto per la sua semplicità e integrazione nativa con Kubernetes, si è rivelato un collo di bottiglia inaspettato durante i cicli rapidi di distruzione e creazione.\nL\u0026rsquo;Investigazione: \u0026ldquo;Volume not ready for workloads\u0026rdquo; # Dopo aver lanciato il comando di creazione, ho osservato i Pod di restore rimanere bloccati in Init:0/1. Analizzando gli eventi con kubectl describe pod, ho riscontrato l\u0026rsquo;errore: AttachVolume.Attach failed for volume \u0026quot;pvc-xxx\u0026quot; : rpc error: code = Aborted desc = volume is not ready for workloads\nIl processo mentale che mi ha portato alla soluzione è stato questo: inizialmente ho sospettato un errore di Talos OS nel montare i target iSCSI. Tuttavia, i log di Longhorn Manager indicavano che il volume era \u0026ldquo;incastrato\u0026rdquo; in una fase di distacco dal nodo precedente, che fisicamente non esisteva più a causa del wipe.\nIl Ragionamento: Perché ho ridotto le repliche e forzato l\u0026rsquo;overprovisioning # Per risolvere questo stallo, ho dovuto prendere due decisioni cruciali:\nReplica Count a 1: In un cluster con soli due nodi worker, pretendere tre repliche per ogni volume database portava a uno stallo dello scheduler. Ho deciso che la ridondanza dello storage sarebbe stata gestita a livello applicativo (via Postgres) e a livello di backup (via S3), permettendo ai volumi locali di essere snelli e rapidi. Overprovisioning al 200%: Ho configurato Longhorn per permettere l\u0026rsquo;allocazione virtuale del doppio dello spazio fisico. Questo è necessario perché durante il bootstrap, il sistema tenta di creare nuovi volumi prima che quelli vecchi siano stati completamente rimossi dal database di stato dei nodi. Fase 2: L\u0026rsquo;Inferno dei Percorsi S3 e la Guerra ai Leading Slash # Una volta stabilizzato lo storage, mi sono scontrato con il cuore del problema: pgBackRest. L\u0026rsquo;integrazione tra CrunchyData PGO v5 e S3 è estremamente potente, ma altrettanto pignola.\nL\u0026rsquo;Analisi del Fallimento: \u0026ldquo;No backup set found\u0026rdquo; # Nonostante i file fossero presenti nel bucket S3, il Job di restore falliva sistematicamente con un laconico FileMissingError: unable to open missing file '/pgbackrest/repo1/backup/db/backup.info'.\nDeep-Dive: Object Storage Pathing A differenza di un filesystem POSIX, un bucket S3 non ha cartelle reali, ma solo chiavi composte da stringhe (prefissi). Quando un tool come pgbackrest cerca un file, la presenza o l\u0026rsquo;assenza di uno slash (/) iniziale nel prefisso configurato può cambiare radicalmente la richiesta API.\nDopo aver utilizzato un Pod temporaneo con AWS CLI per ispezionare il bucket, ho scoperto che i dati risiedevano in pgbackrest/repo1/... (senza slash iniziale). Nel mio manifest cluster.yaml, avevo configurato repo1-path: /pgbackrest/repo1. L\u0026rsquo;operatore cercava quindi una \u0026ldquo;sottocartella\u0026rdquo; fantasma nella root. Ho rimosso il leading slash, allineando la configurazione alla realtà degli oggetti S3.\nFase 3: Il Paradosso dell\u0026rsquo;Autenticazione nel Bootstrap # Risolto il problema del percorso, è emerso l\u0026rsquo;errore più ostico: ERROR: [037]: restore command requires option: repo1-s3-key.\nIl Ragionamento: Perché l\u0026rsquo;operatore non \u0026ldquo;eredita\u0026rdquo; le chiavi # Ho scoperto che l\u0026rsquo;operatore CrunchyData v5 gestisce i backup e i ripristini in modo asimmetrico. Sebbene le credenziali S3 fossero definite nel blocco backups, il Job di bootstrap (quello che fa nascere il cluster dal nulla) non le ereditava automaticamente.\nHo dovuto implementare un refactoring dell\u0026rsquo; ExternalSecret e del manifesto del cluster per forzare l\u0026rsquo;iniezione. La soluzione è stata creare un file s3.conf iniettato dinamicamente tramite un Secret, e referenziarlo esplicitamente nel blocco dataSource.\nImplementazione Tecnica: La Configurazione \u0026ldquo;Sacra\u0026rdquo; # Ecco il segreto che ha sbloccato la situazione, mappando le chiavi di Infisical nel formato richiesto dal file di configurazione di pgBackRest:\n# infrastructure/configs/tazlab-db/s3-external-secret.yaml apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: s3-backrest-creds namespace: tazlab-db spec: refreshInterval: 1h secretStoreRef: kind: ClusterSecretStore name: infisical-tazlab target: name: s3-backrest-creds template: engineVersion: v2 data: # File di config che CrunchyData monta nel pod di restore s3.conf: | [global] repo1-s3-key={{ .AWS_ACCESS_KEY_ID }} repo1-s3-key-secret={{ .AWS_SECRET_ACCESS_KEY }} data: - secretKey: AWS_ACCESS_KEY_ID remoteRef: key: AWS_ACCESS_KEY_ID - secretKey: AWS_SECRET_ACCESS_KEY remoteRef: key: AWS_SECRET_ACCESS_KEY E il manifesto del cluster che richiama esplicitamente questa configurazione per il bootstrap:\n# infrastructure/instances/tazlab-db/cluster.yaml spec: dataSource: pgbackrest: stanza: db configuration: - secret: name: s3-backrest-creds # Fondamentale per l\u0026#39;autenticazione durante il restore repo: name: repo1 s3: bucket: \u0026#34;tazlab-longhorn\u0026#34; endpoint: \u0026#34;s3.amazonaws.com\u0026#34; region: \u0026#34;eu-central-1\u0026#34; options: - --delta # Permette il ripristino su volumi esistenti se necessario Fase 4: La Validazione del Phoenix Protocol (PITR) # Per l\u0026rsquo;ultimo test, ho voluto alzare l\u0026rsquo;asticella. Non mi bastava recuperare un vecchio backup; volevo recuperare dati inseriti secondi prima della distruzione totale del cluster.\nIl Protocollo di Test: # Inserimento DATO_A: Registrato nel Full Backup S3. Trigger manuale del backup. Inserimento DATO_B: Registrato solo nei log di transazione (WAL). Forzatura del pg_switch_wal() per assicurarmi che l\u0026rsquo;ultimo segmento venisse pushato su S3. Nuclear Wipe: Distruzione fisica di tutte le VM su Proxmox. Deep-Dive: Point-In-Time Recovery (PITR) Il PITR è la capacità di un database di tornare a un qualsiasi istante temporale passato combinando un backup completo (\u0026ldquo;la base\u0026rdquo;) con i log delle transazioni (WAL - \u0026ldquo;i mattoni\u0026rdquo;). Se il sistema riesce a riprodurre i WAL su S3 dopo un wipe, significa che non abbiamo perso neanche una singola riga di dati, anche se inserita un istante prima del disastro.\nL\u0026rsquo;Ostacolo Finale: Il flag \u0026ndash;type=immediate # Inizialmente, il ripristino mostrava solo DATO_A. Analizzando i log, ho capito che l\u0026rsquo;operatore utilizzava di default l\u0026rsquo;opzione --type=immediate. Questa opzione istruisce Postgres a fermarsi non appena il database raggiunge uno stato consistente dopo il backup full, ignorando tutti i log di transazione successivi. Ho rimosso il flag dal manifesto, permettendo al processo di \u0026ldquo;masticare\u0026rdquo; tutti i WAL disponibili fino all\u0026rsquo;ultima transazione ricevuta da S3.\nRisultato Finale: 11 Minuti e 38 Secondi # Utilizzando l\u0026rsquo;orologio di sistema per misurare ogni fase della rinascita, ecco la telemetria finale del bootstrap completo:\nLayer Secrets: 33s Layer Platform (Proxmox + Talos): 3m 48s Layer Engine \u0026amp; Networking: 2m 51s Layer GitOps \u0026amp; Storage: 2m 25s Database Restore (PITR da S3): ~2m 00s Totale: 11 minuti e 38 secondi.\nAl termine di questo intervallo, ho interrogato la tabella memories:\nid | content | created_at ----+----------------------------------+------------------------------- 2 | DATO_B_VOLATILE_MA_IMMORTALE_WAL | 2026-02-10 14:55:10 1 | DATO_A_NEL_BACKUP_S3 | 2026-02-10 14:54:02 Entrambi i dati erano lì. Il Phoenix Protocol ha avuto successo.\nRiflessioni Post-Lab: Il Futuro è Nomade # Il raggiungimento di questo traguardo trasforma radicalmente il mio approccio al cluster. Sapere che posso distruggere tutto e riavere ogni singola transazione database in meno di 12 minuti mi libera dalla \u0026ldquo;paura del ferro\u0026rdquo;.\nCosa abbiamo imparato: # L\u0026rsquo;automazione non è magia: È una sequenza di validazioni rigorose. Ogni slash, ogni nome utente (che deve rispettare lo standard RFC 1123, pena il fallimento della riconciliazione), ogni flag di restore conta. Il dato è l\u0026rsquo;unica ancora: L\u0026rsquo;infrastruttura deve essere considerata effimera per definizione. Investire tempo nel rendere il dato \u0026ldquo;immortale\u0026rdquo; via S3 vale mille volte il tempo speso a cercare di rendere \u0026ldquo;stabile\u0026rdquo; una VM. Il cloud è vicino: Questo setup a 3 nodi (1 CP + 2 Workers) con 24GB di RAM totali è già pronto per essere spostato su AWS EC2 o Google Cloud. La configurazione è agnostica; cambierà solo il layer di provisioning delle VM, ma il cuore della rinascita rimarrà lo stesso. Il Castello di TazLab è ora ufficialmente indistruttibile. La sua forza non risiede nelle mura, ma nella sua capacità di risorgere dalle proprie ceneri, esattamente dove e quando decido io.\nCronaca Tecnica a cura di Taz - HomeLab DevOps Engineer.\n","date":"10 febbraio 2026","externalUrl":null,"permalink":"/it/posts/phoenix-protocol-s3-pitr-validation/","section":"Posts","summary":"","title":"Phoenix Protocol: Validazione del Rebirth Zero-Touch e l'Inferno del PITR su S3","type":"posts"},{"content":"","date":"10 febbraio 2026","externalUrl":null,"permalink":"/it/categories/reliability-engineering/","section":"Categories","summary":"","title":"Reliability Engineering","type":"categories"},{"content":"","date":"10 febbraio 2026","externalUrl":null,"permalink":"/it/tags/s3-backup/","section":"Tags","summary":"","title":"S3-Backup","type":"tags"},{"content":"","date":"6 febbraio 2026","externalUrl":null,"permalink":"/it/tags/cryptography/","section":"Tags","summary":"","title":"Cryptography","type":"tags"},{"content":"","date":"6 febbraio 2026","externalUrl":null,"permalink":"/it/categories/engineering/","section":"Categories","summary":"","title":"Engineering","type":"categories"},{"content":"","date":"6 febbraio 2026","externalUrl":null,"permalink":"/it/tags/post-mortem/","section":"Tags","summary":"","title":"Post-Mortem","type":"tags"},{"content":" TazPod v2.0: La Resa a Root e la Rivoluzione della RAM # Nel mondo del DevOps e della Security Engineering, esiste una linea sottile tra l\u0026rsquo;architettura sicura e l\u0026rsquo;architettura inutilizzabile. Con TazPod v1.0, avevo costruito quello che sulla carta sembrava un capolavoro di isolamento: una \u0026ldquo;Ghost Mode\u0026rdquo; che sfruttava i Namespace Linux e i device LUKS per rendere i segreti invisibili persino ai processi concorrenti nello stesso container.\nOggi, con il rilascio della v2.0, documento ufficialmente il fallimento di quell\u0026rsquo;approccio e la completa riscrittura del core del sistema. Questa è la cronaca di come la stabilità operativa ha vinto sulla paranoica teorica, e di come ho imparato a smettere di combattere contro l\u0026rsquo;utente root.\n1. Il Crollo della \u0026ldquo;Ghost Mode\u0026rdquo;: Un Post-Mortem # L\u0026rsquo;ambizione della v1.0 era alta: utilizzare unshare --mount per creare uno spazio di mount privato all\u0026rsquo;interno del container, dove decriptare un volume LUKS (vault.img). L\u0026rsquo;idea era che, uscendo dalla shell, il namespace collassasse e i segreti svanissero.\nL\u0026rsquo;Instabilità dei Loop Device # Il primo segnale di cedimento strutturale è arrivato durante le sessioni di sviluppo intensivo. Il kernel Linux gestisce i loop device (i file montati come dischi) come risorse globali. All\u0026rsquo;interno di un container Docker — che è già un ambiente isolato e spesso \u0026ldquo;privilegiato\u0026rdquo; in modo precario per permettere queste operazioni — la gestione dei lock sui device mapper si è rivelata disastrosa.\nL\u0026rsquo;errore Failed to create loop device o Device or resource busy è diventato una costante. Spesso, un container terminato in modo non pulito lasciava il file vault.img \u0026ldquo;appeso\u0026rdquo; a un loop device fantasma sull\u0026rsquo;host. Questo richiedeva un riavvio della macchina o interventi chirurgici con losetup -d che rompevano il flusso di lavoro.\nLa Perdita di Dati (The Data Loss Event) # Il punto di rottura è stato un evento di corruzione del filesystem. LUKS e ext4 non amano essere terminati bruscamente. In due occasioni distinte, un crash del container ha lasciato il volume criptato in uno stato inconsistente (\u0026ldquo;dirty bit\u0026rdquo;), rendendo impossibile il remount.\nHo perso dati. E tra quei dati, ho perso sessioni preziose di Mnemosyne (la memoria a lungo termine della mia AI), che avevo imprudentemente deciso di salvare dentro il vault per \u0026ldquo;massima sicurezza\u0026rdquo;. Questo evento mi ha costretto a riconsiderare l\u0026rsquo;intera strategia: un sistema di sicurezza che rende i dati inaccessibili al proprietario legittimo è un sistema fallito.\n2. La Resa a Root: Analisi della Minaccia # Mentre lottavo per stabilizzare i mount point, ho dovuto affrontare una verità scomoda riguardante il modello di minaccia.\nLa \u0026ldquo;Ghost Mode\u0026rdquo; proteggeva i segreti da altri processi non privilegiati. Ma TazPod gira come container --privileged per poter effettuare i mount. Chiunque abbia accesso di root al container (o all\u0026rsquo;host) può banalmente usare nsenter per entrare nel namespace \u0026ldquo;segreto\u0026rdquo; o fare un dump della memoria RAM.\nIl Paradosso dell\u0026rsquo;Isolamento # Ho speso settimane a costruire un castello di carte con unshare e mount --make-private, solo per realizzare che stavo proteggendo i segreti da\u0026hellip; me stesso. Un attaccante capace di compromettere l\u0026rsquo;host avrebbe comunque avuto accesso a tutto.\nHo deciso quindi di cambiare approccio: accettare che Root vede tutto. Invece di cercare di nascondere i dati a un utente onnipotente tramite l\u0026rsquo;isolamento del kernel, ho deciso di ridurre la finestra temporale e la superficie fisica in cui i dati esistono in chiaro.\n3. Architettura v2.0: Il RAM Vault (tmpfs + AES-GCM) # La nuova architettura elimina completamente la dipendenza da cryptsetup, dm-crypt e dai loop device. Abbiamo spostato la sicurezza dal livello blocco (kernel) al livello applicativo (Go) e volatile (RAM).\nStorage: Il Formato vault.tar.aes # Invece di un filesystem ext4 criptato, ora i dati a riposo sono un semplice archivio TAR compresso e cifrato.\nPer la crittografia, ho scelto AES-256-GCM (Galois/Counter Mode).\nPerché GCM? A differenza della modalità CBC (Cipher Block Chaining), GCM offre la crittografia autenticata. Questo significa che il file non solo è illeggibile, ma è anche protetto da manomissioni. Se un bit del file cifrato su disco viene corrotto o alterato, la fase di decrittazione fallisce immediatamente con un errore di autenticazione, proteggendo l\u0026rsquo;integrità dei segreti. Derivazione della Chiave: Utilizzo PBKDF2 con un salt casuale generato a ogni salvataggio per derivare la chiave AES dalla passphrase utente. Runtime: La Volatilità di tmpfs # Quando l\u0026rsquo;utente lancia tazpod unlock, la CLI non tocca il disco.\nMount: Viene montato un volume tmpfs (RAM Disk) da 64MB su /home/tazpod/secrets. // Codice interno per il mount volatile func mountRAM() { cmd := exec.Command(\u0026#34;sudo\u0026#34;, \u0026#34;mount\u0026#34;, \u0026#34;-t\u0026#34;, \u0026#34;tmpfs\u0026#34;, \u0026#34;-o\u0026#34;, \u0026#34;size=64M,mode=0700,uid=1000,gid=1000\u0026#34;, \u0026#34;tmpfs\u0026#34;, MountPath) cmd.Run() } Decrypt \u0026amp; Extract: Il file vault.tar.aes viene letto in memoria, decriptato on-the-fly e il flusso TAR risultante viene scompattato direttamente nel mount point in RAM. Zero Trace: Nessun file temporaneo viene mai scritto sul disco fisico dell\u0026rsquo;host. Ciclo di Vita: Pull, Save, Lock # La gestione della persistenza è stata completamente rivista per adattarsi alla natura effimera della RAM.\ntazpod pull: Scarica i segreti da Infisical, li scrive nella RAM e innesca immediatamente un Auto-Save. Auto-Save: La CLI legge ricorsivamente il contenuto della RAM, crea un nuovo TAR in memoria, lo cifra e sovrascrive atomicamente il file vault.tar.aes su disco. tazpod lock (o exit): Il comando finale è brutale ed efficace: umount /home/tazpod/secrets. I dati svaniscono istantaneamente. Non c\u0026rsquo;è bisogno di sovrascritture sicure (shred), perché i bit non hanno mai toccato i piatti magnetici o le celle NAND. 4. Developer Experience: Risolvere le Frizioni # Oltre alla sicurezza, la v1.0 soffriva di problemi di usabilità che rallentavano il mio flusso di lavoro quotidiano.\nIl Problema della Collisione dei Nomi # Inizialmente, il nome del container era hardcoded (tazpod-lab). Questo impediva di lavorare su due progetti contemporaneamente (es. tazlab-k8s e blog-src).\nHo introdotto una logica di inizializzazione dinamica in tazpod init.\n// Generazione di un identificativo univoco per il progetto cwd, _ := os.Getwd() folderName := filepath.Base(cwd) r := rand.New(rand.NewSource(time.Now().UnixNano())) randomSuffix := fmt.Sprintf(\u0026#34;%04d\u0026#34;, r.Intn(10000)) containerName := fmt.Sprintf(\u0026#34;tazpod-%s-%s\u0026#34;, folderName, randomSuffix) Ora, ogni cartella di progetto ha il suo container dedicato (es. tazpod-backend-8492), isolato dagli altri, con il proprio vault e la propria configurazione.\nHot Reloading: Sviluppare la CLI nella CLI # Sviluppare TazPod usando TazPod presentava una sfida \u0026ldquo;Inception\u0026rdquo;. Come testare la nuova versione della CLI senza dover ricostruire l\u0026rsquo;intera immagine Docker (che richiede minuti) ad ogni modifica?\nHo implementato un workflow di Hot Reload:\nCompilazione del binario Go sull\u0026rsquo;host (task build). Copia del binario in ~/.local/bin (per l\u0026rsquo;uso host). Iniezione diretta nel container attivo: docker cp bin/tazpod tazpod-lab:/home/tazpod/.local/bin/tazpod Questo ha ridotto il ciclo di feedback da 4 minuti a 3 secondi, permettendomi di iterare rapidamente sulla logica di crittografia e mount.\n5. Mnemosyne: La Memoria Fuori dal Vault # Una delle lezioni più dure della v1.0 è stata la perdita delle sessioni AI. Per Mnemosyne, la persistenza è più importante della segretezza assoluta. Le chat con Gemini contengono contesto architetturale, non password.\nNella v2.0, ho deciso di disaccoppiare la memoria dell\u0026rsquo;AI dal vault dei segreti. Durante la fase di setupBindAuth, la CLI crea un symlink strategico:\nHost: I log risiedono in /workspace/.tazpod/.gemini (sul disco dell\u0026rsquo;host, persistenti). Container: Vengono linkati in ~/.gemini. Questo garantisce che, anche se distruggo il vault o resetto il container, la \u0026ldquo;coscienza\u0026rdquo; del progetto sopravvive. I segreti (token API per parlare con Gemini) restano nel RAM Vault, ma i ricordi sono salvati su disco standard.\nConclusioni: La Semplicità è una Feature di Sicurezza # TazPod v2.0 è, paradossalmente, tecnologicamente meno avanzato della v1.0. Non usa feature esoteriche del kernel, non manipola i namespace di rete o di mount in modi creativi. È solo un file criptato e un disco RAM.\nTuttavia, è infinitamente più robusto.\nNon si rompe se Proxmox ha un load alto. Non corrompe i dati se il container crasha. È portabile su qualsiasi sistema Linux senza richiedere moduli kernel specifici per la crittografia. Ho imparato che in DevOps, la complessità è spesso un debito tecnico travestito da \u0026ldquo;best practice\u0026rdquo;. Ridurre la superficie di attacco ha significato, in questo caso, ridurre la complessità dell\u0026rsquo;architettura. Ora i miei segreti vivono in una bolla di sapone digitale (la RAM): effimera, fragile se toccata, ma perfettamente isolata finché esiste.\nIl prossimo passo? Portare questa filosofia di \u0026ldquo;semplicità resiliente\u0026rdquo; nel cuore del cluster Kubernetes, dove Mnemosyne troverà la sua casa definitiva.\nCronaca Tecnica a cura di Taz - Ingegneria dei Sistemi e Infrastrutture Zero-Trust.\n","date":"6 febbraio 2026","externalUrl":null,"permalink":"/it/posts/tazpod-v2-ram-vault-evolution/","section":"Posts","summary":"","title":"TazPod v2.0: La Resa a Root e la Rivoluzione della RAM","type":"posts"},{"content":"","date":"5 febbraio 2026","externalUrl":null,"permalink":"/it/posts/tazlab-nomadic-rebirth-cloud-horizon/","section":"Posts","summary":"","title":"La Rinascita Nomade: Verso l'Orizzonte Cloud e l'Evoluzione del Castello","type":"posts"},{"content":"","date":"5 febbraio 2026","externalUrl":null,"permalink":"/it/categories/strategy/","section":"Categories","summary":"","title":"Strategy","type":"categories"},{"content":"","date":"5 febbraio 2026","externalUrl":null,"permalink":"/it/tags/vectordb/","section":"Tags","summary":"","title":"Vectordb","type":"tags"},{"content":"","date":"2 febbraio 2026","externalUrl":null,"permalink":"/it/categories/data-engineering/","section":"Categories","summary":"","title":"Data Engineering","type":"categories"},{"content":"","date":"2 febbraio 2026","externalUrl":null,"permalink":"/it/tags/knowledge-management/","section":"Tags","summary":"","title":"Knowledge-Management","type":"tags"},{"content":"","date":"2 febbraio 2026","externalUrl":null,"permalink":"/it/posts/mnemosyne-local-rebirth-snr/","section":"Posts","summary":"","title":"Mnemosyne: La Rinascita Locale, il Loop Ricorsivo e la Sfida del Rapporto Segnale-Rumore","type":"posts"},{"content":"","date":"2 febbraio 2026","externalUrl":null,"permalink":"/it/tags/pgvector/","section":"Tags","summary":"","title":"Pgvector","type":"tags"},{"content":"","date":"2 febbraio 2026","externalUrl":null,"permalink":"/it/categories/design-patterns/","section":"Categories","summary":"","title":"Design Patterns","type":"categories"},{"content":" L\u0026rsquo;Orchestra del Castello: Il Pivot verso Terragrunt e la Guerra alle Race Condition # Il sogno di ogni ingegnere DevOps che lavora con infrastrutture ephemere è il Determinismo Totale. L\u0026rsquo;idea che, premendo un singolo tasto, un\u0026rsquo;intera cattedrale digitale possa sorgere dal nulla, configurarsi e servire traffico in pochi minuti, per poi svanire senza lasciare traccia, è ciò che spinge il progetto del Castello Effimero. Tuttavia, come spesso accade nel passaggio dal laboratorio alla produzione, la realtà ha presentato un conto salato sotto forma di instabilità, conflitti temporali e stalli infiniti.\nIn questa nuova tappa del mio diario tecnico, documento il pivot architetturale più significativo dall\u0026rsquo;inizio del progetto: l\u0026rsquo;abbandono del monolite Terraform in favore di un\u0026rsquo;orchestrazione a layer gestita da Terragrunt. Non si è trattato di un semplice cambio di tool, ma di un cambiamento filosofico necessario per sconfiggere le Race Condition che stavano rendendo il bootstrap del cluster una scommessa invece che una certezza.\nIl Punto di Rottura: La Tirannia dei Webhook # Fino a pochi giorni fa, il Castello nasceva da un unico, gigantesco main.tf. Terraform si occupava di tutto: creava le VM su Proxmox, configurava Talos OS, installava MetalLB, Longhorn, Cert-Manager e infine Flux. Sulla carta, il grafo delle dipendenze di Terraform avrebbe dovuto gestire l\u0026rsquo;ordine di esecuzione. Nella pratica, mi sono scontrato con la natura asincrona di Kubernetes.\nL\u0026rsquo;Analisi dello Struggle: Webhook in Timeout # Il problema si manifestava sistematicamente durante l\u0026rsquo;installazione di MetalLB o Cert-Manager. Kubernetes utilizza gli Admission Webhooks per convalidare le risorse. Quando Terraform inviava il manifesto di un IPAddressPool (per MetalLB) o di un ClusterIssuer (per Cert-Manager), il controller relativo era ancora in fase di inizializzazione.\nIl risultato era un errore frustrante: failed calling webhook \u0026quot;l2advertisementvalidationwebhook.metallb.io\u0026quot;: connect: connection refused\nNonostante il Pod del controller risultasse Running, il servizio del webhook non era ancora pronto a rispondere. Terraform, vedendo il fallimento, andava in errore e interrompeva l\u0026rsquo;intera catena di provisioning. Ho provato a inserire dei \u0026ldquo;wait\u0026rdquo; artificiali, ma erano fragili: troppo brevi e il sistema falliva, troppo lunghi e perdevo il vantaggio della velocità. Il monolite stava diventando ingestibile perché cercava di gestire troppi stati diversi (infrastruttura, rete, storage, logica applicativa) in un unico ciclo di vita.\nIl Pivot Filosofico: Infrastruttura di Base vs GitOps # Un altro errore tattico che ho dovuto riconoscere è stata la delega eccessiva a Flux. Nel post precedente, avevo celebrato l\u0026rsquo;idea di spostare Longhorn e MetalLB sotto la gestione di Flux per rendere Terraform \u0026ldquo;più leggero\u0026rdquo;.\nIl Ragionamento: Perché sono tornato indietro # Ho capito che MetalLB e Longhorn non sono \u0026ldquo;applicazioni\u0026rdquo;, ma estensioni del Kernel del cluster. Senza MetalLB, l\u0026rsquo;Ingress non riceve un IP. Senza Longhorn, le app che richiedono persistenza (come il blog o database) non possono partire.\nSe delego questi componenti a Flux, creo un loop di dipendenze pericoloso: Flux ha bisogno di segreti per autenticarsi, ma ESO (External Secrets Operator) ha bisogno di un cluster sano per girare. Se Flux fallisce per un motivo qualsiasi, perdo la visibilità sui componenti vitali del cluster. Ho deciso quindi che tutto ciò che è necessario affinché il cluster sia considerato \u0026ldquo;funzionante e capace\u0026rdquo; deve nascere tramite IaC (Infrastructure as Code), mentre Flux deve occuparsi solo di ciò che il cluster \u0026ldquo;ospita\u0026rdquo;.\nL\u0026rsquo;Arrivo di Terragrunt: Il Direttore d\u0026rsquo;Orchestra # Per risolvere questi problemi, ho introdotto Terragrunt. Terragrunt agisce come un wrapper per Terraform, permettendo di dividere l\u0026rsquo;infrastruttura in moduli indipendenti ma collegati da un grafo di dipendenze esplicito.\nDeep-Dive: State Isolation e Dependency Graph # L\u0026rsquo;uso di Terragrunt ha introdotto due concetti chiave che hanno cambiato tutto:\nIsolamento dello Stato: Ogni layer (rete, storage, engine) ha il suo file .tfstate. Se rompo la configurazione di Flux, lo stato delle mie VM su Proxmox rimane intatto. Non rischio più di distruggere l\u0026rsquo;intero cluster per un errore di sintassi in un manifesto Kubernetes. Grafo delle Dipendenze: Posso dire a Terragrunt: \u0026ldquo;Non provare nemmeno a installare MetalLB finché il layer Platform (le VM) non è completamente online e l\u0026rsquo;API di Kubernetes non risponde\u0026rdquo;. L\u0026rsquo;Anatomia del Castello a 6 Layer # Ho riorganizzato l\u0026rsquo;intero repository ephemeral-castle in una struttura a strati, dove ogni strato costruisce sulle fondamenta del precedente.\nLayer 1: Secrets (G1) # Questo strato interagisce solo con Infisical EU. Recupera i token necessari per Proxmox, le chiavi SSH e le credenziali S3. È il \u0026ldquo;punto zero\u0026rdquo; della fiducia.\nLayer 2: Platform (G2) # Qui avviene il provisioning pesante. Vengono create le macchine virtuali su Proxmox e viene iniettata la configurazione di Talos OS.\nDeep-Dive: Quorum e VIP: In questa fase, Terraform attende che i 3 nodi di Control Plane abbiano formato il quorum di etcd. Il Virtual IP (VIP) deve essere stabile prima di passare al layer successivo. Se il VIP non risponde, il bootstrap si ferma qui. Layer 3: Engine (G3) # Una volta che il \u0026ldquo;ferro\u0026rdquo; è pronto, installiamo il motore di identità: External Secrets Operator (ESO). Senza ESO, il cluster non può parlare con Infisical per recuperare i segreti applicativi. È il ponte tra il mondo esterno e il mondo Kubernetes.\nLayer 4: Networking (G4) # Installazione di MetalLB. Qui abbiamo implementato la soluzione definitiva alla race condition del webhook. Lo script di orchestrazione interroga Kubernetes finché l\u0026rsquo;EndpointSlice del webhook non è Ready. Solo allora la configurazione del pool di IP viene iniettata.\nLayer 5 \u0026amp; 6: Storage e GitOps (G5 - In Parallelo) # Qui è avvenuta l\u0026rsquo;ottimizzazione che ho chiamato \u0026ldquo;Parallel Blitz\u0026rdquo;. Mi sono reso conto che Longhorn (Storage) e Flux (GitOps) possono nascere contemporaneamente. Flux può iniziare a scaricare le immagini e preparare i deployment mentre Longhorn sta ancora inizializzando i dischi sui nodi.\nLa Guerra allo Stato: \u0026ldquo;VM Already Exists\u0026rdquo; e il Backend Persistente # Un problema ricorrente durante i test era la corruzione dello stato locale. Se cancellavo accidentalmente la cartella .terraform o se lo stato non veniva salvato dopo un crash, al tentativo successivo ricevevo l\u0026rsquo;errore: 400 Parameter verification failed: vmid: VM 421 already exists on node proxmox\nL\u0026rsquo;Investigazione: Il fantasma nel sistema # Terraform è un sistema \u0026ldquo;state-aware\u0026rdquo;. Se perde il file di stato, pensa che il mondo sia vuoto. Ma Proxmox ha una memoria fisica. Per risolvere questo stallo, ho implementato due strategie:\nBackend Persistente Fuori-Albero: Ho spostato tutti i file di stato in una directory dedicata /home/taz/kubernetes/ephemeral-castle/states/, esterna al repository Git. Questo garantisce che lo stato sopravviva anche a un git clean aggressivo o a un cambio di branch. Nuclear Wipe: Ho creato uno script nuclear-wipe.sh che, in caso di emergenza, usa l\u0026rsquo;API di Proxmox per cancellare forzatamente le VM tra gli ID 421 e 432, permettendo a Terraform di ripartire da una tabula rasa reale. Implementazione Tecnica: Il Cuore di Terragrunt # Ecco come appare il file di configurazione radice che orchestra l\u0026rsquo;intera danza. Notate come vengono generati i provider per tutti i layer sottostanti, garantendo la coerenza totale.\n# live/terragrunt.hcl remote_state { backend = \u0026#34;local\u0026#34; config = { path = \u0026#34;${get_parent_terragrunt_dir()}/../../states/${path_relative_to_include()}/terraform.tfstate\u0026#34; } } generate \u0026#34;provider\u0026#34; { path = \u0026#34;provider.tf\u0026#34; if_exists = \u0026#34;overwrite_terragrunt\u0026#34; contents = \u0026lt;\u0026lt;EOF provider \u0026#34;proxmox\u0026#34; { endpoint = var.pm_api_url api_token = var.pm_api_token insecure = true } provider \u0026#34;kubernetes\u0026#34; { config_path = \u0026#34;${get_parent_terragrunt_dir()}/../../clusters/tazlab-k8s-proxmox/proxmox/configs/kubeconfig\u0026#34; } provider \u0026#34;helm\u0026#34; { kubernetes { config_path = \u0026#34;${get_parent_terragrunt_dir()}/../../clusters/tazlab-k8s-proxmox/proxmox/configs/kubeconfig\u0026#34; } } EOF } E un esempio di come un layer (es. networking) dichiara la sua dipendenza dal layer precedente:\n# live/tazlab-k8s-proxmox/stage4-networking/terragrunt.hcl include \u0026#34;root\u0026#34; { path = find_in_parent_folders() } dependency \u0026#34;engine\u0026#34; { config_path = \u0026#34;../stage3-engine\u0026#34; } inputs = { # Input passati dal layer precedente se necessario } Ottimizzazione: Il \u0026ldquo;Parallel Blitz\u0026rdquo; e il Record degli 8 Minuti # Dopo aver stabilizzato l\u0026rsquo;ordine, la sfida è diventata la velocità. Inizialmente, il bootstrap impiegava circa 14 minuti. Analizzando i log, ho visto che Flux restava in attesa di Longhorn anche se non era strettamente necessario per la sua installazione di base.\nLa Soluzione: Orchestrazione Intelligente # Nello script create.sh, ho separato l\u0026rsquo;applicazione dei layer. Mentre i layer 1, 2, 3 e 4 devono essere sequenziali (Segreti -\u0026gt; VM -\u0026gt; Engine -\u0026gt; Rete), il layer 5 e 6 vengono lanciati quasi simultaneamente.\n# create.sh snippet - Enterprise V4 echo \u0026#34;🚀 STAGE 5 \u0026amp; 6: Launching Storage and GitOps in Parallel...\u0026#34; terragrunt run-all apply --terragrunt-non-interactive --terragrunt-parallelism 2 Questo cambiamento ha ridotto il tempo totale di bootstrap a 8 minuti e 20 secondi. In questo arco di tempo, il sistema passa dal nulla cosmico a un cluster HA con 5 nodi, storage distribuito, networking Layer 2 e Flux che ha già riconciliato l\u0026rsquo;ultima versione di questo blog.\nRiflessioni post-lab: Verso l\u0026rsquo;Agnosticismo Cloud # Il passaggio a Terragrunt ha trasformato il Castello Effimero in una vera Fabbrica di Infrastruttura.\nCosa significa questo setup per il futuro? # Agnosticismo della Piattaforma: Ora posso creare una cartella live/tazlab-k8s-aws/, cambiare solo il layer stage2-platform (usando moduli AWS invece di Proxmox) e mantenere identici tutti gli altri layer. Il networking darà un LoadBalancer AWS invece di MetalLB, ma Flux e le app non se ne accorgeranno nemmeno. Affidabilità Industriale: Abbiamo eliminato il \u0026ldquo;forse funziona\u0026rdquo;. Se un layer fallisce, Terragrunt si ferma esattamente lì, permettendoci di ispezionare lo stato specifico senza dover rincorrere fantasmi in un file di stato da 5000 righe. Velocità come Sicurezza: Un\u0026rsquo;infrastruttura che nasce in 8 minuti permette di non aver paura di distruggere tutto. Se sospettiamo una compromissione o un errore di configurazione, la risposta è sempre: destroy \u0026amp;\u0026amp; create. Il Castello è ora solido, modulare e pronto a scalare oltre i confini del mio laboratorio domestico. L\u0026rsquo;orchestra è pronta, e la musica del codice non è mai stata così armoniosa.\nFine della Cronaca Tecnica - La Rivoluzione Terragrunt\n","date":"2 febbraio 2026","externalUrl":null,"permalink":"/it/posts/orchestrating-ephemeral-castle-terragrunt-pivot/","section":"Posts","summary":"","title":"L'Orchestra del Castello: Il Pivot verso Terragrunt e la Guerra alle Race Condition","type":"posts"},{"content":"","date":"2 febbraio 2026","externalUrl":null,"permalink":"/it/tags/proxmox/","section":"Tags","summary":"","title":"Proxmox","type":"tags"},{"content":" Il Passaggio di Consegne: Terraform, Flux e la Nascita della Fabbrica di Castelli # L\u0026rsquo;ingegneria dei sistemi non è un processo lineare, ma un\u0026rsquo;evoluzione fatta di continue semplificazioni. Dopo aver raggiunto l\u0026rsquo;Alta Affidabilità con un cluster a 5 nodi, mi sono reso conto che l\u0026rsquo;architettura soffriva ancora di un peccato originale: la sovrapposizione delle responsabilità. Terraform stava facendo troppo, e Flux stava facendo troppo poco. In questa cronaca tecnica, documento il salto evolutivo finale del Castello Effimero: la trasformazione in una vera \u0026ldquo;Fabbrica di Infrastruttura\u0026rdquo; dove il codice IaC agisce solo come innesco, delegando l\u0026rsquo;intera costruzione dei pilastri al motore GitOps.\nL\u0026rsquo;obiettivo della sessione è stato radicale: ridurre Terraform al minimo indispensabile, riorganizzare il repository per garantire un isolamento totale tra i progetti e creare un sistema di rinascita capace di risorgere dalle ceneri con un singolo comando automatizzato.\nIl Ragionamento: L\u0026rsquo;Estetica del Minimalismo IaC # Inizialmente, avevo configurato Terraform per installare non solo il cluster Kubernetes, ma anche tutti i suoi componenti fondamentali: MetalLB per il networking, Longhorn per lo storage, Traefik per l\u0026rsquo;ingress e Cert-Manager per i certificati. Sulla carta, sembrava una scelta logica: un unico comando per avere tutto pronto.\nTuttavia, questa scelta ha creato un conflitto di identità. Flux, il mio \u0026ldquo;butler\u0026rdquo; GitOps, cercava a sua volta di gestire quegli stessi componenti leggendo il repository dei manifesti. Il risultato era un duello costante tra Terraform e Flux per il controllo del cluster, con il rischio di drift (scostamento) e collisioni ad ogni aggiornamento.\nLa scelta: Solo l\u0026rsquo;indispensabile # Ho deciso di attuare un refactoring drastico. Terraform ora gestisce solo il \u0026ldquo;Kernel\u0026rdquo; del Castello:\nProvisioning Fisico: Creazione delle VM su Proxmox e configurazione di Talos OS. External Secrets Operator (ESO): Questo è l\u0026rsquo;unico componente Kubernetes che ho mantenuto in Terraform. Il motivo è puramente tecnico: per far sì che Flux possa scaricare le app, ha spesso bisogno di segreti (token Git, chiavi S3). ESO deve essere lì fin dal primo secondo per fare da ponte con Infisical EU. Flux CD: L\u0026rsquo;innesco finale. Terraform installa Flux e gli consegna le chiavi del repository tazlab-k8s. Questa separazione trasforma Terraform in un\u0026rsquo;ostetrica: aiuta il cluster a nascere e poi si fa da parte. Flux diventa l\u0026rsquo;unico sovrano dei pilastri infrastrutturali. Il vantaggio? Gli aggiornamenti di Traefik o MetalLB ora avvengono con un semplice git push, senza dover mai più invocare Terraform per modifiche applicative.\nFase 1: Ricostruzione Project-Centric ed Isolamento # Fino a ieri, la struttura delle cartelle era divisa per piattaforma (providers/proxmox/...). Era un approccio limitato che non scalava bene in uno scenario multi-progetto o multi-cloud.\nIl Ragionamento: Isolamento Totale # Ho deciso di riorganizzare l\u0026rsquo;intero repository ephemeral-castle seguendo una gerarchia orientata al progetto. Un progetto (come \u0026ldquo;Blue\u0026rdquo;) deve poter esistere sia su Proxmox che su AWS in modo totalmente isolato, con i suoi stati Terraform indipendenti e le sue chiavi protette.\nHo implementato la seguente struttura:\nclusters/blue/proxmox/: La logica specifica per il cluster locale. clusters/blue/configs/: Una cartella dedicata per ospitare i file sensibili generati (kubeconfig, talosconfig). Sicurezza e .gitignore # Un errore comune in IaC è lasciarsi scappare file di stato o config nel controllo di versione. Ho aggiornato il .gitignore con una regola ricorsiva e aggressiva:\n**/configs/ *.tfstate* Questo garantisce che, indipendentemente da quanti nuovi cluster creerò, le loro chiavi rimarranno confinate sulla mia workstation o nel vault, mai su GitHub.\nFase 2: Il Telecomando del Castello - destroy.sh e create.sh # La vera sfida dell\u0026rsquo;infrastruttura ephemerale è la velocità di rinascita. Se per ricostruire il cluster servono 10 comandi manuali, l\u0026rsquo;infrastruttura non è ephemerale, è solo faticosa. Ho deciso di condensare l\u0026rsquo;intera intelligenza operativa in due script di orchestrazione.\nL\u0026rsquo;Investigazione: Il blocco di Terraform # Il problema principale era che terraform destroy falliva sistematicamente. I provider di Kubernetes e Helm cercavano di connettersi al cluster per verificare lo stato delle risorse prima di eliminarle. Ma se le macchine erano già state resettate o spente, Terraform rimaneva appeso in attesa di una risposta che non sarebbe mai arrivata.\nLa Soluzione: Il \u0026ldquo;Purge\u0026rdquo; dello Stato # Ho risolto questo stallo inserendo una fase di pulizia forzata nello script destroy.sh. Prima di lanciare il distruggitore, lo script rimuove manualmente le risorse problematiche dallo stato locale:\n# destroy.sh snippet echo \u0026#34;🔥 Phase 1: Cleaning Terraform State...\u0026#34; terraform state list | grep -E \u0026#34;flux_|kubernetes_|kubectl_|helm_\u0026#34; | xargs -n 1 terraform state rm || true Questo comando dice a Terraform: \u0026ldquo;Dimentica di aver mai conosciuto Flux o Helm, pensa solo a cancellare le VM\u0026rdquo;. È una manovra chirurgica che sblocca l\u0026rsquo;intero processo di distruzione.\nFase 3: Lo Struggle delle Corse Critiche (Race Conditions) # Durante i primi test dello script create.sh, il cluster nasceva, ma i servizi (come il blog) rimanevano offline.\nL\u0026rsquo;Analisi dell\u0026rsquo;Errore: Il Webhook di MetalLB # Ho visto i Pod di MetalLB in stato Running, ma Flux segnalava un errore criptico sulle configurazioni del pool di IP: failed calling webhook \u0026quot;l2advertisementvalidationwebhook.metallb.io\u0026quot;: connect: connection refused\nIl processo mentale: Ho sospettato inizialmente di un problema di rete tra i nodi. Ho controllato i log del metallb-controller e ho scoperto la verità: il processo del webhook (che convalida i file YAML) impiega qualche secondo in più rispetto al controller principale per attivarsi. Flux provava a iniettare la configurazione nel millesimo di secondo sbagliato, riceveva un rifiuto e andava in stallo.\nLa Soluzione: La pazienza degli EndpointSlice # Ho aggiornato lo script di creazione per non limitarsi ad aspettare i Pod, ma a interrogare Kubernetes finché l\u0026rsquo;endpoint del webhook non fosse stato realmente pronto a servire. Ho migrato la logica di controllo dal vecchio risorsa Endpoints (ormai deprecata) alla moderna EndpointSlice.\nTuttavia, anche questa logica ha richiesto un affinamento: inizialmente un errore di sintassi Bash nel ciclo di attesa ha bloccato la rinascita proprio sul traguardo. Correggere quel bug è stata l\u0026rsquo;ultima lezione della giornata: in uno script di orchestrazione, la robustezza dei controlli (usando grep -q invece di fragili comparazioni di stringhe) è ciò che separa un\u0026rsquo;automazione \u0026ldquo;giocattolo\u0026rdquo; da una di livello professionale.\n# create.sh logic update echo \u0026#34;⏳ Waiting for MetalLB Webhook to be serving...\u0026#34; until kubectl get endpointslice -n metallb-system -l kubernetes.io/service-name=metallb-webhook-service -o jsonpath=\u0026#39;{range .items[*].endpoints[?(@.conditions.ready==true)]}{.addresses[*]}{\u0026#34;\\n\u0026#34;}{end}\u0026#39; 2\u0026gt;/dev/null | grep -q \u0026#34;\\.\u0026#34;; do printf \u0026#34;.\u0026#34; sleep 5 done echo \u0026#34; Webhook ready!\u0026#34; Questo controllo granulare ha eliminato l\u0026rsquo;ultima \u0026ldquo;corsa critica\u0026rdquo; che impediva l\u0026rsquo;automazione totale.\nFase 4: Idempotenza e il Conflitto con Infisical # Un altro ostacolo era rappresentato dal backup automatico dei config files su Infisical EU. Terraform cercava di creare il segreto KUBECONFIG_CONTENT, ma se questo esisteva già dal tentativo precedente, l\u0026rsquo;API restituiva un errore 400 Bad Request: Secret already exists.\nIl Ragionamento: Importazione Preventiva # Invece di provare a cancellare il segreto (che richiede permessi elevati e tempo), ho deciso di implementare una logica di importazione automatica. Prima di eseguire l\u0026rsquo;apply finale, lo script prova a \u0026ldquo;importare\u0026rdquo; il segreto nello stato di Terraform. Se esiste, Terraform ne prende il controllo e lo aggiorna; se non esiste, l\u0026rsquo;errore viene ignorato e Terraform lo creerà normalmente.\n# create.sh snippet echo \u0026#34;🔗 Checking for existing configs on Infisical...\u0026#34; terraform import -var-file=secrets.tfvars infisical_secret.kubeconfig_upload \u0026#34;$WORKSPACE_ID:$ENV_SLUG:$FOLDER_PATH:KUBECONFIG_CONTENT\u0026#34; || true Deep-Dive: Il concetto di Handover (Passaggio di Consegne) # In questa architettura, il concetto di Handover è fondamentale. Rappresenta il momento esatto in cui la responsabilità del cluster passa dal provisioning (IaC) alla consegna continua (GitOps).\nPerché è un termine tecnico importante? In un sistema tradizionale, Terraform è \u0026ldquo;lo stato\u0026rdquo;. Se vuoi cambiare una porta di Traefik, cambi il codice Terraform. Nel Castello, Terraform non sa nemmeno cosa sia Traefik. Terraform sa solo che deve far nascere un cluster e installare Flux.\nQuesto riduce drasticamente il Blast Radius (raggio d\u0026rsquo;impatto) di un errore in Terraform: se sbagli una riga nel codice IaC, rischi di rompere le VM, ma non romperai mai la logica applicativa del blog, perché quella risiede in un altro mondo (GitOps). È la separazione definitiva tra la \u0026ldquo;macchina\u0026rdquo; e lo \u0026ldquo;scopo\u0026rdquo;.\nLa Fabbrica in Azione: Come nasce un nuovo Progetto # Grazie a questa ristrutturazione, creare un nuovo cluster non è più un\u0026rsquo;opera di artigianato, ma un processo di catena di montaggio. Se oggi volessi far nascere il cluster \u0026ldquo;Green\u0026rdquo;, la procedura sarebbe questa:\nProvisioning (IaC):\nCopio la cartella templates/proxmox-talos in clusters/green/proxmox. Modifico il file terraform.tfvars impostando i nuovi IP, il nome del cluster e il nuovo path di Infisical (es. /ephemeral-castle/green/proxmox). Preparo i segreti su Infisical nella nuova cartella. Delivery (GitOps):\nCreo un nuovo repository su GitHub partendo dal contenuto di gitops-template. Inserisco l\u0026rsquo;URL di questo nuovo repository nel file terraform.tfvars della cartella del progetto. Innesco:\nLancio ./create.sh dalla cartella del progetto. In meno di 10 minuti, Terraform creerebbe le macchine e Flux inizierebbe a popolare il nuovo repository con i componenti di base (MetalLB, Traefik, Cert-Manager) già pre-configurati. Questo è il vero potere del Castello: la capacità di scalare orizzontalmente non solo i nodi, ma interi ecosistemi digitali.\nHardening Finale: Pulizia del Kernel e API v1 # Per concludere la giornata, ho affrontato due bug di \u0026ldquo;pulizia\u0026rdquo; che sporcavano i log.\nModuli Kernel: Talos segnalava errori nel caricamento di iscsi_generic. Investigando la documentazione, ho scoperto che nelle versioni recenti i moduli iSCSI sono stati accorpati. Ho rimosso il modulo inesistente da talos.tf, ottenendo finalmente un boot pulito (\u0026ldquo;Green Boot\u0026rdquo;). Deprecations: Ho migrato ogni risorsa Kubernetes gestita da Terraform alle versioni v1 (es. kubernetes_secret_v1). Questo non cambia la funzionalità, ma garantisce che l\u0026rsquo;infrastruttura sia pronta per le prossime major release di Kubernetes e silenzia i fastidiosi warning nel terminale. Riflessioni post-lab: Il Trionfo dell\u0026rsquo;Automazione # Vedere il Castello risorgere con un solo comando è stata una delle esperienze più soddisfacenti di questo viaggio.\nCosa abbiamo imparato: # IaC come Bootstrapper: Terraform dà il meglio di sé quando si limita a creare le fondamenta. Più codice Kubernetes metti in Terraform, più problemi avrai in futuro. L\u0026rsquo;importanza dei retry: In un mondo distribuito, non puoi assumere che un comando funzioni al primo colpo. Gli script di orchestrazione devono avere la \u0026ldquo;pazienza\u0026rdquo; di attendere che i servizi di rete siano caldi. Isolamento = Replicabilità: Dividere per progetto e piattaforma rende il Castello una vera fabbrica. Oggi ho un cluster \u0026ldquo;Blue\u0026rdquo; su Proxmox, ma la struttura è pronta per far nascere un cluster \u0026ldquo;Green\u0026rdquo; su AWS in meno di 10 minuti. Il Castello ora non è solo solido; è autonomo. Le mura sono alte, il maggiordomo (Flux) è al lavoro e il blog che state leggendo è la prova vivente che il codice, se ben orchestrato, può creare realtà immutabili e indistruttibili.\nFine della Cronaca Tecnica - Fase 5: Automazione e Handover\n","date":"1 febbraio 2026","externalUrl":null,"permalink":"/it/posts/the-immutable-handover-factory-automation/","section":"Posts","summary":"","title":"Il Passaggio di Consegne: Terraform, Flux e la Nascita della Fabbrica di Castelli","type":"posts"},{"content":"","date":"31 gennaio 2026","externalUrl":null,"permalink":"/it/tags/alloydb/","section":"Tags","summary":"","title":"Alloydb","type":"tags"},{"content":"","date":"31 gennaio 2026","externalUrl":null,"permalink":"/it/posts/mnemosyne-long-term-memory/","section":"Posts","summary":"","title":"Mnemosyne: La Memoria a Lungo Termine dell'Agente e l'Integrazione con AlloyDB","type":"posts"},{"content":"","date":"31 gennaio 2026","externalUrl":null,"permalink":"/it/tags/ha/","section":"Tags","summary":"","title":"Ha","type":"tags"},{"content":" L\u0026rsquo;Ascesa della Fortezza: Alta Affidabilità, Immutabilità e la Nascita del Cluster Serio # Il percorso di costruzione del Castello Effimero ha raggiunto una soglia critica. Fino ad ora, l\u0026rsquo;infrastruttura era stata un laboratorio di sperimentazione: un singolo Control Plane, un singolo Worker, un guscio funzionale ma fragile. In ingegneria dei sistemi, un cluster con un solo punto di rottura non è un cluster; è solo un ritardo programmato verso il disastro.\nIn questa cronaca tecnica, documento la trasformazione del Castello in una vera fortezza ad Alta Affidabilità (HA). Ho deciso di scalare l\u0026rsquo;architettura a 3 nodi di Control Plane e 2 Worker, stabilendo il minimo indispensabile per garantire la resilienza del piano di controllo e la continuità dei carichi di lavoro. Contemporaneamente, ho affrontato la migrazione della prima applicazione \u0026ldquo;reale\u0026rdquo;: questo blog, che è passato da un setup dinamico e instabile a un\u0026rsquo;architettura stateless ed immutabile, gettando le basi per una pipeline CI/CD di livello professionale.\nFase 1: Ingegnerizzare l\u0026rsquo;Alta Affidabilità (HA) # La prima decisione della giornata è stata radicale: piallare il setup esistente per far nascere un\u0026rsquo;infrastruttura capace di resistere alla perdita di un intero nodo senza interrompere il servizio.\nIl Ragionamento: Perché 3 Control Plane? # In un cluster Kubernetes, il cervello è rappresentato da etcd, il database distribuito che memorizza lo stato di ogni risorsa. etcd utilizza l\u0026rsquo;algoritmo di consenso Raft per garantire che tutti i nodi siano d\u0026rsquo;accordo sui dati.\nHo scelto la configurazione a 3 nodi per un motivo puramente matematico legato al concetto di Quorum. Il quorum è il numero minimo di nodi che devono essere online affinché il cluster possa prendere decisioni. La formula è (n/2) + 1.\nCon 1 nodo, il quorum è 1 (nessuna tolleranza ai guasti). Con 2 nodi, il quorum è 2 (se uno muore, il cluster si blocca). Con 3 nodi, il quorum è 2. Questo significa che posso perdere un intero nodo e il Castello continuerà a funzionare perfettamente. Passare a 3 nodi trasforma il cluster da un giocattolo a una piattaforma di produzione.\nDettagli dell\u0026rsquo;Infrastruttura Proxmox # Ho configurato Terraform per gestire 5 macchine virtuali su Proxmox:\nVIP (Virtual IP): 192.168.1.210 - Il punto di ingresso unico per l\u0026rsquo;API di Kubernetes. CP-01, 02, 03: IP .211, .212, .213 - Il cervello distribuito. Worker-01, 02: IP .214, .215 - Le braccia operative dove girano i Pod. Fase 2: Lo Struggle del Quorum e la Lotta contro i Fantasmi # L\u0026rsquo;implementazione dell\u0026rsquo;Alta Affidabilità si è rivelata più complessa del previsto a causa di un fenomeno che ho ribattezzato \u0026ldquo;conflitto di identità dei fantasmi\u0026rdquo;.\nL\u0026rsquo;Analisi dell\u0026rsquo;Errore: etcd in stallo # Dopo aver lanciato il provisioning, i nodi sono apparsi su Proxmox, ma il cluster non riusciva a formarsi. Monitorando lo stato con talosctl service etcd, ho visto i servizi bloccati in stato Preparing.\nL\u0026rsquo;investigazione tramite talosctl get members ha rivelato una situazione caotica: i nuovi nodi cercavano di comunicare, ma vedevano nel database identità duplicate associate agli stessi IP. Questo è successo perché, durante i test precedenti, avevo riutilizzato gli stessi indirizzi IP senza eseguire un wipe completo dei dischi. etcd, trovando residui di una vecchia configurazione, rifiutava di formare un nuovo quorum per proteggere l\u0026rsquo;integrità dei dati.\nLa Soluzione: Tabula Rasa e Shift di Rete # Ho deciso di applicare la filosofia suprema del Castello Effimero: se non è pulito, non è affidabile.\nHo eseguito un talosctl reset su tutti i nodi contemporaneamente per piallare ogni residuo magnetico sui dischi virtuali. Ho spostato l\u0026rsquo;intero range di IP del cluster (da .22x a .21x) per forzare ogni componente di rete, inclusa la cache ARP del router, a dimenticare i \u0026ldquo;fantasmi\u0026rdquo; del passato. Dopo questo reset totale, il provisioning è filato liscio. I tre cervelli si sono riconosciuti, hanno eletto un leader e il VIP .210 è salito online in meno di 2 minuti. Questo risultato è stato particolarmente soddisfacente dopo ore di troubleshooting su conflitti di certificati invisibili.\nFase 3: La Rivoluzione Stateless - Migrazione del Blog # Con una base HA solida, era giunto il momento di deployare il primo carico di lavoro non infrastrutturale: il blog Hugo.\nIl Ragionamento: Dallo Stato all\u0026rsquo;Immutabilità # Il setup precedente del blog era basato su un container git-sync che scaricava i sorgenti da GitHub e un\u0026rsquo;istanza di Hugo che compilava il sito all\u0026rsquo;interno del cluster.\nHo deciso di abbandonare questo approccio per tre motivi fondamentali:\nSicurezza (Zero Trust): Il vecchio metodo richiedeva di conservare un token GitHub o una chiave SSH dentro il cluster. Rimuovendo git-sync, il cluster non ha più bisogno di sapere che esiste un repository Git dei sorgenti. Affidabilità: Se GitHub fosse andato giù, il blog non sarebbe partito. Ora, il blog dipende solo dall\u0026rsquo;immagine Docker salvata su Docker Hub. Velocità: Un\u0026rsquo;immagine immutabile contenente solo i file già compilati e un web server leggero parte in millisecondi, mentre Hugo impiegava secondi preziosi per generare il sito ad ogni avvio. Deep-Dive: Docker Multi-Stage Build # Per implementare questa visione, ho scritto un Dockerfile multi-stage. Questo approccio permette di separare l\u0026rsquo;ambiente di build dall\u0026rsquo;ambiente di runtime, garantendo immagini minuscole e sicure.\n# Stage 1: Builder FROM hugomods/hugo:std AS builder WORKDIR /src COPY . . # Generate static site with optimizations RUN hugo --minify # Stage 2: Runner FROM nginx:stable-alpine # Copy build artifacts, leaving behind compiler and source code COPY --from=builder /src/public /usr/share/nginx/html EXPOSE 80 CMD [\u0026#34;nginx\u0026#34;, \u0026#34;-g\u0026#34;, \u0026#34;daemon off;\u0026#34;] Fase 4: GitOps di Livello 2 - Tracciabilità Totale # Un\u0026rsquo;infrastruttura seria richiede un workflow di rilascio serio. Ho deciso di implementare un sistema di Image Tagging basato su Git SHA.\nIl Problema dei Tag Statici # Usare un tag come :latest o :blog è un peccato capitale in Kubernetes. Impedisce il rollback deterministico e inganna Kubernetes, che potrebbe non scaricare la nuova versione se il tag non cambia.\nLa Soluzione: Lo script di pubblicazione intelligente # Ho sviluppato uno script publish.sh che coordina il rilascio tra due repository diversi (blog-src e tazlab-k8s).\nIl processo mentale dello script:\nVerifica che non ci siano modifiche non committate (determinismo). Estrae lo SHA del commit attuale (es. 8c945ac). Builda e pusha l\u0026rsquo;immagine tazzo/tazlab.net:blog-8c945ac. Automazione GitOps: Lo script entra nella cartella locale del repository tazlab-k8s, cerca il file di manifesto del blog e sostituisce il vecchio tag con quello nuovo tramite sed. Esegue il commit e il push automatico su tazlab-k8s. In questo modo, l\u0026rsquo;aggiornamento del blog non è un\u0026rsquo;operazione manuale sul cluster, ma un cambiamento di stato dichiarato su Git. Flux CD rileva il nuovo commit e allinea il cluster entro 60 secondi. Questa è la vera essenza del GitOps: il codice è l\u0026rsquo;unica fonte di verità.\nFase 5: La Caccia al Bug del Port Mapping # Nonostante l\u0026rsquo;architettura fosse corretta, il blog inizialmente rispondeva con un frustrante Connection Refused.\nL\u0026rsquo;Investigazione: Ingress vs Service # Ho iniziato l\u0026rsquo;indagine controllando lo stato dei Pod: erano Running. Ho controllato i log di Traefik e ho notato un comportamento inaspettato: Traefik riceveva il traffico sulla porta 80 ma non riusciva a contattare il backend.\nEseguendo kubectl describe svc hugo-blog, ho scoperto l\u0026rsquo;inghippo. Traefik, per impostazione predefinita nel suo chart Helm, cerca di mappare il traffico verso le porte 8000 (HTTP) e 8443 (HTTPS) dei container. Tuttavia, nel mio manifesto, avevo configurato Nginx per ascoltare sulla porta 80.\nInoltre, l\u0026rsquo;immagine ufficiale di Traefik gira come utente non-root e non ha i permessi per ascoltare su porte inferiori alla 1024 all\u0026rsquo;interno del Pod.\nLa Soluzione: Allineamento delle porte # Ho modificato la configurazione di Traefik in main.tf per gestire esplicitamente il mapping:\nEsterno: Porta 80 (esposta dal LoadBalancer MetalLB). Mapping: Porta 80 del Service -\u0026gt; Porta 8000 del Pod Traefik. Ingress: Traefik instrada poi verso la porta 80 dei Pod del blog (Nginx). # Traefik Port Configuration Fix ports: web: exposedPort: 80 port: 8000 # Internal port where Traefik is authorized to listen websecure: exposedPort: 443 port: 8443 Dopo aver applicato questa modifica, i certificati SSL di Let\u0026rsquo;s Encrypt (gestiti tramite sfida HTTP-01) sono passati istantaneamente da pending a valid. Vedere il lucchetto verde apparire su https://blog.tazlab.net è stato il coronamento di una lunga sessione di debugging.\nRiflessioni post-lab: Verso il Castello Completo # Con un cluster a 5 nodi e un\u0026rsquo;applicazione reale operativa in HA, il Castello Effimero è uscito dalla sua fase embrionale.\nCosa abbiamo ottenuto: # Resilienza Nata dal Consensus: Grazie ai 3 CP, possiamo permetterci guasti hardware senza perdere il controllo della piattaforma. Immutabilità Applicativa: Il blog non è più un ammasso di file sincronizzati, ma un\u0026rsquo;entità congelata nel tempo, facile da scalare e impossibile da corrompere. Automazione del Backup: Non ho più bisogno di preoccuparmi dei file kubeconfig. Terraform li carica su Infisical non appena il cluster nasce, permettendomi di essere operativo su qualsiasi macchina in pochi istanti. Il Castello è ora pronto per accogliere i prossimi pillar: l\u0026rsquo;osservabilità con Prometheus e Grafana e l\u0026rsquo;hardening del filesystem tramite la Disk Encryption della partizione /var. Ogni passo ci allontana dalla fragilità del \u0026ldquo;ferro\u0026rdquo; e ci avvicina alla libertà del codice puro.\nFine della Cronaca Tecnica - Fase 4: HA e Immutabilità\n","date":"31 gennaio 2026","externalUrl":null,"permalink":"/it/posts/scaling-ephemeral-castle-ha-stateless-blog/","section":"Posts","summary":"","title":"L'Ascesa della Fortezza: Alta Affidabilità, Immutabilità e la Nascita del Cluster Serio","type":"posts"},{"content":"","date":"31 gennaio 2026","externalUrl":null,"permalink":"/it/tags/nginx/","section":"Tags","summary":"","title":"Nginx","type":"tags"},{"content":"","date":"30 gennaio 2026","externalUrl":null,"permalink":"/it/tags/cert-manager/","section":"Tags","summary":"","title":"Cert-Manager","type":"tags"},{"content":" Le Fondamenta dell\u0026rsquo;Accessibilità: Traefik, Cert-Manager e il Pivot Filosofico del Castello # Dopo aver blindato il cuore del Castello Effimero con la cifratura di etcd e aver stabilito il ponte sicuro con Infisical, l\u0026rsquo;infrastruttura si trovava in uno stato di \u0026ldquo;solitudine sicura\u0026rdquo;. Il cluster era protetto, ma isolato. In questa nuova tappa del mio diario tecnico, documento il processo di implementazione dei due pilastri che permettono al Castello di comunicare con il mondo esterno in modo sicuro e automatizzato: Traefik e Cert-Manager.\nL\u0026rsquo;obiettivo della giornata era ambizioso: trasformare un cluster \u0026ldquo;nudo\u0026rdquo; in una piattaforma pronta alla produzione, capace di gestire il traffico HTTPS e il ciclo di vita dei certificati SSL senza alcun intervento manuale. Lungo il percorso, mi sono scontrato con scelte architetturali che hanno messo alla prova la filosofia stessa del progetto, portandomi a un cambiamento di rotta radicale.\nIl Preludio della Fiducia: TazPod come Identity Anchor # Nessuna automazione può iniziare senza un\u0026rsquo;identità verificata. Nel contesto del Castello Effimero, dove la portabilità è il dogma supremo, non posso permettermi di lasciare chiavi di accesso seminate sul disco rigido del mio laptop. Qui entra in gioco TazPod.\nIl processo di bootstrap inizia sempre nel terminale. Attraverso il comando tazpod pull, attivo il \u0026ldquo;Ghost Mount\u0026rdquo;: un\u0026rsquo;area di memoria cifrata, isolata tramite Linux Namespaces, dove risiedono i token di sessione di Infisical. È questo passaggio che permette a Terraform di autenticarsi verso l\u0026rsquo;istanza EU di Infisical e recuperare i segreti del cluster (come il token di Proxmox o le chiavi S3).\nHo popolato il file secrets.tfvars attingendo da questa enclave sicura. Questo approccio garantisce che le credenziali \u0026ldquo;madre\u0026rdquo; non vengano mai scritte in chiaro sul filesystem persistente, mantenendo il mio ambiente di lavoro pronto a scomparire in qualsiasi momento senza lasciare tracce. Una volta che Terraform ha i suoi token, la danza del provisioning ha inizio.\nFase 1: Traefik - Il Regista del Traffico # Per gestire il traffico in ingresso, la scelta è ricaduta su Traefik. In Kubernetes, un Ingress Controller è il componente che ascolta le richieste provenienti dall\u0026rsquo;esterno e le smista ai servizi corretti all\u0026rsquo;interno del cluster.\nIl Ragionamento: Perché Traefik e non Nginx? # Ho deciso di utilizzare Traefik principalmente per la sua natura \u0026ldquo;Cloud Native\u0026rdquo; e la sua capacità di auto-configurarsi leggendo le annotazioni delle risorse Kubernetes. Rispetto all\u0026rsquo;Ingress di Nginx, Traefik offre una gestione più fluida dei Custom Resource Definitions (CRD), come l\u0026rsquo;IngressRoute, che permette una granularità di configurazione superiore per il routing del traffico.\nAvrei potuto scegliere un approccio basato su un DaemonSet, facendo girare Traefik su ogni nodo, ma per il cluster \u0026ldquo;Blue\u0026rdquo; (composto da un solo worker operativo) ho optato per un Deployment classico con una singola replica. Questo riduce il consumo di risorse e semplifica la gestione della persistenza, qualora fosse necessaria. In un\u0026rsquo;architettura più vasta, lo scaling sarebbe gestito da un Horizontal Pod Autoscaler basato sulle metriche di traffico.\nIntegrazione IaC # Traefik non è stato installato via Flux, ma integrato direttamente nel main.tf di ephemeral-castle. Questa è una scelta di design fondamentale: l\u0026rsquo;Ingress è un componente dell\u0026rsquo;infrastruttura di base, non un\u0026rsquo;applicazione. Deve nascere insieme al cluster.\n# Traefik Ingress Controller Configuration resource \u0026#34;helm_release\u0026#34; \u0026#34;traefik\u0026#34; { name = \u0026#34;traefik\u0026#34; repository = \u0026#34;https://traefik.github.io/charts\u0026#34; chart = \u0026#34;traefik\u0026#34; namespace = kubernetes_namespace.traefik.metadata[0].name version = \u0026#34;34.0.0\u0026#34; values = [ \u0026lt;\u0026lt;-EOT deployment: kind: Deployment replicas: 1 podSecurityContext: fsGroup: 65532 additionalArguments: - \u0026#34;--entrypoints.web.http.redirections.entryPoint.to=websecure\u0026#34; - \u0026#34;--entrypoints.web.http.redirections.entryPoint.scheme=https\u0026#34; ports: web: exposedPort: 80 websecure: exposedPort: 443 service: enabled: true type: LoadBalancer annotations: # Static IP from MetalLB Pool metallb.universe.tf/loadBalancerIPs: 192.168.1.240 persistence: enabled: false # Switched to stateless EOT ] depends_on = [helm_release.longhorn, kubectl_manifest.metallb_config] } Fase 2: Cert-Manager e il Pivot Filosofico # Un Ingress senza HTTPS è un\u0026rsquo;arma spuntata. Per automatizzare il rilascio dei certificati TLS via Let\u0026rsquo;s Encrypt, ho introdotto Cert-Manager. Qui si è consumato il vero scontro ideologico della giornata.\nL\u0026rsquo;Errore Iniziale: La tentazione del DNS-01 # Inizialmente, ho configurato Cert-Manager per utilizzare la sfida DNS-01 tramite Cloudflare. Il vantaggio tecnico è innegabile: permette di generare certificati Wildcard (*.tazlab.net), semplificando enormemente la gestione dei sottodomini. Ho creato l\u0026rsquo;integrazione con Infisical per recuperare il Cloudflare API Token e ho visto con soddisfazione il primo certificato wildcard apparire nel cluster.\nL\u0026rsquo;Investigazione: Il tradimento dell\u0026rsquo;Agnosticismo # Mentre osservavo il certificato pronto, ho capito che stavo violando il primo comandamento dell\u0026rsquo;Ephemeral Castle: l\u0026rsquo;indipendenza dal fornitore. Legando l\u0026rsquo;infrastruttura di base a Cloudflare, stavo creando un \u0026ldquo;lock-in\u0026rdquo;. Se un domani volessi donare questo progetto alla comunità o utilizzarlo per un cliente che usa DNS diversi, dovrei riscrivere la logica del ClusterIssuer.\nHo deciso di fare un passo indietro. Ho distrutto la configurazione Cloudflare e sono passato alla sfida HTTP-01.\nDeep-Dive: DNS-01 vs HTTP-01 # DNS-01: Cert-manager scrive un record TXT nel tuo DNS per provare la proprietà. Permette i wildcard ma richiede un\u0026rsquo;integrazione specifica per ogni provider (Cloudflare, Route53, ecc.). HTTP-01: Cert-manager espone un file temporaneo sulla porta 80. Let\u0026rsquo;s Encrypt lo legge e valida il dominio. È universale e agnostico rispetto al DNS, ma non permette i wildcard. Per il Castello, l\u0026rsquo;agnosticismo è più importante della comodità di un unico certificato. Ogni app (Blog, Grafana, ecc.) richiederà ora il proprio certificato specifico. È una scelta più pulita e coerente con un\u0026rsquo;architettura modulare.\nFase 3: Analisi degli Errori e \u0026ldquo;The Ephemeral Way\u0026rdquo; # Il passaggio da una configurazione all\u0026rsquo;altra non è stato indolore. Durante l\u0026rsquo;aggiornamento di Traefik tramite Terraform, il comando è andato in timeout.\nLo Struggle: Helm in limbo # Ho visto la risorsa helm_release.traefik bloccata in stato pending-install. Quando Terraform va in timeout durante un\u0026rsquo;installazione Helm, il cluster rimane in uno stato inconsistente: la release esiste nel database di Helm ma Terraform ha perso il \u0026ldquo;puntamento\u0026rdquo; (state).\nAl tentativo successivo, ricevevo l\u0026rsquo;errore: Error: cannot re-use a name that is still in use\nIl processo mentale di risoluzione:\nHo controllato lo stato reale con helm list -n traefik. Ho provato a importare la risorsa nello stato di Terraform (terraform import), ma la release era segnata come \u0026ldquo;failed\u0026rdquo; e non importabile. Ho adottato la soluzione \u0026ldquo;Ephemeral\u0026rdquo;: ho disinstallato manualmente Traefik con helm uninstall, rimosso la risorsa dallo stato di Terraform (terraform state rm) e cancellato il namespace per pulire ogni residuo di PVC. Ho rilanciato terraform apply. Questo approccio \u0026ldquo;tabula rasa\u0026rdquo; è il cuore del progetto. Invece di debuggare per ore un database Helm corrotto, riporto il sistema allo stato zero e lascio che il codice dichiarativo lo ricostruisca correttamente.\nFase 4: La Configurazione Finale Agnostica # Ecco come si presenta ora il ClusterIssuer universale nel Castello. Non ha bisogno di API token esterni, gli basta un\u0026rsquo;email per Let\u0026rsquo;s Encrypt.\n# letsencrypt-issuer.tf (integrated in main.tf) resource \u0026#34;kubectl_manifest\u0026#34; \u0026#34;letsencrypt_issuer\u0026#34; { yaml_body = \u0026lt;\u0026lt;-EOT apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-issuer spec: acme: email: ${var.acme_email} server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-issuer-account-key solvers: - http01: ingress: class: traefik EOT depends_on = [helm_release.cert_manager, helm_release.traefik] } Abbiamo inoltre implementato il Zero-Hardcoding totale. Ogni IP, ogni dominio (tazlab.net) e ogni parametro è gestito via variables.tf e terraform.tfvars. Il codice è ora una \u0026ldquo;scatola vuota\u0026rdquo; pronta ad essere riempita con qualsiasi configurazione.\nRiflessioni post-lab: Cosa significa questo setup? # Con l\u0026rsquo;implementazione di Traefik e Cert-Manager (HTTP-01), il Castello ha completato la sua fase di \u0026ldquo;Infrastruttura di Base\u0026rdquo;.\nCosa abbiamo imparato: # Stateless è meglio: Rimuovendo ACME da Traefik e delegandolo a Cert-Manager, abbiamo reso l\u0026rsquo;Ingress Controller totalmente stateless. Possiamo distruggerlo e ricrearlo senza preoccuparci di perdere i file .json dei certificati. L\u0026rsquo;indipendenza ha un prezzo: Rinunciare ai wildcard cert è un piccolo fastidio operativo, ma garantisce che il Castello possa \u0026ldquo;atterrare\u0026rdquo; su qualsiasi provider DNS senza modifiche al codice core. Il Castello è una Fabbrica: La struttura attuale permette di clonare l\u0026rsquo;intera cartella del provider Proxmox, cambiare tre righe nel file .tfvars e avere un nuovo cluster funzionante in meno di 10 minuti. Mancano ancora degli elementi per definire questa base \u0026ldquo;completa\u0026rdquo; (Prometheus e Grafana sono i prossimi sulla lista), ma la via è tracciata. Il resto del lavoro è ora nelle mani di Flux, che inizierà a popolare il Castello con le applicazioni reali, partendo dal Blog che state leggendo.\nFine della Cronaca Tecnica - Fase 3: Ingress e Automazione Certificati\n","date":"30 gennaio 2026","externalUrl":null,"permalink":"/it/posts/extending-ephemeral-castle-ingress-automation/","section":"Posts","summary":"","title":"Le Fondamenta dell'Accessibilità: Traefik, Cert-Manager e il Pivot Filosofico del Castello","type":"posts"},{"content":"","date":"30 gennaio 2026","externalUrl":null,"permalink":"/it/tags/letsencrypt/","section":"Tags","summary":"","title":"Letsencrypt","type":"tags"},{"content":" Le Mura del Castello: Ingegnerizzare la Sicurezza Zero-Trust nell\u0026rsquo;Infrastruttura Ephemerale # Costruire un\u0026rsquo;infrastruttura immutabile è un esercizio di disciplina, ma renderla sicura senza sacrificare la portabilità è una sfida di architettura. Dopo aver gettato le basi del Castello Effimero su Proxmox e aver stabilito il ciclo di riconciliazione con Flux, mi sono reso conto che le fondamenta erano solide ma le mura erano ancora vulnerabili. I segreti risiedevano in file YAML cifrati con SOPS all\u0026rsquo;interno del repository Git: una soluzione funzionale, ma che introduceva un attrito operativo non trascurabile e un accoppiamento troppo stretto con le chiavi di cifratura locali.\nIn questa cronaca tecnica, documento il passaggio a un modello di sicurezza di livello produttivo, dove la fiducia non è mai presunta (Zero-Trust) e i segreti fluiscono come entità dinamiche, mai persistite su disco in chiaro.\nLa Scintilla: Il Punto Zero della Fiducia con TazPod # Ogni fortezza ha bisogno di una chiave, ma dove risiede questa chiave quando il cavaliere è nomade? La mia risposta è TazPod. Prima di poter lanciare un solo comando Terraform, devo stabilire un canale sicuro verso la mia fonte di verità: Infisical.\nHo deciso di utilizzare il TazPod non solo come ambiente di sviluppo, ma come vero e proprio \u0026ldquo;ancoraggio di identità\u0026rdquo;. Attraverso il comando tazpod pull, attivo il \u0026ldquo;Ghost Mount\u0026rdquo;. In questo stato, il TazPod crea un namespace Linux isolato e monta un\u0026rsquo;area di memoria criptata dove scarica i token di sessione di Infisical. Questo passaggio è cruciale: i token che permettono a Terraform di leggere le chiavi del cluster non toccano mai il disco del computer ospite in chiaro.\nPerché Infisical? La scelta è ricaduta su Infisical (istanza EU per conformità e latenza) per superare i limiti di SOPS. SOPS richiede che ogni collaboratore (o ogni istanza CI/CD) possieda la chiave privata Age o l\u0026rsquo;accesso a un KMS. Con Infisical, ho centralizzato la gestione dei segreti in una piattaforma che offre audit log, rotazione e, soprattutto, un\u0026rsquo;integrazione nativa con Kubernetes tramite Machine Identities.\nUna volta sbloccato il TazPod, ho popolato il file secrets.tfvars con il client_id e il client_secret della Machine Identity. Questo file è la \u0026ldquo;testa di ponte\u0026rdquo;: è l\u0026rsquo;unica informazione sensibile necessaria per avviare la danza dell\u0026rsquo;automazione, ed è rigorosamente escluso dal controllo di versione tramite .gitignore.\nFase 1: Hardening del Cuore - Talos Secretbox ed etcd Encryption # Kubernetes, per sua natura, memorizza tutte le risorse, inclusi i Secret, all\u0026rsquo;interno di etcd. Se un attaccante dovesse ottenere l\u0026rsquo;accesso ai file di dati di etcd sul disco del Control Plane, potrebbe estrarre ogni chiave, certificato o password del cluster. In una configurazione standard, questi dati sono memorizzati in chiaro.\nIl Ragionamento tecnico # Ho deciso di implementare la Secretbox Encryption di Talos. Talos permette di patchare la configurazione del nodo per includere una chiave di cifratura a 32 byte (AES-GCM) che viene utilizzata per criptare i dati prima che vengano scritti in etcd.\nPerché non usare la cifratura nativa di Kubernetes (EncryptionConfiguration)? La risposta risiede nella semplicità operativa di Talos. Gestire l\u0026rsquo;EncryptionConfiguration manualmente richiede la creazione di file sul nodo e la gestione della rotazione tramite API server. Talos astrae questo processo nella sua configurazione dichiarativa, permettendomi di gestire la chiave come un qualsiasi altro parametro IaC.\nL\u0026rsquo;Investigazione: Il disastro della migrazione a caldo # Il piano iniziale prevedeva l\u0026rsquo;applicazione della patch su un cluster già esistente. Ho generato una chiave sicura con:\nopenssl rand -base64 32 L\u0026rsquo;ho caricata su Infisical e ho aggiornato il manifesto Terraform per iniettarla nel Control Plane. Tuttavia, al momento del terraform apply, il disastro: i Pod core del cluster hanno iniziato a fallire. Flux è andato in CrashLoopBackOff, il helm-controller non riusciva più a leggere i suoi token.\nControllando i log del kube-apiserver con talosctl logs, ho trovato l\u0026rsquo;errore fatale: \u0026quot;failed to decrypt data\u0026quot; err=\u0026quot;output array was not large enough for encryption\u0026quot;\nL\u0026rsquo;API server era entrato in uno stato di confusione: cercava di decifrare segreti esistenti (scritti in chiaro) usando la nuova chiave della Secretbox, o peggio, aveva parzialmente cifrato alcuni dati rendendoli illeggibili. Il cluster era corrotto.\nLa Via Ephemerale: Distruzione e Rinascita # Di fronte a un cluster Kubernetes compromesso, un amministratore tradizionale passerebbe ore a tentare di riparare etcd. Ma questo è il Castello Effimero. Ho deciso di onorare la filosofia del progetto: non riparare, ricreare.\nHo eseguito un reset aggressivo:\nHo rimosso manualmente le risorse \u0026ldquo;fantasma\u0026rdquo; dallo stato di Terraform (terraform state rm). Ho distrutto le VM su Proxmox. Ho rilanciato l\u0026rsquo;intero provisioning. Il cluster è rinato in 5 minuti, ma questa volta con la Secretbox attiva fin dal primo secondo di vita. Ogni dato scritto in etcd dal processo di bootstrap è nato già cifrato. Questa è la vera potenza dell\u0026rsquo;immutabilità: la capacità di risolvere problemi complessi tornando a uno stato noto e pulito.\n# Patch snippet applied in main.tf resource \u0026#34;talos_machine_configuration_apply\u0026#34; \u0026#34;cp_config\u0026#34; { client_configuration = talos_machine_secrets.this.client_configuration machine_configuration_input = data.talos_machine_configuration.controlplane.machine_configuration node = var.control_plane_ip config_patches = [ yamlencode({ machine = { # ... networking and installation ... } cluster = { secretboxEncryptionSecret = data.infisical_secrets.talos_secrets.secrets[\u0026#34;TALOS_SECRETBOX_KEY\u0026#34;].value } }) ] } Fase 2: L\u0026rsquo;Ambasciatore Dinamico - External Secrets Operator (ESO) # Con il database del cluster al sicuro, il passo successivo è stato eliminare la necessità di memorizzare segreti applicativi nel repository Git. SOPS è un ottimo strumento, ma introduce un problema: la rotazione dei segreti richiede un nuovo commit e un nuovo push.\nPerché External Secrets Operator? # Ho scelto di installare External Secrets Operator (ESO) come pilastro fondamentale del Castello. ESO non memorizza i segreti; agisce come un ponte tra Kubernetes e un fornitore esterno (Infisical).\nIl vantaggio è radicale: in Git scrivo un oggetto ExternalSecret che descrive quale segreto voglio e dove deve finire in Kubernetes. ESO si occupa di contattare Infisical via API, recuperare il valore e creare un Secret nativo di Kubernetes solo nella memoria RAM del cluster. Se cambio un valore su Infisical, ESO lo aggiorna nel cluster in tempo reale, senza alcun intervento su Git.\nLa Sfida dell\u0026rsquo;Autenticazione: Universal Auth # Per far parlare ESO con Infisical in modo sicuro, ho evitato l\u0026rsquo;uso di semplici token statici. Ho implementato il metodo Universal Auth (Machine Identity).\nIl processo mentale è stato questo: Terraform crea un segreto Kubernetes iniziale contenente il clientId e il clientSecret della Machine Identity. Poi, configura un ClusterSecretStore, una risorsa che istruisce ESO su come autenticarsi a livello di intero cluster.\nDurante l\u0026rsquo;installazione, mi sono scontrato con lo schema rigido della versione 0.10.3 di ESO. Un errore di configurazione nel ClusterSecretStore ha bloccato la sincronizzazione con un laconico InvalidProviderConfig. Analizzando il CRD con:\nkubectl get crd clustersecretstores.external-secrets.io -o yaml Ho scoperto che i campi erano cambiati rispetto alle versioni precedenti. La sezione universalAuth era diventata universalAuthCredentials e richiedeva riferimenti espliciti a chiavi di segreti Kubernetes.\nEcco la configurazione finale e corretta che ho integrato direttamente nel provisioning Terraform:\nresource \u0026#34;kubectl_manifest\u0026#34; \u0026#34;infisical_store\u0026#34; { yaml_body = \u0026lt;\u0026lt;-EOT apiVersion: external-secrets.io/v1beta1 kind: ClusterSecretStore metadata: name: infisical-tazlab spec: provider: infisical: hostAPI: https://eu.infisical.com secretsScope: environmentSlug: ${var.infisical_env_slug} projectSlug: ${var.infisical_project_slug} auth: universalAuthCredentials: clientId: name: ${kubernetes_secret.infisical_machine_identity.metadata[0].name} namespace: ${kubernetes_secret.infisical_machine_identity.metadata[0].namespace} key: clientId clientSecret: name: ${kubernetes_secret.infisical_machine_identity.metadata[0].name} namespace: ${kubernetes_secret.infisical_machine_identity.metadata[0].namespace} key: clientSecret EOT depends_on = [helm_release.external_secrets, kubernetes_secret.infisical_machine_identity] } Fase 3: Modularizzazione e Pulizia - La Fabbrica di Castelli # L\u0026rsquo;ultimo atto di questa giornata di consolidamento è stato il refactoring del codice. Un\u0026rsquo;infrastruttura ephemerale deve essere replicabile. Se domani volessi creare un cluster \u0026ldquo;Green\u0026rdquo; identico al \u0026ldquo;Blue\u0026rdquo; ma isolato, non dovrei riscrivere il codice, ma solo cambiare i parametri.\nIl concetto di Zero-Hardcoding # Ho deciso di applicare rigorosamente il principio del Zero-Hardcoding. Ho rimosso ogni IP statico, ogni nome di cartella Infisical e ogni URL di repository dai file main.tf e providers.tf. Tutto è stato spostato in un sistema a tre livelli:\nvariables.tf: Definisce lo schema. Quali dati servono? Di che tipo sono? Quali sono i default sicuri? terraform.tfvars: Definisce la topologia. Qui risiedono gli IP dei nodi, l\u0026rsquo;URL del repo GitOps e gli slug dei progetti Infisical. Questo file viene committato: descrive cosa è il castello, non come aprirlo. secrets.tfvars: L\u0026rsquo;unico file proibito. Contiene le credenziali della Machine Identity. Grazie alla modifica del .gitignore, questo file rimane solo sulla mia workstation protetta (o nel vault del TazPod). # Modularization example in providers.tf provider \u0026#34;infisical\u0026#34; { host = \u0026#34;https://eu.infisical.com\u0026#34; client_id = var.infisical_client_id client_secret = var.infisical_client_secret } provider \u0026#34;proxmox\u0026#34; { endpoint = var.proxmox_endpoint # Proxmox secrets are now dynamically retrieved from Infisical via data source api_token = \u0026#34;${data.infisical_secrets.talos_secrets.secrets[\u0026#34;PROXMOX_TOKEN_ID\u0026#34;].value}=${data.infisical_secrets.talos_secrets.secrets[\u0026#34;PROXMOX_TOKEN_SECRET\u0026#34;].value}\u0026#34; } L\u0026rsquo;Addio definitivo a SOPS # Con questa mossa, ho potuto finalmente eliminare proxmox-secrets.enc.yaml. Non ci sono più file cifrati che appesantiscono il repository. La dipendenza dal provider SOPS in Terraform è stata rimossa. Il \u0026ldquo;Castello\u0026rdquo; è ora più leggero, più veloce da inizializzare e infinitamente più sicuro.\nRiflessioni post-lab: Cosa abbiamo imparato? # Questa fase di implementazione mi ha insegnato che la sicurezza in un ambiente moderno non è un perimetro, ma un flusso.\nAbbiamo tracciato un percorso che parte dalla mente dello sviluppatore (la passphrase del TazPod), attraversa un canale cifrato in RAM, si materializza temporaneamente in variabili Terraform per costruire l\u0026rsquo;infrastruttura, e infine si stabilizza in un operatore Kubernetes (ESO) che mantiene il segreto fluido e aggiornabile.\nRisultati ottenuti: # etcd blindato: Anche con un accesso fisico ai dischi di Proxmox, i dati del cluster sono illeggibili senza la chiave Secretbox. Git pulito: Il repository contiene solo logica, nessuna chiave, nemmeno cifrata. Replicabilità totale: Posso duplicare la cartella del provider, cambiare tre righe nel .tfvars e avere un nuovo cluster pronto alla produzione in meno di 10 minuti. Il Castello ora ha le sue mura. È pronto ad accogliere i servizi che lo renderanno vivo, sapendo che ogni \u0026ldquo;tesoro\u0026rdquo; depositato al suo interno sarà protetto da una crittografia moderna e da un\u0026rsquo;architettura che non dimentica mai la sua natura ephemerale.\nFine della Cronaca Tecnica - Fase 2: Sicurezza e Segreti\n","date":"29 gennaio 2026","externalUrl":null,"permalink":"/it/posts/fortifying-the-ephemeral-castle-security/","section":"Posts","summary":"","title":"Le Mura del Castello: Implementazione di Sicurezza Zero-Trust e Gestione dei Segreti","type":"posts"},{"content":"","date":"29 gennaio 2026","externalUrl":null,"permalink":"/it/tags/talos/","section":"Tags","summary":"","title":"Talos","type":"tags"},{"content":"L\u0026rsquo;architettura non è solo un disegno su carta o un manifesto d\u0026rsquo;intenti. Dopo aver delineato la visione del Castello Effimero, è giunto il momento di sporcarsi le mani con il silicio, i hypervisor e il codice dichiarativo. Questa è la cronaca della prima fase di implementazione: la transizione da un concetto astratto a un cluster Kubernetes funzionale, nato e gestito interamente tramite Infrastructure as Code (IaC).\nHo deciso di iniziare il viaggio nel mio laboratorio locale basato su Proxmox VE. La scelta non è casuale: il controllo totale sull\u0026rsquo;hardware mi permette di iterare rapidamente, testare i limiti dello storage distribuito e comprendere le dinamiche di rete prima di affrontare la complessità (e i costi) del cloud pubblico.\nIl Fondamento: Talos OS e la Morte di SSH # La prima decisione critica ha riguardato il sistema operativo dei nodi. Ho scelto Talos OS. In un mondo abituato a Ubuntu Server o Debian, Talos rappresenta un cambio di paradigma radicale: è un sistema operativo Linux progettato esclusivamente per Kubernetes. È immutabile, minimale e, soprattutto, non ha una shell SSH.\nPerché questa scelta estrema? In un\u0026rsquo;infrastruttura che ambisce a essere \u0026ldquo;effimera\u0026rdquo;, la persistenza di configurazioni manuali all\u0026rsquo;interno di un nodo è il nemico. Eliminando SSH, ho eliminato la tentazione di applicare \u0026ldquo;fix temporanei\u0026rdquo; che diverrebbero permanenti. Ogni modifica deve passare per l\u0026rsquo;API di Talos tramite file di configurazione YAML. Se un nodo si comporta in modo anomalo, non lo riparo: lo distruggo e lo ricreo.\nDeep-Dive: Immutabilità e Sicurezza # L\u0026rsquo;immutabilità significa che il filesystem di root è in sola lettura. Non ci sono gestori di pacchetti come apt o yum. Questo riduce drasticamente la superficie di attacco: anche se un malintenzionato riuscisse a ottenere l\u0026rsquo;accesso a un processo nel nodo, non potrebbe installare rootkit o modificare i binari di sistema. Il quorum di sicurezza del cluster ne beneficia direttamente.\nL\u0026rsquo;Incubo del DHCP e la Transizione a Terraform # L\u0026rsquo;implementazione iniziale è stata tutt\u0026rsquo;altro che fluida. Durante i primi test, ho lasciato che i nodi acquisissero gli indirizzi IP tramite DHCP. È stato un errore fondamentale che ha portato a un incidente tecnico significativo. Dopo un riavvio programmato del server Proxmox, il server DHCP ha assegnato nuovi indirizzi ai nodi del cluster.\nIl risultato? Il Control Plane è diventato irraggiungibile. kubectl non riusciva più a autenticarsi perché i certificati erano legati ai vecchi IP, e il quorum di etcd era distrutto. Ho passato ore a tentare di patchare manualmente i nodi con talosctl patch, cercando di inseguire la nuova topologia di rete.\nÈ qui che ho capito che la gestione manuale o semi-automatica non era sufficiente. Ho deciso di migrare l\u0026rsquo;intero provisioning su Terraform.\nLa Soluzione: Networking Statico Dichiarativo # Ho riscritto i manifesti Terraform per definire staticamente ogni interfaccia di rete. Questo garantisce che, indipendentemente dai riavvii o dalle fluttuazioni della rete, il \u0026ldquo;Castello\u0026rdquo; mantenga la sua forma.\n# Uno scorcio del file providers.tf con la configurazione dei nodi resource \u0026#34;proxmox_vm_qemu\u0026#34; \u0026#34;talos_worker\u0026#34; { count = 3 name = \u0026#34;worker-${count.index + 1}\u0026#34; target_node = \u0026#34;pve\u0026#34; clone = \u0026#34;talos-template\u0026#34; # Configurazione di rete statica per evitare il drift degli IP ipconfig0 = \u0026#34;ip=192.168.1.15${5 + count.index}/24,gw=192.168.1.1\u0026#34; cores = 4 memory = 8192 # L\u0026#39;integrazione con Talos avviene via machine_config # generato tramite il provider Talos dedicato. } L\u0026rsquo;uso di Terraform mi ha permesso di mappare lo stato desiderato dell\u0026rsquo;infrastruttura. Se voglio aggiungere un worker, cambio semplicemente il count da 3 a 4. Terraform calcolerà la differenza e interagirà con le API di Proxmox per clonare la VM, assegnare l\u0026rsquo;IP corretto e iniettare la configurazione Talos.\nStorage Distribuito: La Sfida di Longhorn # Un cluster senza storage persistente è solo un esercizio accademico. Per il Castello Effimero, avevo bisogno di un sistema di storage che fosse resiliente quanto il cluster stesso. La scelta è ricaduta su Longhorn.\nLonghorn trasforma lo spazio disco locale dei nodi worker in un pool di storage distribuito e replicato. Tuttavia, far girare Longhorn su un sistema operativo immutabile come Talos richiede accortezze specifiche. Talos non include i binari per iSCSI (necessari per il montaggio dei volumi) o NBD (Network Block Device) per impostazione predefinita.\nAnalisi degli Errori: Il Problema del Mount # Inizialmente, i pod non riuscivano a passare dallo stato ContainerCreating a Running. Controllando i log del sistema con kubectl describe pod, ho notato un errore ricorrente: executable file not found in $PATH riferito a iscsid.\nIn un sistema tradizionale, avrei installato open-iscsi con un comando. Su Talos, ho dovuto istruire il sistema a caricare i moduli kernel necessari tramite la machineConfig di Talos, utilizzando le estensioni di sistema (system extensions).\n# Estratto della configurazione Talos per abilitare iSCSI machine: install: extensions: - image: ghcr.io/siderolabs/iscsi-tools:v0.1.4 - image: ghcr.io/siderolabs/util-linux-tools:v2.39.3 Questo passaggio è fondamentale: trasforma il nodo da un\u0026rsquo;entità generica a un componente specializzato dello storage cluster. Una volta configurato, Longhorn ha iniziato a replicare i dati tra i nodi, garantendo che anche in caso di perdita totale di un worker, i volumi del blog o dei database rimangano accessibili.\nGitOps: Il Cuore Pulsante con Flux CD # Il Castello Effimero non è configurato manualmente. Una volta che Terraform ha creato le VM e Talos ha inizializzato Kubernetes, entra in gioco Flux CD.\nFlux è un operatore GitOps che mantiene il cluster sincronizzato con un repository GitHub. Ho creato due repository distinti:\nephemeral-castle: Contiene il codice Terraform e le configurazioni \u0026ldquo;hardware\u0026rdquo; (IP, risorse VM). tazlab-k8s: Contiene i manifesti Kubernetes (Deployment, Service, HelmRelease). Perché non un unico repository? # Ho deciso di separare l\u0026rsquo;infrastruttura dal carico di lavoro. Terraform gestisce il \u0026ldquo;ferro\u0026rdquo; (anche se virtuale), mentre Flux gestisce l\u0026rsquo;ecosistema applicativo. Questa separazione permette di distruggere l\u0026rsquo;intero cluster mantenendo intatta la logica applicativa. Quando il nuovo cluster emerge, Flux rileva la sua presenza e inizia a scaricare i manifesti, ricreando l\u0026rsquo;ambiente esattamente come era prima.\nDeep-Dive: Il Loop di Riconciliazione # Il concetto chiave di Flux è il Reconciliation Loop. Flux monitora costantemente il repository Git. Se modifico il numero di repliche di un microservizio nel file YAML su GitHub, Flux rileva il \u0026ldquo;drift\u0026rdquo; tra lo stato attuale del cluster e lo stato desiderato nel repository e applica la modifica in pochi secondi. Questo elimina la necessità di eseguire comandi manuali come kubectl apply -f.\nSicurezza e Segreti: SOPS e l\u0026rsquo;Integrazione Git # Versionare l\u0026rsquo;infrastruttura su GitHub comporta un rischio: la fuga di segreti. Password di Proxmox, chiavi SSH, token API\u0026hellip; niente di tutto questo deve finire in chiaro nel repository.\nHo adottato SOPS (Secrets Operations) criptando i file sensibili con chiavi Age. I file risultanti (es. proxmox-secrets.enc.yaml) sono perfettamente sicuri da pushare su un repository privato. Terraform e Flux sono configurati per decriptare questi file \u0026ldquo;al volo\u0026rdquo; durante l\u0026rsquo;esecuzione, garantendo che le credenziali non tocchino mai il disco in formato non cifrato.\n# Esempio di cifratura di un file di segreti sops --encrypt --age $(cat key.txt) secrets.yaml \u0026gt; secrets.enc.yaml Riflessioni Post-Lab: Cosa abbiamo imparato? # Questa prima tappa del viaggio ha confermato una verità fondamentale del DevOps moderno: l\u0026rsquo;automazione è dolorosa all\u0026rsquo;inizio, ma libera in seguito.\nConfigurare gli IP statici in Terraform è stato più lento che assegnarli manualmente su Proxmox. Configurare SOPS è stato più complesso che usare variabili d\u0026rsquo;ambiente. Tuttavia, ora dispongo di un\u0026rsquo;infrastruttura che posso replicare premendo un tasto. Il Castello è \u0026ldquo;Effimero\u0026rdquo; perché la sua esistenza fisica è irrilevante; ciò che conta è il codice che lo definisce.\nProssimi Passi # Il Castello ora respira, ma è nudo. Nelle prossime cronache, affronteremo:\nL\u0026rsquo;Ingress Controller: Configurazione di Traefik per gestire il traffico esterno e la generazione automatica di certificati SSL con Let\u0026rsquo;s Encrypt. Il Blog Hugo: Il deploy del sito che state leggendo, completamente automatizzato via CI/CD. Verso le Nuvole: La replica di questa intera architettura su AWS, dimostrando la vera portabilità del Castello Effimero. La strada è ancora lunga, ma le fondamenta sono state gettate nel cemento del codice.\nFine della Cronaca Tecnina - Tappa 1\n","date":"28 gennaio 2026","externalUrl":null,"permalink":"/it/posts/implementing-the-ephemeral-castle-proxmox/","section":"Posts","summary":"","title":"Dalla Visione al Silicio: Implementare il Castello Effimero su Proxmox","type":"posts"},{"content":"","date":"28 gennaio 2026","externalUrl":null,"permalink":"/it/categories/tutorials/","section":"Categories","summary":"","title":"Tutorials","type":"categories"},{"content":" Introduzione: Il Peso della Teoria contro la Realtà del Ferro # Nelle scorse settimane ho dedicato molto tempo alla costruzione di una workstation immutabile e sicura. Tuttavia, un\u0026rsquo;officina perfettamente organizzata non serve a nulla se il \u0026ldquo;cantiere\u0026rdquo; — il mio cluster Kubernetes basato su Talos Linux e Proxmox — non è in grado di reggere l\u0026rsquo;urto di un guasto reale. Il mindset di oggi non era improntato alla costruzione, ma alla distruzione controllata. Volevo capire dove si spezza il filo della resilienza.\nL\u0026rsquo;obiettivo della sessione era chiaro: ora che l\u0026rsquo;infrastruttura è gestita tramite Terraform e vanta 4 nodi worker, è tempo di testare le promesse dell\u0026rsquo;Alta Affidabilità (HA). Ma, come spesso accade nei sistemi distribuiti, ciò che sulla carta è una transizione indolore, nella realtà può trasformarsi in un effetto domino catastrofico. In questa cronaca documenterò come un semplice cambio di IP e uno spegnimento forzato abbiano portato il cluster sull\u0026rsquo;orlo del collasso, e come ho deciso di ricostruire le fondamenta per impedire che accada di nuovo.\nFase 1: Espansione e Consolidamento IaC # Il primo passo è stato l\u0026rsquo;allineamento del cluster alla nuova configurazione desiderata. Ho deciso di utilizzare Terraform per gestire l\u0026rsquo;intero ciclo di vita dei nodi su Proxmox. L\u0026rsquo;uso di un approccio Infrastructure as Code (IaC) non è solo una questione di comodità; è una necessità per garantire la replicabilità del \u0026ldquo;Castello Effimero\u0026rdquo; di cui ho scritto in precedenza.\nHo configurato 4 nodi worker, distribuendo i carichi di lavoro in modo che nessun singolo nodo fosse un Single Point of Failure (SPOF).\nDeep-Dive: Perché 4 Worker e 3 Control Plane? # In Kubernetes, il concetto di Quorum è vitale. Il piano di controllo (Control Plane) utilizza etcd, un database distribuito basato sull\u0026rsquo;algoritmo di consenso Raft. Per sopravvivere alla perdita di un nodo, serve un numero dispari di membri (3 è il minimo sindacale). Per i worker, il numero 4 permette di implementare strategie di Antiaffinity robuste: posso permettermi di perdere un nodo per manutenzione e avere ancora 3 nodi su cui distribuire le repliche, mantenendo un\u0026rsquo;alta densità di risorse senza sovraccaricare il ferro.\nFase 2: Il Disastro Inaspettato - L\u0026rsquo;Effetto Domino del Cambio IP # Il test è iniziato con un evento apparentemente banale: il cambio dell\u0026rsquo;IP del nodo di Control Plane. Quello che doveva essere un aggiornamento di routine si è trasformato in un incubo operativo.\nIl Sintomo # Improvvisamente, i servizi interni al cluster hanno smesso di comunicare. I log di CoreDNS e Longhorn hanno iniziato a mostrare errori di tipo No route to host o Connection refused verso l\u0026rsquo;endpoint 10.96.0.1:443.\nL\u0026rsquo;Investigazione # Ho iniziato l\u0026rsquo;indagine controllando lo stato dei pod con kubectl get pods -A. Molti erano in CrashLoopBackOff. Analizzando i log del longhorn-manager:\ntime=\u0026#34;2026-01-25T20:45:28Z\u0026#34; level=error msg=\u0026#34;Failed to list nodes\u0026#34; error=\u0026#34;Get \\\u0026#34;https://10.96.0.1:443/api/v1/nodes\\\u0026#34;: dial tcp 10.96.0.1:443: connect: no route to host\u0026#34; Il problema era profondo: il servizio interno di Kubernetes (kubernetes.default) puntava ancora al vecchio IP fisico del Control Plane (.71) invece del nuovo (.253). Nonostante io avessi aggiornato il kubeconfig esterno, le tabelle di routing interne (gestite da kube-proxy e iptables) erano rimaste incastrate.\nLa Soluzione: Patching Manuale degli Endpoints # Ho deciso di intervenire chirurgicamente sull\u0026rsquo;oggetto Endpoints nel namespace default. Questa è un\u0026rsquo;operazione rischiosa perché solitamente gestita dal controller manager, ma in uno stato di partizione della rete, l\u0026rsquo;intervento manuale era l\u0026rsquo;unica via.\n# Ho estratto la configurazione, corretto l\u0026#39;IP e riapplicata kubectl patch endpoints kubernetes -p \u0026#39;{\u0026#34;subsets\u0026#34;:[{\u0026#34;addresses\u0026#34;:[{\u0026#34;ip\u0026#34;:\u0026#34;192.168.1.253\u0026#34;}],\u0026#34;ports\u0026#34;:[{\u0026#34;name\u0026#34;:\u0026#34;https\u0026#34;,\u0026#34;port\u0026#34;:6443,\u0026#34;protocol\u0026#34;:\u0026#34;TCP\u0026#34;}]}]}\u0026#39; --kubeconfig=kubeconfig Subito dopo, ho forzato un riavvio di coredns e kube-proxy. La rete ha ripreso a respirare, ma le ferite erano ancora aperte a livello di storage.\nFase 3: Il Deadlock di Longhorn e lo Storage RWO # Risolta la rete, mi sono scontrato con la dura realtà dello storage distribuito. Avevo spento forzatamente alcuni nodi durante la fase di instabilità.\nIl Problema: I Volumi Fantasma # Longhorn utilizza volumi di tipo RWO (ReadWriteOnce). Questo significa che un volume può essere montato da un solo nodo alla volta. Quando il nodo worker-new-03 è stato spento bruscamente, il cluster Kubernetes lo ha marcato come NotReady, ma Longhorn ha mantenuto il \u0026ldquo;lock\u0026rdquo; sul volume di Traefik, pensando che il nodo potesse tornare da un momento all\u0026rsquo;altro.\nHo visto il nuovo pod di Traefik bloccato in ContainerCreating per minuti, con questo errore negli eventi: Multi-Attach error for volume \u0026quot;pvc-...\u0026quot; Volume is already exclusively attached to one node and can't be attached to another.\nAnalisi degli Errori: Perché non si sblocca da solo? # Ho analizzato il comportamento: Kubernetes aspetta circa 5 minuti prima di evincere i pod da un nodo morto. Tuttavia, anche dopo l\u0026rsquo;evizione, il CSI (Container Storage Interface) non stacca il volume se non riceve conferma che il nodo originale è spento. È una misura di protezione contro la corruzione dei dati (Split-Brain).\nLa Soluzione: Forzare la Mano al Cluster # Ho deciso di procedere con una pulizia aggressiva dei VolumeAttachments e dei pod zombi.\n# Cancellazione forzata del pod zombi kubectl delete pod traefik-79fcb6d7fd-pwp9v -n traefik --force --grace-period=0 # Rimozione del VolumeAttachment stale kubectl delete volumeattachment csi-5f3b43f479e048a26187... --kubeconfig=kubeconfig Solo dopo queste azioni, Longhorn ha permesso al nuovo nodo di \u0026ldquo;prendere possesso\u0026rdquo; del disco. Questo mi ha insegnato che lo spegnimento forzato in un ambiente con storage RWO richiede quasi sempre un intervento umano per ripristinare la disponibilità del servizio.\nFase 4: Il Limite di Traefik e la Necessità della Statelessness # Durante i test di replica, ho provato ad alzare il numero di istanze di Traefik a 2. Il risultato è stato un fallimento immediato.\nIl Ragionamento: Perché volevo 2 repliche? # In un\u0026rsquo;ottica di Alta Affidabilità, avere una sola istanza di Ingress Controller è un rischio inaccettabile. Se il nodo che ospita Traefik muore, il blog cade (come abbiamo visto nel test). Un normale Deployment dovrebbe permettermi di scalare orizzontalmente.\nLo Scontro con la Realtà # Traefik è configurato per generare certificati SSL tramite Let\u0026rsquo;s Encrypt e salvarli in un file acme.json. Per persistere questi certificati tra i riavvii, ho usato un volume Longhorn. H Qui sta l\u0026rsquo;errore architetturale: essendo il volume RWO, la seconda replica di Traefik non poteva partire perché il disco era già occupato dalla prima.\nHo deciso quindi di mantenere momentaneamente una sola replica, ma ho tracciato un piano per migrare a cert-manager. Usando i Secret di Kubernetes per i certificati, Traefik diventerà completamente stateless, permettendoci di scalare a 3 o più repliche senza conflitti di disco.\nFase 5: Il Test dei 5 Minuti - Automazione vs Prudenza # Ho voluto fare un ultimo esperimento scientifico: spegnere un nodo e cronometrare quanto tempo ci mette il cluster a reagire da solo.\nT+0: Spento forzatamente worker-new-01. T+1: Il nodo è NotReady. Il pod è ancora considerato Running. T+5: Kubernetes marca il pod come Terminating e ne crea uno nuovo su un altro nodo. T+8: Il nuovo pod è ancora in Init:0/1, bloccato dal volume Longhorn. Conclusione del Test # L\u0026rsquo;automazione di Kubernetes funziona per il calcolo, ma fallisce per lo storage persistente RWO in caso di guasti hardware improvvisi. Senza un sistema di Fencing (che spegne fisicamente il nodo tramite Proxmox API), il recupero automatico non è garantito in tempi brevi.\nRiflessioni Post-Lab: La Roadmap verso lo Zero Trust # Questa sessione di \u0026ldquo;stress e sofferenza\u0026rdquo; è stata più istruttiva di mille installazioni pulite. Ho imparato che la resilienza non è un tasto che si preme, ma un equilibrio che si costruisce pezzo dopo pezzo.\nCosa significa questo per la stabilità a lungo termine? # Il cluster ora è molto più solido perché:\nIP Statici e VIP: Ho spostato tutta la gestione sul VIP .250. Se un nodo di controllo muore, il kubeconfig non deve cambiare. Configurazione di Rete: Ho corretto le rotte interne, assicurando che i componenti di sistema parlino con l\u0026rsquo;API corretta. Gestione dello Storage: Ora conosco i limiti di Longhorn e so come intervenire in caso di deadlock. Prossimi Passi # Ho già messo a bilancio due grandi lavori:\nRistrutturazione Traefik: Migrazione a cert-manager per eliminare i volumi RWO e permettere il multi-replica. Sicurezza Etcd: Implementazione della secretbox e della Disk Encryption su Talos per proteggere i segreti a riposo. In conclusione, il cluster TazLab ha superato il suo battesimo del fuoco. Non è ancora perfetto, ma è diventato un sistema capace di fallire con dignità e di essere riparato con precisione chirurgica. La strada verso il \u0026ldquo;Castello Effimero\u0026rdquo; prosegue, un deadlock alla volta.\n","date":"26 gennaio 2026","externalUrl":null,"permalink":"/it/posts/tazlab-resilience-stress-test/","section":"Posts","summary":"","title":"Il Battesimo del Fuoco: Resilienza, Deadlock e Disaster Recovery nel Cluster TazLab","type":"posts"},{"content":"","date":"25 gennaio 2026","externalUrl":null,"permalink":"/it/tags/digital-nomad/","section":"Tags","summary":"","title":"Digital Nomad","type":"tags"},{"content":" Introduzione: Il Paradosso della Persistenza # Nel mio percorso di evoluzione tecnologica, ho sempre combattuto contro il \u0026ldquo;vincolo fisico\u0026rdquo;. Abbiamo iniziato rendendo immutabile la workstation con il progetto TazPod, trasformando il mio ambiente di sviluppo in un\u0026rsquo;enclave sicura, cifrata e trasportabile. Ma una workstation senza il suo cluster è come un artigiano senza la sua officina.\nOggi voglio parlarvi della fase successiva: la trasformazione del mio intero cluster Kubernetes in un Castello Effimero.\nL\u0026rsquo;obiettivo è radicale: superare il concetto di Infrastructure as Code (IaC) tradizionale per approdare a un\u0026rsquo;infrastruttura che sia, per definizione, senza luogo. Non importa se il mio server Proxmox locale esplode o se la corrente si interrompe mentre sono dall\u0026rsquo;altra parte del mondo. Se ho un laptop con Linux e una connessione internet, il mio intero mondo digitale deve poter rinascere in 10 minuti.\nLo Scenario del Disastro (e la Risposta Nomade) # Immaginate questo scenario: sono in viaggio, il mio cluster di casa è irraggiungibile. Forse un guasto hardware fatale o un blackout prolungato. In passato, questo avrebbe significato la fine della produttività.\nOggi, la procedura è quasi rituale:\nPrendo un computer Linux qualunque. Scarico il binario statico di TazPod. Eseguo il \u0026ldquo;Ghost Mount\u0026rdquo;: inserisco la mia passphrase, TazPod contatta Infisical e scarica le mie identità in un\u0026rsquo;area di memoria cifrata. Sono di nuovo operativo. Ho le chiavi, ho gli strumenti, ho la conoscenza. Da questo momento, inizia la ricostruzione del castello.\nIl TazPod: Il Coltellino Svizzero dello Zero Trust # TazPod non è solo un container; è la mia cassetta degli attrezzi digitale. Grazie alla sua architettura in Go e all\u0026rsquo;uso dei Linux Namespaces, garantisce che le mie credenziali non tocchino mai il disco del computer \u0026ldquo;ospite\u0026rdquo; in chiaro.\nCon un accesso istantaneo (meno di 2 minuti), TazPod mi fornisce il ponte verso il cloud. Il disaccoppiamento tra l\u0026rsquo;hardware fisico e la mia sicurezza è totale. Non mi fido del PC che sto usando; mi fido solo della crittografia che TazPod gestisce per me.\nTerraform e Flux: Ricreare il Castello in 10 Minuti # La forza della rinascita risiede nell\u0026rsquo;unione tra Terraform e la filosofia GitOps di Flux.\n1. Il Terreno (Terraform) # Lancio un comando Terraform. In pochi minuti, i nodi vengono allocati su un provider cloud (es. AWS). Non è un cluster enorme, ma il \u0026ldquo;minimo sindacale\u0026rdquo; per l\u0026rsquo;Alta Affidabilità (HA): 3 nodi di Control Plane e 2 Worker. Terraform configura dinamicamente ciò che serve: che sia S3 per lo storage o il puntamento dei DNS su Cloudflare.\n2. Le Fondamenta e le Mura (Flux) # Una volta che i nodi sono pronti, Terraform installa un solo componente: FluxCD. Flux è il maggiordomo del castello. Si connette ai miei repository Git privati e inizia a leggere i manifesti. In una cascata di automazione, Flux ricrea:\nIl networking e l\u0026rsquo;Ingress (Traefik). Le policy di sicurezza e i certificati (Cert-Manager). Tutti i miei servizi applicativi, dal blog ai tool di monitoraggio. 3. I Tesori (Il Ritorno dei Dati) # Un castello vuoto non serve a nulla. I dati, il vero valore, vengono ripescati dai backup criptati su S3. Grazie a Longhorn o a meccanismi di restore nativi, i volumi vengono ripopolati.\nIn circa 7-10 minuti, giro il DNS di Cloudflare verso il nuovo IP pubblico del LoadBalancer. Il mondo non si è accorto di nulla, ma il mio cluster è rinato in un altro continente, su un altro hardware, con la stessa identica configurazione di prima.\nConclusione: La Libertà è un Algoritmo # Questa visione trasforma l\u0026rsquo;infrastruttura in qualcosa di gassoso, capace di espandersi o condensarsi ovunque sia necessario. Non sono più vincolato a un luogo o a un dispositivo fisico.\nIl mio cluster è effimero perché può morire in qualsiasi momento senza dolore. È trasportabile perché vive nei miei repository Git. È sicuro perché le chiavi per sbloccarlo vivono solo nella mia mente e nel mio TazPod.\nQuesta è la quintessenza della resilienza nell\u0026rsquo;era digitale: non possedere nulla di fisico che non possa essere ricreato da una riga di codice in meno di 10 minuti. Il castello è nell\u0026rsquo;aria, e io ho le chiavi per farlo atterrare ovunque io sia.\n","date":"25 gennaio 2026","externalUrl":null,"permalink":"/it/posts/the-ephemeral-castle-vision/","section":"Posts","summary":"","title":"Il Castello Effimero: Verso un'Infrastruttura Nomade e Zero Trust","type":"posts"},{"content":" Introduzione: Il Salto di Specie dell\u0026rsquo;Homelab # Gestire un cluster Kubernetes in un laboratorio domestico è spesso un atto d\u0026rsquo;amore, una miscela di YAML scritti a mano e piccoli aggiustamenti manuali via GUI. Tuttavia, arriva un momento in cui la complessità supera la capacità di memoria del suo amministratore. In Tazlab, quel momento è arrivato oggi. L\u0026rsquo;obiettivo era chiaro: cessare di trattare i nodi del cluster come \u0026ldquo;animali domestici\u0026rdquo; (pets) — ognuno con il suo nome e la sua storia — e iniziare a trattarli come \u0026ldquo;bestiame\u0026rdquo; (cattle) — risorse fungibili, identiche e riproducibili.\nHo deciso di introdurre Terraform per gestire il ciclo di vita del cluster Talos Linux ospitato su Proxmox. Questa non è stata una passeggiata trionfale, ma una cronaca onesta di errori di permessi, conflitti hardware virtuali e problemi di decodifica crittografica. Ecco come ho trasformato Tazlab in una vera infrastruttura definita dal codice.\nFase 1: La Scelta degli Strumenti e l\u0026rsquo;Architettura Silenziosa # Prima di scrivere una singola riga di codice HCL (HashiCorp Configuration Language), ho dovuto affrontare la scelta dei Provider. Nel mondo Proxmox, esistono due correnti principali: il provider legacy di Telmate e il moderno provider di bpg.\nHo deciso di optare per bpg/proxmox. La ragione risiede nella sua capacità di gestire gli oggetti Proxmox con una granularità superiore, specialmente per quanto riguarda gli snippet e la configurazione SDN. Telmate, pur essendo storico, soffre di instabilità croniche nel rilevamento del drift (configuration drift) sulle interfacce di rete nelle versioni di Proxmox 8.x. In un\u0026rsquo;architettura IaC (Infrastructure as Code) professionale, il rilevamento del drift deve essere preciso: Terraform non deve proporre modifiche se nulla è cambiato nella realtà.\nL\u0026rsquo;importanza del Quorum etcd # Un\u0026rsquo;altra decisione critica ha riguardato il Control Plane. Inizialmente ho ipotizzato la creazione di nodi control plane aggiuntivi, ma ho dovuto riflettere sul concetto di Quorum. In un sistema distribuito basato su etcd come Kubernetes, il quorum richiede una maggioranza assoluta ($n/2 + 1$). Passare da uno a due nodi control plane ridurrebbe paradossalmente l\u0026rsquo;affidabilità: se uno dei due cadesse, il cluster rimarrebbe bloccato. Ho quindi deciso di mantenere un singolo nodo control plane per ora, concentrando l\u0026rsquo;automazione sulla scalabilità orizzontale dei nodi worker.\nFase 2: Il Setup dei Permessi - La Prima Barriera # L\u0026rsquo;automazione richiede un\u0026rsquo;identità. Non si può (e non si deve) usare l\u0026rsquo;utente root@pam per Terraform. Ho dovuto creare un utente dedicato e un ruolo con i privilegi minimi necessari. Questo passaggio ha rivelato una delle prime insidie: la documentazione ufficiale spesso omette permessi granulari che diventano critici durante l\u0026rsquo;esecuzione.\nHo dovuto modificare il ruolo TerraformAdmin su Proxmox più volte. L\u0026rsquo;errore più subdolo è stato legato al QEMU Guest Agent. Senza il permesso VM.GuestAgent.Audit, Terraform non riusciva a interrogare Proxmox per conoscere l\u0026rsquo;indirizzo IP assegnato dal DHCP, entrando in un loop di attesa infinito.\nCodice di Setup Proxmox (Shell): # # Creazione del ruolo professionale con permessi granulari pveum role add TerraformAdmin -privs \u0026#34;Datastore.AllocateSpace Datastore.AllocateTemplate Datastore.Audit Pool.Allocate Pool.Audit Sys.Audit Sys.Console Sys.Modify VM.Allocate VM.Audit VM.Clone VM.Config.CDROM VM.Config.Cloudinit VM.Config.CPU VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.PowerMgmt SDN.Use VM.GuestAgent.Audit VM.GuestAgent.Unrestricted\u0026#34; # Creazione utente e generazione token pveum user add terraform-user@pve pveum aclmod / -user terraform-user@pve -role TerraformAdmin pveum user token add terraform-user@pve terraform-token --privsep=0 Fase 3: Scaffolding e il \u0026ldquo;Debito\u0026rdquo; dei Segreti # Ho strutturato il progetto Terraform in modo modulare per separare le responsabilità: versions.tf per i plugin, variables.tf per lo schema dati, data.tf per la lettura dei segreti e main.tf per la logica di business.\nL\u0026rsquo;integrazione SOPS # Tazlab utilizza SOPS con crittografia Age. Questa è stata la sfida più interessante. Terraform deve decriptare i file YAML di Talos per estrarre la Certification Authority (CA) e i token di join. Ho incontrato un problema frustrante: i certificati salvati in SOPS erano codificati in Base64 e contenevano spesso caratteri di newline (\\n) invisibili che mandavano in crash la validazione di Talos.\nHo deciso di risolvere il problema \u0026ldquo;alla fonte\u0026rdquo; nel file data.tf, implementando una logica di pulizia aggressiva delle stringhe. Senza questa trasformazione, il nodo worker riceveva un certificato corrotto e rifiutava di unirsi al cluster, rimanendo in uno stato di \u0026ldquo;Maintenance Mode\u0026rdquo; perenne.\nterraform/data.tf: # # Decriptazione dei segreti Proxmox e Talos tramite SOPS data \u0026#34;sops_file\u0026#34; \u0026#34;proxmox_secrets\u0026#34; { source_file = \u0026#34;proxmox-secrets.enc.yaml\u0026#34; } data \u0026#34;sops_file\u0026#34; \u0026#34;controlplane_secrets\u0026#34; { source_file = \u0026#34;../talos/controlplane-reference.yaml\u0026#34; } data \u0026#34;sops_file\u0026#34; \u0026#34;worker_secrets\u0026#34; { source_file = \u0026#34;../talos/worker-reference.yaml\u0026#34; } locals { # Gestione multi-documento e pulizia Base64 parts = split(\u0026#34;---\u0026#34;, data.sops_file.controlplane_secrets.raw) cp_raw = yamldecode(local.parts[0] == \u0026#34;\u0026#34; ? local.parts[1] : local.parts[0]) cluster_secrets = { token = trimspace(local.cp_raw.machine.token) # Rimoziome newline e decodifica PEM ca_crt_b64 = replace(replace(local.cp_raw.machine.ca.crt, \u0026#34;\\n\u0026#34;, \u0026#34;\u0026#34;), \u0026#34; \u0026#34;, \u0026#34;\u0026#34;) ca_key_b64 = replace(replace(local.cp_raw.machine.ca.key, \u0026#34;\\n\u0026#34;, \u0026#34;\u0026#34;), \u0026#34; \u0026#34;, \u0026#34;\u0026#34;) ca_crt = base64decode(local.proxmox_token_id) # Logica di decode centralizzata } } Fase 4: La Lotta contro l\u0026rsquo;Hardware Virtuale # Il provisioning di una VM Talos su Proxmox non segue le regole standard di Cloud-Init. Talos si aspetta che la configurazione venga \u0026ldquo;spinta\u0026rdquo; tramite le sue API sulla porta 50000.\nHo riscontrato un conflitto hardware critico: Proxmox, di default, assegna il drive Cloud-Init all\u0026rsquo;interfaccia ide2. Tuttavia, io stavo usando l\u0026rsquo;interfaccia ide2 anche per montare l\u0026rsquo;ISO di Talos. Questo conflitto silenzioso impediva a Talos di leggere la configurazione di rete statica, forzando la VM a richiedere un IP via DHCP (spesso fuori dal range desiderato) o, peggio, a non avere alcuna connettività.\nHo deciso di spostare l\u0026rsquo;ISO sull\u0026rsquo;interfaccia ide0, liberando la porta ide2 per il bus di inizializzazione. Questa mossa, apparentemente banale, è stata la chiave per ottenere IP statici deterministici su un sistema immutabile.\nterraform/main.tf (Estratto): # resource \u0026#34;proxmox_virtual_environment_vm\u0026#34; \u0026#34;worker_nodes\u0026#34; { for_each = var.worker_nodes name = each.key node_name = var.proxmox_node # Allineamento hardware con i nodi fisici esistenti scsi_hardware = \u0026#34;virtio-scsi-single\u0026#34; agent { enabled = true # Cruciale per la visibilità dell\u0026#39;IP nella GUI } disk { datastore_id = \u0026#34;local-lvm\u0026#34; interface = \u0026#34;scsi0\u0026#34; size = each.value.disk_size iothread = true } # Disco dedicato a Longhorn: lo storage distribuito richiede dischi raw disk { datastore_id = \u0026#34;local-lvm\u0026#34; interface = \u0026#34;scsi1\u0026#34; size = each.value.data_disk iothread = true } initialization { ip_config { ipv4 { address = \u0026#34;${each.value.ip_address}/24\u0026#34; gateway = var.gateway } } } cdrom { enabled = true file_id = \u0026#34;local:iso/nocloud-amd64.iso\u0026#34; # ISO Factory personalizzata interface = \u0026#34;ide0\u0026#34; # Risoluzione del conflitto IDE } } Fase 5: Il \u0026ldquo;Debito Tecnico\u0026rdquo; e l\u0026rsquo;Image Factory # Durante la creazione del primo worker (worker-new-01), ho notato che i pod di Longhorn rimanevano in CrashLoopBackOff. L\u0026rsquo;analisi dei log con kubectl logs ha rivelato l\u0026rsquo;assenza del binario iscsiadm all\u0026rsquo;interno del sistema operativo.\nHo capito che Talos Linux, nella sua versione standard, è troppo minimale per Longhorn. I nodi esistenti del cluster stavano usando un\u0026rsquo;immagine generata tramite la Talos Image Factory che includeva l\u0026rsquo;estensione iscsi-tools e il qemu-guest-agent.\nInvece di distruggere il nodo, ho deciso di eseguire un Upgrade In-Place via API:\ntalosctl upgrade --image factory.talos.dev/installer/e187c9b90f773cd8c84e5a3265c5554ee787b2fe67b508d9f955e90e7ae8c96c:v1.12.0 Questo ha \u0026ldquo;saldato il debito\u0026rdquo; tecnico. Ho poi aggiornato immediatamente il codice Terraform per puntare a questa immagine factory per tutti i futuri nodi, garantendo l\u0026rsquo;omogeneità del cluster.\nFase 6: Hugo e la Scalabilità Cloud-Native # Una volta stabilizzato il parco nodi, ho testato la scalabilità con l\u0026rsquo;applicazione del blog Hugo. Il blog usava un PersistentVolumeClaim (PVC) in modalità ReadWriteOnce (RWO). Scalando a 3 repliche, ho visto apparire il temuto Multi-Attach error.\nRWO permette il montaggio di un disco su un solo nodo alla volta. Kubernetes, cercando di distribuire i pod sui miei 3 nuovi worker per garantire l\u0026rsquo;alta affidabilità, si scontrava con il limite fisico del volume.\nHo deciso di implementare un approccio Shared-Nothing usando una emptyDir.\nCos\u0026rsquo;è una emptyDir? È un volume temporaneo che vive finché il pod è attivo, creato sul disco locale del nodo. Perché per Hugo? Hugo è un generatore di siti statici. I suoi dati sorgente vengono scaricati da Git tramite un sidecar container (git-sync). Non serve un disco persistente centralizzato se ogni pod può scaricare la sua copia locale in pochi secondi. Questa modifica ha permesso di scalare il blog a 3 repliche istantaneamente, ognuna residente su un worker diverso, senza alcun conflitto di storage.\nFase 7: Messa in Sicurezza Finale con Terraform Cloud # L\u0026rsquo;ultimo atto è stato risolvere il problema del file terraform.tfstate. Come ho spiegato durante il processo, lo stato di Terraform contiene tutti i segreti decriptati in chiaro. Tenere questo file sul disco fisso è un rischio inaccettabile.\nHo deciso di migrare lo stato su HCP Terraform (Terraform Cloud), ma con una configurazione specifica: Local Execution Mode. In questa modalità, Terraform esegue i comandi sul mio PC (potendo così raggiungere l\u0026rsquo;IP locale di Proxmox e usare la mia chiave Age), ma invia lo stato cifrato nei server sicuri di HashiCorp. Ho rimosso ogni traccia locale di .tfstate, eliminando la possibilità di furto di credenziali dal file system.\nterraform/versions.tf (Configurazione Cloud): # terraform { required_version = \u0026#34;\u0026gt;= 1.5.0\u0026#34; cloud { organization = \u0026#34;tazlab\u0026#34; workspaces { name = \u0026#34;tazlab-k8s\u0026#34; } } # ... provider ... } Riflessioni Post-Lab: Cosa abbiamo imparato? # L\u0026rsquo;introduzione di Terraform in Tazlab non è stata solo l\u0026rsquo;aggiunta di uno strumento, ma un cambio di mentalità. Ho imparato che:\nL\u0026rsquo;astrazione ha un costo: Terraform semplifica la creazione, ma richiede una conoscenza profonda delle API sottostanti (Proxmox in questo caso). I segreti sono vivi: Gestire i segreti non significa solo nasconderli, ma saperli trasformare (Base64 vs PEM) per renderli digeribili dalle macchine. L\u0026rsquo;architettura batte la persistenza: Spesso cerchiamo di risolvere problemi di storage con volumi complessi, quando una semplice emptyDir e un buon processo di sincronizzazione sono più efficaci. Oggi Tazlab ha 3 nuovi worker. Domani potrebbe averne 30. Mi basterà aggiungere una riga di testo. Questa è la vera libertà dell\u0026rsquo;Infrastructure as Code.\n","date":"24 gennaio 2026","externalUrl":null,"permalink":"/it/posts/tazlab-iac-chronicle/","section":"Posts","summary":"","title":"Dall’Artigianato all’Infrastruttura: Cronaca dell’Introduzione di Terraform in Tazlab","type":"posts"},{"content":"","date":"20 gennaio 2026","externalUrl":null,"permalink":"/it/tags/linux-namespaces/","section":"Tags","summary":"","title":"Linux Namespaces","type":"tags"},{"content":"","date":"20 gennaio 2026","externalUrl":null,"permalink":"/it/tags/open-source/","section":"Tags","summary":"","title":"Open Source","type":"tags"},{"content":" Introduzione: Il Momento della Fenice # Nel precedente episodio di questo diario tecnico, ho documentato il fallimento drammatico del tentativo di trasformare DevPod in una enclave Zero Trust. Il conflitto fondamentale tra l\u0026rsquo;architettura \u0026ldquo;Convenience-First\u0026rdquo; di DevPod e i miei requisiti di sicurezza ha portato a una conclusione inevitabile: dovevo abbandonare completamente lo strumento.\nTuttavia, come ogni ingegnere sa, il fallimento è spesso la madre dell\u0026rsquo;innovazione. Le ceneri di DevPod sono diventate il terreno fertile per qualcosa di nuovo: TazPod, una CLI personalizzata in Go progettata da zero per affrontare le sfide di sicurezza specifiche che DevPod non poteva gestire.\nQuesta è la storia di come ho costruito TazPod dalla v1.0 alla v9.9, trasformandolo dalla fragilità degli script Bash alla robustezza di Go, dai mount globali all\u0026rsquo;isolamento dei namespace, e dai compromessi di convenienza alla vera sicurezza Zero Trust.\nFase 1: Le Fondamenta in Go - TazPod v1.0 # La prima decisione tecnica è stata radicale: abbandonare l\u0026rsquo;idea di un ambiente che si autoconfigura \u0026ldquo;magicamente\u0026rdquo; via SSH. Avevo bisogno di determinismo.\nIl Ragionamento: Perché Go? # Dopo l\u0026rsquo;incubo degli script Bash in DevPod, avevo bisogno di un linguaggio con:\nTipizzazione forte per prevenire errori a runtime. Eccellente integrazione con Docker attraverso l\u0026rsquo;SDK. Compilazione cross-platform per la futura portabilità. Gestione robusta degli errori senza la fragilità delle \u0026ldquo;trap\u0026rdquo; di Bash. Go offre un vantaggio critico per un tool di questo tipo: l\u0026rsquo;accesso diretto alle syscall del sistema operativo e la capacità di compilare in un singolo binario statico.\nL\u0026rsquo;Architettura: Design Command-First # Ho strutturato TazPod attorno a un set centrale di comandi, gestiti da uno switch principale nel main.go. Questo approccio trasforma il container in un \u0026ldquo;Demone di Sviluppo\u0026rdquo;. È lì, in attesa (sleep infinity), ma inerte. La magia avviene quando ci entriamo.\n// cmd/tazpod/main.go (Snippet della funzione up) func up() { // ... caricamento configurazione ... runCmd(\u0026#34;docker\u0026#34;, \u0026#34;run\u0026#34;, \u0026#34;-d\u0026#34;, \u0026#34;--name\u0026#34;, cfg.ContainerName, \u0026#34;--privileged\u0026#34;, // Necessario per montare i loop device \u0026#34;--network\u0026#34;, \u0026#34;host\u0026#34;, \u0026#34;-e\u0026#34;, \u0026#34;DISPLAY=\u0026#34;+display, \u0026#34;-v\u0026#34;, cwd+\u0026#34;:/workspace\u0026#34;, // Mount del progetto corrente \u0026#34;-w\u0026#34;, \u0026#34;/workspace\u0026#34;, cfg.Image, \u0026#34;sleep\u0026#34;, \u0026#34;infinity\u0026#34;) // Il container resta vivo in attesa } La prima implementazione era essenzialmente una traduzione diretta degli script Bash. Funzionava, ma soffriva ancora dello stesso problema di mount globale che affliggeva DevPod. Chiunque avesse accesso docker exec poteva vedere i segreti.\nFase 2: La Svolta di Sicurezza - TazPod v2.0 (Ghost Edition) # Durante una revisione della sicurezza il 17 gennaio, ho identificato una falla critica: se sbloccavo il vault e un altro utente accedeva al container, poteva leggere tutti i segreti. La soluzione è arrivata da una fonte inaspettata: i Linux Mount Namespaces.\nIl Concetto: \u0026ldquo;Ghost Mode\u0026rdquo; # L\u0026rsquo;idea era rivoluzionaria: invece di montare il vault globalmente, creare un namespace isolato dove solo la sessione corrente potesse vedere i segreti montati.\nIn Linux, i mount point sono globali per namespace. Se creo un nuovo namespace di mount e monto un disco al suo interno, quel disco esiste solo per i processi che vivono in quel namespace. Per il processo padre (e per l\u0026rsquo;host), quel mount point è semplicemente una directory vuota.\nL\u0026rsquo;Implementazione: Magia di unshare # La chiave è stata usare unshare -m per creare un nuovo namespace di mount. Ecco cosa succede \u0026ldquo;sotto il cofano\u0026rdquo; quando un utente digita la password del vault:\nTrigger: L\u0026rsquo;utente lancia tazpod pull. Fork \u0026amp; Unshare: Il binario Go esegue se stesso con privilegi elevati usando unshare: sudo unshare --mount --propagation private /usr/local/bin/tazpod internal-ghost Enclave Creation: Il nuovo processo internal-ghost nasce in un universo parallelo di mount. Decryption: All\u0026rsquo;interno di questo universo, usiamo cryptsetup per aprire il file vault.img (montato via loop device) e montarlo su /home/tazpod/secrets. Drop Privileges: Una volta montato il disco, il processo \u0026ldquo;degrada\u0026rdquo; i suoi privilegi da root all\u0026rsquo;utente tazpod e lancia una shell Bash. Il Risultato:\nTu (nella ghost shell): Vedi i segreti, usi kubectl, lavori normalmente. Intrusi (in altre shell): Vedono una directory ~/secrets vuota. Exit: Quando esci, il namespace sparisce, portando con sé il mount. Fase 3: La Rivoluzione dell\u0026rsquo;IDE - TazPod v3.0 # Con DevPod andato, ho perso l\u0026rsquo;esperienza integrata di VS Code. Ho deciso di abbracciare un workflow puramente da terminale con Neovim (configurazione LazyVim).\nL\u0026rsquo;Integrazione LazyVim # Ho investito tempo significativo per perfezionare il setup di Neovim direttamente nell\u0026rsquo;immagine Docker base. Volevo che l\u0026rsquo;IDE fosse pronto immediatamente, senza dover attendere il download dei plugin al primo avvio.\n# Installazione LazyVim e sync plugin headless RUN git clone https://github.com/LazyVim/LazyVim ~/.config/nvim \u0026amp;\u0026amp; \\ nvim --headless \u0026#34;+Lazy! sync\u0026#34; +qa \u0026amp;\u0026amp; \\ nvim --headless \u0026#34;+MasonInstall all\u0026#34; +qa Il Risultato: Un ambiente di sviluppo completo pronto in secondi, con Tree-sitter, LSP e tutti i plugin pre-compilati.\nFase 4: La Battaglia per la Persistenza di Infisical # Risolto l\u0026rsquo;isolamento del filesystem, ho dovuto affrontare la gestione dell\u0026rsquo;identità. Uso Infisical per gestire i segreti centralizzati. Tuttavia, Infisical ha bisogno di salvare un token di sessione locale (solitamente in ~/.infisical).\nSe il container è effimero, ad ogni riavvio dovrei fare login. Inaccettabile. Se salvo il token su un volume Docker, è esposto in chiaro sull\u0026rsquo;host. Inaccettabile.\nL\u0026rsquo;Investigazione: Il Bug \u0026ldquo;Cannibale\u0026rdquo; # L\u0026rsquo;idea era semplice: spostare la cartella .infisical dentro il vault criptato e usare un bind-mount per farla apparire nella home utente solo quando il vault è aperto.\nDurante l\u0026rsquo;implementazione in Go, ho incontrato un bug critico che ho soprannominato \u0026ldquo;Il Cannibale\u0026rdquo;. La funzione di migrazione, pensata per spostare i vecchi token nel vault, aveva un difetto logico che portava alla cancellazione del contenuto se i percorsi coincidevano.\nLa Soluzione: Il Bridge Blindato # Ho riscritto la logica implementando controlli rigorosi:\nCheck preliminare: Verifico se il mount è già attivo leggendo /proc/mounts. Doppio Bridge: Monto sia la configurazione (.infisical) che il keyring di sistema (infisical-keyring) dentro il vault (.infisical-vault e .infisical-keyring). Ownership Recursiva: Un problema ricorrente era che i file creati durante il mount (da root) non erano leggibili dall\u0026rsquo;utente. Ho aggiunto un chown -R tazpod:tazpod forzato su tutta la struttura .tazpod ad ogni operazione di init o mount. Ora, la sessione sopravvive ai riavvii, ma esiste fisicamente solo all\u0026rsquo;interno del file criptato vault.img.\nFase 5: Da Hack a Prodotto (TazPod v9.9) # A questo punto, avevo un sistema funzionante ma grezzo. Per renderlo un vero strumento \u0026ldquo;Zero Trust\u0026rdquo; utilizzabile da altri, serviva una pulizia profonda e una standardizzazione.\nStandardizzazione e \u0026ldquo;Smart Init\u0026rdquo; # Ho introdotto il comando tazpod init. Invece di dover copiare manualmente file di configurazione, la CLI ora analizza la directory corrente e genera:\nUna cartella nascosta .tazpod/. Un config.yaml pre-compilato, permettendo di scegliere il \u0026ldquo;verticale\u0026rdquo; (base, k8s, gemini) tramite un argomento (es. tazpod init gemini). Un template di secrets.yml per mappare le variabili d\u0026rsquo;ambiente di Infisical. Un .gitignore che esclude automaticamente il vault e la memoria locale dell\u0026rsquo;AI (montata in ./.gemini per persistere i ricordi del progetto). Il Problema della Collisione dei Nomi # Lanciando più progetti TazPod contemporaneamente, ho notato che Docker andava in conflitto sui nomi dei container (tazpod-lab). Ho implementato una logica di naming dinamico in Go nella versione v9.9:\ncwd, _ := os.Getwd() dirName := filepath.Base(cwd) rng := rand.New(rand.NewSource(time.Now().UnixNano())) containerName := fmt.Sprintf(\u0026#34;tazpod-%s-%d\u0026#34;, dirName, rng.Intn(9000000)+1000000) Ora ogni progetto ha un\u0026rsquo;identità unica, permettendo di lavorare su più cluster o clienti in parallelo senza sovrapposizioni.\nRiflessioni Post-Sviluppo # Il passaggio da DevPod a TazPod è stato un esercizio di sottrazione. Ho rimosso l\u0026rsquo;interfaccia grafica, ho rimosso l\u0026rsquo;agente di sincronizzazione, ho rimosso l\u0026rsquo;astrazione SSH gestita.\nIn cambio, ho ottenuto:\nSicurezza Verificabile: So esattamente dove risiede ogni byte di dati sensibili (nella RAM del processo Ghost). Portabilità Totale: Il progetto è autocontenuto. Basta avere Docker e il binario TazPod. Velocità: Senza overhead di agenti, l\u0026rsquo;avvio della shell è istantaneo una volta scaricata l\u0026rsquo;immagine. Il Progetto su GitHub # Ho deciso di rilasciare TazPod come progetto Open Source sotto licenza MIT. Non è solo uno script personale, ma un framework completo per chi, come me, vive nel terminale e non vuole compromessi sulla sicurezza.\nL\u0026rsquo;installazione è ora ridotta a una singola riga:\ncurl -sSL https://raw.githubusercontent.com/tazzo/tazpod/master/scripts/install.sh | bash Per maggiori dettagli tecnici e per consultare la documentazione completa del progetto, vi invito a visitare il repository ufficiale su GitHub: https://github.com/tazzo/tazpod.\nIl prossimo passo? Utilizzare TazPod per completare il refactoring Terraform del cluster TazLab, sapendo che le chiavi di accesso sono finalmente al sicuro.\n","date":"20 gennaio 2026","externalUrl":null,"permalink":"/it/posts/tazpod-rising-go-cli-zero-trust/","section":"Posts","summary":"","title":"TazPod Rising: Dalle Ceneri di DevPod a una CLI Zero Trust in Go","type":"posts"},{"content":"","date":"14 gennaio 2026","externalUrl":null,"permalink":"/it/tags/devpod/","section":"Tags","summary":"","title":"Devpod","type":"tags"},{"content":" Introduzione: L\u0026rsquo;illusione del Controllo Totale # Nella prima parte di questo diario tecnico, ho delineato l\u0026rsquo;architettura di una workstation immutabile basata su DevPod. L\u0026rsquo;obiettivo era ambizioso: una \u0026ldquo;Golden Image\u0026rdquo; che contenesse ogni strumento necessario per l\u0026rsquo;orchestrazione del mio cluster Kubernetes (Proxmox, Talos, Longhorn), eliminando l\u0026rsquo;entropia della configurazione locale. Tuttavia, come ogni ingegnere sa, il passaggio dalla teoria alla pratica espone falle che nessuna pianificazione può prevedere completamente.\nIn questa sessione, mi sono posto un obiettivo ancora più estremo: trasformare il DevPod in un ambiente Zero Trust. Non volevo solo un container con i miei strumenti; volevo un\u0026rsquo;enclave sicura in cui i segreti critici (Kubeconfig, chiavi SSH, token API) non risiedessero mai su disco in chiaro, nemmeno all\u0026rsquo;interno del container isolato.\nIl mindset della giornata era improntato alla paranoia costruttiva. Mi sono chiesto: \u0026ldquo;Se qualcuno compromettesse fisicamente il mio laptop o riuscisse a eseguire un comando non autorizzato nel container, cosa troverebbe?\u0026rdquo;. La risposta doveva essere: \u0026ldquo;Assolutamente nulla\u0026rdquo;.\nQuesta è la cronaca tecnica di come ho cercato di piegare DevPod a questa visione di sicurezza radicale, scontrandomi con la sua stessa architettura orientata alla comodità, fino a giungere alla decisione inevitabile di abbandonare lo strumento per ricominciare su basi diverse.\nFase 1: Refactoring dell\u0026rsquo;Immagine e l\u0026rsquo;Incubo della Cache # Prima di affrontare la sicurezza, ho dovuto risolvere un problema di efficienza architetturale. Il mio Dockerfile originale stava diventando un monolite ingestibile. Ogni piccola modifica ai dotfiles richiedeva una ricostruzione completa dell\u0026rsquo;intera immagine, un processo che consumava banda e tempo prezioso.\nIl Ragionamento: L\u0026rsquo;Architettura a Layer # Ho deciso di decomporre l\u0026rsquo;immagine in tre layer logici distinti:\nLayer Base (Dockerfile.base): Il fondamento del sistema operativo, i tool di sicurezza (Infisical, SOPS) e i binari stabili (Eza, Neovim, Starship). Layer Kubernetes (Dockerfile.k8s): Lo stack specifico per l\u0026rsquo;orchestrazione (Kubectl, Helm, Talosctl). Layer AI (Dockerfile.gemini): La pesante CLI di Gemini, che richiede un runtime Node.js dedicato. Deep-Dive Concettuale: Docker Layer Caching Il caching dei layer in Docker funziona secondo una logica deterministica: se il contenuto di un\u0026rsquo;istruzione (come un comando RUN o un COPY) non cambia, Docker riutilizza il layer precedentemente costruito. Questo è fondamentale per l\u0026rsquo;integrazione continua (CI/CD). Tuttavia, se un layer alla base della catena cambia, tutti i layer successivi vengono invalidati e devono essere ricostruiti. Separando i tool stabili da quelli pesanti o frequentemente aggiornati, ho cercato di massimizzare la velocità di iterazione.\nIl Sintomo: La Cache \u0026ldquo;Invisibile\u0026rdquo; # Durante i test, sono incappato in un comportamento frustrante. Avevo aggiornato il tema di Starship nei dotfiles (passando da Gruvbox a un più riposante Pastel Powerline), ma nonostante lanciassi la build, il container continuava a presentarsi con il vecchio tema.\nControllando i log di build, ho notato l\u0026rsquo;infame etichetta =\u0026gt; CACHED proprio sul comando COPY dotfiles/. Docker non rilevava che i file all\u0026rsquo;interno della cartella dell\u0026rsquo;host erano cambiati.\nLa Soluzione: Cache Busting Dinamico # Per forzare Docker a invalidare la cache nel punto esatto desiderato, ho introdotto un argomento di build dinamico.\n# Dockerfile.base snippet # ... tool stabili ... # Argomento per forzare l\u0026#39;aggiornamento dei dotfiles ARG CACHEBUST=1 RUN echo \u0026#34;Cache bust: ${CACHEBUST}\u0026#34; # Ora Docker è costretto a rieseguire la copia se CACHEBUST cambia COPY --chown=vscode:vscode dotfiles/ /home/vscode/ Lanciando la build con --build-arg CACHEBUST=$(date +%s), ho iniettato il timestamp attuale nel processo. Poiché il comando RUN echo cambiava ad ogni secondo, Docker era matematicamente obbligato a ricostruire quel layer e tutti i successivi, garantendo l\u0026rsquo;iniezione dei nuovi file di configurazione.\nFase 2: L\u0026rsquo;Enclave in RAM e il Conflitto col Kernel # Risolto il problema della cache, sono passato al cuore del progetto: il Vault Cifrato. L\u0026rsquo;idea era creare un volume LUKS (Linux Unified Key Setup) all\u0026rsquo;interno del container.\nIl Ragionamento: Perché LUKS in un Container? # Normalmente, i container si affidano all\u0026rsquo;isolamento del namespace del kernel. Ma i file all\u0026rsquo;interno di un container sono accessibili a chiunque abbia privilegi di root sull\u0026rsquo;host o possa eseguire un docker exec. Crittografando una porzione di filesystem con LUKS e sbloccandola solo tramite una passphrase inserita manualmente, i segreti vengono protetti da una chiave crittografica che risiede solo nella memoria RAM (e nella mente dell\u0026rsquo;utente).\nDeep-Dive Concettuale: Linux Unified Key Setup (LUKS) LUKS è lo standard per la crittografia dei dischi in Linux. Funziona creando un layer tra il dispositivo fisico (o un file immagine) e il filesystem. Questo layer gestisce la decifratura al volo dei blocchi di dati. Nel contesto di un container, l\u0026rsquo;uso di LUKS richiede l\u0026rsquo;accesso al Device Mapper del kernel host, un\u0026rsquo;operazione intrinsecamente complessa da isolare.\nL\u0026rsquo;Indagine: Il Fallimento del Loop Device # Il primo tentativo di creare il vault in RAM tramite tmpfs ha sbattuto contro un errore del kernel: Attaching loopback device failed (loop device with autoclear flag is required).\nIn un ambiente Docker, anche se il container è lanciato con il flag --privileged, il comando cryptsetup spesso non riesce ad allocare automaticamente i loop device (quei dispositivi virtuali che permettono di trattare un file come un disco rigido). Questo accade perché i nodi in /dev/loop* non vengono creati dinamicamente all\u0026rsquo;interno del container.\nLa Soluzione: Mknod e Losetup Manuale # Ho dovuto implementare una procedura di sblocco robusta che preparasse il terreno per il kernel:\n# Snippet dello script di sblocco (devpod-zt.sh) echo \u0026#34;🛠️ Preparing loop devices (0-63)...\u0026#34; sudo mknod /dev/loop-control c 10 237 2\u0026gt;/dev/null || true for i in $(seq 0 63); do sudo mknod /dev/loop$i b 7 $i 2\u0026gt;/dev/null || true done echo \u0026#34;💾 Engaging Secure Enclave (RAM)...\u0026#34; # Montaggio tmpfs dedicato per evitare i limiti di /dev/shm sudo mount -t tmpfs -o size=256M tmpfs \u0026#34;$VAULT_BASE\u0026#34; # Associazione manuale del loop device LOOP_DEV=$(sudo losetup -f --show \u0026#34;$VAULT_IMG\u0026#34;) echo -n \u0026#34;$PLAIN_PASS\u0026#34; | sudo cryptsetup luksFormat --batch-mode \u0026#34;$LOOP_DEV\u0026#34; - echo -n \u0026#34;$PLAIN_PASS\u0026#34; | sudo cryptsetup open \u0026#34;$LOOP_DEV\u0026#34; \u0026#34;$MAPPER_NAME\u0026#34; - Questa mossa è stata cruciale. Creando manualmente i nodi dei dispositivi e gestendo l\u0026rsquo;associazione losetup al di fuori dell\u0026rsquo;automatismo di cryptsetup, sono riuscito a superare le restrizioni del runtime di Docker e a montare finalmente un filesystem cifrato funzionante in ~/secrets.\nFase 3: Lo Scontro tra Automazione e Hardening # Con il vault funzionante, ho cercato di automatizzare il processo. Volevo che il container chiedesse la password immediatamente all\u0026rsquo;ingresso. Ho implementato una Trap-Shell nel .bashrc: uno script che intercettava l\u0026rsquo;avvio della sessione e lanciava la procedura di sblocco.\nIl Sintomo: I \u0026ldquo;Fantasmi\u0026rdquo; nei Log # Non appena attivata la Trap-Shell, ho iniziato a vedere un output incessante ogni 30 secondi nei log di devpod up: 00:32:47 debug Start refresh ... Device secrets_vault already exists.\nL\u0026rsquo;Analisi: Il Ciclo di Vita del DevPod Agent # Qui ho scoperto la vera natura del DevPod Agent. Per fornire funzionalità come il port forwarding e il sync dei file, l\u0026rsquo;agent di DevPod mantiene un canale SSH o un socket aperto verso il container. Ogni 30 secondi, l\u0026rsquo;agent esegue dei comandi di \u0026ldquo;refresh\u0026rdquo; (come update-config) lanciando nuove shell nel container.\nPoiché la mia Trap-Shell era nel .bashrc, ogni volta che l\u0026rsquo;agent entrava per un controllo di routine, lo script di sicurezza partiva, cercava di chiedere una password (che l\u0026rsquo;agent non poteva dare) o provava a rimontare un volume già attivo, generando errori a catena.\nDeep-Dive Concettuale: Shell Interattive vs Non-interattive In Bash, le shell possono essere interattive (collegate a un terminale/TTY) o non-interattive (eseguite da uno script o un demone). L\u0026rsquo;agent di DevPod lancia shell non-interattive. Ho cercato di risolvere il problema filtrando l\u0026rsquo;esecuzione dello script di sicurezza:\n# Modifica nel .bashrc if [[ $- == *i* ]]; then # Esegui sblocco solo se l\u0026#39;utente è davanti allo schermo tazpod-unlock fi Sebbene questo abbia ridotto il rumore, non ha risolto il problema di fondo: DevPod Agent continuava a \u0026ldquo;litigare\u0026rdquo; con il mio ambiente blindato.\nFase 4: La Caduta di SSH e la Scoperta del \u0026ldquo;Fail-Open\u0026rdquo; # L\u0026rsquo;ultimo chiodo sulla bara dell\u0026rsquo;approccio basato su DevPod è stato il tentativo di blindare l\u0026rsquo;accesso SSH. Volevo che anche dopo aver sbloccato il pod, l\u0026rsquo;uscita dalla shell smontasse tutto e che il rientro richiedesse di nuovo la password.\nHo provato a rimuovere le chiavi SSH iniettate da DevPod (rm ~/.ssh/authorized_keys). Risultato? L\u0026rsquo;agent di DevPod è andato in panico, perdendo la capacità di gestire il workspace. Ho provato a implementare un Watchdog in background che contasse i processi bash attivi e smontasse il vault al termine dell\u0026rsquo;ultima sessione. Ma la complessità stava scalando esponenzialmente rispetto ai benefici.\nLa Vulnerabilità \u0026ldquo;Ctrl+C\u0026rdquo; # Durante un test di penetrazione manuale, ho scoperto una falla imbarazzante: se premevo Ctrl+C durante la richiesta della password di Infisical, lo script veniva interrotto ma la shell mi dava comunque il prompt dei comandi. Era un sistema di sicurezza che poteva essere bypassato con un semplice tasto.\nHo risposto implementando una Trap SIGINT brutale:\n# Nel .bashrc trap \u0026#34;echo \u0026#39;❌ Interrupted. Exiting.\u0026#39;; exit 1; kill -9 $$\u0026#34; INT Funzionava. Ma a quel punto, il mio ambiente di sviluppo era diventato una ragnatela di hack, script Bash fragili che cercavano di gestire segnali del kernel, e conflitti perenni con l\u0026rsquo;agente di orchestrazione di DevPod.\nFase 5: La Resa e il Cambio di Paradigma # Dopo ore passate a combattere contro il Device already exists del Device Mapper e i refresh infiniti dell\u0026rsquo;agente, sono giunto a una conclusione dolorosa ma necessaria: DevPod non è lo strumento adatto per un\u0026rsquo;enclave Zero Trust.\nDevPod è costruito sulla filosofia della Convenience-First. Vuole che tu sia operativo in un click, che le tue chiavi SSH siano sincronizzate ovunque, che il tuo ambiente sia \u0026ldquo;sempre pronto\u0026rdquo;. La mia visione di sicurezza, invece, richiede un ambiente che sia \u0026ldquo;mai pronto\u0026rdquo; finché l\u0026rsquo;utente non lo decide esplicitamente.\nLa Decisione: Ho deciso di buttare via tutto il lavoro fatto con DevPod. Ho deciso di eliminare l\u0026rsquo;agente, le chiavi SSH automatiche e il server VS Code integrato.\nIl nuovo approccio sarà basato su:\nPure Docker: Un container Debian Slim lanciato manualmente con script di avvio controllati al 100%. Go CLI: Una CLI dedicata scritta in Go (che chiameremo tazpod) per gestire in modo robusto e atomico l\u0026rsquo;intero ciclo di vita della sicurezza, eliminando la fragilità degli script Bash. Terminal-Only Workflow: Abbandono di VS Code in favore di Neovim (LazyVim), eliminando la necessità di canali SSH persistenti per l\u0026rsquo;IDE. Conclusioni: Cosa abbiamo imparato in questa tappa # Questa sessione, apparentemente un fallimento, è stata in realtà una lezione magistrale di ingegneria dei sistemi. Ho imparato che:\nL\u0026rsquo;automazione non è sempre alleata della sicurezza estrema. Il kernel host e il container hanno un rapporto di dipendenza molto stretto quando si parla di crittografia, e gli intermediari rendono il debug impossibile. Saper rinunciare a uno strumento quando non risponde più ai requisiti è una competenza senior fondamentale quanto saperlo configurare. L\u0026rsquo;Officina Immutabile non è morta; sta solo cambiando pelle. Nel prossimo post, documenterò la nascita della CLI TazPod in Go e il passaggio a un ambiente Pure Docker, dove il controllo non è più un\u0026rsquo;opzione, ma il fondamento stesso dell\u0026rsquo;architettura.\n","date":"14 gennaio 2026","externalUrl":null,"permalink":"/it/posts/devpod-zero-trust-struggle/","section":"Posts","summary":"","title":"Il Canto del Cigno di DevPod: Scontro tra Automazione e Sicurezza Zero Trust","type":"posts"},{"content":"","date":"14 gennaio 2026","externalUrl":null,"permalink":"/it/tags/luks/","section":"Tags","summary":"","title":"Luks","type":"tags"},{"content":"","date":"14 gennaio 2026","externalUrl":null,"permalink":"/it/tags/troubleshooting/","section":"Tags","summary":"","title":"Troubleshooting","type":"tags"},{"content":"","date":"12 gennaio 2026","externalUrl":null,"permalink":"/it/tags/automazione/","section":"Tags","summary":"","title":"Automazione","type":"tags"},{"content":" Introduzione: Il Paradosso della Configurazione Locale # Nel panorama attuale dell\u0026rsquo;infrastruttura come codice (IaC), esiste un paradosso fondamentale: spendiamo ore a rendere i nostri server immutabili (tramite sistemi come Talos Linux) e i nostri carichi di lavoro effimeri (tramite Kubernetes), ma continuiamo a gestire l\u0026rsquo;infrastruttura da laptop \u0026ldquo;artigianali\u0026rdquo;, configurati manualmente e soggetti a una lenta ma inesorabile entropia.\nLavorando sul mio cluster Proxmox/Talos, mi sono reso conto che la mia workstation (Zorin OS) stava diventando un collo di bottiglia. Versioni disallineate di talosctl, conflitti tra versioni di Python, e la gestione precaria dei file kubeconfig stavano introducendo un rischio operativo inaccettabile. Inoltre, la necessità di operare in mobilità richiedeva un ambiente che non fosse vincolato all\u0026rsquo;hardware fisico del mio laptop principale.\nL\u0026rsquo;obiettivo di questa sessione è stato la costruzione di un DevPod (Development Pod): un ambiente di lavoro containerizzato, portabile e rigorosamente definito via codice. Non stiamo parlando di un semplice container Docker usa e getta, ma di una workstation ingegneristica completa, persistente nelle configurazioni ma effimera nell\u0026rsquo;esecuzione.\nIl Mindset: Sicurezza vs Usabilità # Prima di scrivere la prima riga di codice, ho valutato un approccio radicale alla sicurezza. L\u0026rsquo;idea iniziale era quella di implementare un filesystem crittografato residente esclusivamente in RAM. Immaginavo uno script che, all\u0026rsquo;avvio, allocasse un blocco di RAM, lo formattasse con LUKS (Linux Unified Key Setup) e lo montasse nel container.\nIl Ragionamento: In uno scenario di \u0026ldquo;Cold Boot Attack\u0026rdquo; o di compromissione fisica della macchina spenta, i segreti (chiavi SSH, kubeconfig) sarebbero stati matematicamente irrecuperabili, essendo svaniti insieme alla corrente elettrica.\nLa Decisione: Dopo un\u0026rsquo;analisi costi-benefici, ho deciso di scartare questa complessità per il momento. Sebbene tecnicamente affascinante, avrebbe introdotto un attrito eccessivo nel workflow quotidiano (necessità di inserire passphrase di decrittazione ad ogni riavvio, gestione complessa dei mount point privilegiati). Ho optato per un approccio più pragmatico: i segreti risiedono in una directory dell\u0026rsquo;host non versionata su Git, montata dinamicamente nel container. La sicurezza è delegata alla crittografia del disco dell\u0026rsquo;host (LUKS standard), che è un compromesso accettabile per un ambiente di laboratorio, permettendomi di concentrarmi sulla stabilità dell\u0026rsquo;ambiente di sviluppo.\nFase 1: Il Networking e l\u0026rsquo;Incubo dell\u0026rsquo;MTU # La prima barriera tecnica incontrata durante il bootstrap del container debian:slim è stata, prevedibilmente, la rete. Il mio host utilizza una connessione VPN (WireGuard/Tailscale) per raggiungere la rete di gestione del cluster Proxmox.\nIl Sintomo # Avviando il container, il comando apt-get update rimaneva bloccato indefinitamente allo 0% o falliva in timeout su determinati repository.\nL\u0026rsquo;Indagine # Questo comportamento è un \u0026ldquo;classico\u0026rdquo; sintomo di problemi di MTU (Maximum Transmission Unit). Docker, per impostazione predefinita, crea un bridge network (docker0) e incapsula il traffico dei container. Lo standard Ethernet prevede un MTU di 1500 byte. Tuttavia, i tunnel VPN devono aggiungere i propri header ai pacchetti, riducendo lo spazio utile (payload) disponibile, spesso portando l\u0026rsquo;MTU effettivo a 1420 byte o meno.\nQuando il container tenta di inviare un pacchetto di 1500 byte, questo arriva all\u0026rsquo;interfaccia VPN dell\u0026rsquo;host. Se il bit \u0026ldquo;Don\u0026rsquo;t Fragment\u0026rdquo; (DF) è impostato (come avviene spesso nel traffico HTTPS/TLS), il pacchetto viene scartato silenziosamente perché troppo grande per il tunnel. In teoria, il router dovrebbe inviare un messaggio ICMP \u0026ldquo;Fragmentation Needed\u0026rdquo;, ma molti firewall moderni bloccano l\u0026rsquo;ICMP, creando un \u0026ldquo;buco nero\u0026rdquo; (Path MTU Discovery Blackhole).\nLa Soluzione: --network=host # Invece di tentare un fragile tuning dei valori MTU nel demone Docker (che avrebbe reso la configurazione specifica per la mia macchina e non portabile), ho deciso di bypassare completamente lo stack di rete di Docker.\nNel file devcontainer.json, ho introdotto:\n\u0026#34;runArgs\u0026#34;: [ \u0026#34;--network=host\u0026#34; ] Deep-Dive Concettuale: Host Networking Utilizzando il driver di rete host, il container non riceve un proprio namespace di rete isolato. Condivide direttamente lo stack di rete dell\u0026rsquo;host. Se l\u0026rsquo;host ha un\u0026rsquo;interfaccia tun0 (la VPN), il container la vede e la utilizza direttamente. Questo elimina il doppio NAT e i problemi di frammentazione dei pacchetti, garantendo che la connettività del DevPod sia esattamente identica a quella della macchina fisica.\nFase 2: Gestione dello Stato e Iniezione dei Segreti # Un ambiente effimero deve poter essere distrutto senza perdere dati, ma non deve nemmeno contenere dati sensibili nella sua immagine di base. Questo ha richiesto una strategia di gestione dei volumi molto precisa.\nLa Strategia dei Bind Mounts # Ho deciso di mantenere i file di configurazione critici (kubeconfig, talosconfig) in una directory locale dell\u0026rsquo;host (~/kubernetes/tazlab-configs), rigorosamente esclusa dal versionamento Git tramite .gitignore.\nQuesta directory viene \u0026ldquo;innestata\u0026rdquo; nel container a runtime:\n\u0026#34;mounts\u0026#34;: [ \u0026#34;source=/home/vscode/.cluster-configs,target=/home/vscode/.cluster-configs,type=bind,consistency=cached\u0026#34; ] Il Conflitto delle Variabili d\u0026rsquo;Ambiente # Montare i file non è sufficiente. Gli strumenti come kubectl si aspettano i file di configurazione in percorsi standard (~/.kube/config). Avendo spostato i file in un percorso custom per pulizia, dovevo istruire gli strumenti tramite variabili d\u0026rsquo;ambiente (KUBECONFIG, TALOSCONFIG).\nInizialmente, ho tentato di esportare queste variabili tramite uno script di avvio (postCreateCommand) che le accodava al file .bashrc. Ma ho riscontrato che aprendo una shell nel container, le variabili non erano presenti.\nAnalisi del Fallimento: Il problema risiedeva nella gestione delle shell. L\u0026rsquo;immagine base includeva una configurazione che lanciava Zsh invece di Bash, oppure (nel caso di tmux) lanciava una login shell che resettava l\u0026rsquo;ambiente. Affidarsi agli script di init per settare variabili d\u0026rsquo;ambiente è intrinsecamente fragile a causa delle \u0026ldquo;Race Conditions\u0026rdquo;: se l\u0026rsquo;utente entra nel terminale prima che lo script abbia finito, l\u0026rsquo;ambiente è incompleto.\nLa Soluzione Robusta: Ho spostato la definizione delle variabili direttamente nella configurazione del container, utilizzando la proprietà containerEnv di DevContainer.\n\u0026#34;containerEnv\u0026#34;: { \u0026#34;KUBECONFIG\u0026#34;: \u0026#34;/home/vscode/.cluster-configs/kubeconfig\u0026#34;, \u0026#34;TALOSCONFIG\u0026#34;: \u0026#34;/home/vscode/.cluster-configs/talosconfig\u0026#34; } In questo modo, è il demone Docker stesso a iniettare queste variabili nel processo padre del container al momento della creazione (docker run -e ...). Le variabili sono quindi disponibili istantaneamente e universalmente, indipendentemente dalla shell utilizzata (Bash, Zsh, Fish) o dall\u0026rsquo;ordine di caricamento dei profili utente.\nFase 3: La Strategia \u0026ldquo;Golden Image\u0026rdquo; e l\u0026rsquo;Architettura a Strati # Nelle prime iterazioni, il mio devcontainer.json definiva un\u0026rsquo;immagine base generica e demandava a uno script install-extras.sh l\u0026rsquo;installazione di tutti i tool (kubectl, talosctl, neovim, yazi). Il risultato era un tempo di avvio inaccettabile (5-8 minuti) ad ogni ricostruzione del container, con un alto rischio di fallimento se un repository esterno (es. GitHub o apt) fosse stato momentaneamente irraggiungibile.\nHo deciso di virare verso un approccio Golden Image: costruire l\u0026rsquo;ambiente \u0026ldquo;offline\u0026rdquo; e distribuirlo come immagine Docker monolitica.\nLayering Ottimizzato # Per bilanciare la velocità di build e la flessibilità, ho strutturato i Dockerfile in tre livelli gerarchici distinti.\n1. Il Livello Base (Dockerfile.base) # Questo è il fondamento. Contiene il sistema operativo (Debian Bookworm), la configurazione dei Locales (fondamentale per evitare crash di tool TUI come btop che richiedono UTF-8), e i binari pesanti e stabili.\nDeep-Dive Concettuale: Locales in Docker Le immagini Docker minimali spesso non hanno i locales generati per risparmiare spazio (POSIX o C). Tuttavia, strumenti moderni come starship o interfacce grafiche terminali richiedono caratteri Unicode. Ho dovuto forzare la generazione di en_US.UTF-8 nel Dockerfile per garantire la stabilità dell\u0026rsquo;interfaccia.\n# Dockerfile.base snippet RUN echo \u0026#34;en_US.UTF-8 UTF-8\u0026#34; \u0026gt; /etc/locale.gen \u0026amp;\u0026amp; \\ locale-gen \u0026amp;\u0026amp; \\ update-locale LANG=en_US.UTF-8 ENV LANG=en_US.UTF-8 2. Il Livello Intermedio (Dockerfile.gemini) # Questo strato estende la base aggiungendo tool specifici e potenzialmente opzionali, nel mio caso la CLI di Gemini. Separarlo mi permette di avere, in futuro, versioni \u0026ldquo;light\u0026rdquo; dell\u0026rsquo;ambiente senza dover ricompilare tutto il layer base.\n3. Il Livello Finale (Dockerfile) # È il punto di ingresso consumato da DevPod. Eredita dal livello intermedio e viene taggato come latest. Questo approccio \u0026ldquo;a matrioska\u0026rdquo; mi permette di aggiornare un tool nel layer base e propagare la modifica a tutte le immagini figlie con una semplice rebuild della catena.\nRisultato Operativo # Il tempo di avvio (devpod up) è crollato da minuti a pochi secondi. L\u0026rsquo;immagine è immutabile: ho la certezza matematica che le versioni dei tool che uso oggi saranno identiche tra un mese, eliminando alla radice il problema del \u0026ldquo;Configuration Drift\u0026rdquo;.\nFase 4: Personalizzazione e GNU Stow # Un ambiente di sviluppo sterile è improduttivo. Avevo bisogno della mia specifica configurazione di Neovim (basata su LazyVim), dei miei binding per Tmux, e dei miei script custom.\nHo scelto GNU Stow per gestire i miei dotfiles. Stow è un gestore di link simbolici che permette di mantenere i file di configurazione in una directory centralizzata (un repo Git) e creare symlink nelle posizioni target (~/.config/nvim, ~/.bashrc).\nLa Sfida dei Link Sporchi # Stow opera per default \u0026ldquo;specchiando\u0026rdquo; la struttura della directory sorgente. Questo ha creato un problema con la mia cartella scripts/. Stow tentava di creare un link ~/scripts nella home del container, mentre la convenzione Linux richiede che gli eseguibili utente risiedano in ~/.local/bin per essere automaticamente inclusi nel $PATH.\nHo dovuto scrivere uno script di runtime intelligente (setup-runtime.sh) che esegue Stow in modo condizionale:\n# Logica di stowing differenziata for package in *; do if [ \u0026#34;$package\u0026#34; == \u0026#34;scripts\u0026#34; ]; then # Forza la destinazione per gli script in .local/bin stow --target=\u0026#34;$HOME/.local/bin\u0026#34; --adopt \u0026#34;$package\u0026#34; else # Comportamento standard per nvim, tmux, git stow --target=\u0026#34;$HOME\u0026#34; --adopt \u0026#34;$package\u0026#34; fi done Inoltre, ho dovuto gestire un conflitto critico con Neovim. Il mio Dockerfile pre-installa una configurazione \u0026ldquo;starter\u0026rdquo; di Neovim. Quando Stow tentava di linkare la mia configurazione personale, falliva perché la directory target esisteva già. Ho aggiunto una logica di pulizia preventiva che rileva la presenza di dotfiles personali e rimuove la configurazione di default (\u0026ldquo;nuke and pave\u0026rdquo;) prima di applicare i symlink.\nFase 5: Decoupling Architetturale # Durante la ristrutturazione, ho notato un \u0026ldquo;odore\u0026rdquo; nel codice (Code Smell): i file di definizione dell\u0026rsquo;immagine (Dockerfile, script di build) risiedevano nello stesso repository dell\u0026rsquo;infrastruttura Kubernetes (tazlab-k8s).\nIl Ragionamento: Mescolare la definizione degli strumenti con la definizione dell\u0026rsquo;infrastruttura viola il principio di separazione delle responsabilità (Separation of Concerns). Se in futuro volessi usare lo stesso ambiente DevPod per un progetto Terraform su AWS, o per sviluppare un\u0026rsquo;applicazione Go, sarei costretto a duplicare il codice o a dipendere impropriamente dal repository Kubernetes.\nL\u0026rsquo;Azione: Ho deciso di estrarre tutta la logica di costruzione dell\u0026rsquo;immagine in un nuovo repository dedicato: tazzo/devpod. Il repository tazlab-k8s è stato ripulito e ora contiene solo un riferimento leggero nel devcontainer.json:\n\u0026#34;image\u0026#34;: \u0026#34;tazzo/tazlab.net:devpod\u0026#34; Questo trasforma l\u0026rsquo;immagine DevPod in un Prodotto di Piattaforma autonomo, versionabile e riutilizzabile trasversalmente su tutti i progetti dell\u0026rsquo;organizzazione, pulendo significativamente la codebase del cluster.\nRiflessioni Post-Lab # Il risultato di questa maratona ingegneristica è un ambiente che definirei \u0026ldquo;Anti-Fragile\u0026rdquo;. Non dipendo più dalla configurazione del laptop ospite. Posso formattare la macchina fisica, installare Docker e DevPod, e tornare operativo al 100% nel tempo necessario a scaricare l\u0026rsquo;immagine Docker (circa 2 minuti su una connessione fibra).\nQuesto setup ha implicazioni profonde per la stabilità a lungo termine del cluster:\nUniformità: Ogni operazione sul cluster viene eseguita con la stessa identica versione dei binari, eliminando bug dovuti a incompatibilità tra client e server. Sicurezza: I segreti sono confinati in memoria o in mount temporanei, riducendo la superficie di attacco. Onboarding: Se dovessi collaborare con un altro ingegnere, il tempo di setup del suo ambiente sarebbe nullo. La lezione più importante appresa oggi riguarda l\u0026rsquo;importanza di investire tempo nel proprio \u0026ldquo;meta-lavoro\u0026rdquo;. Le ore spese per costruire questo ambiente verranno ripagate in minuti risparmiati ogni singolo giorno di operatività futura. Il prossimo passo logico sarà portare questo DevPod dal motore Docker locale direttamente dentro il cluster Kubernetes, trasformandolo in un bastione di gestione persistente e accessibile ovunque, ma questa è una storia per il prossimo log.\n","date":"12 gennaio 2026","externalUrl":null,"permalink":"/it/posts/devpod-architecture-deep-dive/","section":"Posts","summary":"","title":"L'Officina Immutabile: Architettura di un Ambiente DevPod \"Golden Image\" per l'Orchestrazione Kubernetes","type":"posts"},{"content":"","date":"12 gennaio 2026","externalUrl":null,"permalink":"/it/tags/produttivit%C3%A0/","section":"Tags","summary":"","title":"Produttività","type":"tags"},{"content":"In questa sezione troverai guide complete per il tuo Homelab e il setup di Kubernetes.\n","date":"10 gennaio 2026","externalUrl":null,"permalink":"/it/guides/","section":"Guide","summary":"","title":"Guide","type":"guides"},{"content":"","date":"10 gennaio 2026","externalUrl":null,"permalink":"/it/tags/sops/","section":"Tags","summary":"","title":"Sops","type":"tags"},{"content":" Paradigmi della Sicurezza Cloud-Native e l\u0026rsquo;Inadeguatezza dei Meccanismi Nativi # L\u0026rsquo;evoluzione delle infrastrutture verso modelli cloud-native e l\u0026rsquo;adozione massiva di Kubernetes come orchestratore di container hanno introdotto sfide di sicurezza senza precedenti. In questo contesto, la gestione dei segreti — ovvero di quelle informazioni sensibili come chiavi API, password di database, certificati TLS e token di accesso — è diventata il pilastro fondamentale di ogni strategia di sicurezza moderna.1 Tradizionalmente, la gestione dei dati sensibili era afflitta dal fenomeno della dispersione, o \u0026ldquo;sprawl\u0026rdquo;, in cui le credenziali venivano spesso codificate direttamente nel codice sorgente, archiviate in chiaro nei file di configurazione o esposte in modo insicuro tramite variabili d\u0026rsquo;ambiente.3 Con il passaggio ai microservizi, il numero di queste credenziali è cresciuto esponenzialmente, rendendo i metodi manuali non solo insicuri, ma anche operativamente insostenibili.\nKubernetes offre un sistema nativo per la gestione dei segreti, ma l\u0026rsquo;analisi tecnica approfondita rivela limitazioni strutturali critiche per gli ambienti di produzione. Per impostazione predefinita, i segreti di Kubernetes sono archiviati in etcd, il database chiave-valore del cluster, utilizzando la codifica Base64. È essenziale sottolineare che la codifica Base64 non costituisce in alcun modo una forma di crittografia; essa serve esclusivamente a permettere la memorizzazione di dati binari arbitrari.1 Senza una configurazione esplicita della crittografia a riposo (Encryption at Rest) per etcd, chiunque ottenga l\u0026rsquo;accesso al backend di archiviazione o all\u0026rsquo;API server con privilegi sufficienti può recuperare i segreti in chiaro.5 Inoltre, i segreti nativi mancano di funzionalità avanzate come la rotazione automatica delle credenziali, il controllo degli accessi granulare basato sull\u0026rsquo;identità e un sistema di audit logging robusto che possa tracciare chi ha effettuato l\u0026rsquo;accesso a un segreto e quando.1\nPer rispondere a queste esigenze, il panorama DevOps ha integrato strumenti specializzati come HashiCorp Vault e Mozilla SOPS. Vault agisce come un\u0026rsquo;autorità centrale per i segreti, fornendo un piano di controllo unificato che trascende il singolo cluster Kubernetes.4 SOPS, d\u0026rsquo;altro canto, risolve la sfida dell\u0026rsquo;integrazione tra segretezza e sistemi di controllo versione (Git), permettendo di cifrare i dati sensibili prima che vengano archiviati nei repository.9 La combinazione di questi strumenti, supportata dall\u0026rsquo;automazione tramite Terraform, permette di costruire pipeline CI/CD sicure e resilienti, adatte sia a un piccolo laboratorio domestico (homelab) sia a infrastrutture professionali su larga scala.11\nArchitettura Interna di HashiCorp Vault: Il Cuore della Gestione dei Segreti # HashiCorp Vault non è un semplice database crittografato, ma un framework completo per la sicurezza basata sull\u0026rsquo;identità. La sua architettura è progettata attorno al concetto di barriera crittografica che protegge tutti i dati archiviati nel backend.13 Quando Vault viene avviato, si trova in uno stato di \u0026ldquo;sealed\u0026rdquo; (sigillato). In questo stato, Vault è in grado di accedere al proprio storage fisico ma non può decifrare i dati in esso contenuti, poiché la chiave master (Master Key) non è disponibile in memoria.13\nIl Processo di Unseal e l\u0026rsquo;Algoritmo di Shamir # Il processo di sblocco, noto come \u0026ldquo;unseal\u0026rdquo;, richiede tradizionalmente la ricostruzione della Master Key. Vault utilizza l\u0026rsquo;algoritmo di Shamir\u0026rsquo;s Secret Sharing per suddividere la Master Key in più frammenti (key shares). Un numero minimo specificato di questi frammenti (threshold) deve essere fornito per ricostruire la chiave master e consentire a Vault di decifrare la chiave di crittografia dei dati (Barrier Key).15 Negli ambienti Kubernetes, dove i pod sono effimeri e possono essere rischedulati frequentemente, l\u0026rsquo;unseal manuale è impraticabile.13 Per questo motivo, si adotta quasi universalmente la funzionalità di Auto-unseal, che delega la protezione della Master Key a un servizio KMS esterno (come AWS KMS, Azure Key Vault o Google Cloud KMS) o a un altro cluster Vault tramite l\u0026rsquo;engine Transit.13\nMotori di Segreti e Metodi di Autenticazione # La flessibilità di Vault deriva dai suoi motori di segreti (Secret Engines) e metodi di autenticazione (Auth Methods). Mentre i motori KV (Key-Value) memorizzano segreti statici, i motori dinamici possono generare credenziali \u0026ldquo;on-the-fly\u0026rdquo; per database, fornitori cloud o sistemi di messaggistica.1 Queste credenziali hanno un ciclo di vita limitato (TTL) e vengono revocate automaticamente allo scadere del tempo, riducendo drasticamente la \u0026ldquo;blast radius\u0026rdquo; in caso di compromissione.2\nComponente di Vault Funzione Principale Applicazione in Kubernetes Barrier Barriera crittografica tra storage e API Protezione dei dati sensibili in etcd o Raft Storage Backend Persistenza dei dati (es. Raft, Consul) Archiviazione dei segreti su Persistent Volumes Secret Engines Generazione/Archiviazione segreti Gestione certificati PKI, credenziali DB dinamiche Auth Methods Verifica dell\u0026rsquo;identità dei client Integrazione con Kubernetes ServiceAccounts Audit Broker Registrazione di ogni richiesta/risposta Monitoraggio accessi per conformità e sicurezza Implementazione di Vault su Kubernetes: Raft e Alta Affidabilità # La distribuzione di Vault su Kubernetes richiede un\u0026rsquo;attenta pianificazione per garantire la disponibilità e la persistenza dei dati. L\u0026rsquo;approccio moderno raccomandato da HashiCorp prevede l\u0026rsquo;uso dell\u0026rsquo;Integrated Storage basato sul protocollo di consenso Raft.13 A differenza dei backend esterni come Consul, Raft permette a Vault di gestire autonomamente la replica dei dati all\u0026rsquo;interno del cluster, semplificando la topologia e riducendo il numero di componenti da monitorare.13\nTopologia del Cluster e Quorum # Un\u0026rsquo;implementazione resiliente di Vault richiede un numero dispari di nodi per evitare scenari di \u0026ldquo;split-brain\u0026rdquo;. In produzione, si consiglia un minimo di tre nodi per tollerare il fallimento di un singolo nodo, mentre una configurazione a cinque nodi è preferibile per gestire la perdita di due nodi o di un\u0026rsquo;intera zona di disponibilità senza interruzioni del servizio.14 Ogni nodo partecipa alla replica del log Raft, garantendo che ogni operazione di scrittura sia confermata dalla maggioranza prima di essere considerata definitiva.14\nConfigurazioni del Grafico Helm e Hardening # L\u0026rsquo;installazione avviene tipicamente tramite il grafico Helm ufficiale di HashiCorp. Le configurazioni critiche includono l\u0026rsquo;abilitazione del modulo server.ha.enabled e la definizione dello storage tramite volumeClaimTemplates per garantire che ogni replica di Vault abbia il proprio volume persistente dedicato.13 Per massimizzare la sicurezza, è necessario implementare l\u0026rsquo;isolamento dei carichi di lavoro (workload isolation). Vault non dovrebbe condividere i nodi con altre applicazioni per mitigare i rischi di attacchi via side-channel. Questo si ottiene utilizzando nodeSelector, tolerations e regole di affinità per confinare i pod di Vault su hardware dedicato.11\nUn aspetto spesso trascurato è la configurazione dei probe di liveness e readiness. Poiché un\u0026rsquo;istanza di Vault può essere attiva ma sigillata (sealed), il probe di readiness deve essere configurato in modo intelligente per distinguere tra un processo in esecuzione e un servizio pronto a rispondere alle richieste di decrittografia. Il grafico Helm gestisce gran parte di questa logica, utilizzando comandi CLI come vault status per verificare lo stato interno del nodo.13\nTerraform: Il Tessuto Connettivo dell\u0026rsquo;Automazione DevOps # Terraform si integra nell\u0026rsquo;ecosistema come lo strumento di Infrastructure as Code (IaC) per eccellenza, permettendo di configurare non solo l\u0026rsquo;infrastruttura di base (cluster Kubernetes, reti, storage), ma anche le policy di accesso e i segreti all\u0026rsquo;interno di Vault.12 Il valore di Terraform risiede nella sua capacità di gestire le dipendenze tra i diversi provider.\nGestione del Ciclo di Vita e Dipendenze # L\u0026rsquo;uso del provider hashicorp/vault consente agli operatori di definire segreti, policy e configurazioni di autenticazione in modo dichiarativo. Allo stesso tempo, il provider hashicorp/kubernetes permette di mappare queste informazioni all\u0026rsquo;interno del cluster.17 Un pattern comune prevede l\u0026rsquo;estrazione di un segreto da Vault tramite un data source e la sua successiva creazione come segreto Kubernetes per applicazioni legacy che non supportano l\u0026rsquo;integrazione nativa con Vault.7\nSicurezza del File di Stato e Variabili Sensibili # Una sfida critica nell\u0026rsquo;uso di Terraform è la protezione del file di stato (terraform.tfstate). Questo file contiene spesso informazioni sensibili in chiaro, inclusi i segreti recuperati da Vault durante la fase di pianificazione o applicazione.7 È imperativo archiviare lo stato in un backend remoto sicuro, come AWS S3 con crittografia lato server e blocco dello stato (DynamoDB), o utilizzare HashiCorp Terraform Cloud che gestisce nativamente la crittografia dello stato a riposo.20 Inoltre, le variabili marcate come sensitive = true impediscono a Terraform di stamparne i valori nell\u0026rsquo;output della console, riducendo il rischio di esposizione nei log della pipeline CI/CD.7\nStrategia di Terraform Beneficio per la Sicurezza Rischio Mitigato Backend Remoto Cifrato Crittografia dello stato a riposo Accesso non autorizzato ai segreti nel tfstate Variabili Sensitive Offuscamento dei valori nei log Esposizione accidentale in CI/CD stdout Provider Vault Gestione centralizzata dei segreti Hardcoding di credenziali nei file.tf RBAC per il Piano di Controllo Limitazione di chi può eseguire apply Modifiche non autorizzate all\u0026rsquo;infrastruttura critica Mozilla SOPS: Sicurezza per il Controllo Versione e Flussi GitOps # Mozilla SOPS (Secrets OPerationS) nasce dalla necessità di integrare i segreti all\u0026rsquo;interno del flusso di lavoro basato su Git (GitOps) senza compromettere la sicurezza. A differenza dei segreti di Kubernetes, che non dovrebbero mai essere archiviati in Git nemmeno se codificati, i file cifrati con SOPS sono sicuri per il versionamento.9\nCrittografia Envelope e Multidestinatario # SOPS implementa la crittografia envelope, dove i dati sono cifrati con una Data Encryption Key (DEK) simmetrica, la quale viene a sua volta cifrata da una o più Master Keys (KEK) gestite esternamente.23 Questo approccio permette di avere più destinatari per lo stesso segreto: ad esempio, un file può essere decifrato da un team di amministratori tramite le loro chiavi PGP personali e, contemporaneamente, dal cluster Kubernetes tramite un servizio KMS cloud.23\nIntegrazione con age per Semplicità Operativa # Mentre PGP è stato storicamente lo standard per SOPS, lo strumento age (Actually Good Encryption) è diventato la scelta preferita per gli ambienti DevOps moderni grazie alla sua semplicità, all\u0026rsquo;assenza di configurazioni complesse e alla velocità crittografica.25 In un flusso di lavoro basato su age, ogni operatore genera una coppia di chiavi; la chiave pubblica viene inserita nel file .sops.yaml del repository, mentre la chiave privata rimane protetta sulla macchina dell\u0026rsquo;operatore o caricata come segreto nel cluster Kubernetes.25\nYAML\n# Esempio di file.sops.yaml\ncreation_rules:\n- path_regex:.*\\.enc\\.yaml$\nencrypted_regex: ^(data|stringData)$\nage: age1vwd8j93mx9l99k\u0026hellip; # Chiave pubblica del cluster\npgp: 0123456789ABCDEF\u0026hellip; # Chiave di backup dell\u0026rsquo;admin\nL\u0026rsquo;uso di encrypted_regex è una best practice fondamentale: permette di cifrare solo i valori sensibili (come i campi data e stringData di un segreto Kubernetes) lasciando in chiaro i metadati come apiVersion, kind e metadata.name. Questo consente agli strumenti di GitOps e agli operatori di identificare il tipo di risorsa senza doverla decifrare.25\nMeccanismi di Consumo dei Segreti in Kubernetes # Una volta che i segreti sono stati archiviati in Vault o cifrati con SOPS, il carico di lavoro in esecuzione su Kubernetes deve potervi accedere. Esistono tre pattern principali, ognuno dei quali risponde a requisiti diversi in termini di sicurezza e complessità.6\n1. Vault Agent Injector # Questo metodo utilizza un Sidecar container iniettato automaticamente nei pod tramite un Mutating Admission Webhook. Il Vault Agent si occupa dell\u0026rsquo;autenticazione con Vault utilizzando il ServiceAccount del pod e scrive i segreti in un volume di memoria condiviso (emptyDir).6 È la soluzione ideale per applicazioni che non sono \u0026ldquo;cloud-native\u0026rdquo; e che si aspettano di leggere i segreti da file locali, poiché permette di formattare i dati tramite template HCL o Go.6\n2. Vault Secrets Operator (VSO) # Il VSO rappresenta l\u0026rsquo;approccio nativo per il GitOps. L\u0026rsquo;operatore monitora le risorse personalizzate (CRD) nel cluster, recupera i dati da Vault e crea/aggiorna segreti Kubernetes standard.6 Questo metodo è estremamente potente perché permette alle applicazioni di utilizzare segreti Kubernetes nativi (montati come volumi o variabili d\u0026rsquo;ambiente) senza alcuna modifica al codice, pur mantenendo Vault come unica fonte di verità.6\n3. Secrets Store CSI Driver # Questo driver permette di montare segreti esterni direttamente come volumi nel file system del pod, senza mai creare un oggetto Secret di Kubernetes.6 Questo approccio è considerato il più sicuro poiché il segreto esiste solo all\u0026rsquo;interno della memoria effimera del pod e scompare quando il pod viene terminato, riducendo la persistenza dei dati sensibili nel cluster.6\nMetodo di Integrazione Archiviazione nel Cluster Dinamicità Complessità Vault Agent Injector Volume in memoria (Sidecar) Molto Alta (Rinnovo automatico) Media Vault Secrets Operator Oggetto Secret Kubernetes Alta (Sincronizzazione periodica) Bassa Secrets Store CSI File system del Pod Alta (Aggiornamento al volo) Alta Native K8s Secrets etcd (Base64) Nulla (Manuale) Minima Il Problema del Secret Zero e la Soluzione basata sull\u0026rsquo;Identità # Il dilemma del \u0026ldquo;Secret Zero\u0026rdquo; è una sfida logica fondamentale nella sicurezza informatica: per recuperare i propri segreti in modo sicuro, un\u0026rsquo;applicazione ha bisogno di una credenziale iniziale per dimostrare la propria identità al gestore dei segreti.32 Se questa credenziale iniziale viene hardcoded nell\u0026rsquo;immagine del container o passata come variabile d\u0026rsquo;ambiente insicura, l\u0026rsquo;intero sistema diventa vulnerabile.32\nAttestazione Crittografica dell\u0026rsquo;Identità # La soluzione moderna al Secret Zero consiste nello spostare il focus dal \u0026ldquo;cosa possiedi\u0026rdquo; (una password) al \u0026ldquo;chi sei\u0026rdquo; (un\u0026rsquo;identità verificabile).33 In Kubernetes, questo viene realizzato tramite il metodo di autenticazione Kubernetes di Vault. Quando un pod tenta di accedere a Vault, invia il proprio token JWT del ServiceAccount, che è iniettato automaticamente da Kubernetes nel file system del pod.13 Vault riceve questo token e contatta l\u0026rsquo;API server di Kubernetes tramite una richiesta TokenReview per verificare che il token sia valido e appartenga al ServiceAccount dichiarato.13 Una volta confermata l\u0026rsquo;identità, Vault rilascia un token di sessione con privilegi limitati, eliminando la necessità di distribuire segreti di bootstrap.15\nFederazione OIDC in CI/CD # Lo stesso principio si applica alle pipeline CI/CD. Utilizzando la federazione di identità OIDC (OpenID Connect), una pipeline di GitHub Actions o GitLab CI può ottenere un token JWT temporaneo firmato dal fornitore della pipeline.35 Vault può essere configurato per fidarsi di questo fornitore OIDC, verificando le \u0026ldquo;claims\u0026rdquo; (come il nome del repository, il ramo o l\u0026rsquo;ambiente) per decidere se concedere l\u0026rsquo;accesso ai segreti necessari per la distribuzione.35 Questo rimuove completamente la necessità di memorizzare token di Vault a lungo termine all\u0026rsquo;interno dei segreti di GitHub o GitLab, risolvendo efficacemente il problema del Secret Zero per l\u0026rsquo;automazione.35\nCaso d\u0026rsquo;Uso Homelab: Implementazione su Raspberry Pi con k3s, Flux e SOPS # In un contesto domestico, le risorse sono limitate e la semplicità operativa è fondamentale. Un Raspberry Pi 4 (con 4GB o 8GB di RAM) rappresenta la piattaforma ideale per eseguire k3s, una distribuzione Kubernetes leggera ottimizzata per l\u0026rsquo;edge computing.29\nPreparazione dell\u0026rsquo;Hardware e del Sistema Operativo # L\u0026rsquo;installazione parte dall\u0026rsquo;uso del Raspberry Pi Imager per scrivere Raspberry Pi OS Lite (64-bit) su una scheda SD.29 Una configurazione critica per k3s è l\u0026rsquo;abilitazione dei cgroups nel file /boot/firmware/cmdline.txt, aggiungendo i parametri cgroup_memory=1 cgroup_enable=memory, senza i quali il servizio k3s non riuscirebbe ad avviarsi correttamente.29 Per garantire la stabilità, si raccomanda di assegnare un IP statico al dispositivo tramite una prenotazione DHCP sul router domestico.29\nConfigurazione del Flusso GitOps # In un homelab, la gestione dei segreti tramite SOPS e chiavi age è spesso preferita all\u0026rsquo;installazione di un\u0026rsquo;istanza completa di Vault, a causa del minor overhead di memoria.27 Il flusso di lavoro si articola come segue:\nBootstrap di FluxCD: Si installa Flux sul cluster e lo si collega a un repository Git privato.29 Gestione delle chiavi: Si genera una coppia di chiavi age sulla macchina di gestione. La chiave privata viene caricata nel cluster k3s come un segreto Kubernetes nel namespace flux-system.28 Cifratura dei manifesti: Gli sviluppatori (ovvero gli utenti dell\u0026rsquo;homelab) scrivono i propri manifesti YAML per applicazioni come Pi-hole o Home Assistant, includendo le credenziali necessarie.29 Questi file vengono cifrati localmente con SOPS prima del commit.26 Decrittografia Automatica: Quando Flux rileva un nuovo commit, il suo controller Kustomize utilizza la chiave age presente nel cluster per decifrare i manifesti e applicarli, garantendo che i segreti non siano mai esposti in chiaro nel repository.26 Questo setup fornisce un\u0026rsquo;esperienza di livello professionale con costi minimi e massima sicurezza, permettendo di gestire l\u0026rsquo;intera infrastruttura domestica come codice.29\nCaso d\u0026rsquo;Uso Professionale: Infrastruttura Multi-Cluster Enterprise # In un ambiente aziendale, i requisiti di disponibilità, audit e separazione dei compiti (Separation of Duties) impongono un\u0026rsquo;architettura più complessa. Qui, HashiCorp Vault diventa il centro nevralgico della sicurezza.\nReference Architecture Multi-Cluster # La best practice enterprise prevede la separazione fisica tra il cluster che ospita Vault (Tooling Cluster) e i cluster che eseguono i carichi di lavoro applicativi (Production Clusters).11 Questa separazione garantisce che un eventuale \u0026ldquo;cluster failure\u0026rdquo; dovuto a un carico eccessivo delle applicazioni non impedisca l\u0026rsquo;accesso ai segreti, bloccando di fatto ogni operazione di ripristino o autoscaling.11\nIl cluster Vault deve essere distribuito su tre zone di disponibilità (AZ) per garantire l\u0026rsquo;alta affidabilità.14 Viene implementato l\u0026rsquo;Auto-unseal tramite il servizio KMS del cloud provider (es. AWS KMS) per eliminare il rischio operativo dello sblocco manuale.13\nIntegrazione Avanzata con Terraform e CI/CD # Nelle grandi organizzazioni, la configurazione di Vault non viene fatta manualmente. Si utilizzano pipeline Terraform che definiscono:\nPolicy Granulari: Ogni applicazione ha una policy dedicata che permette l\u0026rsquo;accesso in sola lettura esclusivamente ai percorsi dei segreti ad essa assegnati.2 Audit Logging Centralizzato: Vault viene configurato per inviare i log di audit a un sistema SIEM (come Splunk o Elasticsearch) per il rilevamento di anomalie in tempo reale.13 PKI as a Service: Vault viene utilizzato come autorità di certificazione (CA) intermedia per emettere certificati TLS a breve durata per la comunicazione tra pod, integrandosi spesso con Service Mesh come Istio tramite l\u0026rsquo;integrazione cert-manager.6 Conformance e Governance # Un pilastro fondamentale della produzione è la rotazione dei segreti. Mentre nell\u0026rsquo;homelab la rotazione può essere semestrale e manuale, in produzione deve essere automatizzata.2 Vault ruota periodicamente le Master Keys e le credenziali dei database ogni 30 giorni o meno, riducendo la validità temporale di ogni segreto rubato.16 Questo processo è trasparente per le applicazioni se integrato tramite il Vault Agent, che aggiorna automaticamente il file del segreto sul disco quando viene ruotato.6\nIntegrazione tra Vault e SOPS: Il Meglio dei Due Mondi # Un\u0026rsquo;evoluzione sofisticata del workflow DevOps consiste nell\u0026rsquo;utilizzare Vault come backend di crittografia per SOPS.24 Invece di fare affidamento su chiavi age distribuite, SOPS utilizza l\u0026rsquo;engine Transit di Vault per cifrare la Data Encryption Key (DEK).\nIl Flusso di Lavoro Ibrido # In questo scenario, uno sviluppatore che deve modificare un segreto cifrato in Git non ha bisogno di possedere una chiave privata sul proprio laptop. Egli deve semplicemente autenticarsi a Vault (tramite SSO aziendale). SOPS invia la DEK cifrata a Vault; Vault verifica le policy dell\u0026rsquo;utente e, se autorizzato, decifra la DEK e la restituisce a SOPS per sbloccare il file.24\nQuesto approccio offre vantaggi unici:\nNessuna distribuzione di chiavi: Le chiavi crittografiche non lasciano mai la barriera di sicurezza di Vault.24 Revoca Istantanea: Se un dipendente lascia l\u0026rsquo;azienda, è sufficiente disabilitare il suo account in Vault per impedirgli di decifrare qualsiasi segreto nel repository Git, anche se ne possiede una copia locale.24 Audit centralizzato: Ogni tentativo di decifrare un segreto in Git lascia una traccia nei log di Vault, permettendo di identificare chi sta accedendo a quali informazioni sensibili durante lo sviluppo.24 Caratteristica Solo SOPS (age) Solo Vault (Dynamic) Ibrido (SOPS + Vault Transit) Fonte di Verità Git (Repository) Vault (API) Git (Cifrato) + Vault (Chiavi) Accesso Offline Sì (con chiave privata) No (richiede connessione) No (richiede autenticazione) Audit delle Operazioni Limitato (Git logs) Completo (Vault logs) Completo per ogni decrittografia Gestione Chiavi Manuale (Distribuzione file) Automatica (HSM/KMS) Centralizzata in Vault Monitoraggio, Audit e Manutenzione Operativa (Giorno 2) # La gestione dei segreti non si esaurisce con l\u0026rsquo;implementazione iniziale. Il successo a lungo termine dipende dalle operazioni del \u0026ldquo;Giorno 2\u0026rdquo;, che includono il monitoraggio della salute del cluster e l\u0026rsquo;auditing rigoroso.13\nStrategie di Backup e Disaster Recovery # Per Vault, il backup non riguarda solo i dati, ma anche le Master Keys. Utilizzando Raft, è possibile eseguire snapshot periodici dello stato del cluster tramite il comando vault operator raft snapshot save. Questi snapshot devono essere archiviati in un bucket S3 con crittografia e versionamento abilitati.13 In caso di fallimento totale del cluster Kubernetes, è fondamentale avere una procedura documentata per il ripristino di Vault da uno snapshot su un nuovo cluster, inclusa la riconnessione al servizio KMS per l\u0026rsquo;Auto-unseal.14\nRilevamento della Deriva e Auto-Healing # Negli ecosistemi GitOps, la deriva (drift) si verifica quando lo stato reale del cluster diverge da quello definito in Git. Flux e ArgoCD monitorano costantemente questa deriva. Se un amministratore modifica manualmente un segreto decifrato tramite kubectl edit, il controller GitOps rileverà la discrepanza e sovrascriverà le modifiche con lo stato cifrato presente in Git.43 Questo garantisce l\u0026rsquo;immutabilità della configurazione e impedisce modifiche silenziose e potenzialmente dannose.42\nAnalisi dei Log e Rilevamento di Intrusioni # I log di audit di Vault sono una miniera d\u0026rsquo;oro per la sicurezza. Un\u0026rsquo;analisi sofisticata dovrebbe cercare pattern anomali, come un improvviso picco di richieste di lettura di segreti da parte di un ServiceAccount che solitamente ne legge solo pochi, o tentativi di accesso a percorsi non autorizzati.13 L\u0026rsquo;integrazione con strumenti di anomaly detection basati su Machine Learning può aiutare a identificare questi comportamenti prima che portino a una violazione dei dati su larga scala.44\nConsiderazioni su Prestazioni e Scalabilità # L\u0026rsquo;introduzione di Vault e SOPS aggiunge strati di astrazione che possono influenzare le prestazioni. La latenza di rete tra l\u0026rsquo;applicazione e Vault è un fattore critico, specialmente per applicazioni che effettuano centinaia di richieste di segreti al secondo.1\nOttimizzazione tramite Caching e Token Rinnovabili # Per ridurre il carico su Vault, il Vault Agent implementa meccanismi di caching e rinnovo dei token. Invece di richiedere un nuovo segreto per ogni transazione, l\u0026rsquo;agente può mantenere il segreto in memoria e rinnovarne il \u0026ldquo;lease\u0026rdquo; periodicamente, riducendo il traffico verso il cluster Vault.6 In ambienti multi-regione, si possono utilizzare le repliche di performance di Vault per distribuire i dati geograficamente, permettendo alle applicazioni di leggere i segreti dal nodo Vault più vicino, minimizzando la latenza intercontinentale.14\nGestione del Carico in Kubernetes # Le risorse CPU e memoria per Vault devono essere dimensionate correttamente. Un cluster Vault con storage Raft richiede dischi ad alte prestazioni con bassi tempi di seek (IOPS elevati) per evitare ritardi nel commit dei log di consenso.14\nSnippet di codice\nT_{commit} = L_{network} + T_{disk\\_write} + T_{consensus\\_logic}\nLa formula semplificata sopra evidenzia che il tempo di commit di un segreto ($T_{commit}$) è la somma della latenza di rete tra i nodi ($L_{network}$), il tempo di scrittura fisica su disco ($T_{disk\\_write}$) e l\u0026rsquo;overhead computazionale del protocollo Raft. In ambienti enterprise, l\u0026rsquo;uso di storage SSD NVMe è caldamente raccomandato per mantenere le performance sotto i livelli di guardia.14\nConclusioni Operative e Roadmap di Adozione # La gestione dei segreti è un viaggio incrementale. Per le organizzazioni che iniziano oggi, la roadmap consigliata è:\nFase 1 (Igiene di Base): Implementare SOPS con chiavi age per tutti i segreti archiviati in Git, eliminando immediatamente i file in chiaro.22 Fase 2 (Centralizzazione): Installare HashiCorp Vault in alta affidabilità su Kubernetes e migrare i segreti critici dei database, implementando la rotazione automatica.4 Fase 3 (Identità): Abilitare il metodo di autenticazione Kubernetes e OIDC per eliminare il problema del Secret Zero e passare a un\u0026rsquo;autenticazione basata sulla fiducia nell\u0026rsquo;infrastruttura.15 Fase 4 (Ottimizzazione): Integrare SOPS con Vault Transit per centralizzare la gestione delle chiavi e implementare audit logging avanzato per ogni accesso ai dati sensibili.24 Adottando questi strumenti e metodologie, i team DevOps possono garantire che la sicurezza non sia un ostacolo alla velocità, ma un acceleratore che permette di distribuire codice in modo sicuro, auditable e resiliente, dalle modeste risorse di un Raspberry Pi alle vaste infrastrutture del cloud globale.11\nBibliografia # Secrets Management in Kubernetes: Native Tools vs HashiCorp Vault - PufferSoft, accesso eseguito il giorno gennaio 8, 2026, https://puffersoft.com/secrets-management-in-kubernetes-native-tools-vs-hashicorp-vault/ 10 Best Practices For Cloud Secrets Management (2025 Guide) | by Beck Cooper - Medium, accesso eseguito il giorno gennaio 8, 2026, https://beckcooper.medium.com/10-best-practices-for-cloud-secrets-management-2025-guide-ffed6858e76b Secrets Management: Vault, AWS Secrets Manager, or SOPS? - DEV Community, accesso eseguito il giorno gennaio 8, 2026, https://dev.to/instadevops/secrets-management-vault-aws-secrets-manager-or-sops-2ce1 5 best practices for secrets management - HashiCorp, accesso eseguito il giorno gennaio 8, 2026, https://www.hashicorp.com/en/resources/5-best-practices-for-secrets-management Kubernetes Secrets Management in 2025 - A Complete Guide - Infisical, accesso eseguito il giorno gennaio 8, 2026, https://infisical.com/blog/kubernetes-secrets-management-2025 How HashiCorp\u0026rsquo;s Solutions Suite Secures Kubernetes for Business Success, accesso eseguito il giorno gennaio 8, 2026, https://somerford-ltd.medium.com/how-hashicorps-solutions-suite-secures-kubernetes-for-business-success-7a561ceee6fc How to Manage Kubernetes Secrets with Terraform - Spacelift, accesso eseguito il giorno gennaio 8, 2026, https://spacelift.io/blog/terraform-kubernetes-secret Run Vault on Kubernetes - HashiCorp Developer, accesso eseguito il giorno gennaio 8, 2026, https://developer.hashicorp.com/vault/docs/deploy/kubernetes Building a Secure and Efficient GitOps Pipeline with SOPS | by Platform Engineers - Medium, accesso eseguito il giorno gennaio 8, 2026, https://medium.com/@platform.engineers/building-a-secure-and-efficient-gitops-pipeline-with-sops-44ca1a4e505f Secrets Management With GitOps and Kubernetes - Stakater, accesso eseguito il giorno gennaio 8, 2026, https://www.stakater.com/post/secrets-management-with-gitops-and-kubernetes HashiCorp Vault on production-ready Kubernetes: Architecture guide, accesso eseguito il giorno gennaio 8, 2026, https://flowfactor.be/blogs/hashicorp-vault-on-production-ready-kubernetes-complete-architecture-guide/ Master DevOps: Kubernetes, Terraform, \u0026amp; Vault | Kite Metric, accesso eseguito il giorno gennaio 8, 2026, https://kitemetric.com/blogs/mastering-devops-practical-guide-to-kubernetes-terraform-and-vault Vault on Kubernetes deployment guide - HashiCorp Developer, accesso eseguito il giorno gennaio 8, 2026, https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-raft-deployment-guide Vault with integrated storage reference architecture - HashiCorp Developer, accesso eseguito il giorno gennaio 8, 2026, https://developer.hashicorp.com/vault/tutorials/day-one-raft/raft-reference-architecture How to Setup Vault in Kubernetes- Beginners Tutorial - DevOpsCube, accesso eseguito il giorno gennaio 8, 2026, https://devopscube.com/vault-in-kubernetes/ CI/CD Pipeline Security Best Practices: The Ultimate Guide - Wiz, accesso eseguito il giorno gennaio 8, 2026, https://www.wiz.io/academy/application-security/ci-cd-security-best-practices Manage Kubernetes resources with Terraform - HashiCorp Developer, accesso eseguito il giorno gennaio 8, 2026, https://developer.hashicorp.com/terraform/tutorials/kubernetes/kubernetes-provider Terraform - HashiCorp Developer, accesso eseguito il giorno gennaio 8, 2026, https://developer.hashicorp.com/terraform Terraform Project for Managing Vault Secrets in a Kubernetes Cluster - GitGuardian Blog, accesso eseguito il giorno gennaio 8, 2026, https://blog.gitguardian.com/terraform-project-for-managing-vault-secrets-in-a-kubernetes-cluster/ Managing Secrets in Terraform: A Complete Guide, accesso eseguito il giorno gennaio 8, 2026, https://ezyinfra.dev/blog/managing-secrets-in-terraform Access secrets from Hashicorp Vault in Github Action to implement in Terraform code, accesso eseguito il giorno gennaio 8, 2026, https://www.reddit.com/r/hashicorp/comments/1hzz3r4/access_secrets_from_hashicorp_vault_in_github/ Securing Secrets in a GitOps Environment with SOPS | by Paolo Carta | ITNEXT, accesso eseguito il giorno gennaio 8, 2026, https://itnext.io/securing-secrets-in-a-gitops-environment-with-sops-dccd8e8952d9 Securely store secrets in Git using SOPS and Azure Key Vault - Patrick van Kleef, accesso eseguito il giorno gennaio 8, 2026, https://www.patrickvankleef.com/2023/01/18/securely-store-secrets-with-sops-and-keyvault Use vault as backend of sops - by Eric Mourgaya - Medium, accesso eseguito il giorno gennaio 8, 2026, https://medium.com/@eric.mourgaya/use-vault-as-backend-of-sops-1141fcaab07a Secure Secret Management with SOPS in Terraform \u0026amp; Terragrunt - DEV Community, accesso eseguito il giorno gennaio 8, 2026, https://dev.to/hkhelil/secure-secret-management-with-sops-in-terraform-terragrunt-231a Manage Kubernetes secrets with SOPS - Flux, accesso eseguito il giorno gennaio 8, 2026, https://fluxcd.io/flux/guides/mozilla-sops/ Managing secrets with SOPS in your homelab | code and society - codedge, accesso eseguito il giorno gennaio 8, 2026, https://www.codedge.de/posts/managing-secrets-sops-homelab/ Using SOPS Secrets with Age - Federico Serini | Site Reliability Engineer, accesso eseguito il giorno gennaio 8, 2026, https://www.federicoserinidev.com/blog/using_sops_secrets_with_age/ From Zero to GitOps: Building a k3s Homelab on a Raspberry Pi with \u0026hellip;, accesso eseguito il giorno gennaio 8, 2026, https://dev.to/shankar_t/from-zero-to-gitops-building-a-k3s-homelab-on-a-raspberry-pi-with-flux-sops-55b7 List Of Secrets Management Tools For Kubernetes In 2025 - Techiescamp, accesso eseguito il giorno gennaio 8, 2026, https://blog.techiescamp.com/secrets-management-tools/ Solving secret zero with Vault and OpenShift Virtualization - HashiCorp, accesso eseguito il giorno gennaio 8, 2026, https://www.hashicorp.com/en/blog/solving-secret-zero-with-vault-and-openshift-virtualization Secret Zero Problem: Risks and Solutions Explained - GitGuardian, accesso eseguito il giorno gennaio 8, 2026, https://www.gitguardian.com/nhi-hub/the-secret-zero-problem-solutions-and-alternatives What is the Secret Zero Problem? A Deep Dive into Cloud-Native Authentication - Infisical, accesso eseguito il giorno gennaio 8, 2026, https://infisical.com/blog/solving-secret-zero-problem Use Case: Solving the Secret Zero Problem - Aembit, accesso eseguito il giorno gennaio 8, 2026, https://aembit.io/use-case/solving-the-secret-zero-problem/ Integrating Azure DevOps pipelines with HashiCorp Vault, accesso eseguito il giorno gennaio 8, 2026, https://www.hashicorp.com/en/blog/integrating-azure-devops-pipelines-with-hashicorp-vault HashiCorp Vault · Actions · GitHub Marketplace, accesso eseguito il giorno gennaio 8, 2026, https://github.com/marketplace/actions/hashicorp-vault Use HashiCorp Vault secrets in GitLab CI/CD, accesso eseguito il giorno gennaio 8, 2026, https://docs.gitlab.com/ci/secrets/hashicorp_vault/ Tutorial: Authenticating and reading secrets with HashiCorp Vault - GitLab Docs, accesso eseguito il giorno gennaio 8, 2026, https://docs.gitlab.com/ci/secrets/hashicorp_vault_tutorial/ Building a Self-Hosted Homelab: Deploying Kubernetes (K3s), NAS (OpenMediaVault), and Pi-hole for Ad-Free Browsing | by PJames | Medium, accesso eseguito il giorno gennaio 8, 2026, https://medium.com/@james.prakash/building-a-self-hosted-homelab-deploying-kubernetes-k3s-nas-openmediavault-and-pi-hole-for-7390d5a59bac Modern Java developement with Devops and AI – Modern Java developement with Devops and AI, accesso eseguito il giorno gennaio 8, 2026, https://coresynapseai.com/ Secrets and configuration management in IaC: best practices in HashiCorp Vault and SOPS for security and efficiency - Semantive, accesso eseguito il giorno gennaio 8, 2026, https://www.semantive.com/blog/secrets-and-configuration-management-in-iac-best-practices-in-hashicorp-vault-and-sops-for-security-and-efficiency Managing Kubernetes in 2025: 7 Pillars of Production-Grade Platform Management, accesso eseguito il giorno gennaio 8, 2026, https://scaleops.com/blog/the-complete-guide-to-kubernetes-management-in-2025-7-pillars-for-production-scale/ Mastering GitOps with Flux and Argo CD: Automating Infrastructure as Code in Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://www.clutchevents.co/resources/mastering-gitops-with-flux-and-argo-cd-automating-infrastructure-as-code-in-kubernetes Data Science - Noise, accesso eseguito il giorno gennaio 8, 2026, https://noise.getoto.net/tag/data-science/ ","date":"10 gennaio 2026","externalUrl":null,"permalink":"/it/guides/hashicorp-vault-sops-kubernetes-guide/","section":"Guide","summary":"","title":"Strategie Avanzate di Gestione dei Segreti: HashiCorp Vault, SOPS e l'Ecosistema Kubernetes","type":"guides"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/tags/csi/","section":"Tags","summary":"","title":"Csi","type":"tags"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/tags/immutabilit%C3%A0/","section":"Tags","summary":"","title":"Immutabilità","type":"tags"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/tags/immutability/","section":"Tags","summary":"","title":"Immutability","type":"tags"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/tags/persistence/","section":"Tags","summary":"","title":"Persistence","type":"tags"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/tags/persistenza/","section":"Tags","summary":"","title":"Persistenza","type":"tags"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/tags/pki/","section":"Tags","summary":"","title":"Pki","type":"tags"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/tags/sicurezza/","section":"Tags","summary":"","title":"Sicurezza","type":"tags"},{"content":"L\u0026rsquo;avvento di Talos Linux rappresenta un cambiamento di paradigma fondamentale nel modo in cui i professionisti della sicurezza e dell\u0026rsquo;ingegneria delle piattaforme concepiscono il sistema operativo sottostante i cluster Kubernetes. A differenza delle distribuzioni Linux tradizionali, progettate per un uso general-purpose e basate su una gestione mutabile tramite shell e SSH, Talos Linux nasce come una soluzione puramente orientata alle API, immutabile e minimale.1 Questa architettura non è semplicemente un\u0026rsquo;ottimizzazione tecnica, ma una risposta strutturale alle vulnerabilità intrinseche dei sistemi operativi legacy. Eliminando l\u0026rsquo;accesso SSH, i gestori di pacchetti e le utility GNU superflue, Talos riduce drasticamente la superficie di attacco, limitandola a circa 12 binari essenziali contro gli oltre 1.500 di una distribuzione standard.1 La sicurezza in questo contesto non è un\u0026rsquo;aggiunta successiva (bolt-on), ma è integrata nel DNA del sistema, dove ogni interazione avviene tramite chiamate gRPC autenticate e crittografate.2\nArchitettura della Sicurezza Immutabile e Modello di Minaccia # Il cuore della proposta di sicurezza di Talos Linux risiede nella sua natura immutabile e nella gestione dichiarativa. Il sistema operativo viene eseguito da un\u0026rsquo;immagine SquashFS a sola lettura, il che garantisce che, anche in caso di compromissione temporanea del runtime, il sistema possa essere ripristinato a uno stato noto e sicuro semplicemente tramite un riavvio.2 Questo modello elimina il \u0026ldquo;configuration drift\u0026rdquo;, un fenomeno critico dove piccoli cambiamenti manuali nel tempo rendono i server unici e difficili da proteggere.5 In Talos, l\u0026rsquo;intera configurazione della macchina è definita in un singolo manifesto YAML, che include non solo i parametri del sistema operativo, ma anche la configurazione dei componenti di Kubernetes che esso orchestra.2\nL\u0026rsquo;eliminazione di SSH è forse la caratteristica più distintiva e discussa. Tradizionalmente, SSH rappresenta un vettore di attacco primario a causa di chiavi deboli, configurazioni errate e la possibilità per un attaccante di muoversi lateralmente una volta ottenuta una shell.1 Sostituendo SSH con un\u0026rsquo;interfaccia API gRPC, Talos impone che ogni azione amministrativa sia strutturata, tracciabile e basata su certificati.2 Questo sposta il focus della sicurezza dall\u0026rsquo;accesso al nodo alla protezione dei certificati client e delle chiavi API.8\nComponente Tradizionale Approccio Talos Linux Implicazione per la Sicurezza Accesso Remoto SSH (Porta 22) API gRPC (Porta 50000) 8 Gestione Pacchetti apt, yum, pacman Immagine Immutabile (SquashFS) 2 Configurazione Script Bash, Cloud-init Manifesto YAML Dichiarativo 2 Userland GNU Utilities, Shell Minimale (solo 12-50 binari) 1 Privilegi Sudo, Root RBAC basato su API 8 Infrastruttura a Chiave Pubblica (PKI) e Gestione dei Certificati # La sicurezza delle comunicazioni all\u0026rsquo;interno di un cluster Talos e Kubernetes è interamente basata su una complessa gerarchia di certificati X.509. Talos automatizza la creazione e la gestione di queste autorità di certificazione (CA) durante la fase di generazione dei segreti del cluster.7 Esistono tre domini PKI principali che operano in parallelo: il dominio dell\u0026rsquo;API Talos, il dominio dell\u0026rsquo;API Kubernetes e il dominio del database etcd.9\nAutorità di Certificazione Radice e Lifetimes # Per impostazione predefinita, Talos genera CA radice con una durata di 10 anni.13 Questa scelta riflette la filosofia del progetto di fornire un\u0026rsquo;infrastruttura stabile dove la rotazione della CA radice è considerata un\u0026rsquo;operazione eccezionale, necessaria solo in caso di compromissione della chiave privata o per necessità di revoca massiva degli accessi.13 Tuttavia, i certificati emessi da queste CA per i componenti del server e per i client hanno durate significativamente più brevi.9\nI certificati lato server per etcd, i componenti di Kubernetes (come l\u0026rsquo;apiserver) e l\u0026rsquo;API di Talos sono gestiti e ruotati automaticamente dal sistema.9 Un dettaglio critico è rappresentato dal kubelet: sebbene la rotazione sia automatica, il kubelet deve essere riavviato (o il nodo aggiornato/rebootato) almeno una volta all\u0026rsquo;anno per garantire che i nuovi certificati vengano caricati correttamente.9 La verifica dello stato dei certificati dinamici di Kubernetes può essere effettuata tramite il comando talosctl get KubernetesDynamicCerts -o yaml direttamente dal control plane.9\nCertificati Client: talosconfig e kubeconfig # A differenza dei certificati server, i certificati client sono responsabilità esclusiva dell\u0026rsquo;operatore.9 Ogni volta che un utente scarica un file kubeconfig tramite talosctl, viene generato un nuovo certificato client con validità di un anno.9 Allo stesso modo, il file talosconfig, essenziale per interagire con l\u0026rsquo;API di Talos, deve essere rinnovato annualmente.9 La perdita di validità di questi certificati può portare al blocco totale dell\u0026rsquo;accesso amministrativo, rendendo fondamentale l\u0026rsquo;integrazione di processi di rinnovo periodico nelle pipeline operative.9\nCambio Programmato e Rotazione dei Certificati # La rotazione della CA radice, sebbene rara, è un processo ben definito in Talos Linux. Non si tratta di una sostituzione istantanea, che causerebbe un\u0026rsquo;interruzione totale del servizio, ma di un processo di transizione multi-fase.13\nProcesso di Rotazione Automatizzato della CA # Talos fornisce il comando talosctl rotate-ca per orchestrare la rotazione sia per l\u0026rsquo;API Talos che per l\u0026rsquo;API Kubernetes.13 Il flusso di lavoro segue un modello \u0026ldquo;Accepted -\u0026gt; Issuing -\u0026gt; Remove\u0026rdquo; che garantisce la continuità operativa.13\nFase di Accettazione: Viene generata una nuova CA. Questa nuova CA viene aggiunta alla lista delle acceptedCAs nella configurazione della macchina di tutti i nodi.13 In questa fase, il sistema accetta certificati firmati sia dalla vecchia che dalla nuova CA, ma continua a emettere certificati con la vecchia.13 Fase di Emissione (Swap): La nuova CA viene impostata come autorità emittente primaria. I servizi iniziano a generare nuovi certificati utilizzando la nuova chiave privata.13 La vecchia CA rimane tra le acceptedCAs per permettere ai componenti non ancora aggiornati di comunicare.13 Fase di Refresh: Tutti i certificati nel cluster vengono aggiornati. Per Kubernetes, questo comporta il riavvio dei componenti del piano di controllo e del kubelet su ciascun nodo.13 Fase di Rimozione: Una volta confermato che tutti i componenti utilizzano i nuovi certificati, la vecchia CA viene rimossa dalle acceptedCAs. Da questo momento, qualsiasi vecchio talosconfig o kubeconfig diventa inutilizzabile, completando di fatto la revoca degli accessi precedenti.13 Automazione del Rinnovo dei Certificati Client # Poiché i certificati client scadono annualmente, l\u0026rsquo;uso di cronjob o script di automazione è una pratica consolidata. Un amministratore può generare un nuovo talosconfig partendo da uno esistente ancora valido utilizzando il comando talosctl config new --roles os:admin --crt-ttl 24h contro un nodo del control plane.9 Per una gestione più robusta, è possibile estrarre la CA radice e la chiave privata direttamente dai segreti salvati (es. secrets.yaml) per generare nuovi certificati offline, una tecnica vitale per il disaster recovery qualora tutti i certificati client siano scaduti contemporaneamente.9\nGestione dei Segreti: Il Ruolo di Mozilla SOPS # In un\u0026rsquo;architettura GitOps, dove ogni configurazione deve risiedere in un repository Git, la protezione dei segreti presenti nei manifesti di Talos (come chiavi CA, token di bootstrap e segreti di crittografia etcd) diventa la sfida principale. Mozilla SOPS (Secrets OPerationS) si è affermato come lo strumento di riferimento in questo dominio.17\nPerché SOPS è lo Standard per Talos # A differenza di strumenti che crittografano l\u0026rsquo;intero file (come Ansible Vault), SOPS è \u0026ldquo;struttura-consapevole\u0026rdquo; (structure-aware). Può crittografare solo i valori all\u0026rsquo;interno di un file YAML, lasciando le chiavi in chiaro.19 Questo è fondamentale per Talos per diversi motivi:\nDifferenziazione (Diffing): Gli sviluppatori possono vedere quali campi sono cambiati in un commit senza dover decrittografare l\u0026rsquo;intero file, facilitando le revisioni del codice.19 Integrazione con age: SOPS si integra perfettamente con age, uno strumento di crittografia moderno e minimale che evita le complessità di PGP.19 Supporto Nativo negli Strumenti Talos: Tool come talhelper e talm includono il supporto nativo per SOPS, permettendo di gestire l\u0026rsquo;intero ciclo di vita della configurazione (generazione, crittografia, applicazione) in modo fluido.23 Implementazione Pratica: talhelper e SOPS # Il flusso di lavoro raccomandato per la produzione prevede l\u0026rsquo;uso di talhelper per generare i file di configurazione specifici per i nodi partendo da un template centrale (talconfig.yaml) e un file di segreti crittografato (talsecret.sops.yaml).24\nInizializzazione: Si genera una coppia di chiavi age con age-keygen.19 Configurazione di SOPS: Si crea un file .sops.yaml nella root del repository per definire le regole di crittografia, specificando quali campi proteggere tramite espressioni regolari (es. crt, key, secret, token).19 Gestione dei Segreti: Si generano i segreti di base con talhelper gensecret \u0026gt; talsecret.sops.yaml e si procede alla crittografia immediata con sops -e -i talsecret.sops.yaml.24 Generazione Configurazione: Durante la pipeline CI/CD, talhelper genconfig decrittografa automaticamente i segreti necessari per produrre i manifesti finali della macchina, che vengono poi applicati ai nodi.22 Integrazione CI/CD e Pipeline di Sicurezza # L\u0026rsquo;integrazione di Talos Linux in una pipeline CI/CD (GitHub Actions, GitLab CI) trasforma la gestione dell\u0026rsquo;infrastruttura in un processo software rigoroso. Il principio cardine è che nessuna configurazione sensibile deve essere decrittografata sulla macchina dello sviluppatore, ma solo all\u0026rsquo;interno dell\u0026rsquo;ambiente protetto della pipeline.18\nFlusso della Pipeline in Produzione # Una pipeline tipica per il deployment di Talos segue questi passaggi critici per la sicurezza:\nIniezione della Chiave age: La chiave privata age viene memorizzata come un segreto della pipeline (es. SOPS_AGE_KEY). Questo garantisce che solo la pipeline autorizzata possa decrittografare i manifesti.19 Validazione e Linting: Prima di applicare qualsiasi modifica, la pipeline esegue controlli statici sulla configurazione YAML per assicurarsi che non siano stati introdotti errori di sintassi o violazioni delle policy di sicurezza.17 Aggiornamento Staged: Talos supporta la modalità --mode=staged per l\u0026rsquo;applicazione della configurazione. Questo permette di caricare la nuova configurazione sul nodo, che verrà applicata solo al successivo riavvio, consentendo finestre di manutenzione controllate.29 Notifiche e Auditing: Strumenti come ntfy.sh o integrazioni Slack vengono utilizzati per notificare l\u0026rsquo;esito del rinnovo dei certificati o dell\u0026rsquo;applicazione delle patch, garantendo visibilità totale sulle operazioni infrastrutturali.31 Confronto: SOPS vs Vault vs External Secrets Operator # Molti team si chiedono se SOPS sia sufficiente per la produzione o se siano necessarie soluzioni più complesse come HashiCorp Vault. La risposta risiede nella distinzione tra \u0026ldquo;Segreti dell\u0026rsquo;Infrastruttura\u0026rdquo; (necessari per far partire il cluster) e \u0026ldquo;Segreti dell\u0026rsquo;Applicazione\u0026rdquo; (necessari per i carichi di lavoro).33\nCriterio Mozilla SOPS HashiCorp Vault External Secrets Operator (ESO) Punto di Forza Semplicità e GitOps puro. 18 Sicurezza dinamica e auditing avanzato. 33 Ponte tra K8s e cloud KMS. 37 Complessità Bassa (CLI e file). 19 Alta (richiede gestione cluster Vault). 36 Media (operatore nel cluster). 38 Segreti Dinamici No (Statici in Git). 35 Sì (credenziali DB temporanee). 33 Dipende dal backend. 38 Uso ideale per Talos Configurazione Macchina e Bootstrap. 24 Carichi di lavoro Enterprise regolamentati. 33 Sincronizzazione segreti cloud verso Pod. 38 Licenza Open Source (MPL). 41 BSL (BSL non è Open Source). 34 Open Source (Apache 2.0). 38 Analisi critica: Per la gestione della sicurezza del sistema operativo Talos e della PKI iniziale, SOPS è spesso superiore a Vault perché non richiede un\u0026rsquo;infrastruttura preesistente per essere decrittografato.25 Tuttavia, una volta che il cluster è operativo, integrare Vault tramite ESO o l\u0026rsquo;iniettore sidecar di Vault è la pratica migliore per gestire le credenziali applicative, riducendo la proliferazione di segreti statici in Kubernetes.33\nHardening Avanzato: Crittografia del Disco e TPM # Un cluster Kubernetes in produzione non può prescindere dalla protezione dei dati a riposo (data-at-rest). Talos Linux offre una delle implementazioni più avanzate di crittografia del disco tramite LUKS2, integrata direttamente nel ciclo di vita del sistema operativo.29\nCrittografia tramite TPM 2.0 e SecureBoot # L\u0026rsquo;approccio più sicuro su bare metal prevede l\u0026rsquo;uso del chip TPM (Trusted Platform Module). Quando la crittografia è configurata per utilizzare il TPM, Talos \u0026ldquo;sigilla\u0026rdquo; la chiave di cifratura del disco allo stato del firmware e del bootloader.29\nMisurazione del Boot: Durante il processo di avvio, i componenti del Unified Kernel Image (UKI) vengono misurati nei registri PCR (Platform Configuration Registers) del TPM.29 Sblocco Condizionale: La partizione STATE o EPHEMERAL viene sbloccata solo se il SecureBoot è attivo e se le misurazioni PCR-7 (che indicano l\u0026rsquo;integrità dei certificati UEFI) corrispondono a quelle attese.29 Questo impedisce a un attaccante che sottragga fisicamente il disco di accedere ai dati, poiché la chiave non verrebbe rilasciata se inserita in un hardware diverso o con un bootloader manomesso.29 Integrazione con KMS di Rete # Per ambienti cloud o data center dove il TPM non è disponibile o desiderato, Talos supporta la crittografia tramite KMS (Key Management Service) esterno.29 In questa configurazione, il nodo Talos genera una chiave di cifratura casuale, la invia a un endpoint KMS (come Omni o un proxy custom) per essere cifrata (sealed) e memorizza il risultato nei metadati di LUKS2.43 Al riavvio, il nodo deve essere in grado di raggiungere il KMS tramite rete per decrittografare la chiave.43\nImplicazione di rete: L\u0026rsquo;uso del KMS per la partizione STATE introduce una sfida: la configurazione di rete deve essere definita nei parametri del kernel o tramite DHCP, poiché la partizione che normalmente contiene la configurazione è ancora criptata e inaccessibile fino a quando la connessione al KMS non viene stabilita.29\nSicurezza del Network e del Runtime: Cilium e KubeArmor # La sicurezza di Talos non si ferma al sistema operativo. Essendo un sistema \u0026ldquo;purpose-built\u0026rdquo; per Kubernetes, Talos facilita l\u0026rsquo;adozione di stack di networking e sicurezza basati su eBPF, che offrono prestazioni e visibilità superiori rispetto a iptables.11\nCilium come Standard di Produzione # Sebbene Flannel sia il CNI predefinito, Cilium è la scelta consolidata per l\u0026rsquo;enterprise.11 L\u0026rsquo;uso di Cilium su Talos permette di:\nEnforce di Network Policy: Implementare policy L3/L4 e L7 che non sono possibili con Flannel.11 Trasparente Crittografia (mTLS): Cilium può crittografare tutto il traffico pod-to-pod in modo trasparente utilizzando IPsec o WireGuard.45 Sostituzione di Kube-proxy: Eliminare kube-proxy a favore di un\u0026rsquo;implementazione basata su eBPF molto più efficiente.44 Hardening Applicativo con KubeArmor # Mentre Talos isola il nodo, KubeArmor viene utilizzato per proteggere il runtime dei pod. KubeArmor sfrutta i moduli LSM (Linux Security Modules) del kernel (come AppArmor o BPF-LSM) per impedire attacchi di tipo \u0026ldquo;breakout\u0026rdquo; o l\u0026rsquo;esecuzione di file non autorizzati all\u0026rsquo;interno dei contenitori.46 La combinazione di un sistema operativo minimale come Talos con un motore di enforcement come KubeArmor realizza una vera architettura \u0026ldquo;Zero Trust\u0026rdquo; a tutti i livelli dello stack.46\nStrategie di Gestione Operativa e Conclusioni # La gestione della sicurezza in Talos Linux richiede una transizione mentale dall\u0026rsquo;amministrazione di server all\u0026rsquo;orchestrazione di API. Le pratiche comuni e consolidate riflettono questa necessità di automazione e rigore formale.\nImmutabilità Totale: Ogni modifica deve passare attraverso Git e la pipeline CI/CD. L\u0026rsquo;uso di talosctl patch deve essere riservato esclusivamente al debug o a emergenze temporanee, con l\u0026rsquo;obbligo di riflettere immediatamente i cambiamenti nel manifesto YAML principale.1 Monitoraggio Attivo dei Certificati: Dato che i certificati client sono il punto debole del ciclo di vita annuale, è essenziale implementare alert basati sulla scadenza (es. tramite Prometheus) per evitare l\u0026rsquo;interruzione dell\u0026rsquo;accesso amministrativo.9 Governance dei Segreti: SOPS deve essere utilizzato per crittografare i file sensibili del cluster, ma la chiave privata di decrittografia (age) deve essere gestita con la massima severità, preferibilmente tramite un HSM o il servizio di gestione segreti del provider cloud.18 Integrazione Hardware: Ove possibile, attivare il SecureBoot e il TPM per garantire l\u0026rsquo;integrità del boot e la protezione fisica dei dati. Questo trasforma il nodo in una \u0026ldquo;black box\u0026rdquo; sicura e non manomettibile.29 Talos Linux, se configurato seguendo queste pratiche, offre probabilmente il livello di sicurezza più elevato oggi disponibile per Kubernetes. La sua natura restrittiva costringe i team DevOps ad adottare flussi di lavoro moderni e sicuri per necessità, piuttosto che per scelta, elevando lo standard di sicurezza dell\u0026rsquo;intera organizzazione.1 La scelta tra SOPS e soluzioni più pesanti come Vault non deve essere vista come mutuamente esclusiva; al contrario, un\u0026rsquo;architettura matura utilizza SOPS per il bootstrap dell\u0026rsquo;infrastruttura e Vault per i segreti applicativi dinamici, ottenendo il meglio da entrambi i mondi.33\nBibliografia # Using Talos Linux and Kubernetes bootstrap on OpenStack - Safespring, accesso eseguito il giorno gennaio 8, 2026, https://www.safespring.com/blogg/2025/2025-03-talos-linux-on-openstack/ Philosophy - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 8, 2026, https://docs.siderolabs.com/talos/v1.9/learn-more/philosophy What is Talos Linux? - Sidero Documentation, accesso eseguito il giorno gennaio 8, 2026, https://docs.siderolabs.com/talos/v1.12/overview/what-is-talos Talos Linux: Bringing Immutability and Security to Kubernetes Operations - InfoQ, accesso eseguito il giorno gennaio 8, 2026, https://www.infoq.com/news/2025/10/talos-linux-kubernetes/ Talos Linux: Kubernetes Important API Management Improvement - Linux Security, accesso eseguito il giorno gennaio 8, 2026, https://linuxsecurity.com/features/talos-linux-redefining-kubernetes-security Talos Linux - The Kubernetes Operating System, accesso eseguito il giorno gennaio 8, 2026, https://www.talos.dev/ Getting Started - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 8, 2026, https://docs.siderolabs.com/talos/v1.9/getting-started/getting-started Role-based access control (RBAC) | TALOS LINUX, accesso eseguito il giorno gennaio 8, 2026, https://www.talos.dev/v1.6/talos-guides/configuration/rbac/ How to manage PKI and certificate lifetimes with Talos Linux - Sidero Documentation, accesso eseguito il giorno gennaio 8, 2026, https://docs.siderolabs.com/talos/v1.7/security/cert-management Troubleshooting - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 8, 2026, https://docs.siderolabs.com/talos/v1.9/troubleshooting/troubleshooting Kubernetes Cluster Reference Architecture with Talos Linux for 2025-05 - Sidero Labs, accesso eseguito il giorno gennaio 8, 2026, https://www.siderolabs.com/wp-content/uploads/2025/08/Kubernetes-Cluster-Reference-Architecture-with-Talos-Linux-for-2025-05.pdf Role-based access control (RBAC) - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 8, 2026, https://docs.siderolabs.com/talos/v1.9/security/rbac CA Rotation - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 8, 2026, https://docs.siderolabs.com/talos/v1.8/security/ca-rotation How to Rotate Certificate Authority - Cozystack, accesso eseguito il giorno gennaio 8, 2026, https://cozystack.io/docs/operations/cluster/rotate-ca/ First anniversary and predictably the client certs were all broken : r/TalosLinux - Reddit, accesso eseguito il giorno gennaio 8, 2026, https://www.reddit.com/r/TalosLinux/comments/1mtss8g/first_anniversary_and_predictably_the_client/ talos package - github.com/siderolabs/talos/pkg/rotate/pki/talos - Go Packages, accesso eseguito il giorno gennaio 8, 2026, https://pkg.go.dev/github.com/siderolabs/talos/pkg/rotate/pki/talos A template for deploying a Talos Kubernetes cluster including Flux for GitOps - GitHub, accesso eseguito il giorno gennaio 8, 2026, https://github.com/onedr0p/cluster-template Building a Secure and Efficient GitOps Pipeline with SOPS | by Platform Engineers - Medium, accesso eseguito il giorno gennaio 8, 2026, https://medium.com/@platform.engineers/building-a-secure-and-efficient-gitops-pipeline-with-sops-44ca1a4e505f Doing Secrets The GitOps Way | Mircea Anton, accesso eseguito il giorno gennaio 8, 2026, https://mirceanton.com/posts/doing-secrets-the-gitops-way/ Mozilla SOPS - K8s Security, accesso eseguito il giorno gennaio 8, 2026, https://k8s-security.geek-kb.com/docs/best_practices/cluster_setup_and_hardening/secrets_management/mozilla_sops/ Best Secrets Management Tools for 2026 - Cycode, accesso eseguito il giorno gennaio 8, 2026, https://cycode.com/blog/best-secrets-management-tools/ Guides - Talhelper, accesso eseguito il giorno gennaio 8, 2026, https://budimanjojo.github.io/talhelper/latest/guides/ cozystack/talm: Manage Talos Linux the GitOps Way! - GitHub, accesso eseguito il giorno gennaio 8, 2026, https://github.com/cozystack/talm joeypiccola/k8s_home - GitHub, accesso eseguito il giorno gennaio 8, 2026, https://github.com/joeypiccola/k8s_home Talhelper, accesso eseguito il giorno gennaio 8, 2026, https://budimanjojo.github.io/talhelper/ Kubernetes CI/CD Pipelines – 8 Best Practices and Tools - Spacelift, accesso eseguito il giorno gennaio 8, 2026, https://spacelift.io/blog/kubernetes-ci-cd Manage your secrets in Git with SOPS \u0026amp; GitLab CI - DEV Community, accesso eseguito il giorno gennaio 8, 2026, https://dev.to/stack-labs/manage-your-secrets-in-git-with-sops-gitlab-ci-2jnd Best practices for continuous integration and delivery to Google Kubernetes Engine, accesso eseguito il giorno gennaio 8, 2026, https://docs.cloud.google.com/kubernetes-engine/docs/concepts/best-practices-continuous-integration-delivery-kubernetes Disk Encryption - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 8, 2026, https://docs.siderolabs.com/talos/v1.8/configure-your-talos-cluster/storage-and-disk-management/disk-encryption talos_machine_configuration_ap, accesso eseguito il giorno gennaio 8, 2026, https://registry.terraform.io/providers/siderolabs/talos/0.1.0-alpha.11/docs/resources/machine_configuration_apply Automatically regenerate Tailscale TLS certs using systemd timers - STFN, accesso eseguito il giorno gennaio 8, 2026, https://stfn.pl/blog/78-tailscale-certs-renew/ CI/CD Pipeline Security Best Practices: The Ultimate Guide - Wiz, accesso eseguito il giorno gennaio 8, 2026, https://www.wiz.io/academy/application-security/ci-cd-security-best-practices Secrets Management in Kubernetes: Native Tools vs HashiCorp Vault - PufferSoft, accesso eseguito il giorno gennaio 8, 2026, https://puffersoft.com/secrets-management-in-kubernetes-native-tools-vs-hashicorp-vault/ Open Source Secrets Management for DevOps in 2025 - Infisical, accesso eseguito il giorno gennaio 8, 2026, https://infisical.com/blog/open-source-secrets-management-devops Secrets Management: Vault, AWS Secrets Manager, or SOPS? - DEV Community, accesso eseguito il giorno gennaio 8, 2026, https://dev.to/instadevops/secrets-management-vault-aws-secrets-manager-or-sops-2ce1 Top-10 Secrets Management Tools in 2025 - Infisical, accesso eseguito il giorno gennaio 8, 2026, https://infisical.com/blog/best-secret-management-tools Comparison between Hashicorp Vault Agent Injector and External Secrets Operator, accesso eseguito il giorno gennaio 8, 2026, https://unparagonedwisdom.medium.com/comparison-between-hashicorp-vault-agent-injector-and-external-secrets-operator-c3cabd89afca Unlocking Secrets with External Secrets Operator - DEV Community, accesso eseguito il giorno gennaio 8, 2026, https://dev.to/hkhelil/unlocking-secrets-with-external-secrets-operator-2f89 List Of Secrets Management Tools For Kubernetes In 2025 - Techiescamp, accesso eseguito il giorno gennaio 8, 2026, https://blog.techiescamp.com/secrets-management-tools/ Kubernetes integrations comparison | Vault - HashiCorp Developer, accesso eseguito il giorno gennaio 8, 2026, https://developer.hashicorp.com/vault/docs/deploy/kubernetes/comparisons getsops/sops: Simple and flexible tool for managing secrets - GitHub, accesso eseguito il giorno gennaio 8, 2026, https://github.com/getsops/sops Building an IPv6-Only Kubernetes Cluster with Talos and talhelper - DevOps Diaries, accesso eseguito il giorno gennaio 8, 2026, https://blog.spanagiot.gr/posts/talos-ipv6-only-cluster/ Omni KMS Disk Encryption - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 8, 2026, https://docs.siderolabs.com/omni/security-and-authentication/omni-kms-disk-encryption Installing Cilium and Multus on Talos OS for Advanced Kubernetes Networking, accesso eseguito il giorno gennaio 8, 2026, https://www.itguyjournals.com/installing-cilium-and-multus-on-talos-os-for-advanced-kubernetes-networking/ Kubernetes \u0026amp; Talos - Reddit, accesso eseguito il giorno gennaio 8, 2026, https://www.reddit.com/r/kubernetes/comments/1hs6bui/kubernetes_talos/ Talos Linux And KubeArmor Integration \\[2025 Edition\\]- AccuKnox, accesso eseguito il giorno gennaio 8, 2026, https://accuknox.com/technical-papers/talos-os-protection Kubernetes Best Practices in 2025: Scaling, Security, and Cost Optimization - KodeKloud, accesso eseguito il giorno gennaio 8, 2026, https://kodekloud.com/blog/kubernetes-best-practices-2025/ Talos Linux is powerful. But do you need more? - Sidero Labs, accesso eseguito il giorno gennaio 8, 2026, https://www.siderolabs.com/blog/do-you-need-omni/ ","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/guides/talos-linux-security-secrets/","section":"Guide","summary":"","title":"Sicurezza e Gestione del Ciclo di Vita in Kubernetes su Talos Linux: Architetture, PKI e Strategie di Segretezza","type":"guides"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/tags/statefulset/","section":"Tags","summary":"","title":"Statefulset","type":"tags"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/tags/storage/","section":"Tags","summary":"","title":"Storage","type":"tags"},{"content":"L\u0026rsquo;evoluzione dell\u0026rsquo;orchestrazione dei container ha trasformato radicalmente il paradigma della gestione dello stato nelle applicazioni distribuite. All\u0026rsquo;interno dell\u0026rsquo;ecosistema Kubernetes, la gestione dello storage non rappresenta più un semplice accessorio infrastrutturale, ma costituisce il fondamento critico su cui poggia l\u0026rsquo;affidabilità delle applicazioni enterprise.1 Sebbene i container siano stati originariamente concepiti come entità effimere e stateless, la realtà operativa dei carichi di lavoro moderni richiede che i dati sopravvivano non solo ai crash dei singoli processi, ma anche alla rischedulazione dei Pod tra i diversi nodi del cluster.3 Questa analisi tecnica esplora in profondità la tassonomia dei volumi di Kubernetes, i meccanismi di astrazione, le configurazioni YAML avanzate e le strategie di ottimizzazione per scenari di produzione complessi.\nAnalisi del formato YAML e l\u0026rsquo;orchestrazione dichiarativa # Prima di approfondire le specifiche dello storage, è essenziale comprendere lo strumento di comunicazione primario di Kubernetes: il formato YAML (YAML Ain\u0026rsquo;t Markup Language). La scelta di questo formato di serializzazione non è casuale; essa risponde all\u0026rsquo;esigenza di una sintassi leggibile dall\u0026rsquo;uomo che permetta di definire lo stato desiderato dell\u0026rsquo;infrastruttura in modo dichiarativo.6 YAML eccelle nella rappresentazione di strutture dati gerarchiche complesse, fondamentali per descrivere le relazioni tra i componenti di storage e i carichi di lavoro.6\nLa sintassi YAML si basa su coppie chiave-valore e liste, dove l\u0026rsquo;indentazione (rigorosamente eseguita con spazi e mai con tabulazioni) determina la gerarchia degli elementi.6 Questa struttura è vitale per definire le specifiche dei volumi all\u0026rsquo;interno dei manifesti dei Pod. Per esempio, l\u0026rsquo;uso di ancore (\u0026amp;) e alias (*) in YAML permette di ridurre la duplicazione nelle configurazioni di storage simili, migliorando la manutenibilità dei file di configurazione complessi.6 Kubernetes sfrutta queste caratteristiche per validare i file rispetto ai propri schemi API, garantendo che le definizioni di storage siano sintatticamente corrette prima dell\u0026rsquo;applicazione al cluster.6\nTassonomia e ciclo di vita dei volumi # Un volume in Kubernetes è fondamentalmente una directory accessibile ai container all\u0026rsquo;interno di un Pod, la cui natura, contenuto e ciclo di vita sono determinati dal tipo di volume specifico utilizzato.5 Kubernetes risolve due sfide fondamentali: la persistenza dei dati oltre il crash di un container (poiché al riavvio il container parte da uno stato pulito) e la condivisione di file tra più container che risiedono nello stesso Pod.5\nClassificazione per persistenza: volumi effimeri e persistenti # La distinzione primaria nel sistema di storage di Kubernetes riguarda il legame tra la vita del volume e quella del Pod.3\nCaratteristica Volumi Effimeri Volumi Persistenti Durata della vita Coincide con la vita del Pod.3 Indipendente dalla vita del Pod.3 Persistenza post-riavvio container I dati vengono mantenuti tra i riavvii.5 I dati vengono mantenuti tra i riavvii.8 Persistenza post-eliminazione Pod I dati vengono distrutti.3 I dati persistono nello storage esterno.3 Esempi comuni emptyDir, ConfigMap, Secret, downwardAPI.3 PersistentVolume, NFS, Azure Disk, AWS EBS.13 I volumi effimeri sono ideali per scenari che richiedono spazio di scratch, cache temporanee o l\u0026rsquo;iniezione di configurazioni.5 Al contrario, i volumi persistenti sono essenziali per applicazioni stateful come i database, dove la perdita del Pod non deve comportare la perdita dell\u0026rsquo;informazione.4\nDeep dive sui volumi effimeri: emptyDir e hostPath # Il tipo di volume emptyDir viene creato nel momento in cui un Pod viene assegnato a un nodo e rimane esistente finché il Pod è in esecuzione su quel nodo.3 Inizialmente vuoto, permette a tutti i container nel Pod di leggere e scrivere nello stesso spazio.5 Una configurazione avanzata prevede l\u0026rsquo;uso della memoria (RAM) come backend per emptyDir impostando il campo medium a Memory, il che è utile per cache ad altissime prestazioni ma consuma la quota di RAM del nodo.2\nIl volume hostPath, invece, monta un file o una directory dal filesystem dell\u0026rsquo;host direttamente nel Pod.3 Questo tipo è particolarmente utile per carichi di lavoro di sistema che devono monitorare il nodo, come agenti di log che leggono /var/log.3 Tuttavia, presenta rischi di sicurezza significativi esponendo il filesystem dell\u0026rsquo;host e compromette la portabilità, poiché il Pod diviene dipendente dai file presenti su uno specifico nodo.3\nMeccanismi di proiezione: ConfigMap e Secret # Kubernetes utilizza volumi speciali per iniettare dati di configurazione e segreti.15 A differenza dell\u0026rsquo;uso di variabili d\u0026rsquo;ambiente, il montaggio di ConfigMap e Secret come volumi permette l\u0026rsquo;aggiornamento dinamico dei file all\u0026rsquo;interno del container senza dover riavviare il processo, grazie al meccanismo di aggiornamento atomico dei link simbolici gestito dal Kubelet.16 Questo approccio è fondamentale per le moderne architetture microservizi che richiedono ricaricamenti di configurazione a caldo (\u0026ldquo;hot reload\u0026rdquo;).16\nUn dettaglio tecnico importante riguarda l\u0026rsquo;uso di subPath. Mentre subPath permette di montare un singolo file da un volume in una specifica cartella del container senza sovrascrivere l\u0026rsquo;intera directory di destinazione, i file montati tramite questa tecnica non beneficiano dell\u0026rsquo;aggiornamento automatico quando la risorsa sorgente cambia nel cluster.5\nIl modello di astrazione: PersistentVolume e PersistentVolumeClaim # Per gestire lo storage persistente in modo scalabile e agnostico rispetto all\u0026rsquo;infrastruttura, Kubernetes introduce tre concetti chiave: PersistentVolume (PV), PersistentVolumeClaim (PVC) e StorageClass (SC).13\nDefinizione e responsabilità # Un PersistentVolume è una risorsa fisica di storage all\u0026rsquo;interno del cluster, paragonabile a un nodo in termini di risorsa computazionale.14 Esso cattura i dettagli dell\u0026rsquo;implementazione dello storage (che sia NFS, iSCSI o uno storage specifico di un cloud provider).19 Al contrario, una PersistentVolumeClaim rappresenta la richiesta di storage da parte dell\u0026rsquo;utente, specificando dimensioni e modalità di accesso senza dover conoscere i dettagli del backend.12\nIl ciclo di vita di queste risorse segue quattro fasi distinte:\nProvisioning: Lo storage può essere creato staticamente da un amministratore o dinamicamente tramite una StorageClass.13 Binding: Kubernetes monitora le nuove PVC e cerca un PV corrispondente. Una volta trovato, il PV e la PVC vengono legati in una relazione esclusiva 1 a 1.12 Using: Il Pod utilizza la PVC come se fosse un volume locale. Il cluster ispeziona la claim per trovare il volume legato e lo monta nel filesystem del container.12 Reclaiming: Quando l\u0026rsquo;utente ha terminato l\u0026rsquo;uso del volume e cancella la PVC, la politica di recupero (Reclaim Policy) definisce cosa accade al PV.13 Analisi delle Reclaim Policy # La gestione del dato post-utilizzo è critica per la sicurezza e la conformità. Esistono tre politiche principali 10:\nRetain: Il PV rimane intatto dopo la cancellazione della PVC. L\u0026rsquo;amministratore deve gestire manualmente la pulizia o il riutilizzo del volume.10 Delete: Il volume fisico e il PV associato vengono eliminati automaticamente. È il comportamento standard per lo storage dinamico in ambiente cloud.13 Recycle: Esegue una cancellazione dei file (pulisce il filesystem) rendendo il volume disponibile per nuove claim. Questa politica è ora considerata obsoleta a favore del provisioning dinamico.13 StorageClass e Provisioning Dinamico # Il provisioning dinamico rappresenta una pietra miliare dell\u0026rsquo;automazione in Kubernetes, eliminando la necessità per gli amministratori di pre-creare manualmente i volumi.14 Attraverso l\u0026rsquo;oggetto StorageClass, è possibile definire diversi tier di storage (es. \u0026ldquo;fast\u0026rdquo; per SSD, \u0026ldquo;slow\u0026rdquo; per HDD) e delegare a Kubernetes la creazione on-demand del volume fisico tramite il relativo provisioner.25\nCloud Provider Provisioner (CSI) Esempio Parametri Note Operative AWS ebs.csi.aws.com type: gp3, iops: 3000 Supporta espansione online.27 Azure disk.csi.azure.com storageaccounttype: Premium_LRS Richiede PVC di tipo RWO.29 GCP pd.csi.storage.gke.io type: pd-balanced Supporta snapshot tramite CSI.26 L\u0026rsquo;uso del parametro volumeBindingMode: WaitForFirstConsumer all\u0026rsquo;interno di una StorageClass è una best practice fondamentale in ambienti multi-zona.24 Questo parametro istruisce il cluster ad attendere la schedulazione del Pod prima di creare il volume, assicurando che lo storage venga allocato nella stessa zona di disponibilità dove il Pod è effettivamente in esecuzione, evitando errori di montaggio cross-zone.2\nModalità di Accesso e Scenari Applicativi # La corretta selezione della modalità di accesso (AccessMode) è determinante per la stabilità delle applicazioni stateful.1\nReadWriteOnce (RWO): Il volume può essere montato in lettura/scrittura da un singolo nodo. È la modalità ideale per database come MySQL o PostgreSQL che richiedono l\u0026rsquo;esclusività per garantire l\u0026rsquo;integrità del dato.1 ReadOnlyMany (ROX): Molti nodi possono montare il volume simultaneamente ma solo in modalità sola lettura. Questo scenario è tipico per la distribuzione di contenuti statici (es. una cartella /html per un cluster Nginx).1 ReadWriteMany (RWX): Molti nodi possono leggere e scrivere simultaneamente. Questa modalità è supportata da sistemi come NFS o Azure Files ed è utile per applicazioni che condividono uno stato comune, sebbene richieda attenzione per evitare corruzioni dovute a scritture sovrapposte.1 ReadWriteOncePod (RWOP): Introdotta in versioni recenti, garantisce che un solo Pod in tutto il cluster possa accedere al volume, offrendo un livello di sicurezza superiore rispetto a RWO (che limita l\u0026rsquo;accesso a livello di nodo).1 Architettura dei Carichi di Lavoro Stateful: StatefulSet # La gestione dei dati in Kubernetes culmina nell\u0026rsquo;uso dello StatefulSet, l\u0026rsquo;API object progettata per gestire applicazioni che necessitano di identità persistenti e storage stabile.18 A differenza dei Deployment, dove i Pod sono intercambiabili, in uno StatefulSet ogni Pod riceve un indice ordinale (0, 1, 2\u0026hellip;) che mantiene per tutta la sua esistenza.18\nIl ruolo di volumeClaimTemplates # L\u0026rsquo;elemento di forza dello StatefulSet è il volumeClaimTemplates.18 Invece di condividere una singola PVC tra tutti i Pod, lo StatefulSet genera automaticamente una PVC unica per ogni istanza.18 Se il Pod db-1 viene eliminato e rischedulato, Kubernetes ricollegherà esattamente la PVC data-db-1 a quella nuova istanza, garantendo che il database mantenga la sua continuità storica dei dati.18\nEsempio Pratico: Architettura PostgreSQL Resiliente # Nell\u0026rsquo;implementare un database PostgreSQL, è fondamentale utilizzare un Headless Service (con clusterIP: None) per fornire nomi DNS stabili (es. postgres-0.postgres.namespace.svc.cluster.local) che permettano la comunicazione tra primario e repliche.18\nYAML\napiVersion: apps/v1\nkind: StatefulSet\nmetadata:\nname: postgresql\nspec:\nserviceName: \u0026ldquo;postgresql\u0026rdquo;\nreplicas: 3\ntemplate:\nmetadata:\nlabels:\napp: postgres\nspec:\ncontainers:\n- name: postgres\nimage: postgres:15\nvolumeMounts:\n- name: pgdata\nmountPath: /var/lib/postgresql/data\nvolumeClaimTemplates:\n- metadata:\nname: pgdata\nspec:\naccessModes:\nstorageClassName: \u0026ldquo;managed-csi\u0026rdquo;\nresources:\nrequests:\nstorage: 100Gi\nIn questo scenario, Kubernetes gestisce l\u0026rsquo;ordine di creazione e terminazione dei Pod, assicurando che le repliche vengano create solo dopo che il primario è pronto, minimizzando i rischi di inconsistenze durante il bootstrap del cluster.33\nContainer Storage Interface (CSI) e Evoluzione dello Storage # Il Container Storage Interface (CSI) rappresenta lo standard moderno per l\u0026rsquo;integrazione dello storage in Kubernetes, avendo sostituito i vecchi driver \u0026ldquo;in-tree\u0026rdquo; (compilati direttamente nel codice di Kubernetes).37 CSI permette ai produttori di storage di sviluppare driver indipendenti dal ciclo di rilascio di Kubernetes, favorendo l\u0026rsquo;innovazione e la stabilità del core.37\nArchitettura del Driver CSI # Un driver CSI opera attraverso due componenti principali 37:\nController Plugin: Gestisce le operazioni ad alto livello come la creazione, cancellazione e il collegamento (attachment) dei volumi ai nodi fisici.37 È tipicamente supportato da sidecar container come external-provisioner e external-attacher.38 Node Plugin: In esecuzione su ogni nodo (solitamente come DaemonSet), è responsabile del montaggio e smontaggio effettivo del volume nel filesystem del container tramite chiamate gRPC fornite dal Kubelet.37 Questa architettura permette funzionalità avanzate come il ridimensionamento dei volumi senza interruzioni e il monitoraggio dello stato di salute dello storage direttamente tramite l\u0026rsquo;API di Kubernetes.5\nPerformance Tuning e Ottimizzazione # L\u0026rsquo;ottimizzazione delle performance richiede un bilanciamento tra IOPS, throughput e latenza.2\nParametri di Storage e Tiers # Le organizzazioni dovrebbero definire classi di storage diverse in base ai requisiti del carico di lavoro.1 Per database ad alte prestazioni, l\u0026rsquo;uso di volumi NVMe over TCP o SSD premium con throughput configurabile è essenziale.1\nPer calcolare le performance necessarie, si può fare riferimento alla densità di throughput. Ad esempio, su Google Cloud Hyperdisk, è necessario prevedere un bilanciamento basato sulla capacità:\n$$\\\\text{Throughput Minimo} \\= 10 \\\\text{ MiB/s per ogni TiB di capacità}$$Mentre il limite superiore è fissato a 600 MiB/s per volume.30\nVolumeAttributesClass (VAC) # Una delle innovazioni più recenti (beta in v1.31) è la VolumeAttributesClass (VAC).22 Essa permette di modificare dinamicamente i parametri di performance di un volume (come IOPS o throughput) senza dover ricreare la PVC o il PV, eliminando i tempi di inattività che precedentemente erano necessari per migrare tra diverse classi di storage.28 Questo è particolarmente utile per gestire picchi di traffico stagionali dove è necessario aumentare temporaneamente la velocità del database.28\nSicurezza e Gestione degli Accessi # La protezione del dato a riposo e in transito è un requisito non negoziabile in ambienti enterprise.1\nCrittografia e RBAC # È fondamentale abilitare la crittografia a riposo fornita dallo storage backend.1 Inoltre, l\u0026rsquo;accesso alle PVC deve essere regolato tramite Role-Based Access Control (RBAC), assicurando che solo gli utenti e i ServiceAccount autorizzati possano manipolare le risorse di storage.15\nPermessi del Filesystem e fsGroup # Molti problemi di \u0026ldquo;Permission Denied\u0026rdquo; nei Pod derivano da disallineamenti tra l\u0026rsquo;utente che esegue il container e i permessi del volume montato.39 Kubernetes risolve questo problema attraverso il securityContext. Utilizzando il parametro fsGroup, Kubernetes applica automaticamente l\u0026rsquo;ownership del gruppo specificato a tutti i file all\u0026rsquo;interno del volume nel momento del montaggio, garantendo che i processi nel container possano scrivere dati senza interventi manuali di chmod o chown.5\nYAML\nspec:\nsecurityContext:\nfsGroup: 2000\nfsGroupChangePolicy: \u0026ldquo;OnRootMismatch\u0026rdquo;\nL\u0026rsquo;impostazione OnRootMismatch ottimizza i tempi di avvio dei Pod che montano volumi molto grandi, evitando di scansionare ricorsivamente tutti i file se la directory root ha già i permessi corretti.5\nBackup, Snapshot e Disaster Recovery # La persistenza non garantisce da sola la protezione contro la cancellazione accidentale o la corruzione dei dati.40 È essenziale implementare una strategia di backup solida.40\nMeccanismi di Snapshotting CSI # Kubernetes supporta nativamente gli snapshot dei volumi tramite l\u0026rsquo;oggetto VolumeSnapshot.22 Questo meccanismo permette di creare copie \u0026ldquo;point-in-time\u0026rdquo; dei dati che possono essere utilizzate per clonare volumi o per ripristinare uno stato precedente in caso di errore applicativo.5\nVelero: Protezione Dati Enterprise # Velero è lo standard open-source per il backup e il ripristino di Kubernetes.40 Esso offre due modalità principali:\nCSI Snapshots: Sfrutta le capacità native dello storage backend per creare istantanee veloci dei volumi.41 File System Backup (FSB): Utilizza strumenti come Restic o Kopia per eseguire backup a livello di file, ideale quando il driver CSI non supporta gli snapshot o quando si desidera spostare i dati su un object storage differente (off-site backup).41 Una best practice avanzata prevede l\u0026rsquo;adozione del \u0026ldquo;CSI Snapshot Data Movement Mode\u0026rdquo;, che combina la velocità dello snapshot hardware con la sicurezza del trasferimento dei dati verso un repository esterno, garantendo che il backup sia accessibile anche in caso di distruzione totale del cluster primario.41\nConclusioni: Verso un\u0026rsquo;Infrastruttura Dati Flessibile # La gestione dello storage in Kubernetes è maturata da una necessità accessoria a un ecosistema di astrazione altamente sofisticato.1 La comprensione della distinzione tra volumi effimeri e persistenti, unitamente alla padronanza del modello PV/PVC/StorageClass, permette agli ingegneri di progettare sistemi che non solo sopravvivono ai guasti, ma che possono scalare dinamicamente per rispondere alle esigenze di business.2\nIl futuro dello storage cloud-native è orientato verso una maggiore intelligenza dei driver CSI, con funzionalità di auto-tuning delle performance e una sempre più profonda integrazione con le policy di sicurezza.28 Per le organizzazioni che operano carichi di lavoro critici, la chiave del successo risiede nell\u0026rsquo;adozione di standard aperti, nell\u0026rsquo;automazione del provisioning tramite SC e nella validazione rigorosa dei processi di backup, trasformando lo storage da potenziale collo di bottiglia a catalizzatore di innovazione tecnologica.27\nBibliografia # Kubernetes Persistent Volumes - Best Practices \u0026amp; Guide | simplyblock, accesso eseguito il giorno gennaio 8, 2026, https://www.simplyblock.io/blog/kubernetes-persistent-volumes-how-to-best-practices/ Kubernetes Performance Tuning Guide: Optimize Your K8s Cluster - Kubegrade, accesso eseguito il giorno gennaio 8, 2026, https://kubegrade.com/kubernetes-performance-tuning-guide/ Kubernetes Volumes Explained: Use Cases \u0026amp; Best Practices - Groundcover, accesso eseguito il giorno gennaio 8, 2026, https://www.groundcover.com/learn/storage/kubernetes-volumes Kubernetes persistent vs ephemeral storage volumes and their uses - StarWind, accesso eseguito il giorno gennaio 8, 2026, https://www.starwindsoftware.com/blog/kubernetes-persistent-vs-ephemeral-storage-volumes-and-their-uses/ Volumes | Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://kubernetes.io/docs/concepts/storage/volumes/ YAML nel dettaglio: guida completa al formato di serializzazione - Codegrind, accesso eseguito il giorno gennaio 8, 2026, https://codegrind.it/blog/yaml-spiegato YAML: The Ultimate Guide with Examples and Best Practices | by Mahalingam SRE, accesso eseguito il giorno gennaio 8, 2026, https://medium.com/@lingeshcbz/yaml-the-ultimate-guide-with-examples-and-best-practices-7040f9e389ed Kubernetes Volumes and How To Use Them – ReviewNPrep, accesso eseguito il giorno gennaio 8, 2026, https://reviewnprep.com/blog/kubernetes-volumes-and-how-to-use-them/ Ephemeral Volumes - Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/ What Is a Kubernetes Persistent Volume? - Pure Storage, accesso eseguito il giorno gennaio 8, 2026, https://www.purestorage.com/knowledge/what-is-kubernetes-persistent-volume.html Ephemeral Storage in Kubernetes: Overview \u0026amp; Guide - Portworx, accesso eseguito il giorno gennaio 8, 2026, https://portworx.com/knowledge-hub/ephemeral-storage-in-kubernetes-overview-guide/ Persistent Volume Claim (PVC) in Kubernetes: Guide - Portworx, accesso eseguito il giorno gennaio 8, 2026, https://portworx.com/tutorial-kubernetes-persistent-volumes/ Che cos\u0026rsquo;è un volume persistente Kubernetes? - Pure Storage, accesso eseguito il giorno gennaio 8, 2026, https://www.purestorage.com/it/knowledge/what-is-kubernetes-persistent-volume.html Kubernetes Persistent Volume: Examples \u0026amp; Best Practices - vCluster, accesso eseguito il giorno gennaio 8, 2026, https://www.vcluster.com/blog/kubernetes-persistent-volume In-Depth Guide to Kubernetes ConfigMap \u0026amp; Secret Management Strategies - Gravitee, accesso eseguito il giorno gennaio 8, 2026, https://www.gravitee.io/blog/kubernetes-configurations-secrets-configmaps Kubernetes ConfigMaps and Secrets Part 2 | by Sandeep Dinesh | Google Cloud - Medium, accesso eseguito il giorno gennaio 8, 2026, https://medium.com/google-cloud/kubernetes-configmaps-and-secrets-part-2-3dc37111f0dc Mounting ConfigMaps and Secrets as files - DuploCloud Documentation, accesso eseguito il giorno gennaio 8, 2026, https://docs.duplocloud.com/docs/automation-platform/kubernetes-overview/configs-and-secrets/mounting-config-as-files Run a Replicated Stateful Application | Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://kubernetes.io/docs/tasks/run-application/run-replicated-stateful-application/ Kubernetes Persistent Volumes and the PV Lifecycle - NetApp, accesso eseguito il giorno gennaio 8, 2026, https://www.netapp.com/learn/kubernetes-persistent-storage-why-where-and-how/ How to manage Kubernetes storage access modes - LabEx, accesso eseguito il giorno gennaio 8, 2026, https://labex.io/tutorials/kubernetes-how-to-manage-kubernetes-storage-access-modes-419137 Persistent Volumes - Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://kubernetes.io/docs/concepts/storage/persistent-volumes/ Kubernetes PVC Guide: Best Practices \u0026amp; Troubleshooting - Plural, accesso eseguito il giorno gennaio 8, 2026, https://www.plural.sh/blog/kubernetes-pvc-guide/ Kubernetes Persistent Volumes - Tutorial and Examples - Spacelift, accesso eseguito il giorno gennaio 8, 2026, https://spacelift.io/blog/kubernetes-persistent-volumes Kubernetes Persistent Volume Claims: Tutorial \u0026amp; Top Tips - Groundcover, accesso eseguito il giorno gennaio 8, 2026, https://www.groundcover.com/blog/kubernetes-pvc Dynamic Provisioning and Storage Classes in Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://kubernetes.io/blog/2017/03/dynamic-provisioning-and-storage-classes-kubernetes/ Dynamic Volume Provisioning | Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/ Kubernetes StorageClass: A technical Guide | by Fortismanuel - Medium, accesso eseguito il giorno gennaio 8, 2026, https://medium.com/@fortismanuel/kubernetes-storageclass-a-technical-guide-58cfb28619ee Modify Amazon EBS volumes on Kubernetes with Volume Attributes Classes | Containers, accesso eseguito il giorno gennaio 8, 2026, https://aws.amazon.com/blogs/containers/modify-amazon-ebs-volumes-on-kubernetes-with-volume-attributes-classes/ Creare un volume permanente con Dischi di Azure nel servizio \u0026hellip;, accesso eseguito il giorno gennaio 8, 2026, https://learn.microsoft.com/it-it/azure/aks/azure-csi-disk-storage-provision Scale your storage performance with Hyperdisk | Google Kubernetes Engine (GKE), accesso eseguito il giorno gennaio 8, 2026, https://docs.cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/hyperdisk Optimizing Persistent Storage in Kubernetes - Astuto AI, accesso eseguito il giorno gennaio 8, 2026, https://www.astuto.ai/blogs/optimizing-persistent-storage-in-kubernetes Using NFS as External Storage in Kubernetes with PersistentVolume and PersistentVolumeClaim to Deploy Nginx | by Bshreyasharma | Medium, accesso eseguito il giorno gennaio 8, 2026, https://medium.com/@bshreyasharma1/using-nfs-as-external-storage-in-kubernetes-with-persistentvolume-and-persistentvolumeclaim-to-112994f3ad59 StatefulSets - Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ Guide to Kubernetes StatefulSet – When to Use It and Examples - Spacelift, accesso eseguito il giorno gennaio 8, 2026, https://spacelift.io/blog/kubernetes-statefulset Kubernetes StatefulSet - Examples \u0026amp; Best Practices - vCluster, accesso eseguito il giorno gennaio 8, 2026, https://www.vcluster.com/blog/kubernetes-statefulset-examples-and-best-practices Deploying the PostgreSQL Pod on Kubernetes with StatefulSets - Nutanix Support Portal, accesso eseguito il giorno gennaio 8, 2026, https://portal.nutanix.com/page/documents/solutions/details?targetId=TN-2192-Deploying-PostgreSQL-Nutanix-Data-Services-Kubernetes:deploying-the-postgresql-pod-on-kubernetes-with-statefulsets.html How the CSI (Container Storage Interface) Works - simplyblock, accesso eseguito il giorno gennaio 8, 2026, https://www.simplyblock.io/blog/how-the-csi-container-storage-interface-works/ Container Storage Interface (CSI) for Kubernetes GA | Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/ Configure a Pod to Use a PersistentVolume for Storage - Kubernetes, accesso eseguito il giorno gennaio 8, 2026, https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/ Chapter 6: Backups - Kubernetes Guides - Apptio, accesso eseguito il giorno gennaio 8, 2026, https://www.apptio.com/topics/kubernetes/best-practices/backups/ Kubernetes Backup using Velero - Afi.ai, accesso eseguito il giorno gennaio 8, 2026, https://afi.ai/blog/kubernetes-velero-backup Snapshot Backups with Velero - MSR Documentation, accesso eseguito il giorno gennaio 8, 2026, https://docs.mirantis.com/msr/4.13/backup/ha-backup/snapshot-backups-with-velero/ Velero Backup and Restore using Replicated PV Mayastor Snapshots - Raw Block Volumes, accesso eseguito il giorno gennaio 8, 2026, https://openebs.io/docs/Solutioning/backup-and-restore/velerobrrbv File System Backup - Velero Docs, accesso eseguito il giorno gennaio 8, 2026, https://velero.io/docs/v1.17/file-system-backup/ ","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/guides/kubernetes-volumes-guide/","section":"Guide","summary":"","title":"Strategie e architetture per la gestione dello storage in Kubernetes: analisi tecnica dei volumi, della persistenza e delle operazioni cloud-native","type":"guides"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/tags/volumes/","section":"Tags","summary":"","title":"Volumes","type":"tags"},{"content":"","date":"8 gennaio 2026","externalUrl":null,"permalink":"/it/tags/volumi/","section":"Tags","summary":"","title":"Volumi","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/aws-s3/","section":"Tags","summary":"","title":"Aws-S3","type":"tags"},{"content":" Dalla Persistenza alla Resilienza: Orchestrazione di Backup Longhorn su AWS S3 in un Ambiente Talos Linux # Introduzione: Il Paradosso della Disponibilità Locale # Nelle ultime settimane, il mio Homelab basato su Talos Linux e virtualizzato su Proxmox ha raggiunto un livello di stabilità operativa notevole. I servizi core come Traefik e il blog Hugo girano senza interruzioni, e il networking è stato blindato grazie all\u0026rsquo;assegnazione di IP statici ai nodi. Tuttavia, analizzando l\u0026rsquo;architettura con occhio critico, è emersa una vulnerabilità fondamentale: la confusione tra High Availability (HA) e Disaster Recovery (DR).\nLonghorn, il motore di storage distribuito che ho scelto per questo cluster, eccelle nella replica sincrona dei dati. Configurando una replicaCount: 2, ogni blocco scritto sul disco viene duplicato istantaneamente su un secondo nodo. Questo mi protegge se un singolo nodo fallisce o se un disco si corrompe. Ma cosa succederebbe se un errore di configurazione cancellasse il namespace traefik? O se un guasto catastrofico all\u0026rsquo;hardware fisico di Proxmox rendesse inaccessibili entrambi i nodi virtuali? La risposta è inaccettabile per un ambiente che ambisce a essere \u0026ldquo;Production Grade\u0026rdquo;: perdita totale dei dati.\nL\u0026rsquo;obiettivo della sessione odierna era colmare questo divario implementando una strategia di backup offsite automatizzata, utilizzando AWS S3 come target remoto e gestendo l\u0026rsquo;intera configurazione secondo i principi dell\u0026rsquo;Infrastructure as Code (IaC). Quella che doveva essere una semplice configurazione di parametri si è trasformata in una complessa operazione di upgrade del software e refactoring delle definizioni dichiarative.\nFase 1: Fondamenta di Sicurezza e Gestione dei Segreti # Prima di toccare Kubernetes, ho dovuto preparare il terreno su AWS. Il principio guida in questo contesto è il Principio del Minimo Privilegio (PoLP). Non è accettabile utilizzare le credenziali dell\u0026rsquo;account root o di un utente amministratore per un processo automatizzato di backup. Se quelle chiavi venissero compromesse, l\u0026rsquo;intero account AWS sarebbe a rischio.\nCreazione dell\u0026rsquo;Identità IAM e del Bucket # Ho creato un bucket S3 dedicato nella regione eu-central-1 (Francoforte), scelta per minimizzare la latenza con il mio laboratorio in Europa. Successivamente, ho configurato un utente IAM tecnico, longhorn-backup-user, associandogli una policy JSON restrittiva. Questa policy concede esclusivamente i permessi necessari per leggere e scrivere oggetti in quel specifico bucket, negando l\u0026rsquo;accesso a qualsiasi altra risorsa cloud.\n{ \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Action\u0026#34;: [ \u0026#34;s3:PutObject\u0026#34;, \u0026#34;s3:GetObject\u0026#34;, \u0026#34;s3:ListBucket\u0026#34;, \u0026#34;s3:DeleteObject\u0026#34;, \u0026#34;s3:GetBucketLocation\u0026#34; ], \u0026#34;Resource\u0026#34;: [ \u0026#34;arn:aws:s3:::tazlab-longhorn\u0026#34;, \u0026#34;arn:aws:s3:::tazlab-longhorn/*\u0026#34; ] } ] } Crittografia dei Segreti con SOPS # Il passo successivo riguardava come portare queste credenziali (Access Key e Secret Key) all\u0026rsquo;interno del cluster Kubernetes. L\u0026rsquo;approccio ingenuo sarebbe stato creare il Secret manualmente con kubectl create secret o, peggio, committare un file YAML con le chiavi in chiaro nel repository Git.\nHo optato per SOPS (Secrets OPerationS) combinato con Age per la crittografia asimmetrica. Questo workflow permette di versionare i file dei segreti nel repository Git in formato cifrato. Solo chi possiede la chiave privata Age (nel mio caso, presente sulla mia workstation di gestione) può decifrare il file al momento dell\u0026rsquo;applicazione.\nIl file aws-secrets.enc.yaml generato contiene solo i metadati in chiaro, mentre il payload stringData è un blocco crittografato incomprensibile. L\u0026rsquo;applicazione al cluster è avvenuta tramite una pipeline di decifrazione al volo:\nsops --decrypt aws-secrets.enc.yaml | kubectl apply -f - Questo metodo garantisce che non esista mai un file in chiaro sul disco rigido che possa essere inavvertitamente committato o esposto.\nFase 2: L\u0026rsquo;Odissea dell\u0026rsquo;Upgrade (Longhorn 1.8 -\u0026gt; 1.10) # Per sfruttare le ultime funzionalità di gestione dei backup e delle StorageClass, ho deciso di aggiornare Longhorn dalla versione 1.8.0 alla versione corrente 1.10.1. Qui mi sono scontrato con la rigidità (giustificata) dei sistemi stateful.\nIl Blocco del Pre-Upgrade Hook # Lanciando un helm upgrade diretto alla versione 1.10.1, il processo è fallito istantaneamente. I log del job di pre-upgrade riportavano un messaggio inequivocabile:\nfailed to upgrade since upgrading from v1.8.0 to v1.10.1 for minor version is not supported\nQuesto errore evidenzia una differenza critica tra applicazioni stateless (come un web server Nginx) e applicazioni stateful (come uno storage engine). Un\u0026rsquo;applicazione stateless può saltare versioni a piacimento. Uno storage engine gestisce strutture dati su disco e formati di metadati che evolvono nel tempo. Longhorn richiede che ogni aggiornamento di versione \u0026ldquo;minor\u0026rdquo; (il secondo numero della versione semantica) venga eseguito sequenzialmente per permettere ai job di migrazione del database di convertire i dati in modo sicuro.\nLa Strategia di Mitigazione Incrementale # Ho dovuto adottare un approccio a gradini, simulando manualmente il ciclo di vita del software che avrei dovuto seguire se avessi mantenuto il cluster aggiornato regolarmente.\nStep 1: Upgrade alla v1.9.2. Ho forzato Helm a installare l\u0026rsquo;ultima patch della serie 1.9. Questo ha permesso a Longhorn di migrare i suoi CRD (Custom Resource Definitions) e i formati interni. Ho atteso che tutti i pod longhorn-manager tornassero in stato Running e completi (2/2). Step 2: Upgrade alla v1.10.1. Solo dopo aver validato la salute del cluster sulla 1.9, ho lanciato l\u0026rsquo;aggiornamento finale. Questa procedura ha richiesto tempo e pazienza, monitorando i log per assicurarsi che i volumi non venissero scollegati o corrotti durante il riavvio dei demoni. È un promemoria del fatto che la manutenzione in ambito Kubernetes non è mai un semplice \u0026ldquo;lancio e dimentico\u0026rdquo;.\nFase 3: La Battaglia per la Configurazione Dichiarativa (IaC) # Una volta aggiornato il software, il vero problema è emerso nel tentativo di configurare il BackupTarget (l\u0026rsquo;URL S3) in modo dichiarativo. La mia intenzione era definino tutto nel file longhorn-values.yaml passato a Helm, per evitare configurazioni manuali tramite la UI web.\nIl Limite di defaultSettings # Ho inserito le configurazioni nel blocco defaultSettings del chart Helm:\ndefaultSettings: backupTarget: \u0026#34;s3://tazlab-longhorn@eu-central-1/\u0026#34; backupTargetCredentialSecret: \u0026#34;aws-backup-secret\u0026#34; Tuttavia, dopo l\u0026rsquo;applicazione, la configurazione in Longhorn rimaneva vuota. Analizzando la documentazione e il comportamento del chart, ho riscoperto un dettaglio tecnico spesso trascurato: Longhorn applica i defaultSettings solo durante la prima installazione. Se il cluster Longhorn è già inizializzato, questi valori vengono ignorati per evitare di sovrascrivere configurazioni che l\u0026rsquo;amministratore potrebbe aver cambiato a runtime.\nIl Fallimento dell\u0026rsquo;Approccio Imperativo Dichiarato # Ho tentato di aggirare il problema creando manifesti YAML per oggetti di tipo Setting (es. settings.longhorn.io), sperando che Kubernetes forzasse la configurazione. Il risultato è stato un rifiuto da parte del Validating Webhook di Longhorn:\nadmission webhook \u0026quot;validator.longhorn.io\u0026quot; denied the request: setting backup-target is not supported\nQuesto errore criptico nascondeva un cambiamento architetturale introdotto nelle versioni recenti. L\u0026rsquo;impostazione backup-target non è più una semplice chiave-valore globale gestita tramite l\u0026rsquo;oggetto Setting, ma è stata promossa a una CRD dedicata chiamata BackupTarget. Cercare di configurarla come un vecchio setting generava un errore di validazione perché la chiave non esisteva più nello schema delle impostazioni semplici.\nLa Soluzione \u0026ldquo;Tabula Rasa\u0026rdquo; # Di fronte a uno stato del cluster disallineato rispetto al codice (Configuration Drift) e all\u0026rsquo;impossibilità di riconciliarlo pulitamente a causa dei residui delle versioni precedenti, ho preso una decisione drastica ma necessaria: la disinstallazione completa del control plane di Longhorn.\nÈ fondamentale distinguere tra la cancellazione del software di controllo e la cancellazione dei dati. Disinstallando Longhorn (helm uninstall), ho rimosso i Pod, i Servizi e i DaemonSet. Tuttavia, i dati fisici sui dischi (/var/lib/longhorn sui nodi) e le definizioni dei Persistent Volume in Kubernetes sono rimasti intatti.\nReinstallando Longhorn v1.10.1 da zero con il file values.yaml corretto, il sistema ha letto i defaultSettings come se fosse una nuova installazione, applicando correttamente la configurazione S3 fin dal primo avvio. Al riavvio, i manager hanno scansionato i dischi, ritrovato i dati esistenti e ricollegato i volumi senza alcuna perdita di informazioni. Questa operazione ha validato non solo la configurazione, ma anche la resilienza intrinseca dell\u0026rsquo;architettura disaccoppiata di Kubernetes.\nFase 4: Automazione e Strategie di Backup # Avere un target di backup configurato non significa avere dei backup. Senza automazione, il backup dipende dalla memoria umana, il che garantisce il fallimento.\nImplementazione di RecurringJob # Ho definito una risorsa RecurringJob per automatizzare il processo. A differenza dei cronjob di sistema, questi sono gestiti internamente da Longhorn e sono consapevoli dello stato dei volumi.\napiVersion: longhorn.io/v1beta2 kind: RecurringJob metadata: name: nightly-s3-backup spec: cron: \u0026#34;0 3 * * *\u0026#34; task: backup retain: 7 groups: - traefik-only La scelta di mantenere solo 7 backup (retain: 7) è un compromesso tra sicurezza e costi di storage su S3.\nGranularità tramite Labels e Gruppi # Inizialmente, tutti i volumi erano nel gruppo default. Tuttavia, non tutti i dati hanno lo stesso valore. Il volume del blog Hugo contiene dati che sono già versionati su GitHub; il volume di Traefik contiene i certificati SSL privati, che sono insostituibili e critici.\nHo deciso di implementare una strategia di backup granulare:\nHo creato un gruppo personalizzato traefik-only nel RecurringJob. Ho applicato una label specifica al volume di Traefik: recurring-job-group.longhorn.io/traefik-only: enabled. Ho rimosso le label generiche dagli altri volumi. Questo approccio riduce il traffico di rete e i costi di storage, salvando solo ciò che è strettamente necessario.\nStorageClass Avanzata: Automazione alla Nascita # Per chiudere il cerchio dell\u0026rsquo;IaC, ho creato una nuova StorageClass dedicata: longhorn-traefik-backup.\nkind: StorageClass metadata: name: longhorn-traefik-backup parameters: recurringJobSelector: \u0026#39;[{\u0026#34;name\u0026#34;:\u0026#34;nightly-s3-backup\u0026#34;, \u0026#34;isGroup\u0026#34;:true}]\u0026#39; reclaimPolicy: Retain L\u0026rsquo;uso del parametro recurringJobSelector direttamente nella StorageClass è potente: qualsiasi volume futuro creato con questa classe erediterà automaticamente la politica di backup, senza bisogno di interventi manuali o patch successive. Inoltre, la policy Retain assicura che se anche cancellassi per errore il Deployment di Traefik, il volume rimarrebbe nel cluster in attesa di essere reclamato, prevenendo la cancellazione accidentale dei certificati.\nConclusioni e Riflessioni # Questa sessione di lavoro ha trasformato il layer di storage del cluster da una semplice persistenza locale a una soluzione di livello enterprise resiliente ai disastri.\nLezioni chiave apprese:\nMai sottovalutare gli upgrade stateful: I salti di versione nei database e negli storage engine richiedono pianificazione e passaggi incrementali. L\u0026rsquo;IaC richiede disciplina: È facile risolvere un problema con kubectl patch, ma ricostruire l\u0026rsquo;infrastruttura da zero (come abbiamo fatto disinstallando Longhorn) è l\u0026rsquo;unico modo per garantire che il codice descriva fedelmente la realtà. Default vs Runtime: Capire quando una configurazione viene applicata (init vs runtime) è cruciale per il debugging di Helm chart complessi. L\u0026rsquo;infrastruttura è ora pronta per affrontare il peggio. Il prossimo passo logico sarà validare questo setup eseguendo un Disaster Recovery Test reale: distruggere intenzionalmente un volume e tentare il ripristino da S3, per trasformare la \u0026ldquo;speranza\u0026rdquo; del backup nella \u0026ldquo;certezza\u0026rdquo; del recupero.\n","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/posts/longhorn-s3-backup-talos/","section":"Posts","summary":"","title":"Dalla Persistenza alla Resilienza: Orchestrazione di Backup Longhorn su AWS S3 in un Ambiente Talos Linux","type":"posts"},{"content":"Il panorama dei generatori di siti statici (SSG) ha subito un\u0026rsquo;evoluzione paradigmatica negli ultimi anni, con Hugo che si è affermato come una delle soluzioni più performanti grazie alla sua velocità di compilazione quasi istantanea e alla sua robusta architettura in Go. In questo contesto, il tema Blowfish emerge non come un semplice template grafico, ma come un framework modulare e sofisticato, costruito sopra Tailwind CSS 3.0, progettato per soddisfare le esigenze di sviluppatori, ricercatori e creatori di contenuti che richiedono un equilibrio impeccabile tra estetica minimalista e potenza funzionale.1 Blowfish si distingue per la sua capacità di gestire flussi di lavoro complessi, integrazioni serverless e una personalizzazione granulare che va ben oltre la superficie visiva, posizionandosi come uno dei temi più avanzati nell\u0026rsquo;ecosistema Hugo.1\nEvoluzione e Filosofia Progettuale di Blowfish # La genesi di Blowfish risiede nella necessità di superare i limiti dei temi monolitici, offrendo una struttura che privilegia l\u0026rsquo;ottimizzazione automatizzata degli asset e l\u0026rsquo;accessibilità out-of-the-box. L\u0026rsquo;adozione di Tailwind CSS non è solo una scelta estetica, ma una decisione architettonica che permette di generare bundle CSS estremamente ridotti, contenenti solo le classi effettivamente utilizzate, garantendo prestazioni di alto livello documentate da punteggi eccellenti nei test Lighthouse.1 Il tema è intrinsecamente orientato al contenuto, strutturato per sfruttare appieno i \u0026ldquo;Page Bundles\u0026rdquo; di Hugo, un sistema che organizza le risorse multimediali direttamente accanto ai file di testo, migliorando la portabilità e la manutenibilità del progetto nel lungo periodo.5\nL\u0026rsquo;architettura di Blowfish è progettata per essere \u0026ldquo;future-proof\u0026rdquo;, supportando nativamente integrazioni dinamiche in un ambiente statico, come il conteggio delle visualizzazioni e i sistemi di interazione tramite Firebase, la ricerca avanzata lato client con Fuse.js e la visualizzazione di dati complessi tramite Chart.js e Mermaid.1 Questa versatilità lo rende adatto a una vasta gamma di applicazioni, dal blog personale alla documentazione tecnica di livello enterprise.\nProcedure di Installazione e Inizializzazione del Progetto # L\u0026rsquo;implementazione di Blowfish richiede un ambiente di sviluppo configurato correttamente con Hugo (versione 0.87.0 o superiore, preferibilmente la versione \u0026ldquo;extended\u0026rdquo;) e Git.3 Esistono tre percorsi principali per l\u0026rsquo;installazione, ognuno con implicazioni specifiche sulla gestione del workflow.\nMetodologia CLI: Blowfish Tools # L\u0026rsquo;approccio più moderno e consigliato per i nuovi utenti è l\u0026rsquo;utilizzo di blowfish-tools, uno strumento a riga di comando che automatizza la creazione del sito e la configurazione iniziale.3\nComando Funzione Contesto d\u0026rsquo;Uso npm i -g blowfish-tools Installazione globale Preparazione dell\u0026rsquo;ambiente di sviluppo Node.js. blowfish-tools new Creazione sito completo Ideale per nuovi progetti partendo da zero. blowfish-tools Menu interattivo Configurazione di funzionalità specifiche in progetti esistenti. Questo strumento riduce significativamente la barriera all\u0026rsquo;ingresso, gestendo la creazione della complessa struttura di cartelle richiesta per una configurazione modulare.5\nMetodologia Professionale: Moduli Hugo e Sottomoduli Git # Per i professionisti che operano in ambienti di Continuous Integration (CI), l\u0026rsquo;uso dei Moduli Hugo rappresenta la soluzione più elegante. Questo metodo tratta il tema come una dipendenza gestita da Go, permettendo aggiornamenti rapidi tramite il comando hugo mod get -u.1 In alternativa, l\u0026rsquo;installazione come sottomodulo Git (git submodule add https://github.com/nunocoracao/blowfish.git themes/blowfish) è preferibile per chi desidera mantenere il codice del tema all\u0026rsquo;interno del proprio repository senza però mescolarlo con il contenuto, facilitando il tracciamento delle versioni specifiche.1\nIl Sistema di Configurazione Modulare # Una delle caratteristiche distintive di Blowfish è l\u0026rsquo;abbandono del singolo file config.toml in favore di una directory config/_default/ contenente file TOML specializzati. Questa separazione logica è fondamentale per gestire la complessità delle opzioni offerte dal tema.2\nHugo.toml: La Struttura Portante del Sito # Il file hugo.toml (o config.toml se non si utilizza la struttura modulare) definisce i parametri globali del motore Hugo e le impostazioni di base del sito.8\nParametro Descrizione Rilevanza Tecnica baseURL URL radice del sito Essenziale per la generazione corretta di link assoluti e SEO.4 theme \u0026ldquo;blowfish\u0026rdquo; Indica a Hugo quale tema caricare (omissibile con i Moduli).8 defaultContentLanguage Lingua predefinita Determina le traduzioni i18n da utilizzare inizialmente.8 outputs.home `` Cruciale: il formato JSON è necessario per la ricerca interna.8 summaryLength Lunghezza riassunto Un valore di 0 indica a Hugo di usare la prima frase come sommario.8 L\u0026rsquo;abilitazione del formato JSON nella homepage è un passaggio tecnico critico spesso trascurato; senza di esso, il modulo di ricerca Fuse.js non avrà un indice da interrogare, rendendo la barra di ricerca non funzionale.8\nParams.toml: Il Pannello di Controllo delle Funzionalità # Il file params.toml ospita le configurazioni specifiche del tema, permettendo di attivare o disattivare moduli complessi senza modificare il codice sorgente.4\nLa gestione dell\u0026rsquo;aspetto visivo è controllata dai parametri defaultAppearance e autoSwitchAppearance. Il primo definisce se il sito debba caricarsi in modalità \u0026ldquo;light\u0026rdquo; o \u0026ldquo;dark\u0026rdquo;, mentre il secondo, se impostato su true, permette al sito di rispettare le preferenze del sistema operativo dell\u0026rsquo;utente, garantendo un\u0026rsquo;esperienza visiva coerente con l\u0026rsquo;ecosistema del visitatore.8 Inoltre, il parametro colorScheme permette di selezionare una delle palette predefinite, ognuna delle quali trasforma radicalmente l\u0026rsquo;identità cromatica del sito senza richiedere modifiche CSS manuali.5\nL\u0026rsquo;Architettura Multilingue e la Configurazione dell\u0026rsquo;Autore # Blowfish eccelle nel supporto multilingue, richiedendo un file di configurazione dedicato per ogni lingua (es. languages.it.toml).5 In questo file vengono definiti non solo il titolo del sito per quella specifica lingua, ma anche i metadati dell\u0026rsquo;autore che appariranno nei box biografici sotto gli articoli.2\nCampo Autore Funzione Impatto UI name Nome dell\u0026rsquo;autore Visualizzato nell\u0026rsquo;header e nel footer degli articoli.2 image Avatar dell\u0026rsquo;autore Immagine profilo circolare nei widget biografici.2 headline Breve slogan Testo di impatto visualizzato nella homepage layout \u0026ldquo;profile\u0026rdquo;.2 bio Biografia completa Testo descrittivo visualizzato nel footer dei post se showAuthor è attivo.7 links Social media Array di icone cliccabili che collegano ai profili esterni.2 Questo approccio permette una personalizzazione estrema: un sito può avere autori diversi per versioni linguistiche diverse, o semplicemente tradurre la biografia dell\u0026rsquo;autore principale per adattarsi al pubblico locale.5\nNavigazione e Menu: Gerarchie e Iconografia # La configurazione dei menu avviene tramite file dedicati come menus.en.toml o menus.it.toml. Blowfish supporta tre aree di navigazione principali: il menu main (header), il menu footer e la subnavigation.5\nIl tema introduce un sistema di icone semplificato tramite il parametro pre, che permette di inserire icone SVG (come quelle di FontAwesome o icone social) direttamente accanto al testo del menu.5 Un aspetto avanzato è il supporto ai menu nidificati: definendo un elemento con un identifier unico e impostando altri elementi con un parametro parent corrispondente a quell\u0026rsquo;identificativo, Blowfish genererà automaticamente menu a discesa eleganti e funzionali.5\nGestione dei Contenuti: Page Bundles e Tassonomie # La forza di Hugo, e di Blowfish in particolare, risiede nella gestione strutturata dei contenuti. Il tema è progettato per operare in armonia con il concetto di \u0026ldquo;Page Bundles\u0026rdquo;, distinguendo tra Branch Pages e Leaf Pages.5\nBranch Pages e l\u0026rsquo;Organizzazione delle Sezioni # Le Branch Pages sono nodi della gerarchia che contengono altri file, come le homepage di sezione o le liste di categorie. Sono identificate dal file _index.md. Blowfish onora i parametri nel front matter di questi file, permettendo di sovrascrivere le impostazioni globali per una specifica sezione del sito.6 Ad esempio, è possibile decidere che la sezione \u0026ldquo;Portfolio\u0026rdquo; utilizzi una visualizzazione a card, mentre la sezione \u0026ldquo;Blog\u0026rdquo; utilizzi una lista classica.6\nLeaf Pages e la Gestione degli Asset # Le Leaf Pages rappresentano il contenuto atomico, come un singolo post o una pagina \u0026ldquo;About\u0026rdquo;. Se un articolo include immagini o altri media, deve essere creato come un \u0026ldquo;bundle\u0026rdquo;: una directory con il nome dell\u0026rsquo;articolo contenente un file index.md (senza underscore) e tutti gli asset correlati.6 Questo sistema non solo mantiene l\u0026rsquo;ordine nel filesystem, ma permette a Blowfish di processare le immagini tramite Hugo Pipes per ottimizzarne il peso e le dimensioni automaticamente.1\nIntegrazione di Contenuti Esterni # Blowfish offre una funzionalità sofisticata per includere link a piattaforme esterne (come Medium, LinkedIn o repository GitHub) direttamente nel flusso degli articoli del sito.1 Utilizzando il parametro externalUrl nel front matter e istruendo Hugo a non generare una pagina locale (build: render: \u0026ldquo;false\u0026rdquo;), il post apparirà nell\u0026rsquo;elenco degli articoli ma reindirizzerà l\u0026rsquo;utente direttamente alla risorsa esterna, mantenendo però la coerenza visiva e la categorizzazione interna del sito.6\nVisual Support e Media Optimization # L\u0026rsquo;impatto visivo di Blowfish è fortemente legato alla sua gestione delle immagini, che bilancia l\u0026rsquo;estetica con le prestazioni attraverso l\u0026rsquo;uso di tecnologie moderne come il lazy-loading e il ridimensionamento dinamico.1\nImmagini in Evidenza e Hero Sections # Per impostare un\u0026rsquo;immagine di anteprima che appaia nelle card e nell\u0026rsquo;intestazione di un articolo, Blowfish segue una convenzione di denominazione rigorosa: il file deve iniziare con feature* (es. feature.png, featured-image.jpg) e trovarsi nella cartella dell\u0026rsquo;articolo.5 Queste immagini non solo fungono da thumbnail, ma vengono utilizzate per generare i metadati Open Graph necessari per una corretta visualizzazione sui social media tramite il protocollo oEmbed.7\nIl layout dell\u0026rsquo;intestazione (Hero Style) può essere configurato globalmente o per singolo post:\nStile Hero Effetto Visivo Uso Consigliato basic Layout semplice con titolo e immagine affiancati. Post informativi standard.7 big Immagine grande sopra il titolo con supporto a didascalie. Articoli di copertina o long-form.7 background L\u0026rsquo;immagine di feature diventa lo sfondo dell\u0026rsquo;intestazione. Pagine d\u0026rsquo;impatto o landing pages.7 thumbAndBackground Combina l\u0026rsquo;immagine di sfondo con una thumbnail in primo piano. Brand identity forte o portfolio.7 Sfondi Personalizzati e Immagini di Sistema # Blowfish permette di definire sfondi globali tramite il parametro defaultBackgroundImage in params.toml. Per garantire tempi di caricamento rapidi, il tema scala automaticamente queste immagini a una larghezza predefinita (solitamente 1200px), riducendo il consumo di dati per gli utenti su dispositivi mobili.7 Inoltre, è possibile disabilitare globalmente lo zoom delle immagini o l\u0026rsquo;ottimizzazione per scenari specifici dove la fedeltà visiva assoluta è prioritaria rispetto alle prestazioni.8\nRich Content e Shortcodes Avanzati # Gli shortcode di Blowfish estendono le capacità del Markdown standard, permettendo l\u0026rsquo;inserimento di componenti UI complessi senza scrivere codice HTML.16\nAlerts e Callouts # Lo shortcode alert è uno strumento fondamentale per la comunicazione tecnica, permettendo di evidenziare avvertimenti, note o suggerimenti. Supporta parametri per l\u0026rsquo;icona, il colore della card, il colore dell\u0026rsquo;icona e il colore del testo, garantendo che l\u0026rsquo;avviso sia perfettamente in linea con il contesto semantico del contenuto.16\nEsempio di utilizzo con parametri nominati:\n{{\u0026lt; alert icon=\u0026ldquo;fire\u0026rdquo; cardColor=\u0026quot;#e63946\u0026quot; iconColor=\u0026quot;#1d3557\u0026quot; textColor=\u0026quot;#f1faee\u0026quot; \u0026gt;}}\nMessaggio di errore critico!\n{{\u0026lt; /alert \u0026gt;}}.16\nCaroselli e Gallerie Interattive # Per la gestione di molteplici immagini, lo shortcode carousel offre un\u0026rsquo;interfaccia scorrevole ed elegante. Una funzione particolarmente potente è la possibilità di passare una stringa regex al parametro images (es. images=\u0026ldquo;gallery/*\u0026rdquo;), istruendo il tema a caricare automaticamente tutte le immagini presenti in una specifica sottodirectory del Page Bundle.16 Questo elimina la necessità di aggiornare manualmente il codice Markdown ogni volta che viene aggiunta una foto alla galleria.\nFigure ed Embedding Video # Lo shortcode figure di Blowfish sostituisce quello nativo di Hugo offrendo prestazioni superiori tramite l\u0026rsquo;ottimizzazione delle immagini basata sulla risoluzione del dispositivo (Responsive Images). Supporta didascalie in Markdown, link ipertestuali sull\u0026rsquo;immagine e il controllo granulare sulla funzione di zoom.16\nPer quanto riguarda il video, Blowfish fornisce wrapper responsivi per YouTube, Vimeo e file locali. L\u0026rsquo;uso dello shortcode youtubeLite è raccomandato per i siti che puntano alla massima velocità: invece di caricare l\u0026rsquo;intero iframe di Google all\u0026rsquo;avvio della pagina, carica solo una miniatura leggera, attivando il player pesante solo quando l\u0026rsquo;utente clicca effettivamente sul tasto play.16\nComunicazione Scientifica: Matematica e Diagrammi # Blowfish è diventato uno standard de facto per i blog accademici e tecnici grazie alla sua integrazione nativa con strumenti di typesetting e visualizzazione dati di alto livello.1\nNotazione Matematica con KaTeX # Il rendering delle formule matematiche è affidato a KaTeX, noto per essere il motore di typesetting matematico più veloce per il web. Per preservare le prestazioni, Blowfish non carica gli asset di KaTeX globalmente; vengono inclusi nel bundle della pagina solo se viene rilevato lo shortcode {{\u0026lt; katex \u0026gt;}} all\u0026rsquo;interno dell\u0026rsquo;articolo.16\nLa sintassi supportata segue gli standard LaTeX:\nNotazione Inline: Formule inserite nel flusso del testo tramite i delimitatori \\( e \\). Esempio: $\\nabla \\cdot \\mathbf{E} = \\frac{\\rho}{\\varepsilon_0}$.18\nNotazione a Blocco: Formule centrate e isolate tramite i delimitatori $$. Esempio:\n$$e^{i\\\\pi} \\+ 1 \\= 0$$\n.18\nQuesta implementazione permette di scrivere equazioni complesse che rimangono leggibili e ricercabili, con un impatto nullo sulla velocità di caricamento delle pagine non scientifiche del sito.\nDiagrammi e Grafici Dinamici # Attraverso gli shortcode mermaid e chart, Blowfish permette di generare visualizzazioni complesse partendo da dati testuali.1\nMermaid.js: Consente la creazione di diagrammi di flusso, diagrammi di sequenza, grafi di Gantt e diagrammi di classe utilizzando una sintassi testuale semplice. È ideale per documentare architetture software o processi logici senza dover gestire file immagine esterni.1 Chart.js: Permette di incorporare grafici a barre, a torta, a linee e radar fornendo dati strutturati direttamente nello shortcode. Poiché i grafici sono renderizzati su un elemento HTML5 Canvas, rimangono nitidi a qualsiasi livello di zoom e sono interattivi (mostrano i valori al passaggio del mouse).1 Integrazioni Dinamiche e Dynamic Data Support # Nonostante la sua natura statica, Blowfish può evolversi in una piattaforma dinamica grazie all\u0026rsquo;integrazione intelligente con servizi serverless, in particolare Firebase.1\nFirebase: Views, Likes e Analytics Dinamici # L\u0026rsquo;integrazione con Firebase permette di aggiungere funzionalità tipiche dei sistemi CMS tradizionali, come il conteggio delle visualizzazioni in tempo reale e il sistema di \u0026ldquo;like\u0026rdquo; per gli articoli.1 Il processo di configurazione prevede:\nCreazione di un progetto Firebase e abilitazione del database Firestore in modalità produzione.9 Configurazione delle regole di sicurezza per permettere letture e scritture anonime (previa abilitazione dell\u0026rsquo;Anonymous Authentication).9 Inserimento delle chiavi API nel file params.toml sotto la sezione Firebase.8 Una volta configurato, Blowfish gestisce automaticamente l\u0026rsquo;incremento delle visualizzazioni ogni volta che una pagina viene caricata, memorizzando i dati nel database serverless e visualizzandoli negli elenchi degli articoli.8\nRicerca Avanzata con Fuse.js # La ricerca interna di Blowfish non richiede database esterni. Durante la fase di build, Hugo genera un file index.json contenente il titolo, il sommario e il contenuto di tutti gli articoli.1 Fuse.js, una libreria di ricerca fuzzy leggera, scarica questo indice e permette ricerche istantanee direttamente nel browser dell\u0026rsquo;utente. Per garantire il funzionamento di questa feature, è imperativo che la configurazione outputs.home includa il formato JSON.8\nSEO, Accessibilità e Ottimizzazione per i Motori di Ricerca # Blowfish è costruito seguendo le migliori pratiche SEO per garantire che i contenuti siano facilmente indicizzabili e presentati in modo ottimale sui social media.1\nMetadati e Structured Data # Il tema genera automaticamente tag meta Open Graph e Twitter Cards, utilizzando l\u0026rsquo;immagine di feature dell\u0026rsquo;articolo e la descrizione fornita nel front matter. Se non viene fornita una descrizione, Blowfish utilizza il sommario generato automaticamente da Hugo.7 Inoltre, il supporto per i breadcrumbs strutturati (abilitabile tramite enableStructuredBreadcrumbs) aiuta i motori di ricerca a comprendere la gerarchia del sito e a visualizzare percorsi di navigazione puliti nei risultati di ricerca.8\nPerformance e Lighthouse Scores # L\u0026rsquo;ottimizzazione delle prestazioni non è solo una questione di velocità, ma un fattore di ranking critico (Core Web Vitals). Blowfish ottiene punteggi vicini al 100 in tutte le categorie Lighthouse grazie a:\nGenerazione di CSS critico minimo tramite Tailwind.1 Lazy-loading nativo per tutte le immagini.8 Minimizzazione degli asset JS.1 Supporto nativo per i formati immagine moderni come WebP (tramite Hugo Pipes).1 Strategie di Deployment e Pipeline di Produzione # La natura statica dei siti generati con Blowfish permette una distribuzione globale ed economica tramite CDN (Content Delivery Networks).12\nHosting e Continuous Deployment # Le piattaforme di hosting moderne offrono integrazioni dirette con GitHub o GitLab, automatizzando il processo di build e deployment.\nPiattaforma Metodo di Build Note Tecniche GitHub Pages GitHub Actions Richiede la creazione di un workflow YAML che esegua hugo --gc --minify.4 Netlify Build Bot interno Configurazione tramite netlify.toml; supporta le preview dei branch e i form.3 Firebase Hosting Firebase CLI Ideale se si utilizza già Firebase per le visualizzazioni e i like.9 Durante la configurazione del deployment, è fondamentale impostare correttamente la variabile baseURL per l\u0026rsquo;ambiente di produzione, specialmente se il sito risiede in una sottocartella, per evitare che gli asset (CSS, immagini) vengano caricati da percorsi errati.4\nConclusioni: Verso un Web Statico senza Compromessi # La configurazione del tema Blowfish per Hugo rappresenta un esercizio di bilanciamento tra la semplicità della gestione dei contenuti in Markdown e la complessità delle esigenze tecnologiche moderne. Attraverso una struttura modulare, un\u0026rsquo;attenzione maniacale alle prestazioni e una serie di integrazioni di alto livello per dati scientifici e dinamici, Blowfish si conferma come una soluzione d\u0026rsquo;eccellenza per la realizzazione di siti web professionali.1\nL\u0026rsquo;adozione di questo tema permette agli sviluppatori di concentrarsi sulla qualità del contenuto e sulla struttura dell\u0026rsquo;informazione, delegando al framework la gestione degli aspetti tecnici legati all\u0026rsquo;accessibilità, alla SEO e all\u0026rsquo;ottimizzazione degli asset. In un ecosistema web sempre più esigente, Blowfish offre gli strumenti necessari per costruire una presenza online solida, performante e visivamente appagante, definendo lo stato dell\u0026rsquo;arte per i temi Hugo di nuova generazione.3\nBibliografia # Blowfish | Hugo Themes, accesso eseguito il giorno gennaio 3, 2026, https://www.gohugothemes.com/theme/nunocoracao-blowfish/ Gitlab Pages, Hugo and Blowfish to set up your website in minutes - Mariano González, accesso eseguito il giorno gennaio 3, 2026, https://blog.mariano.cloud/your-website-in-minutes-gitlab-hugo-blowfish nunocoracao/blowfish: Personal Website \u0026amp; Blog Theme for Hugo - GitHub, accesso eseguito il giorno gennaio 3, 2026, https://github.com/nunocoracao/blowfish Blowfish - True Position Tools, accesso eseguito il giorno gennaio 3, 2026, https://truepositiontools.com/crypto/blowfish-guide Getting Started - Blowfish, accesso eseguito il giorno gennaio 3, 2026, https://blowfish.page/docs/getting-started/ Content Examples · Blowfish, accesso eseguito il giorno gennaio 3, 2026, https://blowfish.page/docs/content-examples/ Thumbnails · Blowfish, accesso eseguito il giorno gennaio 3, 2026, https://blowfish.page/docs/thumbnails/ Configuration - Blowfish, accesso eseguito il giorno gennaio 3, 2026, https://blowfish.page/docs/configuration/ Firebase: Views \u0026amp; Likes - Blowfish, accesso eseguito il giorno gennaio 3, 2026, https://blowfish.page/docs/firebase-views/ Installation - Blowfish, accesso eseguito il giorno gennaio 3, 2026, https://blowfish.page/docs/installation/ How To Make A Hugo Blowfish Website - YouTube, accesso eseguito il giorno gennaio 3, 2026, https://www.youtube.com/watch?v=-05mOdHmQVc A Beginner-Friendly Tutorial for Building a Blog with Hugo, the Blowfish Theme, and GitHub Pages, accesso eseguito il giorno gennaio 3, 2026, https://www.gigigatgat.ca/en/posts/how-to-create-a-blog/ Step-by-Step Guide to Creating a Hugo Website · - dasarpAI, accesso eseguito il giorno gennaio 3, 2026, https://main\u0026ndash;dasarpai.netlify.app/dsblog/step-by-step-guide-creating-hugo-website/ Partials - Blowfish, accesso eseguito il giorno gennaio 3, 2026, https://blowfish.page/docs/partials/ Build your homepage using Blowfish and Hugo · N9O - Nuno Coração, accesso eseguito il giorno gennaio 3, 2026, https://n9o.xyz/posts/202310-blowfish-tutorial/ Shortcodes · Blowfish, accesso eseguito il giorno gennaio 3, 2026, https://blowfish.page/docs/shortcodes/ Shortcodes - Hugo, accesso eseguito il giorno gennaio 3, 2026, https://gohugo.io/content-management/shortcodes/ Mathematical notation · Blowfish, accesso eseguito il giorno gennaio 3, 2026, https://blowfish.page/samples/mathematical-notation/ Hosting \u0026amp; Deployment - Deepfaces, accesso eseguito il giorno gennaio 3, 2026, https://deepfaces.pt/docs/hosting-deployment/ Getting Started With Hugo | FREE COURSE - YouTube, accesso eseguito il giorno gennaio 3, 2026, https://www.youtube.com/watch?v=hjD9jTi_DQ4 ","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/guides/hugo-blowfish-theme-guide/","section":"Guide","summary":"","title":"Architettura e Configurazione Avanzata del Tema Blowfish per Hugo: Una Disamina Tecnica Integrale","type":"guides"},{"content":"L\u0026rsquo;evoluzione dei sistemi operativi cloud-native ha portato alla nascita di soluzioni radicalmente diverse rispetto alle distribuzioni Linux tradizionali. Talos Linux si colloca all\u0026rsquo;avanguardia di questa trasformazione, proponendo un modello operativo basato sull\u0026rsquo;immutabilità, l\u0026rsquo;assenza di shell interattive e una gestione interamente mediata da API.1 In questo ecosistema, l\u0026rsquo;integrazione di Tailscale, una soluzione di rete mesh basata sul protocollo WireGuard, non rappresenta una semplice installazione di software, ma un\u0026rsquo;operazione di ingegneria dei sistemi che richiede la comprensione profonda dei meccanismi di estensione del kernel e del filesystem di Talos.3 Il presente rapporto analizza le metodologie di implementazione, le strategie di configurazione dichiarativa e la risoluzione delle problematiche di rete che emergono dalla convergenza di queste due tecnologie.\nParadigmi operativi e architettura di Talos Linux # Per comprendere le sfide dell\u0026rsquo;installazione di Tailscale, è necessario analizzare la struttura fondamentale di Talos Linux. A differenza delle distribuzioni generaliste, Talos non utilizza gestori di pacchetti come apt o yum.1 Il filesystem di root è montato in sola lettura e il sistema è progettato per essere effimero, con l\u0026rsquo;eccezione della partizione dedicata ai dati persistenti.3 Questo approccio elimina il problema della deriva della configurazione (configuration drift) ma impedisce l\u0026rsquo;esecuzione dei comuni script di installazione di Tailscale.1\nLa gestione del sistema avviene esclusivamente tramite talosctl, un\u0026rsquo;utilità CLI che comunica con le API gRPC esposte dal demone machined.3 In questo contesto, ogni componente software aggiuntivo deve essere integrato come estensione di sistema o come carico di lavoro all\u0026rsquo;interno di Kubernetes.3\nCaratteristica Talos Linux Distribuzioni Tradizionali Gestione Pacchetti Assente (Estensioni OCI) apt, yum, zypper, pacman Accesso Remoto API gRPC (Porta 50000) SSH (Porta 22) Filesystem Root Immutabile (Read-only) Mutabile (Read-write) Configurazione Dichiarativa (YAML) Imperativa (Script/CLI) Kernel Hardened / Minimalista Generalista / Modulare L\u0026rsquo;assenza di un terminale locale e di strumenti di diagnostica standard come iproute2 o iptables accessibili direttamente dall\u0026rsquo;utente rende indispensabile l\u0026rsquo;uso di Tailscale non solo per la sicurezza della rete, ma anche come potenziale ponte per la gestione fuori banda del cluster.3\nIl meccanismo delle estensioni di sistema # Il metodo primario per iniettare binari come tailscaled e tailscale in Talos Linux è il sistema delle estensioni (System Extensions).9 Un\u0026rsquo;estensione di sistema è un\u0026rsquo;immagine container conforme alle specifiche OCI che contiene una struttura di file predefinita, destinata a essere sovrapposta al filesystem di root durante la fase di boot.12\nAnatomia di un\u0026rsquo;estensione OCI # Un\u0026rsquo;estensione valida deve contenere un file manifest.yaml alla radice, il quale definisce il nome, la versione e i requisiti di compatibilità con la versione di Talos.3 Il contenuto effettivo dei binari deve essere collocato nella directory /rootfs/usr/local/lib/containers/\u0026lt;nome-estensione\u0026gt;.3 Talos scansiona la directory /usr/local/etc/containers alla ricerca di definizioni di servizio in formato YAML, che descrivono come il demone machined debba avviare il processo.9\nIl servizio Tailscale, quando eseguito come estensione, opera come un container privilegiato con accesso al dispositivo /dev/net/tun dell\u0026rsquo;host, essenziale per la creazione dell\u0026rsquo;interfaccia di rete virtuale.4 Poiché l\u0026rsquo;interfaccia tailscale0 deve essere disponibile per il sistema operativo host e non solo all\u0026rsquo;interno di un namespace di rete isolato, l\u0026rsquo;estensione utilizza il networking dell\u0026rsquo;host.14\nCiclo di vita del servizio ext-tailscale # Quando Talos rileva la configurazione di Tailscale, registra un servizio denominato ext-tailscale.9 Questo servizio entra in uno stato di attesa finché non vengono soddisfatte le dipendenze di rete, come l\u0026rsquo;assegnazione di indirizzi IP alle interfacce fisiche e la connettività verso i gateway predefiniti.9 La telemetria di questo servizio può essere monitorata tramite il comando talosctl service ext-tailscale, che fornisce dettagli sullo stato operativo, gli eventi di restart e la salute del processo.9\nMetodologie di installazione e generazione delle immagini # Esistono tre percorsi principali per implementare Tailscale su un nodo Talos, ognuno con implicazioni diverse sulla manutenibilità e sulla stabilità del sistema.3\nUtilizzo della Talos Image Factory # La Talos Image Factory rappresenta l\u0026rsquo;approccio più moderno e consigliato.5 Si tratta di un servizio API gestito da Sidero Labs che consente di assemblare dinamicamente immagini ISO, asset PXE o immagini disco (raw) includendo estensioni certificate.3 L\u0026rsquo;utente seleziona la versione di Talos, l\u0026rsquo;architettura (amd64 o arm64) e aggiunge l\u0026rsquo;estensione siderolabs/tailscale dalla lista delle estensioni ufficiali.5\nIl risultato di questa operazione è un identificativo schematico (Schematic ID).10 Questo hash garantisce che l\u0026rsquo;immagine sia riproducibile e che tutti i nodi di un cluster utilizzino l\u0026rsquo;esatta combinazione di kernel e driver.\nPiattaforma Formato Immagine Metodo di Distribuzione Bare Metal ISO / RAW USB Flash / iDRAC / IPMI Virtualizzazione (Proxmox/ESXi) ISO Caricamento Datastore Cloud (AWS/GCP/Azure) AMI / Disk Image Importazione Immagine Network Boot PXE / iPXE Server TFTP/HTTP L\u0026rsquo;installazione avviene fornendo l\u0026rsquo;URL dell\u0026rsquo;installer basato sullo schema nel file di configurazione della macchina, sotto la chiave machine.install.image.3 Durante il processo di installazione o aggiornamento, Talos recupera l\u0026rsquo;immagine OCI, estrae i componenti necessari e li persiste nella partizione di sistema.3\nInstallazione tramite OCI Installer su nodi esistenti # Per i nodi già operativi, è possibile iniettare Tailscale senza rigenerare l\u0026rsquo;intero supporto di avvio fisico.3 Questo avviene modificando dinamicamente l\u0026rsquo;immagine di installazione nel MachineConfig.3 Tuttavia, questo metodo presenta un rischio: se l\u0026rsquo;immagine specificata non contiene l\u0026rsquo;estensione durante un successivo aggiornamento del sistema operativo, Tailscale verrà rimosso al riavvio.3 È quindi imperativo che lo schematic ID rimanga coerente attraverso l\u0026rsquo;intero ciclo di vita del nodo.\nBuild personalizzate tramite Imager # In ambienti air-gapped o dove è richiesta la massima personalizzazione, gli operatori possono utilizzare l\u0026rsquo;utility imager di Sidero Labs per creare immagini offline.12 Questo strumento permette di scaricare i pacchetti necessari, includere configurazioni di rete statiche e integrare Tailscale localmente prima di produrre l\u0026rsquo;asset di boot finale.12\nConfigurazione dichiarativa e gestione delle identità # Una volta installati i binari, Tailscale deve essere configurato per unirsi al tailnet. In Talos, questo non avviene tramite l\u0026rsquo;invocazione manuale di tailscale up, ma attraverso la risorsa ExtensionServiceConfig.3\nAutenticazione tramite Auth Keys # Il metodo più semplice consiste nell\u0026rsquo;uso di una chiave di autenticazione pre-generata dal pannello di controllo di Tailscale.4 Esistono diverse tipologie di chiavi, ognuna adatta a uno scenario specifico:\nChiavi Riutilizzabili: Ideali per l\u0026rsquo;espansione automatica dei worker node in un cluster Kubernetes. Una singola chiave può autenticare più macchine.10 Chiavi Ephemeral: Raccomandate per i nodi Talos, in quanto garantiscono che, se un nodo viene distrutto o resettato, la sua voce venga automaticamente rimossa dal tailnet, evitando la proliferazione di nodi orfani.10 Chiavi Pre-approvate: Consentono di bypassare l\u0026rsquo;approvazione manuale dei dispositivi se il tailnet ha tale funzione abilitata.22 Integrazione di OAuth2 per la sicurezza avanzata # Per installazioni di livello enterprise, l\u0026rsquo;integrazione con OAuth2 è la soluzione preferita.16 Talos Linux supporta il flusso di autenticazione OAuth2 direttamente nei parametri del kernel o nella configurazione della macchina.24 Fornendo un clientId e un clientSecret, il sistema può negoziare le proprie credenziali di accesso, riducendo la necessità di gestire chiavi di lunga durata.16\nQuesta configurazione viene inserita nel file YAML di patch del nodo:\nYAML\napiVersion: v1alpha1\nkind: ExtensionServiceConfig\nmetadata:\nname: tailscale\nspec:\nenvironment:\n- TS_AUTHKEY=tskey-auth-abcdef123456\n- TS_EXTRA_ARGS=\u0026ndash;advertise-tags=tag:talos,tag:k8s --accept-dns=false\nL\u0026rsquo;applicazione della patch avviene tramite talosctl patch mc -p @tailscale-patch.yaml -n \u0026lt;node-ip\u0026gt;, che forza il caricamento dei parametri nel demone machined e il conseguente riavvio del servizio dell\u0026rsquo;estensione.3\nPersistenza dello stato e stabilità dell\u0026rsquo;identità # Uno dei problemi più comuni segnalati dagli utenti è la creazione di nodi duplicati nel pannello Tailscale dopo ogni riavvio.11 Questo accade perché lo stato di Tailscale (che include la chiave privata del nodo e il certificato della macchina) è solitamente memorizzato in /var/lib/tailscale, che nelle estensioni di sistema è effimero per impostazione predefinita.6\nStrategie di persistenza su filesystem immutabili # In Talos Linux, la directory /var è montata su una partizione persistente che sopravvive ai riavvii e agli aggiornamenti del sistema operativo.6 Per garantire la stabilità dell\u0026rsquo;identità del nodo, è necessario configurare l\u0026rsquo;estensione affinché monti una directory host persistente.3\nParametro di Configurazione Valore Scopo TS_STATE_DIR /var/lib/tailscale Percorso per la memorizzazione della chiave del nodo Mount Source /var/lib/tailscale Directory persistente sull\u0026rsquo;host Talos Mount Destination /var/lib/tailscale Destinazione all\u0026rsquo;interno del container dell\u0026rsquo;estensione Mount Options bind, rw Permette l\u0026rsquo;accesso in lettura e scrittura Senza questo accorgimento, ogni aggiornamento di Talos (che comporta un riavvio e la cancellazione dello stato effimero) provocherebbe la generazione di una nuova identità crittografica, rompendo le rotte statiche e le policy ACL configurate nel tailnet.11\nAnalisi dei conflitti di networking e multihoming # L\u0026rsquo;introduzione di un\u0026rsquo;interfaccia di rete virtuale come tailscale0 su un host che gestisce già interfacce fisiche e il networking di Kubernetes (tramite CNI) può portare a conflitti di routing complessi.27\nIl problema del binding del Kubelet e dell\u0026rsquo;API Server # Kubernetes, per impostazione predefinita, tenta di identificare l\u0026rsquo;indirizzo IP primario del nodo per le comunicazioni interne del cluster.27 Se Tailscale viene avviato prima che l\u0026rsquo;interfaccia fisica abbia stabilito una connessione stabile, o se il Kubelet rileva l\u0026rsquo;interfaccia tailscale0 come prioritaria, potrebbe tentare di registrare il nodo con l\u0026rsquo;IP del tailnet (nell\u0026rsquo;intervallo 100.64.0.0/10).27\nQuesto scenario impedisce al CNI (Cilium, Flannel, ecc.) di stabilire tunnel corretti tra i pod, poiché il traffico incapsulato potrebbe tentare di transitare attraverso il tunnel Tailscale invece che sulla rete locale, causando un degrado delle prestazioni o il fallimento completo della connettività.27\nSoluzione Documentata:\nLa configurazione di Talos deve istruire esplicitamente il Kubelet e l\u0026rsquo;Etcd a utilizzare solo le subnet della rete locale per il traffico del cluster.27\nYAML\nmachine:\nkubelet:\nnodeIP:\nvalidSubnets:\n- 192.168.1.0/24 # Sostituire con la propria subnet locale\ncluster:\netcd:\nadvertisedSubnets:\n- 192.168.1.0/24\nQuesta configurazione garantisce che, nonostante la presenza di Tailscale, il piano di controllo di Kubernetes e il traffico tra i worker rimangano sulla rete fisica, mentre Tailscale viene utilizzato esclusivamente per l\u0026rsquo;accesso remoto e la gestione.27\nGestione del DNS e di resolv.conf # Tailscale tenta spesso di assumere il controllo della risoluzione DNS per abilitare MagicDNS, un servizio che permette di contattare i nodi del tailnet tramite nomi host semplici.4 In Talos Linux, il file /etc/resolv.conf è gestito in modo deterministico e le modifiche esterne vengono spesso sovrascritte.4\nMolti utenti segnalano che l\u0026rsquo;attivazione di MagicDNS rompe la risoluzione dei nomi interni di Kubernetes (come kubernetes.default.svc.cluster.local).27 La raccomandazione tecnica è di disabilitare la gestione del DNS da parte di Tailscale tramite il flag --accept-dns=false e, se necessario, configurare CoreDNS nel cluster Kubernetes affinché inoltri le query per il dominio .ts.net all\u0026rsquo;IP del resolver di Tailscale (100.100.100.100).15\nPrestazioni, MTU e ottimizzazione del traffico # Tailscale utilizza un valore MTU (Maximum Transmission Unit) predefinito di $1280$ byte.35 Questo valore è scelto per garantire che i pacchetti WireGuard (che aggiungono un overhead di incapsulamento) non superino l\u0026rsquo;MTU standard di $1500$ byte tipico della maggior parte delle reti Ethernet.35\nCriticità legate alla frammentazione dei pacchetti # In alcuni ambienti, come le connessioni DSL con PPPoE o gli hotspot cellulari, l\u0026rsquo;MTU della rete sottostante potrebbe essere inferiore a $1500$. In questi casi, un MTU di $1280$ per Tailscale potrebbe essere troppo alto, portando alla frammentazione dei pacchetti.36 Poiché WireGuard droppa silenziosamente i pacchetti frammentati per motivi di sicurezza, le sessioni TCP (come SSH o i trasferimenti di file) potrebbero apparire \u0026ldquo;congelate\u0026rdquo; o estremamente lente.35\nL\u0026rsquo;esperienza degli utenti suggerisce che l\u0026rsquo;impostazione manuale dell\u0026rsquo;MTU a $1200$ può risolvere drasticamente problemi di throughput in reti problematiche.36\nScenario di Rete MTU Consigliato Tecnica di Ottimizzazione Ethernet Standard (LAN) 1280 Default DSL / PPPoE 1240 - 1260 MSS Clamping Reti Mobili (LTE/5G) 1200 - 1240 TS_DEBUG_MTU Overlay su Overlay (VPN in VPN) 1100 - 1200 Riduzione manuale Per applicare queste ottimizzazioni su Talos, è necessario utilizzare la variabile d\u0026rsquo;ambiente TS_DEBUG_MTU all\u0026rsquo;interno dell\u0026rsquo; ExtensionServiceConfig.36 Inoltre, per il traffico che attraversa il cluster come Subnet Router, è fondamentale implementare l\u0026rsquo;MSS Clamping tramite regole di firewalling (sebbene questo sia complesso in Talos senza estensioni specifiche per iptables o nftables).35\nConfigurazione di Subnet Router ed Exit Node su Talos # Un nodo Talos può fungere da gateway per l\u0026rsquo;intero cluster o per la rete locale, permettendo ad altri membri del tailnet di accedere a risorse che non possono eseguire direttamente il client Tailscale (come database legacy, stampanti o i singoli Pod Kubernetes).32\nAbilitazione dell\u0026rsquo;IP Forwarding a livello Kernel # Il prerequisito assoluto per il funzionamento di un Subnet Router è l\u0026rsquo;abilitazione dell\u0026rsquo;inoltro dei pacchetti IP a livello di kernel.32 Mentre nelle distribuzioni standard questo si fa modificando /etc/sysctl.conf, in Talos deve essere definito nel MachineConfig.8\nYAML\nmachine:\nsysctls:\nnet.ipv4.ip_forward: \u0026ldquo;1\u0026rdquo;\nnet.ipv6.conf.all.forwarding: \u0026ldquo;1\u0026rdquo;\nQuesta modifica richiede un riavvio del nodo (o l\u0026rsquo;applicazione a caldo tramite talosctl apply-config) affinché il kernel inizi a instradare i pacchetti tra le interfacce fisiche e l\u0026rsquo;interfaccia tailscale0.42\nPubblicizzazione delle rotte Pod e Service # Per esporre i servizi Kubernetes, il nodo deve pubblicizzare le rotte corrispondenti ai CIDR del cluster.32 Ad esempio, se il Pod CIDR è 10.244.0.0/16, il comando Tailscale deve includere --advertise-routes=10.244.0.0/16.32\nÈ importante ricordare che la pubblicizzazione delle rotte nel comando non è sufficiente; esse devono essere approvate manualmente nel pannello di controllo di Tailscale, a meno che non siano configurati degli \u0026ldquo;Auto Approvers\u0026rdquo;.32 L\u0026rsquo;uso di --snat-subnet-routes=false è consigliato per preservare l\u0026rsquo;indirizzo IP del client originale nelle comunicazioni interne al cluster, facilitando il logging e il monitoraggio della sicurezza.32\nAnalisi comparativa: Estensione di Sistema vs Operatore Kubernetes # Esiste un dibattito tra gli utenti su quale sia il metodo migliore per integrare Tailscale in un cluster Talos.3\nL\u0026rsquo;approccio basato su Estensioni di Sistema # L\u0026rsquo;estensione opera a livello di sistema operativo host. È la soluzione preferita quando l\u0026rsquo;obiettivo principale è la gestione del nodo stesso.3\nVantaggi: Permette di accedere alla Talos API (porta 50000) anche se Kubernetes non è avviato o è in crash.3 È ideale per il bootstrap iniziale del cluster su reti remote.10 Svantaggi: Richiede la gestione di chiavi e stati a livello di singolo nodo, aumentando il sovraccarico amministrativo se il cluster ha molti nodi.3 L\u0026rsquo;approccio basato su Tailscale Operator # L\u0026rsquo;operatore viene installato all\u0026rsquo;interno di Kubernetes tramite Helm e gestisce Proxy Pod dedicati per ogni servizio che deve essere esposto.16\nVantaggi: Integrazione nativa con Kubernetes. La creazione di un Ingress di tipo Tailscale genera automaticamente una voce nel tailnet con il nome del servizio.16 Non richiede modifiche al MachineConfig di Talos.16 Svantaggi: Non fornisce accesso alla gestione del sistema operativo host.16 Se il piano di controllo di Kubernetes fallisce, l\u0026rsquo;accesso tramite Tailscale viene interrotto.16 Raccomandazione per l\u0026rsquo;architettura ibrida # Per un\u0026rsquo;infrastruttura robusta, si raccomanda l\u0026rsquo;uso di entrambi i sistemi: l\u0026rsquo;estensione di sistema su almeno un nodo di controllo (control plane) per l\u0026rsquo;accesso di emergenza e l\u0026rsquo;amministrazione tramite talosctl, e l\u0026rsquo;operatore Kubernetes per esporre le applicazioni agli utenti finali in modo scalabile e granulare.20\nErrori comuni segnalati dagli utenti e risoluzioni documentate # L\u0026rsquo;analisi dei thread di supporto e delle issue su GitHub evidenzia una serie di \u0026ldquo;trappole\u0026rdquo; tipiche dell\u0026rsquo;integrazione Talos-Tailscale.\nErrore 1: Conflitti tra KubeSpan e Tailscale # KubeSpan è la soluzione nativa di Talos per il networking mesh tra i nodi, anch\u0026rsquo;essa basata su WireGuard.6 Sebbene teoricamente compatibili, l\u0026rsquo;attivazione simultanea di entrambi può causare problemi di performance e conflitti di porte (entrambi potrebbero tentare di utilizzare la porta UDP 51820).49\nSoluzione:\nSe si utilizza Tailscale per la connettività tra i nodi, KubeSpan dovrebbe essere disabilitato.49 In alternativa, è necessario configurare Tailscale per utilizzare una porta UDP diversa tramite il flag --port o lasciare che utilizzi la negoziazione NAT dinamica.36\nErrore 2: Rottura del networking di Portainer e altri agenti privilegiati # Un caso specifico segnalato riguarda l\u0026rsquo;installazione di Tailscale che interrompe il funzionamento di Portainer o degli agenti di monitoraggio che si basano sulla comunicazione inter-pod.27 Questo accade quando l\u0026rsquo;agente tenta di unirsi a un cluster utilizzando l\u0026rsquo;IP di Tailscale invece dell\u0026rsquo;IP del pod, riscontrando un errore \u0026ldquo;no route to host\u0026rdquo;.27\nRisoluzione:\nL\u0026rsquo;errore è una conseguenza diretta del problema del multihoming discusso in precedenza. La soluzione definitiva è l\u0026rsquo;impostazione di machine.kubelet.nodeIP.validSubnets per escludere l\u0026rsquo;intervallo IP di Tailscale dalle rotte interne di Kubernetes.27\nErrore 3: Certificati API non validi a causa di IP dinamici # Se un nodo Talos riceve un nuovo IP dal tailnet e l\u0026rsquo;utente tenta di connettersi tramite quell\u0026rsquo;IP, talosctl potrebbe restituire un errore di validazione del certificato mTLS.30 Talos genera i certificati API includendo solo gli indirizzi IP noti al momento del bootstrap.28\nSoluzione:\nÈ necessario aggiungere gli intervalli IP di Tailscale (o i nomi MagicDNS) alla lista dei Subject Alternative Names (SAN) nel MachineConfig.30\nYAML\nmachine:\ncertSANs:\n- 100.64.0.0/10\n- my-node.tailnet-id.ts.net\nProspettive future e considerazioni finali # L\u0026rsquo;integrazione di Tailscale su Talos Linux rappresenta la sintesi tra la sicurezza di un sistema operativo immutabile e la flessibilità di una rete mesh moderna. Nonostante le sfide iniziali legate alla configurazione dichiarativa e alla gestione del multihoming, i benefici in termini di semplicità operativa e sicurezza sono innegabili.\nLe discussioni all\u0026rsquo;interno della comunità suggeriscono un interesse crescente verso la creazione di immagini Talos ancora più specializzate, che potrebbero includere Tailscale direttamente nel kernel per ridurre ulteriormente l\u0026rsquo;impronta di memoria e migliorare le prestazioni crittografiche.11 Fino ad allora, il sistema delle estensioni OCI rimane il meccanismo più robusto e flessibile per estendere le capacità di rete di Talos.9\nGli operatori che adottano questo stack devono privilegiare l\u0026rsquo;uso dell\u0026rsquo;Image Factory per garantire la riproducibilità, implementare politiche di persistenza rigorose per mantenere l\u0026rsquo;identità dei nodi e prestare particolare attenzione alla configurazione delle subnet del Kubelet per evitare conflitti di routing che potrebbero compromettere la stabilità dell\u0026rsquo;intero cluster Kubernetes.3 Con queste precauzioni, Tailscale diventa un componente invisibile ma fondamentale per l\u0026rsquo;orchestrazione di infrastrutture cloud-native sicure e resilienti.\nBibliografia # Talos Linux - The Kubernetes Operating System, accesso eseguito il giorno gennaio 6, 2026, https://www.talos.dev/ siderolabs/talos: Talos Linux is a modern Linux distribution built for Kubernetes. - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/siderolabs/talos Customizing Talos with Extensions - A cup of coffee, accesso eseguito il giorno gennaio 6, 2026, https://a-cup-of.coffee/blog/talos-ext/ Install Tailscale on Linux, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1031/install-linux System Extensions - Image Factory - Talos Linux, accesso eseguito il giorno gennaio 6, 2026, https://factory.talos.dev/?arch=amd64\u0026amp;platform=metal\u0026amp;target=metal\u0026amp;version=1.7.6 What\u0026rsquo;s New in Talos 1.8.0 - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 6, 2026, https://docs.siderolabs.com/talos/v1.8/getting-started/what\u0026rsquo;s-new-in-talos Install talosctl - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 6, 2026, https://docs.siderolabs.com/omni/getting-started/how-to-install-talosctl MachineConfig - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 6, 2026, https://docs.siderolabs.com/talos/v1.8/reference/configuration/v1alpha1/config Extension Services - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 6, 2026, https://docs.siderolabs.com/talos/v1.8/build-and-extend-talos/custom-images-and-development/extension-services Creating a Kubernetes Cluster With Talos Linux on Tailscale | Josh Noll, accesso eseguito il giorno gennaio 6, 2026, https://joshrnoll.com/creating-a-kubernetes-cluster-with-talos-linux-on-tailscale/ FR: Minimal Purpose-Built OS for Tailscale · Issue #17761 - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/tailscale/tailscale/issues/17761 How to build a Talos system extension - Sidero Labs, accesso eseguito il giorno gennaio 6, 2026, https://www.siderolabs.com/blog/how-to-build-a-talos-system-extension/ Package tailscale - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/orgs/siderolabs/packages/container/package/tailscale How to make Tailscale container persistant? - ZimaOS - IceWhale Community Forum, accesso eseguito il giorno gennaio 6, 2026, https://community.zimaspace.com/t/how-to-make-tailscale-container-persistant/5987 Using Tailscale with Docker, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1282/docker Kubernetes operator · Tailscale Docs, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1236/kubernetes-operator talosctl - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 6, 2026, https://docs.siderolabs.com/talos/v1.6/reference/cli Talos Linux Image Factory, accesso eseguito il giorno gennaio 6, 2026, https://factory.talos.dev/ siderolabs/extensions: Talos Linux System Extensions - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/siderolabs/extensions Deploy Talos Linux with Local VIP, Tailscale, Longhorn, MetalLB and Traefik - Josh\u0026rsquo;s Notes, accesso eseguito il giorno gennaio 6, 2026, https://notes.joshrnoll.com/notes/deploy-talos-linux-with-local-vip-tailscale-longhorn-metallb-and-traefik/ Securely handle an auth key · Tailscale Docs, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1595/secure-auth-key-cli Auth keys · Tailscale Docs, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1085/auth-keys OAuth clients · Tailscale Docs, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1215/oauth-clients Machine Configuration OAuth2 Authentication - What is Talos Linux?, accesso eseguito il giorno gennaio 6, 2026, https://docs.siderolabs.com/talos/v1.8/security/machine-config-oauth A collection of scripts for creating and managing kubernetes clusters on talos linux - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/joshrnoll/talos-scripts Troubleshooting guide · Tailscale Docs, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1023/troubleshooting Tailscale on Talos os breaks Portainer : r/kubernetes - Reddit, accesso eseguito il giorno gennaio 6, 2026, https://www.reddit.com/r/kubernetes/comments/1izy26m/tailscale_on_talos_os_breaks_portainer/ Production Clusters - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 6, 2026, https://docs.siderolabs.com/talos/v1.7/getting-started/prodnotes Issues · siderolabs/talos - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/siderolabs/talos/issues Troubleshooting - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 6, 2026, https://docs.siderolabs.com/talos/v1.9/troubleshooting/troubleshooting Split dns on talos machine config · Issue #7287 · siderolabs/talos - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/siderolabs/talos/issues/7287 Subnet routers · Tailscale Docs, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1019/subnets Configure a subnet router · Tailscale Docs, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1406/quick-guide-subnets README.md - michaelbeaumont/k8rn - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/michaelbeaumont/k8rn/blob/main/README.md Slow direct connection, get better result with UDP + MTU tweak : r/Tailscale - Reddit, accesso eseguito il giorno gennaio 6, 2026, https://www.reddit.com/r/Tailscale/comments/1p5dxtq/slow_direct_connection_get_better_result_with_udp/ PSA: Tailscale yields higher throughput if you lower the MTU - Reddit, accesso eseguito il giorno gennaio 6, 2026, https://www.reddit.com/r/Tailscale/comments/1ismen1/psa_tailscale_yields_higher_throughput_if_you/ Unable to lower the MTU · Issue #8219 · tailscale/tailscale - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/tailscale/tailscale/issues/8219 Site-to-site networking · Tailscale Docs, accesso eseguito il giorno gennaio 6, 2026, https://tailscale.com/kb/1214/site-to-site Using Tailscale and subnet routers to access legacy devices - Ryan Freeman, accesso eseguito il giorno gennaio 6, 2026, https://ryanfreeman.dev/writing/using-tailscale-and-subnet-routers-to-access-legacy-devices Check Linux IP Forwarding for Access Server Routing - OpenVPN, accesso eseguito il giorno gennaio 6, 2026, https://openvpn.net/as-docs/faq-ip-forwarding-on-linux.html Setting loadBalancer.acceleration=native causes Cilium Status to report unexpected end of JSON input #35873 - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/cilium/cilium/issues/35873 2.5. Turning on Packet Forwarding | Load Balancer Administration - Red Hat Documentation, accesso eseguito il giorno gennaio 6, 2026, https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/load_balancer_administration/s1-lvs-forwarding-vsa Sysctl: net.ipv4.ip_forward - Linux Audit, accesso eseguito il giorno gennaio 6, 2026, https://linux-audit.com/kernel/sysctl/net/net.ipv4.ip_forward/ Rootless podman without privileged flag on talos/Setting max_user_namespaces · Issue #4385 - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/talos-systems/talos/issues/4385 Set Up a Tailscale Exit Node and Subnet Router on an Ubuntu 24.04 VPS - Onidel, accesso eseguito il giorno gennaio 6, 2026, https://onidel.com/blog/setup-tailscale-exit-node-ubuntu Configuring tailscale subnet router using a Linux box and OpnSense : r/homelab - Reddit, accesso eseguito il giorno gennaio 6, 2026, https://www.reddit.com/r/homelab/comments/18zds4l/configuring_tailscale_subnet_router_using_a_linux/ OpenZiti meets Talos Linux!, accesso eseguito il giorno gennaio 6, 2026, https://openziti.discourse.group/t/openziti-meets-talos-linux/2988 Is there a better way than system extensions to run simple commands on boot as root? · siderolabs talos · Discussion #9857 - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/siderolabs/talos/discussions/9857 hcloud-talos/terraform-hcloud-talos: This repository contains a Terraform module for creating a Kubernetes cluster with Talos in the Hetzner Cloud. - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/hcloud-talos/terraform-hcloud-talos How I Setup Talos Linux. My journey to building a secure… | by Pedro Chang | Medium, accesso eseguito il giorno gennaio 6, 2026, https://medium.com/@pedrotychang/how-i-setup-talos-linux-bc2832ec87cc Talos VM Setup on macOS ARM64 with QEMU #9799 - GitHub, accesso eseguito il giorno gennaio 6, 2026, https://github.com/siderolabs/talos/discussions/9799 ","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/guides/talos-linux-tailscale-guide/","section":"Guide","summary":"","title":"Architettura e implementazione di Tailscale su Talos Linux: Analisi tecnica e risoluzione delle criticità operative","type":"guides"},{"content":"L\u0026rsquo;evoluzione delle infrastrutture IT verso paradigmi completamente dichiarativi e immutabili ha trovato nel binomio composto da Talos OS e Kubernetes una delle espressioni più avanzate. Tuttavia, l\u0026rsquo;adozione di un sistema operativo immutabile e privo di shell introduce sfide significative quando si rende necessaria l\u0026rsquo;integrazione di soluzioni di archiviazione a blocchi distribuita come Longhorn. Questa relazione tecnica analizza in modo esaustivo l\u0026rsquo;intero ciclo di vita dell\u0026rsquo;installazione, partendo dalla configurazione dell\u0026rsquo;hypervisor Proxmox VE, passando per la personalizzazione di Talos OS tramite estensioni di sistema, fino alla messa in produzione di Longhorn, con un focus particolare sulle ottimizzazioni delle prestazioni e sulla risoluzione delle problematiche di rete e di montaggio.\nConfigurazione dell\u0026rsquo;Hypervisor: Proxmox VE come Fondazione del Cluster # La stabilità di un cluster Kubernetes distribuito dipende in larga misura dalla corretta configurazione delle macchine virtuali sottostanti. Proxmox VE offre una flessibilità notevole, ma richiede impostazioni specifiche per soddisfare i requisiti rigorosi di Talos OS e le esigenze di input/output (I/O) di Longhorn.\nRequisiti della Microarchitettura CPU e Istruzioni Necessarie # A partire dalla versione 1.0, Talos OS richiede esplicitamente la microarchitettura x86-64-v2. Questo requisito è fondamentale poiché molte installazioni predefinite di Proxmox utilizzano il tipo di CPU kvm64 per massimizzare la compatibilità durante la migrazione live, ma questo modello manca di istruzioni critiche come cx16, popcnt e sse4.2, necessarie per il corretto funzionamento del kernel e dei binari di Talos.1\nLa scelta del tipo di processore all\u0026rsquo;interno di Proxmox influenza direttamente la capacità di Longhorn di eseguire operazioni di crittografia e di gestione dei volumi. L\u0026rsquo;impostazione consigliata è host, che espone tutte le capacità della CPU fisica alla macchina virtuale, garantendo le massime prestazioni per il motore di archiviazione.1 Se la migrazione live tra nodi con CPU diverse è un requisito, l\u0026rsquo;amministratore deve configurare manualmente i flag CPU nel file di configurazione della VM /etc/pve/qemu-server/\u0026lt;vmid\u0026gt;.conf aggiungendo la stringa args: -cpu kvm64,+cx16,+lahf_lm,+popcnt,+sse3,+ssse3,+sse4.1,+sse4.2.1\nParametro CPU Valore Consigliato Impatto Tecnico Tipo Processore host Supporto nativo x86-64-v2 e prestazioni crittografiche superiori.1 Core (Control Plane) Minimo 2 Necessari per la gestione dei processi di sistema e etcd.1 Core (Worker Node) 4 o più Supporto per il polling del motore Longhorn V2 e carichi di lavoro.4 NUMA Abilitato Ottimizzazione dell\u0026rsquo;accesso alla memoria su server multi-socket.6 Gestione della Memoria e del Controller SCSI # Talos OS è progettato per operare interamente in RAM durante le fasi critiche, il che rende la gestione della memoria un punto di potenziale fallimento. Una limitazione nota di Talos riguarda il mancato supporto per l\u0026rsquo;hot-plug della memoria. Se questa funzione è abilitata in Proxmox, Talos non sarà in grado di rilevare correttamente la memoria totale allocata, portando a errori di installazione per memoria insufficiente.1 La dotazione minima di RAM deve essere di 2 GB per i nodi del piano di controllo e preferibilmente 8 GB per i nodi worker che ospitano Longhorn, dato che quest\u0026rsquo;ultimo richiede risorse per la replica dei dati e la gestione dei pod di gestione delle istanze.4\nPer quanto riguarda l\u0026rsquo;archiviazione, il controller VirtIO SCSI single rappresenta la scelta d\u0026rsquo;elezione. Questa configurazione permette l\u0026rsquo;utilizzo di thread I/O dedicati per ogni disco virtuale, riducendo la contesa tra i processi e migliorando la latenza, un fattore critico quando Longhorn deve replicare blocchi di dati su più nodi attraverso la rete.6 L\u0026rsquo;abilitazione dell\u0026rsquo;opzione Discard sul disco virtuale è altrettanto essenziale per permettere al sistema operativo guest di inviare comandi TRIM, assicurando che lo storage sottostante (specialmente se basato su ZFS o LVM-thin in Proxmox) possa recuperare lo spazio non più utilizzato.7\nProvisioning di Talos OS: Immutabilità e Personalizzazione # La natura immutabile di Talos OS implica che non sia possibile installare software o driver dopo il boot tramite i canali tradizionali come apt o yum. Pertanto, la preparazione dell\u0026rsquo;immagine di installazione deve includere preventivamente tutti gli strumenti necessari per Longhorn.\nUtilizzo dell\u0026rsquo;Image Factory e Estensioni di Sistema # Longhorn dipende da binari e demoni che risiedono solitamente a livello di host, come iscsid per la connessione ai volumi e vari strumenti di gestione del filesystem. In Talos, queste dipendenze vengono soddisfatte attraverso le \u0026ldquo;System Extensions\u0026rdquo;. L\u0026rsquo;Image Factory di Sidero Labs permette di generare ISO e installer personalizzati che integrano queste estensioni direttamente nell\u0026rsquo;immagine di sistema.1\nLe estensioni indispensabili per un\u0026rsquo;installazione funzionante di Longhorn includono:\nsiderolabs/iscsi-tools: fornisce il demone iscsid e l\u0026rsquo;utilità iscsiadm, necessari per mappare i volumi Longhorn come dispositivi a blocchi locali.4 siderolabs/util-linux-tools: include strumenti come fstrim, utilizzati per la manutenzione del filesystem e la riduzione dell\u0026rsquo;occupazione di spazio dei volumi.4 siderolabs/qemu-guest-agent: fondamentale in ambiente Proxmox per permettere all\u0026rsquo;hypervisor di comunicare con il guest, facilitando arresti puliti e la corretta visualizzazione degli indirizzi IP nella console di gestione.1 Il processo di generazione dell\u0026rsquo;immagine produce un ID schematico unico, che garantisce che ogni nodo nel cluster sia configurato in modo identico, eliminando alla radice il problema della deriva della configurazione (configuration drift).9\nBootstrapping del Cluster e Configurazione Dichiarativa # Una volta avviate le VM Proxmox con la ISO personalizzata, il cluster entra in una modalità di manutenzione in attesa della configurazione. L\u0026rsquo;interazione avviene esclusivamente tramite l\u0026rsquo;utility talosctl dal terminale dell\u0026rsquo;amministratore. La generazione dei file di configurazione avviene tramite il comando talosctl gen config, specificando l\u0026rsquo;endpoint del piano di controllo.1\nDurante la fase di modifica dei file controlplane.yaml e worker.yaml, è fondamentale verificare l\u0026rsquo;identificativo del disco di installazione. In Proxmox, a seconda del controller utilizzato, il disco potrebbe apparire come /dev/sda o /dev/vda. L\u0026rsquo;utilizzo del comando talosctl get disks --insecure --nodes \u0026lt;IP\u0026gt; permette di identificare con certezza il dispositivo corretto prima di applicare la configurazione.1\nIl bootstrap del cluster segue una sequenza rigorosa:\nApplicazione della configurazione al nodo del piano di controllo: talosctl apply-config --insecure --nodes $CP_IP --file controlplane.yaml.1 Inizializzazione del cluster (Bootstrap ETCD): talosctl bootstrap --nodes $CP_IP.1 Recupero del file kubeconfig per l\u0026rsquo;accesso amministrativo a Kubernetes tramite kubectl.1 Integrazione di Longhorn: Requisiti e Architettura dei Volumi # L\u0026rsquo;installazione di Longhorn su Talos richiede un\u0026rsquo;attenzione meticolosa alla gestione dei privilegi e alla visibilità dei percorsi del filesystem, poiché Talos isola i processi del piano di controllo e i servizi di sistema in namespace di montaggio separati.\nModuli del Kernel e Parametri di Macchina # Longhorn necessita che determinati moduli del kernel siano caricati per gestire i dispositivi a blocchi virtuali e la comunicazione iSCSI. Poiché Talos non carica tutti i moduli per impostazione predefinita, è necessario dichiararli esplicitamente nella sezione kernel della configurazione di macchina dei nodi worker.11\nI moduli richiesti includono nbd (Network Block Device), iscsi_tcp, iscsi_generic e configfs.11 La loro inclusione assicura che il manager di Longhorn possa creare correttamente i dispositivi sotto /dev, che verranno poi montati dai pod delle applicazioni.\nYAML\nmachine:\nkernel:\nmodules:\n- name: nbd\n- name: iscsi_tcp\n- name: iscsi_generic\n- name: configfs\nQuesto frammento di configurazione, una volta applicato, forza il nodo al riavvio per caricare i moduli necessari, rendendo il sistema pronto per l\u0026rsquo;archiviazione distribuita.11\nPropagazione dei Montaggi e Kubelet Extra Mounts # Uno degli ostacoli tecnici più comuni nell\u0026rsquo;installazione di Longhorn su Talos è l\u0026rsquo;isolamento del processo kubelet. In Talos, kubelet gira all\u0026rsquo;interno di un contenitore e, per impostazione predefinita, non ha visibilità sui dischi montati dall\u0026rsquo;utente o sulle directory specifiche dell\u0026rsquo;host necessarie per le operazioni CSI (Container Storage Interface).10\nPer risolvere questo problema, è necessario configurare gli extraMounts per il kubelet. Questa impostazione assicura che il percorso dove Longhorn memorizza i dati sia mappato all\u0026rsquo;interno del namespace del kubelet con la propagazione dei montaggi impostata su rshared.4 Senza questa configurazione, Kubernetes non sarebbe in grado di collegare i volumi Longhorn ai pod delle applicazioni, risultando in errori di tipo \u0026ldquo;MountVolume.SetUp failed\u0026rdquo;.14\nPercorso Host Percorso Kubelet Opzioni di Montaggio Funzione /var/lib/longhorn /var/lib/longhorn bind, rshared, rw Percorso predefinito per i dati dei volumi.15 /var/mnt/sdb /var/mnt/sdb bind, rshared, rw Utilizzato se si impiega un secondo disco dedicato.4 La propagazione rshared è fondamentale: essa permette a un montaggio effettuato all\u0026rsquo;interno di un contenitore (come il plugin CSI di Longhorn) di essere visibile all\u0026rsquo;host e, di conseguenza, ad altri contenitori gestiti dal kubelet.15\nStrategia di Archiviazione: Dischi Secondari e Persistenza # Sebbene Longhorn possa tecnicamente archiviare i dati sulla partizione EPHEMERAL di Talos, questa pratica è sconsigliata per ambienti di produzione. La partizione di sistema di Talos è soggetta a modifiche durante gli aggiornamenti del sistema operativo, e l\u0026rsquo;utilizzo di un disco secondario offre una netta separazione tra i dati applicativi e il sistema operativo immutabile.4\nVantaggi dell\u0026rsquo;Utilizzo di Dischi Dedicati in Proxmox # L\u0026rsquo;aggiunta di un secondo disco virtuale (ad esempio /dev/sdb) in Proxmox per ogni nodo worker offre diversi vantaggi architetturali. In primo luogo, isola il traffico di I/O dello storage dal traffico di sistema, riducendo la latenza per le applicazioni sensibili. In secondo luogo, permette una gestione semplificata dello spazio: se un nodo esaurisce lo spazio per i volumi Longhorn, è possibile espandere il disco virtuale in Proxmox senza interferire con le partizioni critiche di Talos.4\nPer implementare questa strategia, la configurazione di Talos deve includere istruzioni per formattare e montare il disco aggiuntivo all\u0026rsquo;avvio:\nYAML\nmachine:\ndisks:\n- device: /dev/sdb\npartitions:\n- mountpoint: /var/mnt/sdb\nUna volta che il disco è montato su /var/mnt/sdb, questo percorso deve essere comunicato a Longhorn durante l\u0026rsquo;installazione tramite il file di valori di Helm, impostando defaultDataPath su tale directory.4\nAnalisi dei Formati Disco: RAW vs QCOW2 # La scelta del formato del file immagine in Proxmox ha un impatto diretto sulle prestazioni di Longhorn, che implementa già internamente meccanismi di replica e snapshotting.\nCaratteristica RAW QCOW2 Prestazioni Massime (nessun overhead di metadati).18 Inferiori (overhead dovuto al Copy-on-Write).8 Gestione Spazio Occupa l\u0026rsquo;intero spazio allocato (se non supportato dai buchi nel FS).19 Supporta il thin provisioning nativo.8 Snapshot Hypervisor Non supportati nativamente su storage a file.19 Supportati nativamente.8 In un\u0026rsquo;architettura dove Longhorn gestisce la ridondanza a livello di cluster, l\u0026rsquo;utilizzo del formato RAW è spesso preferito per evitare il fenomeno del \u0026ldquo;doppio snapshotting\u0026rdquo; e ridurre la latenza di scrittura.18 Tuttavia, se l\u0026rsquo;infrastruttura Proxmox sottostante è basata su ZFS, è cruciale evitare l\u0026rsquo;uso di QCOW2 sopra ZFS per prevenire un\u0026rsquo;amplificazione massiccia delle scritture, che degraderebbe rapidamente le prestazioni degli SSD.20\nImplementazione e Configurazione Software di Longhorn # Dopo aver preparato l\u0026rsquo;infrastruttura Talos, l\u0026rsquo;installazione di Longhorn avviene tipicamente tramite Helm o operatori GitOps come Flux o ArgoCD.\nSicurezza e Namespace Privilegiato # A causa delle operazioni a basso livello che deve compiere, Longhorn richiede privilegi elevati. Con l\u0026rsquo;introduzione dei Pod Security Standards in Kubernetes, è imperativo etichettare correttamente il namespace longhorn-system per permettere l\u0026rsquo;esecuzione di pod in modalità privilegiata.11\nL\u0026rsquo;applicazione del seguente manifesto garantisce che i componenti di Longhorn non vengano bloccati dal controller di ammissione:\nYAML\napiVersion: v1\nkind: Namespace\nmetadata:\nname: longhorn-system\nlabels:\npod-security.kubernetes.io/enforce: privileged\npod-security.kubernetes.io/audit: privileged\npod-security.kubernetes.io/warn: privileged\nQuesto passaggio è critico: senza di esso, i pod del manager di Longhorn o i plugin CSI non riuscirebbero ad avviarsi, lasciando il sistema in uno stato di attesa perpetua.11\nParametri di Installazione Helm Consigliati # Durante l\u0026rsquo;installazione tramite Helm, alcuni parametri devono essere adattati per l\u0026rsquo;ambiente Talos-Proxmox. L\u0026rsquo;uso di un file values.yaml personalizzato permette di automatizzare queste impostazioni:\ndefaultSettings.defaultDataPath: impostato sul percorso del disco secondario (es. /var/mnt/sdb).4 defaultSettings.numberOfReplicas: solitamente impostato a 3 per garantire l\u0026rsquo;alta affidabilità.4 defaultSettings.createDefaultDiskLabeledNodes: se impostato su true, permette di selezionare solo nodi specifici come nodi di archiviazione tramite etichette Kubernetes.4 Inoltre, per evitare problemi durante gli aggiornamenti in ambienti Talos, si raccomanda spesso di disabilitare il preUpgradeChecker se questo causa blocchi inspiegabili dovuti alla natura immutabile del filesystem host.11\nOttimizzazione delle Prestazioni e Networking # L\u0026rsquo;archiviazione distribuita è intrinsecamente dipendente dalle prestazioni della rete. In un ambiente virtualizzato Proxmox, la configurazione dei bridge e delle interfacce VirtIO può fare la differenza tra un sistema reattivo e uno afflitto da timeout.\nProblematiche di MTU e Frammentazione dei Pacchetti # Un errore comune nelle configurazioni Proxmox riguarda il mismatch dell\u0026rsquo;MTU (Maximum Transmission Unit). Se il bridge fisico di Proxmox è configurato per i Jumbo Frames (MTU 9000) per ottimizzare il traffico storage, ma le interfacce delle VM Talos sono lasciate al valore predefinito di 1500, si verificherà una frammentazione dei pacchetti che aumenterà drasticamente l\u0026rsquo;uso della CPU e ridurrà il throughput dei volumi Longhorn.23\nLa coerenza dell\u0026rsquo;MTU deve essere garantita lungo l\u0026rsquo;intero percorso:\nSwitch fisico e NIC del server Proxmox. Linux Bridge (vmbr0) o OVS Bridge in Proxmox. Configurazione di rete nel file YAML di Talos OS. Configurazione del CNI (es. Cilium o Flannel) all\u0026rsquo;interno di Kubernetes.23 In alcune versioni recenti di Proxmox (8.2+), sono stati segnalati bug relativi alla gestione dell\u0026rsquo;MTU con i driver VirtIO, che possono causare il blocco delle connessioni TCP durante trasferimenti intensivi. In questi casi, forzare l\u0026rsquo;MTU a 1500 su tutti i livelli può risolvere instabilità inspiegabili, a scapito di una leggera riduzione dell\u0026rsquo;efficienza.24\nMotore V2 e SPDK: Requisiti di Risorse Elevati # Longhorn ha introdotto un nuovo motore di archiviazione (V2) basato su SPDK (Storage Performance Development Kit). Sebbene offra prestazioni superiori, i requisiti per i nodi Talos aumentano notevolmente. Il motore V2 utilizza driver in modalità polling invece che basati su interrupt, il che significa che i processi di gestione delle istanze consumeranno il 100% di un core CPU dedicato per minimizzare la latenza.5\nRequisiti per il motore V2 su Talos:\nHuge Pages: è necessario configurare l\u0026rsquo;allocazione di pagine di memoria grandi (2 MiB) tramite sysctl nella configurazione di Talos (es. 1024 pagine per un totale di 2 GiB).5 Istruzioni CPU: il supporto per SSE4.2 è obbligatorio, rinforzando la necessità del tipo CPU host in Proxmox.5 L\u0026rsquo;attivazione del motore V2 deve essere una scelta ponderata in base al carico di lavoro: per database ad alte prestazioni è consigliata, mentre per carichi di lavoro generici il motore V1 rimane più efficiente in termini di consumo di risorse.5\nGestione Operativa: Aggiornamenti, Backup e Troubleshooting # Il mantenimento di un cluster Longhorn su Talos richiede una comprensione dei flussi di lavoro specifici per sistemi immutabili.\nGestione degli Aggiornamenti di Talos OS # L\u0026rsquo;aggiornamento di un nodo Talos comporta il riavvio della macchina virtuale con una nuova immagine. Durante questo processo, Longhorn deve gestire la temporanea indisponibilità di una replica.\nProcedura di aggiornamento sicuro:\nVerificare che tutti i volumi Longhorn siano in stato \u0026ldquo;Healthy\u0026rdquo; e abbiano il numero completo di repliche. Eseguire l\u0026rsquo;aggiornamento di un nodo alla volta utilizzando talosctl upgrade. Attendere che il nodo rientri nel cluster Kubernetes e che Longhorn completi la ricostruzione (rebuilding) delle repliche prima di procedere al nodo successivo.9 È fondamentale che l\u0026rsquo;immagine utilizzata per l\u0026rsquo;aggiornamento contenga le stesse estensioni di sistema (iscsi-tools) dell\u0026rsquo;immagine originale, altrimenti Longhorn perderà la capacità di comunicare con i dischi al primo riavvio.9\nBackup dei Dati e Disaster Recovery # Sebbene Proxmox permetta di eseguire backup dell\u0026rsquo;intera VM, per i dati contenuti nei volumi Longhorn è preferibile utilizzare la funzione di backup nativa della soluzione. Longhorn può esportare snapshot verso un archivio esterno (S3 o NFS).11\nIn ambiente Talos, se si sceglie NFS come target di backup, è necessario assicurarsi che l\u0026rsquo;estensione per il client NFSv4 sia presente nell\u0026rsquo;immagine di sistema o che il supporto kernel sia abilitato.15 La configurazione di un BackupTarget predefinito è una best practice che evita errori di inizializzazione dei volumi in alcune versioni di Longhorn.11\nRisoluzione dei Problemi Comuni # Un problema frequente riguarda l\u0026rsquo;impossibilità per i nodi di unirsi al cluster dopo l\u0026rsquo;applicazione della configurazione, spesso manifestandosi con uno stato di \u0026ldquo;Installing\u0026rdquo; infinito nella console Proxmox. Questo è solitamente dovuto a problemi di rete (gateway errato, mancanza di DHCP o DNS non funzionante) che impediscono a Talos di scaricare l\u0026rsquo;immagine di installazione finale.28 L\u0026rsquo;uso di indirizzi IP statici prenotati tramite MAC address nel server DHCP è la soluzione raccomandata per garantire coerenza durante i molteplici riavvii del processo di installazione.3\nUn altro errore critico è il \u0026ldquo;Missing Kind\u0026rdquo; durante l\u0026rsquo;uso di talosctl patch. Questo accade se il file di patch YAML non include le intestazioni apiVersion e kind. Talos richiede che ogni patch sia un oggetto Kubernetes valido o che la struttura rispetti esattamente lo schema previsto per la risorsa specifica.9\nModellazione delle Prestazioni I/O in Ambiente Virtualizzato # Il rendimento di Longhorn può essere analizzato matematicamente considerando le latenze introdotte dai vari strati di astrazione. La latenza totale di scrittura ($L_{total}$) in una configurazione con replicazione sincrona può essere espressa come:\n$$L\\_{total} \\\\approx L\\_{virt} \\+ L\\_{fs\\\\\\_guest} \\+ \\\\max(L\\_{net\\\\\\_RTT} \\+ L\\_{io\\\\\\_remote})$$\nDove:\n$L_{virt}$: latenza introdotta dall\u0026rsquo;hypervisor Proxmox e dal driver VirtIO. $L_{fs\\_guest}$: overhead del filesystem all\u0026rsquo;interno della VM (es. XFS o Ext4). $L_{net\\_RTT}$: tempo di andata e ritorno della rete tra i nodi worker per la replica del blocco. $L_{io\\_remote}$: latenza di scrittura sul disco fisico del nodo remoto. In una rete a 1 Gbps, $L_{net\\_RTT}$ può diventare il collo di bottiglia principale, specialmente sotto carico pesante. L\u0026rsquo;adozione di una rete a 10 Gbps riduce drasticamente questo valore, permettendo a Longhorn di avvicinarsi alle prestazioni di un\u0026rsquo;archiviazione locale.23\nSintesi e Raccomandazioni Finali # L\u0026rsquo;implementazione di Longhorn su un cluster Kubernetes basato su Talos OS e Proxmox rappresenta una soluzione di eccellenza per la gestione di carichi di lavoro stateful in ambienti moderni. La chiave del successo risiede nella preparazione meticolosa dello strato infrastrutturale e nella comprensione della natura dichiarativa di Talos.\nSi raccomandano le seguenti azioni per una messa in produzione ottimale:\nPersonalizzazione Preventiva: Integrare sempre iscsi-tools e util-linux-tools nelle immagini Talos tramite l\u0026rsquo;Image Factory per evitare problemi di runtime.4 Configurazione Hardware: Utilizzare il tipo CPU host e controller SCSI dedicati con thread I/O abilitati in Proxmox.1 Separazione dei Dati: Implementare sempre dischi secondari per l\u0026rsquo;archiviazione dei dati Longhorn, evitando l\u0026rsquo;uso della partizione di sistema.4 Monitoraggio della Rete: Garantire la coerenza dell\u0026rsquo;MTU su tutti i livelli della rete virtuale e fisica per prevenire degradazioni delle prestazioni.23 Sicurezza Dichiarativa: Gestire tutte le configurazioni, inclusi i montaggi extra e i moduli del kernel, tramite file YAML versionati, sfruttando appieno la filosofia GitOps supportata da Talos.29 Questa architettura, sebbene richieda una curva di apprendimento iniziale superiore rispetto alle distribuzioni Linux tradizionali, offre garanzie di sicurezza e riproducibilità che la rendono ideale per le sfide della moderna ingegneria del software.\nBibliografia # Proxmox - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno dicembre 30, 2025, https://docs.siderolabs.com/talos/v1.9/platform-specific-installations/virtualized-platforms/proxmox Talos on Proxmox, accesso eseguito il giorno dicembre 30, 2025, https://homelab.casaursus.net/talos-on-proxmox-3/ Talos with Kubernetes on Proxmox - Secsys, accesso eseguito il giorno dicembre 30, 2025, https://secsys.pages.dev/posts/talos/ Storage Solution: Longhorn, accesso eseguito il giorno dicembre 30, 2025, https://www.xelon.ch/en/docs/storage-solution-longhorn Longhorn | Prerequisites, accesso eseguito il giorno dicembre 30, 2025, https://longhorn.io/docs/1.10.1/v2-data-engine/prerequisites/ Best CPU Settings for a VM? I want best Per Thread Performance from 13,900k : r/Proxmox, accesso eseguito il giorno dicembre 30, 2025, https://www.reddit.com/r/Proxmox/comments/16i7i2w/best_cpu_settings_for_a_vm_i_want_best_per_thread/ Windows 2022 guest best practices - Proxmox VE, accesso eseguito il giorno dicembre 30, 2025, https://pve.proxmox.com/wiki/Windows_2022_guest_best_practices Using the QCOW2 disk format in Proxmox - 4sysops, accesso eseguito il giorno dicembre 30, 2025, https://4sysops.com/archives/using-the-qcow2-disk-format-in-proxmox/ Improve Documentation for Longhorn and System Extensions \u0026hellip;, accesso eseguito il giorno dicembre 30, 2025, https://github.com/siderolabs/talos/issues/12064 Install Longhorn on Talos Kubernetes - HackMD, accesso eseguito il giorno dicembre 30, 2025, https://hackmd.io/@QI-AN/Install-Longhorn-on-Talos-Kubernetes Installing Longhorn on Talos Linux: A Step-by-Step Guide - Phin3has Tech Blog, accesso eseguito il giorno dicembre 30, 2025, https://phin3has.blog/posts/talos-longhorn/ A collection of scripts for creating and managing kubernetes clusters on talos linux - GitHub, accesso eseguito il giorno dicembre 30, 2025, https://github.com/joshrnoll/talos-scripts Automating Talos Installation on Proxmox with Packer and Terraform, Integrating Cilium and Longhorn | Suraj Remanan, accesso eseguito il giorno dicembre 30, 2025, https://surajremanan.com/posts/automating-talos-installation-on-proxmox-with-packer-and-terraform/ Why are Kubelet extra mounts for Longhorn needed? · siderolabs talos · Discussion #9674, accesso eseguito il giorno dicembre 30, 2025, https://github.com/siderolabs/talos/discussions/9674 Longhorn | Quick Installation, accesso eseguito il giorno dicembre 30, 2025, https://longhorn.io/docs/1.10.1/deploy/install/ Kubernetes - Reddit, accesso eseguito il giorno dicembre 30, 2025, https://www.reddit.com/r/kubernetes/hot/ Longhorn | Multiple Disk Support, accesso eseguito il giorno dicembre 30, 2025, https://longhorn.io/docs/1.10.1/nodes-and-volumes/nodes/multidisk/ Which is better image format, raw or qcow2, to use as a baseimage for other VMs?, accesso eseguito il giorno dicembre 30, 2025, https://serverfault.com/questions/677639/which-is-better-image-format-raw-or-qcow2-to-use-as-a-baseimage-for-other-vms Raw vs Qcow2 Image | Storware BLOG, accesso eseguito il giorno dicembre 30, 2025, https://storware.eu/blog/raw-vs-qcow2-image/ RAW or QCOW2 ? : r/Proxmox - Reddit, accesso eseguito il giorno dicembre 30, 2025, https://www.reddit.com/r/Proxmox/comments/1jh4rlp/raw_or_qcow2/ Performance Tweaks - Proxmox VE, accesso eseguito il giorno dicembre 30, 2025, https://pve.proxmox.com/wiki/Performance_Tweaks Longhorn - Rackspace OpenStack Documentation, accesso eseguito il giorno dicembre 30, 2025, https://docs.rackspacecloud.com/storage-longhorn/ Strange Issue Using Virtio on 10Gb Network Adapters | Page 2 | Proxmox Support Forum, accesso eseguito il giorno dicembre 30, 2025, https://forum.proxmox.com/threads/strange-issue-using-virtio-on-10gb-network-adapters.167666/page-2 qemu virtio issues after upgrade to 9 - Proxmox Support Forum, accesso eseguito il giorno dicembre 30, 2025, https://forum.proxmox.com/threads/qemu-virtio-issues-after-upgrade-to-9.169625/ working interface fails when added to bridge - Proxmox Support Forum, accesso eseguito il giorno dicembre 30, 2025, https://forum.proxmox.com/threads/working-interface-fails-when-added-to-bridge.106271/ qemu virtio issues after upgrade to 9 | Page 2 - Proxmox Support Forum, accesso eseguito il giorno dicembre 30, 2025, https://forum.proxmox.com/threads/qemu-virtio-issues-after-upgrade-to-9.169625/page-2 Installing Longhorn on Talos With Helm - Josh Noll, accesso eseguito il giorno dicembre 30, 2025, https://joshrnoll.com/installing-longhorn-on-talos-with-helm/ Completely unable to configure Talos in a Proxmox VM · siderolabs \u0026hellip;, accesso eseguito il giorno dicembre 30, 2025, https://github.com/siderolabs/talos/discussions/9291 What Longhorn Talos Actually Does and When to Use It - hoop.dev, accesso eseguito il giorno dicembre 30, 2025, https://hoop.dev/blog/what-longhorn-talos-actually-does-and-when-to-use-it/ ","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/guides/talos-proxmox-longhorn/","section":"Guide","summary":"","title":"Architettura Tecnica e Implementazione di Longhorn su Kubernetes con Talos OS in Ambienti Virtualizzati Proxmox","type":"guides"},{"content":"L\u0026rsquo;evoluzione tecnologica dei data center domestici e delle infrastrutture aziendali ha portato alla nascita di soluzioni che sfidano i paradigmi tradizionali dell\u0026rsquo;amministrazione di sistema. In questo contesto, Talos OS si distingue non come una semplice distribuzione Linux, ma come una reinterpretazione radicale del sistema operativo progettato esclusivamente per Kubernetes. La sua natura immutabile, minimale e governata interamente tramite API rappresenta una soluzione ideale per chi desidera un ambiente Proxmox stabile, sicuro e privo del debito tecnico associato alla gestione manuale via SSH.1 Questa relazione esamina in profondità ogni aspetto necessario per portare un cluster Talos OS da zero a una configurazione di produzione su Proxmox VE, analizzando le complessità del networking, della persistenza dei dati e delle ottimizzazioni specifiche per l\u0026rsquo;hypervisor.\nFondamenti Architetturali di Talos OS e l\u0026rsquo;Approccio Immutabile # La filosofia alla base di Talos OS è centrata sull\u0026rsquo;eliminazione di tutto ciò che non è strettamente necessario all\u0026rsquo;esecuzione di Kubernetes. A differenza di una distribuzione Linux tradizionale, Talos non include una shell, non ha un gestore di pacchetti e non permette l\u0026rsquo;accesso SSH.1 Tutta la gestione avviene attraverso un\u0026rsquo;interfaccia gRPC protetta da mTLS (Mutual TLS), garantendo che ogni interazione con il sistema sia autenticata e crittografata alla base.2\nLa Struttura del File System e la Gestione dei Layer # L\u0026rsquo;architettura del file system di Talos è uno dei suoi tratti più distintivi e garantisce la resilienza del sistema contro la corruzione accidentale o gli attacchi malevoli. Il nucleo del sistema risiede in una partizione root di sola lettura, strutturata come un\u0026rsquo;immagine SquashFS.5 Durante il boot, questa immagine viene montata come un dispositivo di loop in memoria, creando una base immutabile. Sopra questa base, Talos sovrappone diversi livelli per gestire le necessità di runtime:\nLayer del File System Tipologia Funzione Principale Persistenza Rootfs SquashFS (Sola Lettura) Nucleo del sistema operativo e binari essenziali. Immutabile System tmpfs (In Memoria) File di configurazione temporanei come /etc/hosts. Ricreato al Boot Ephemeral XFS (Su Disco) Directory /var per container, immagini e dati di etcd. Persistente (Wipe su Reset) State Partizione dedicata Configurazione della macchina e identità del nodo. Persistente Questa separazione assicura che un errore di configurazione o un file temporaneo corrotto non compromettano mai l\u0026rsquo;integrità del sistema operativo sottostante. La partizione EPHEMERAL, montata in /var, ospita tutto ciò che Kubernetes richiede per funzionare: dal database di etcd nei nodi control plane alle immagini scaricate dal container runtime (containerd).5 Un aspetto critico del design di Talos è che i cambiamenti effettuati a file come /etc/resolv.conf o /etc/hosts sono gestiti tramite bind mount da una directory di sistema che viene completamente rigenerata a ogni riavvio, forzando l\u0026rsquo;amministratore a definire tali impostazioni esclusivamente nel file di configurazione dichiarativo.5\nIl Modello Operativo basato su API # Il passaggio da una gestione imperativa (comandi eseguiti via shell) a una dichiarativa (stato desiderato definito in YAML) è il cuore dell\u0026rsquo;esperienza Talos. Il tool talosctl agisce come il client primario che comunica con il demone apid in esecuzione su ogni nodo.5 Questa architettura permette di trattare i nodi del cluster come \u0026ldquo;bestiame\u0026rdquo; anziché \u0026ldquo;animali domestici\u0026rdquo; (cattle vs pets), dove la sostituzione di un nodo non funzionante è preferibile alla sua riparazione manuale. L\u0026rsquo;assenza di SSH riduce drasticamente la superficie di attacco, poiché elimina una delle porte d\u0026rsquo;ingresso più comuni per i malware e i movimenti laterali all\u0026rsquo;interno di una rete.2\nPianificazione dell\u0026rsquo;Infrastruttura su Proxmox VE # L\u0026rsquo;implementazione di Talos su Proxmox richiede una configurazione attenta delle macchine virtuali per garantire che i driver paravirtualizzati e le funzionalità di sicurezza siano sfruttati correttamente. Proxmox, basandosi su KVM/QEMU, offre un supporto eccellente per Talos, ma alcune impostazioni di default possono causare instabilità o prestazioni subottimali.8\nAllocazione delle Risorse e Requisiti Hardware # Sebbene Talos sia estremamente efficiente, Kubernetes richiede risorse minime per gestire il piano di controllo e i carichi di lavoro. La distribuzione delle risorse deve tenere conto non solo delle necessità attuali, ma anche della crescita futura del cluster.\nParametro Risorsa Control Plane (Minimo) Worker (Minimo) Produzione Consigliata vCPU 2 Core 1 Core 4+ Core (Control Plane) RAM 2 GB 2 GB 4-8 GB+ Storage (OS) 10 GB 10 GB 40-100 GB (NVMe/SSD) Tipo CPU x86-64-v2 o Superiore x86-64-v2 o Superiore Host (Passthrough) Un dettaglio tecnico fondamentale riguarda la microarchitettura della CPU. A partire dalla versione 1.0, Talos richiede il supporto al set di istruzioni x86-64-v2.10 In Proxmox, il tipo di CPU predefinito \u0026ldquo;kvm64\u0026rdquo; potrebbe non esporre le flag necessarie (come cx16, popcnt, o sse4.2). È caldamente raccomandato impostare il tipo di CPU della VM su \u0026ldquo;host\u0026rdquo; o utilizzare una configurazione personalizzata che abiliti esplicitamente queste estensioni per evitare il fallimento del boot o crash improvvisi durante l\u0026rsquo;esecuzione di carichi di lavoro intensivi.10\nConfigurazione della VM per Prestazioni Ottimali # Per un\u0026rsquo;integrazione fluida, la configurazione della macchina virtuale deve rispecchiare i moderni standard di virtualizzazione. L\u0026rsquo;uso di UEFI (OVMF) è preferibile rispetto al BIOS tradizionale, poiché permette una gestione più sicura del boot e supporta dischi di dimensioni maggiori con partizionamento GPT.10 Il chipset dovrebbe essere impostato su q35, che offre un supporto PCIe nativo superiore rispetto all\u0026rsquo;antiquato i440fx. Per quanto riguarda lo storage, l\u0026rsquo;uso del controller VirtIO SCSI Single con l\u0026rsquo;opzione \u0026ldquo;iothread\u0026rdquo; e l\u0026rsquo;abilitazione del supporto \u0026ldquo;discard\u0026rdquo; (se supportato dal backend fisico) assicura una gestione efficiente dello spazio disco e prestazioni di input/output elevate.6\nImplementazione: Dal Boot al Cluster Ready # Il processo di installazione di Talos non prevede un installer interattivo tradizionale. Il boot avviene tramite una ISO che carica il sistema operativo interamente in RAM, lasciando il nodo in attesa di una configurazione remota.6\nPreparazione della Workstation e talosctl # Prima di interagire con le VM su Proxmox, è necessario preparare l\u0026rsquo;ambiente di gestione locale. Il binario talosctl deve essere installato sulla workstation dell\u0026rsquo;amministratore. Questo strumento gestisce la generazione dei segreti, la configurazione dei nodi e il monitoraggio del cluster.6 È fondamentale che la versione di talosctl sia allineata con la versione di Talos OS che si intende distribuire per evitare incompatibilità nel protocollo gRPC.13\nBash\n# Esempio di installazione su macOS tramite Homebrew\nbrew install siderolabs/tap/talosctl\nUna volta scaricata l\u0026rsquo;immagine ISO di Talos (preferibilmente personalizzata tramite la Image Factory per includere i driver necessari), questa deve essere caricata nello storage ISO di Proxmox.6 Al primo avvio della VM, la console mostrerà un indirizzo IP temporaneo ottenuto tramite DHCP. Questo IP è il punto di ingresso per l\u0026rsquo;invio della configurazione iniziale.6\nGenerazione dei File di Configurazione e Gestione dei Segreti # La sicurezza di Talos si basa su un set di segreti generati localmente. Questi segreti non vengono mai trasmessi in chiaro e costituiscono la base per la firma dei certificati mTLS.14 La generazione della configurazione richiede la definizione dell\u0026rsquo;endpoint dell\u0026rsquo;API di Kubernetes, che solitamente coincide con l\u0026rsquo;IP del primo nodo master o con un IP virtuale gestito.6\nBash\n# Generazione dei segreti del cluster\ntalosctl gen secrets -o secrets.yaml\n# Generazione dei file di configurazione per nodi master e worker\ntalosctl gen config my-homelab-cluster https://192.168.1.50:6443 \\\n--with-secrets secrets.yaml \\\n--output-dir _out\nQuesta operazione genera tre componenti principali:\ncontrolplane.yaml: Contiene le definizioni per i nodi che gestiranno etcd e l\u0026rsquo;API server. worker.yaml: Contiene la configurazione per i nodi che eseguiranno i carichi di lavoro. talosconfig: Il file client che permette all\u0026rsquo;amministratore di autenticarsi presso il cluster.6 Applicazione della Configurazione e Bootstrap di etcd # L\u0026rsquo;applicazione della configurazione trasforma il nodo dalla modalità manutenzione a un sistema operativo installato e funzionale. È essenziale verificare il nome del disco target (es. /dev/sda o /dev/vda) prima dell\u0026rsquo;invio del file YAML.8 L\u0026rsquo;invio iniziale avviene in modalità \u0026ldquo;insicura\u0026rdquo; poiché i certificati mTLS non sono ancora stati distribuiti sul nodo.6\nBash\ntalosctl apply-config --insecure --nodes 192.168.1.10 --file _out/controlplane.yaml\nDopo il riavvio, il primo nodo control plane deve essere istruito per inizializzare il cluster Kubernetes tramite il comando di bootstrap. Questa operazione configura il database distribuito etcd e avvia i componenti principali del piano di controllo.6 Solo dopo questa fase, il cluster diventa consapevole di se stesso e l\u0026rsquo;endpoint dell\u0026rsquo;API Kubernetes diventa raggiungibile.\nNetworking: Ottimizzazione e Alta Affidabilità # Il networking è il settore in cui Talos esprime la sua massima flessibilità, permettendo all\u0026rsquo;amministratore di scegliere tra configurazioni standard e soluzioni avanzate basate su eBPF.17\nLa Scelta tra Flannel e Cilium # Di default, Talos utilizza Flannel come interfaccia di rete (CNI), una soluzione semplice che fornisce connettività pod-to-pod tramite un overlay VXLAN.17 Tuttavia, Flannel manca di supporto per le Network Policies e non offre funzionalità di osservabilità avanzate. Per un homelab orientato alla produzione, Cilium rappresenta il gold standard.17 Grazie all\u0026rsquo;uso intensivo di eBPF, Cilium può sostituire interamente il componente kube-proxy, migliorando drasticamente le prestazioni del routing e riducendo il carico sulla CPU eliminando le migliaia di regole iptables tipiche dei cluster Kubernetes tradizionali.19\nL\u0026rsquo;implementazione di Cilium richiede la disabilitazione esplicita del CNI di default e di kube-proxy nella configurazione di Talos.16 Questo viene fatto tramite un patch YAML applicato durante la generazione o la modifica della configurazione:\nYAML\ncluster:\nnetwork:\ncni:\nname: none\nproxy:\ndisabled: true\nLa rimozione di kube-proxy non è priva di sfide. Cilium deve essere configurato per gestire i servizi tramite l\u0026rsquo;eBPF host routing. Un dettaglio critico spesso trascurato è la necessità di impostare bpf.hostLegacyRouting=true se si riscontrano problemi di risoluzione DNS o connettività tra pod e host in particolari versioni del kernel.21\nAlta Affidabilità con kube-vip # In un cluster con più nodi control plane, è essenziale che l\u0026rsquo;API server sia raggiungibile attraverso un unico indirizzo IP stabile, anche se uno dei nodi master fallisce. Talos offre una funzionalità di IP virtuale (VIP) integrata che opera al livello 2 (ARP) o livello 3 (BGP).14 Questa funzione si basa sull\u0026rsquo;elezione del leader gestita direttamente da etcd.22\nUn\u0026rsquo;alternativa molto diffusa è kube-vip, che può operare sia come VIP per il control plane sia come Load Balancer per i servizi Kubernetes di tipo LoadBalancer.23 Kube-vip in modalità ARP elegge un leader tra i nodi che ospita l\u0026rsquo;IP virtuale. Per evitare colli di bottiglia, è possibile abilitare la \u0026ldquo;leader election per service\u0026rdquo;, permettendo a diversi nodi del cluster di ospitare diversi IP di servizio, distribuendo così il carico di rete.24\nCaratteristica VIP Nativo di Talos Kube-vip Control Plane HA Integrato, molto semplice da configurare. Supportato via Static Pods o DaemonSet. Service LoadBalancer Non supportato nativamente. Core feature, supporta diversi range di IP. Dipendenze Dipende direttamente da etcd. Dipende da Kubernetes o etcd. Configurazione Dichiarativa nel file controlplane.yaml. Richiede manifest Kubernetes o patches. L\u0026rsquo;uso del VIP nativo di Talos è consigliato per la sua semplicità nel garantire l\u0026rsquo;accesso all\u0026rsquo;API server, mentre kube-vip è la scelta ideale se si desidera esporre servizi interni (come un Ingress Controller) con IP statici della propria rete locale.23\nOttimizzazioni Proxmox e Personalizzazioni Avanzate # Per far sì che Talos si comporti come un cittadino di prima classe all\u0026rsquo;interno di Proxmox, è necessario implementare alcune ottimizzazioni che colmano il divario tra l\u0026rsquo;hypervisor e il sistema operativo minimale.\nQEMU Guest Agent e System Extensions # Il QEMU Guest Agent è un helper fondamentale che permette a Proxmox di gestire gli shutdown puliti e di leggere informazioni sulla rete direttamente dalla VM.4 Poiché Talos non ha un gestore di pacchetti, non è possibile installarlo con un comando apt install. La soluzione risiede nelle \u0026ldquo;System Extensions\u0026rdquo; di Talos.4 Utilizzando la(https://factory.talos.dev), è possibile generare un\u0026rsquo;immagine ISO o un installer che includa l\u0026rsquo;estensione siderolabs/qemu-guest-agent.4\nUna volta inclusa l\u0026rsquo;estensione, il servizio deve essere abilitato nel file di configurazione della macchina:\nYAML\nmachine:\nfeatures:\nqemuGuestAgent:\nenabled: true\nQuesto approccio garantisce che l\u0026rsquo;agente sia parte integrante dell\u0026rsquo;immagine immutabile del sistema, mantenendo la coerenza tra i nodi e facilitando le operazioni di manutenzione dall\u0026rsquo;interfaccia web di Proxmox.4\nPersistenza con iSCSI e Longhorn # In molti homelab, lo storage non è locale ma risiede su un NAS o una SAN. Per utilizzare soluzioni di storage distribuito come Longhorn o per montare volumi via iSCSI, Talos necessita dei relativi binari di sistema. Anche in questo caso, le estensioni giocano un ruolo cruciale. L\u0026rsquo;aggiunta di siderolabs/iscsi-tools e siderolabs/util-linux-tools fornisce i driver necessari al kernel e le utilità allo spazio utente per gestire i target iSCSI.4\nÈ inoltre necessario configurare il kubelet per permettere il montaggio di directory specifiche come /var/lib/longhorn con i permessi corretti (rshared, rw). Questo garantisce che i container che gestiscono lo storage abbiano accesso diretto all\u0026rsquo;hardware o ai volumi di rete senza interferenze da parte dei meccanismi di isolamento del sistema operativo.9\nCiclo di Vita: Aggiornamenti e Manutenzione Atomica # La manutenzione di un cluster Talos differisce radicalmente dai sistemi tradizionali. Gli aggiornamenti sono atomici e basati su immagini, riducendo quasi a zero il rischio di lasciare il sistema in uno stato intermedio incoerente.2\nStrategie di Update e Rollback # Talos implementa un sistema di aggiornamento A-B. Quando viene inviato un comando di upgrade, il sistema scarica la nuova immagine in una partizione inattiva, aggiorna il bootloader e si riavvia.26 Se il boot della nuova versione fallisce (ad esempio per una configurazione incompatibile con il nuovo kernel), Talos esegue automaticamente il rollback alla versione precedente.26 Questo meccanismo, preso in prestito dai sistemi operativi per smartphone (come Android), garantisce una disponibilità elevatissima.\nLe procedure consigliate prevedono l\u0026rsquo;aggiornamento un nodo alla volta, iniziando dai nodi worker e procedendo infine ai nodi control plane.13 Durante l\u0026rsquo;aggiornamento, Talos esegue automaticamente il \u0026ldquo;cordon\u0026rdquo; (impedisce nuovi pod) e il \u0026ldquo;drain\u0026rdquo; (sposta i pod esistenti) del nodo in Kubernetes, assicurando che i carichi di lavoro non subiscano interruzioni brusche.26\nMonitoraggio con la Dashboard Integrata # Per la diagnostica immediata, Talos mette a disposizione una dashboard integrata accessibile via talosctl. Questo strumento fornisce una panoramica dello stato di salute dei servizi core, dell\u0026rsquo;uso delle risorse e dei log di sistema, eliminando la necessità di installare agenti di monitoraggio esterni pesanti durante le fasi iniziali di troubleshooting.8\nBash\n# Avvio della dashboard per un nodo specifico\ntalosctl dashboard --nodes 192.168.1.10\nQuesta dashboard è particolarmente utile durante la fase di bootstrap per identificare perché un nodo non riesce a unirsi al cluster o perché etcd non raggiunge il quorum.8\nConsiderazioni Finali e Prospettive Future # L\u0026rsquo;adozione di Talos OS su Proxmox VE rappresenta una scelta di eccellenza per chiunque voglia costruire un\u0026rsquo;infrastruttura Kubernetes robusta e moderna. La combinazione della gestione dichiarativa, dell\u0026rsquo;immutabilità e dell\u0026rsquo;assenza di componenti legacy come SSH eleva lo standard di sicurezza e stabilità ben oltre ciò che è possibile ottenere con distribuzioni Linux generaliste.1\nLe sfide iniziali legate all\u0026rsquo;apprendimento di un nuovo paradigma sono ampiamente compensate dalla facilità con cui è possibile gestire gli aggiornamenti di sistema e dalla prevedibilità del comportamento del cluster. In un ecosistema dove la complessità di Kubernetes può spesso diventare travolgente, Talos offre un approccio \u0026ldquo;opinionated\u0026rdquo; che riduce le variabili in gioco, permettendo agli amministratori di concentrarsi sulle applicazioni piuttosto che sul sistema operativo. L\u0026rsquo;integrazione con Proxmox, supportata da VirtIO e dalle System Extensions, fornisce il perfetto equilibrio tra la potenza della virtualizzazione e l\u0026rsquo;agilità del Cloud Native, rendendo questa configurazione un punto di riferimento per il settore degli homelab professionali e delle infrastrutture edge.\nBibliografia # siderolabs/talos: Talos Linux is a modern Linux distribution built for Kubernetes. - GitHub, accesso eseguito il giorno dicembre 29, 2025, https://github.com/siderolabs/talos Introduction to Talos, the Kubernetes OS | Yet another enthusiast blog!, accesso eseguito il giorno dicembre 29, 2025, https://blog.yadutaf.fr/2024/03/14/introduction-to-talos-kubernetes-os/ Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno dicembre 29, 2025, https://docs.siderolabs.com/talos/v1.7/overview/what-is-talos Customizing Talos with Extensions - A cup of coffee, accesso eseguito il giorno dicembre 29, 2025, https://a-cup-of.coffee/blog/talos-ext/ Architecture - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno dicembre 29, 2025, https://docs.siderolabs.com/talos/v1.9/learn-more/architecture Talos with Kubernetes on Proxmox | Secsys, accesso eseguito il giorno dicembre 29, 2025, https://secsys.pages.dev/posts/talos/ Using Talos Linux and Kubernetes bootstrap on OpenStack - Safespring, accesso eseguito il giorno dicembre 29, 2025, https://www.safespring.com/blogg/2025/2025-03-talos-linux-on-openstack/ Proxmox - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno dicembre 29, 2025, https://docs.siderolabs.com/talos/v1.9/platform-specific-installations/virtualized-platforms/proxmox Creating a Kubernetes Cluster With Talos Linux on Tailscale | Josh \u0026hellip;, accesso eseguito il giorno dicembre 29, 2025, https://joshrnoll.com/creating-a-kubernetes-cluster-with-talos-linux-on-tailscale/ Talos on Proxmox, accesso eseguito il giorno dicembre 29, 2025, https://homelab.casaursus.net/talos-on-proxmox-3/ Talos ProxMox - k8s development - GitLab, accesso eseguito il giorno dicembre 29, 2025, https://gitlab.com/k8s_development/talos-proxmox Getting Started - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno dicembre 29, 2025, https://docs.siderolabs.com/talos/v1.9/getting-started/getting-started Upgrade Talos Linux and Kubernetes | Eric Daly\u0026rsquo;s Blog, accesso eseguito il giorno dicembre 29, 2025, https://blog.dalydays.com/post/kubernetes-talos-upgrades/ Production Clusters - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno dicembre 29, 2025, https://docs.siderolabs.com/talos/v1.7/getting-started/prodnotes How to Deploy a Kubernetes Cluster on Talos Linux - HOSTKEY, accesso eseguito il giorno dicembre 29, 2025, https://hostkey.com/blog/102-setting-up-a-k8s-cluster-on-talos-linux/ “ServiceLB” with cilium on Talos Linux | by Stefan Le Breton | Dev Genius, accesso eseguito il giorno dicembre 29, 2025, https://blog.devgenius.io/servicelb-with-cilium-on-talos-linux-8a290d524cb7 Kubernetes \u0026amp; Talos - Reddit, accesso eseguito il giorno dicembre 29, 2025, https://www.reddit.com/r/kubernetes/comments/1hs6bui/kubernetes_talos/ Installing Cilium and Multus on Talos OS for Advanced Kubernetes Networking, accesso eseguito il giorno dicembre 29, 2025, https://www.itguyjournals.com/installing-cilium-and-multus-on-talos-os-for-advanced-kubernetes-networking/ Deploy Cilium CNI - Sidero Documentation, accesso eseguito il giorno dicembre 29, 2025, https://docs.siderolabs.com/kubernetes-guides/cni/deploying-cilium Install in eBPF mode - Calico Documentation - Tigera.io, accesso eseguito il giorno dicembre 29, 2025, https://docs.tigera.io/calico/latest/operations/ebpf/install Validating Talos Linux Install and Maintenance Operations - Safespring, accesso eseguito il giorno dicembre 29, 2025, https://www.safespring.com/blogg/2025/2025-04-validating-talos-linux-install/ Virtual (shared) IP - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno dicembre 29, 2025, https://docs.siderolabs.com/talos/v1.8/networking/vip kube-vip: Documentation, accesso eseguito il giorno dicembre 29, 2025, https://kube-vip.io/ Kubernetes Load-Balancer service | kube-vip, accesso eseguito il giorno dicembre 29, 2025, https://kube-vip.io/docs/usage/kubernetes-services/ Qemu-guest-agent - Proxmox VE, accesso eseguito il giorno dicembre 29, 2025, https://pve.proxmox.com/wiki/Qemu-guest-agent Upgrading Talos Linux - Sidero Documentation, accesso eseguito il giorno dicembre 29, 2025, https://docs.siderolabs.com/talos/v1.8/configure-your-talos-cluster/lifecycle-management/upgrading-talos omni-docs/tutorials/upgrading-clusters.md at main - GitHub, accesso eseguito il giorno dicembre 29, 2025, https://github.com/siderolabs/omni-docs/blob/main/tutorials/upgrading-clusters.md Talos OS - Documentation \u0026amp; FAQ - HOSTKEY, accesso eseguito il giorno dicembre 29, 2025, https://hostkey.com/documentation/marketplace/kubernetes/talos/ ","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/guides/talos-proxmox-complete-guide/","section":"Guide","summary":"","title":"Architettura, Implementazione e Ottimizzazione di Talos OS su Proxmox: La Guida Definitiva per Homelab e Ambienti di Produzione","type":"guides"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/blowfish/","section":"Tags","summary":"","title":"Blowfish","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/controllers/","section":"Tags","summary":"","title":"Controllers","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/css/","section":"Tags","summary":"","title":"Css","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/distributed-storage/","section":"Tags","summary":"","title":"Distributed-Storage","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/hugo/","section":"Tags","summary":"","title":"Hugo","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/hypervisor/","section":"Tags","summary":"","title":"Hypervisor","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/immutable-os/","section":"Tags","summary":"","title":"Immutable-Os","type":"tags"},{"content":"L\u0026rsquo;adozione di Kubernetes in contesti on-premise ha introdotto la necessità di gestire il bilanciamento del carico in assenza dei servizi nativi offerti dai cloud provider pubblici. In questo scenario, la combinazione di Proxmox Virtual Environment (VE) come hypervisor, Talos OS come sistema operativo per i nodi del cluster e MetalLB come soluzione per il LoadBalancer di rete, rappresenta una delle architetture più robuste, sicure ed efficienti per la gestione di carichi di lavoro moderni.1 Proxmox fornisce la flessibilità della virtualizzazione enterprise combinando KVM e LXC, mentre Talos OS ridefinisce il concetto di sistema operativo per Kubernetes, eliminando la complessità delle distribuzioni Linux tradizionali a favore di un approccio immutabile e API-driven.1 MetalLB interviene per colmare il divario critico nel networking bare-metal, consentendo ai servizi Kubernetes di tipo LoadBalancer di ricevere indirizzi IP esterni raggiungibili dalla rete locale.3\nAnalisi Architetturale del Layer di Virtualizzazione Proxmox # La progettazione di un\u0026rsquo;infrastruttura Kubernetes su Proxmox richiede una comprensione profonda di come l\u0026rsquo;hypervisor gestisce le risorse e il networking. Proxmox si basa su concetti di networking Linux standard, utilizzando principalmente bridge virtuali (vmbr) per collegare le macchine virtuali alla rete fisica.5 Quando si pianifica l\u0026rsquo;installazione di MetalLB, la configurazione di questi bridge diventa il fondamento su cui poggia l\u0026rsquo;intera raggiungibilità dei servizi.\nConfigurazione del Networking Host # La best practice in Proxmox prevede l\u0026rsquo;utilizzo di bridge Linux (Linux Bridge) o, in scenari più complessi, di Open vSwitch. Per la maggior parte dei deployment Kubernetes, un bridge vmbr0 configurato correttamente è sufficiente, a patto che supporti il traffico Layer 2 necessario per le operazioni di ARP (Address Resolution Protocol) di MetalLB.4 Un aspetto spesso trascurato è la necessità di gestire VLAN diverse per isolare il traffico di gestione del cluster (Corosync), il traffico delle API di Kubernetes e il traffico dei dati applicativi.5 La latenza è un fattore critico per Corosync; pertanto, si raccomanda di non saturare il bridge principale con carichi di dati pesanti che potrebbero causare instabilità nel quorum del cluster Proxmox.5\nComponente di Rete Configurazione Ottimale Funzione Critica Bridge (vmbr0) VLAN Aware, No IP (opzionale) Switch virtuale principale per il traffico delle VM.5 Bonding (LACP) 802.3ad (se supportato dallo switch) Ridondanza e aumento della banda passante.5 MTU 1500 (standard) o 9000 (Jumbo Frames) Ottimizzazione del throughput per storage e traffico pod-to-pod.5 VirtIO Model Paravirtualizzazione Massime prestazioni di rete con il minimo overhead di CPU.7 L\u0026rsquo;integrazione di MetalLB richiede che il bridge di Proxmox non interferisca con i pacchetti ARP gratuiti inviati dagli speaker di MetalLB per annunciare i Virtual IP (VIP). In alcuni scenari di routing avanzato, potrebbe essere necessario abilitare il proxy_arp sull\u0026rsquo;interfaccia del bridge host per facilitare la comunicazione tra subnet diverse, sebbene questa pratica debba essere valutata attentamente per le implicazioni di sicurezza.8\nTalos OS: L\u0026rsquo;Evoluzione del Sistema Operativo Immutabile # Talos OS si distingue radicalmente dalle distribuzioni Linux generaliste. È un sistema operativo minimale, privo di shell, SSH e gestori di pacchetti, progettato esclusivamente per far girare Kubernetes.1 Questa riduzione della superficie di attacco, che porta il sistema ad avere solo circa 12 binari contro i consueti 1500 delle distribuzioni standard, lo rende ideale per ambienti che richiedono elevata sicurezza e manutenibilità.2 La gestione di Talos avviene interamente tramite API gRPC utilizzando il tool talosctl.2\nSpecifiche delle Macchine Virtuali per Nodi Kubernetes # La creazione delle VM su Proxmox per ospitare Talos deve seguire requisiti tecnici rigorosi per garantire la stabilità di etcd e delle API di sistema.\nRisorsa VM Requisito Minimo Configurazione Consigliata CPU Type host Abilita tutte le estensioni hardware della CPU fisica.7 Core CPU 2 Core 4 Core per i nodi Control Plane.7 Memoria RAM 2 GB 4-8 GB per garantire fluidità operativa e caching.7 Disk Controller VirtIO SCSI Supporto per il comando TRIM e latenze ridotte.7 Storage 20 GB 32 GB o superiore per log e storage locale effimero.10 L\u0026rsquo;uso del tipo di CPU \u0026ldquo;host\u0026rdquo; è fondamentale poiché permette a Talos di accedere alle istruzioni di virtualizzazione e crittografia avanzate del processore fisico, migliorando le prestazioni di etcd e dei processi di cifratura del traffico.7 Inoltre, l\u0026rsquo;abilitazione dell\u0026rsquo;agente QEMU nelle impostazioni della VM Proxmox consente una gestione più granulare del sistema operativo, come l\u0026rsquo;arresto pulito e la sincronizzazione dell\u0026rsquo;orologio, sebbene Talos gestisca molte di queste funzioni nativamente tramite le sue API.7\nImplementazione di MetalLB: Teoria e Meccanismi di Rete # MetalLB risolve il problema della raggiungibilità esterna dei servizi Kubernetes agendo come un\u0026rsquo;implementazione software di un load balancer di rete. Funziona monitorando i servizi di tipo LoadBalancer e assegnando loro un indirizzo IP da un pool configurato dall\u0026rsquo;amministratore.11 Esistono due modalità operative principali: Layer 2 (ARP/NDP) e BGP.\nIl Funzionamento della Modalità Layer 2 # In modalità Layer 2, MetalLB utilizza il protocollo ARP per IPv4 e NDP per IPv6. Quando un IP viene assegnato a un servizio, MetalLB elegge uno dei nodi del cluster come \u0026ldquo;proprietario\u0026rdquo; di quell\u0026rsquo;IP.4 Quel nodo inizierà a rispondere alle richieste ARP per l\u0026rsquo;External-IP del servizio con il proprio indirizzo MAC fisico. Dal punto di vista della rete esterna (ad esempio, il router del laboratorio o dell\u0026rsquo;ufficio), sembra che il nodo abbia più indirizzi IP associati alla sua scheda di rete.4\nQuesta modalità è estremamente popolare nei laboratori domestici (home-lab) e nelle piccole imprese perché non richiede alcuna configurazione sui router esistenti; funziona su qualsiasi switch Ethernet standard.4 Tuttavia, presenta un limite strutturale: tutto il traffico in ingresso per un determinato VIP viene convogliato verso un singolo nodo. Sebbene kube-proxy provveda poi a distribuire tale traffico ai pod effettivi su altri nodi, la larghezza di banda in ingresso è limitata dalla capacità di rete del singolo nodo leader.4\nBorder Gateway Protocol (BGP) e Scalabilità # Per ambienti di produzione con traffico elevato, la modalità BGP è la scelta d\u0026rsquo;elezione. In questo caso, ogni nodo del cluster stabilisce una sessione di peering BGP con i router dell\u0026rsquo;infrastruttura.4 Quando un servizio riceve un External-IP, MetalLB annuncia tale rotta al router. Se il router supporta ECMP (Equal-Cost Multi-Pathing), il traffico può essere distribuito equamente tra tutti i nodi che annunciano la rotta, permettendo un vero bilanciamento del carico a livello di rete e superando i limiti della modalità Layer 2.13\nL\u0026rsquo;uso di BGP su Talos richiede una configurazione attenta, specialmente se si utilizzano CNI avanzati come Cilium, che possiedono a loro volta capacità BGP.14 È fondamentale evitare conflitti tra MetalLB e il CNI, decidendo quale componente debba gestire il peering con i router fisici.15\nGuida Pratica all\u0026rsquo;Installazione: Dal Bootstrap alla Configurazione # Il processo di installazione inizia dopo che il cluster Talos è stato bootstrappato con successo e kubectl è operativo.\nPreparazione di Talos: Il Patching di Sistema # Prima di installare MetalLB, è necessario applicare alcune modifiche alla configurazione dei nodi Talos. Uno dei requisiti fondamentali di MetalLB, quando opera con kube-proxy in modalità IPVS, è l\u0026rsquo;abilitazione del parametro strictARP.16 In Talos, questo non si fa modificando una ConfigMap, ma patchando il MachineConfig.\nIl file di configurazione deve includere la sezione relativa a kube-proxy per forzare l\u0026rsquo;accettazione di ARP gratuiti e gestire correttamente il routing dei VIP.16 Inoltre, se si desidera che i nodi del Control Plane partecipino all\u0026rsquo;annuncio degli IP (molto comune in cluster piccoli), è necessario rimuovere le label restrittive che Kubernetes e Talos applicano per default.18\nYAML\n# Esempio di patch per abilitare strictARP e rimuovere restrizioni sui nodi\ncluster:\nproxy:\nconfig:\nipvs:\nstrictARP: true\nallowSchedulingOnControlPlanes: true # Se si usano i master come worker\nmachine:\nnodeLabels:\nnode.kubernetes.io/exclude-from-external-load-balancers: \u0026quot;\u0026quot;\n$patch: delete\nQuesta patch assicura che il piano di controllo non venga escluso dalle operazioni di bilanciamento del carico, permettendo a MetalLB di far girare i propri \u0026ldquo;speaker\u0026rdquo; su ogni nodo disponibile.18\nInstallazione di MetalLB tramite Helm # L\u0026rsquo;utilizzo di Helm è il metodo raccomandato per installare MetalLB poiché facilita la gestione delle versioni e delle dipendenze RBAC.16\nCreazione del Namespace e Label di Sicurezza:\nKubernetes applica Pod Security Admissions. MetalLB, dovendo manipolare lo stack di rete dell\u0026rsquo;host, richiede un profilo privileged. È essenziale etichettare il namespace prima dell\u0026rsquo;installazione.16\nBash\nkubectl create namespace metallb-system\nkubectl label namespace metallb-system pod-security.kubernetes.io/enforce=privileged\nkubectl label namespace metallb-system pod-security.kubernetes.io/audit=privileged\nkubectl label namespace metallb-system pod-security.kubernetes.io/warn=privileged\nEsecuzione di Helm:\nSi aggiunge il repository ufficiale e si procede all\u0026rsquo;installazione.10\nBash\nhelm repo add metallb https://metallb.github.io/metallb\nhelm repo update\nhelm install metallb metallb/metallb -n metallb-system\nDefinizione delle Risorse Custom (CRD) # Una volta installato, MetalLB rimane inattivo finché non vengono definite le pool di indirizzi IP e le modalità di annuncio.16 Queste configurazioni sono ora gestite tramite Custom Resource Definitions (CRD) e non più tramite ConfigMap come nelle versioni precedenti allo 0.13.\nIPAddressPool: definisce l\u0026rsquo;intervallo di indirizzi IP che MetalLB può assegnare. È cruciale che questi IP non siano nel range DHCP del router per evitare conflitti.11\nYAML\napiVersion: metallb.io/v1beta1\nkind: IPAddressPool\nmetadata:\nname: default-pool\nnamespace: metallb-system\nspec:\naddresses:\n- 192.168.1.50-192.168.1.70\nL2Advertisement: questa risorsa associa il pool di indirizzi alla modalità di annuncio Layer 2. Senza di essa, MetalLB assegnerà gli IP ma non risponderà alle richieste ARP, rendendo gli IP irraggiungibili.20\nYAML\napiVersion: metallb.io/v1beta1\nkind: L2Advertisement\nmetadata:\nname: default-advertisement\nnamespace: metallb-system\nspec:\nipAddressPools:\n- default-pool\nIntegrazione e Sicurezza con Proxmox: Gestione dello Spoofing # Un ostacolo comune nell\u0026rsquo;installazione di MetalLB su Proxmox è il sistema di protezione di rete integrato nell\u0026rsquo;hypervisor. Il firewall di Proxmox include funzionalità di \u0026ldquo;IP Filter\u0026rdquo; e \u0026ldquo;MAC Filter\u0026rdquo; volte a prevenire che una VM utilizzi indirizzi IP o MAC diversi da quelli assegnati ufficialmente nel pannello di controllo.21\nPoiché MetalLB in modalità Layer 2 \u0026ldquo;finge\u0026rdquo; che il nodo possieda gli indirizzi IP dei servizi (VIP), inviando risposte ARP per IP non configurati sull\u0026rsquo;interfaccia di rete primaria, il firewall di Proxmox potrebbe bloccare questo traffico identificandolo come ARP Spoofing.21\nRisoluzione delle Restrizioni del Firewall Proxmox # Per permettere a MetalLB di funzionare, esistono tre approcci principali:\nDisabilitazione del MAC Filter: Nelle opzioni del firewall della VM (o del bridge), disabilitare la voce MAC filter. Questo permette alla VM di inviare traffico con sorgenti IP diverse da quella principale.22 Configurazione di IPSet: Se si desidera mantenere un livello di sicurezza elevato, è possibile creare un IPSet chiamato ipfilter-net0 (dove net0 è l\u0026rsquo;interfaccia della VM) e includere in questo set tutti gli indirizzi IP presenti nel pool di MetalLB. In questo modo, il firewall di Proxmox saprà che quegli IP sono autorizzati per quella specifica VM.21 Regole ebtables manuali: In scenari avanzati, l\u0026rsquo;amministratore può inserire regole ebtables sull\u0026rsquo;host Proxmox per consentire specificamente il traffico ARP per il range di MetalLB.23 Bash\n# Esempio di comando ebtables per consentire ARP su una specifica VM\nebtables -I FORWARD 1 -i fwln\u0026lt;VMID\u0026gt;i0 -p ARP --arp-ip-dst 192.168.1.50/32 -j ACCEPT\nL\u0026rsquo;omissione di questi passaggi è la causa principale del fallimento delle installazioni di MetalLB su Proxmox, portando a situazioni in cui il servizio Kubernetes mostra un External-IP correttamente assegnato, ma tale IP risulta non raggiungibile (non pingabile) dall\u0026rsquo;esterno del cluster.19\nMonitoraggio e Risoluzione dei Problemi (Troubleshooting) # La natura immutabile di Talos OS rende il troubleshooting differente rispetto ai sistemi tradizionali. Non potendo accedere tramite SSH per eseguire tcpdump direttamente sul nodo, è necessario affidarsi ai log dei pod di MetalLB e agli strumenti di talosctl.\nAnalisi dei Log degli Speaker # I pod \u0026ldquo;speaker\u0026rdquo; sono i responsabili dell\u0026rsquo;annuncio degli IP. Se un IP non è raggiungibile, il primo passo è controllare i log dello speaker sul nodo che dovrebbe essere il leader per quel servizio.4\nBash\nkubectl logs -n metallb-system -l component=speaker\nNei log è possibile osservare se lo speaker ha rilevato il servizio, se ha eletto correttamente un leader e se sta incontrando errori nell\u0026rsquo;invio dei pacchetti gratuiti. Se i log mostrano che l\u0026rsquo;annuncio è avvenuto correttamente ma il router non vede l\u0026rsquo;indirizzo, il problema risiede quasi certamente nel layer di virtualizzazione di Proxmox o nello switch fisico.8\nVerifica dello Stato L2 (ServiceL2Status) # MetalLB fornisce una risorsa di stato che permette di vedere quale nodo sta attualmente servendo un determinato IP.19\nBash\nkubectl get servicel2statuses.metallb.io -n metallb-system\nQuesta informazione è vitale per capire se il traffico sta venendo indirizzato verso il nodo corretto e per verificare il comportamento del cluster durante un failover simulato (ad esempio, riavviando un nodo worker).6\nConflitti con il CNI e Routing Pod-to-Pod # In alcuni casi, il traffico raggiunge il nodo ma non viene instradato correttamente verso i pod. Questo può accadere se il CNI (come Cilium o Calico) ha una configurazione che entra in conflitto con le regole di routing create da kube-proxy in modalità IPVS.12 Se si utilizza Cilium, è raccomandato verificare se la funzionalità \u0026ldquo;L2 Announcement\u0026rdquo; di Cilium sia attiva; se lo è, essa svolgerà la stessa funzione di MetalLB, rendendo quest\u0026rsquo;ultimo ridondante o addirittura dannoso per la stabilità della rete.14\nOttimizzazione delle Performance e Alta Affidabilità # Un cluster Kubernetes professionale su Proxmox deve essere progettato per resistere ai guasti e scalare in modo efficiente.\nBilanciamento del Carico e Hardware Offloading # L\u0026rsquo;uso di VirtIO in Proxmox permette l\u0026rsquo;offloading di alcune funzioni di rete (come il checksum offload) alla CPU dell\u0026rsquo;host, riducendo il carico sulla VM Talos.7 Inoltre, l\u0026rsquo;implementazione di MetalLB in modalità BGP, come discusso, permette di sfruttare l\u0026rsquo;hardware di rete fisico (router enterprise come MikroTik o Cisco) per gestire il bilanciamento a livello di pacchetto, garantendo che nessun singolo nodo diventi il collo di bottiglia per il traffico applicativo.13\nFailover e Tempi di Convergenza # In modalità Layer 2, il tempo di failover dipende dalla velocità con cui i nodi rilevano la caduta di un peer e dalla rapidità con cui il router aggiorna la propria tabella ARP.6 Talos ottimizza questo processo grazie a un kernel Linux estremamente snello e reattivo. Per accelerare ulteriormente il failover, è possibile configurare MetalLB con protocolli come BFD (Bidirectional Forwarding Detection) in modalità BGP, riducendo i tempi di rilevamento del guasto da secondi a millisecondi.13\nConsiderazioni Finali sulla Gestione Day-2 # L\u0026rsquo;integrazione di MetalLB su Talos e Proxmox non si esaurisce con l\u0026rsquo;installazione iniziale. La gestione \u0026ldquo;Day-2\u0026rdquo; riguarda gli aggiornamenti, il monitoraggio della sicurezza e l\u0026rsquo;espansione del cluster. Grazie alla natura dichiarativa di Talos e MetalLB, è possibile gestire l\u0026rsquo;intera infrastruttura come codice (Infrastructure as Code). L\u0026rsquo;uso di strumenti come Terraform per la creazione delle VM su Proxmox e Helm per la gestione dei componenti Kubernetes permette di ricreare l\u0026rsquo;intero ambiente in modo deterministico in caso di disaster recovery.8\nIn conclusione, la sinergia tra la stabilità di Proxmox, la sicurezza intrinseca di Talos OS e la versatilità di MetalLB crea un ecosistema ideale per ospitare applicazioni moderne. L\u0026rsquo;attenzione ai dettagli nella configurazione del networking Layer 2 e l\u0026rsquo;eliminazione dei filtri restrittivi di Proxmox sono i pilastri per una distribuzione di successo che garantisca che i servizi non solo siano operativi, ma anche costantemente accessibili e performanti per gli utenti finali. La continua evoluzione di questi strumenti suggerisce un futuro in cui la distinzione tra cloud pubblico e data center privato diventerà sempre più sottile, grazie a soluzioni software-defined che portano l\u0026rsquo;agilità del cloud direttamente sul metallo nudo della propria infrastruttura.1\nBibliografia # Proxmox vs Talos – Deciding on the Best Infrastructure Solution - simplyblock, accesso eseguito il giorno gennaio 2, 2026, https://www.simplyblock.io/blog/proxmox-vs-talos/ Using Talos Linux and Kubernetes bootstrap on OpenStack - Safespring, accesso eseguito il giorno gennaio 2, 2026, https://www.safespring.com/blogg/2025/2025-03-talos-linux-on-openstack/ How to setup the MetalLB | kubernetes-under-the-hood - GitHub Pages, accesso eseguito il giorno gennaio 2, 2026, https://mvallim.github.io/kubernetes-under-the-hood/documentation/kube-metallb.html MetalLB: A Load Balancer for Bare Metal Kubernetes Clusters | by 8grams - Medium, accesso eseguito il giorno gennaio 2, 2026, https://8grams.medium.com/metallb-a-load-balancer-for-bare-metal-kubernetes-clusters-ef8a9e00c2bd Networking best practice | Proxmox Support Forum, accesso eseguito il giorno gennaio 2, 2026, https://forum.proxmox.com/threads/networking-best-practice.163550/ MetalLB in layer 2 mode :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 2, 2026, https://metallb.universe.tf/concepts/layer2/ Talos with Kubernetes on Proxmox | Secsys, accesso eseguito il giorno gennaio 2, 2026, https://secsys.pages.dev/posts/talos/ epyc-kube/docs/proxmox-metallb-subnet-configuration.md at main \u0026hellip;, accesso eseguito il giorno gennaio 2, 2026, https://github.com/xalgorithm/epyc-kube/blob/main/docs/proxmox-metallb-subnet-configuration.md How I Setup Talos Linux. My journey to building a secure… | by Pedro Chang | Medium, accesso eseguito il giorno gennaio 2, 2026, https://medium.com/@pedrotychang/how-i-setup-talos-linux-bc2832ec87cc Highly available kubernetes cluster with etcd, Longhorn and \u0026hellip;, accesso eseguito il giorno gennaio 2, 2026, https://wiki.joeplaa.com/tutorials/highly-available-kubernetes-cluster-on-proxmox MetalLB Load Balancer - Documentation - K0s docs, accesso eseguito il giorno gennaio 2, 2026, https://docs.k0sproject.io/v1.34.2+k0s.0/examples/metallb-loadbalancer/ MetalLB - Ubuntu, accesso eseguito il giorno gennaio 2, 2026, https://ubuntu.com/kubernetes/charmed-k8s/docs/metallb MetalLB in BGP mode :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 2, 2026, https://metallb.universe.tf/concepts/bgp/ Kubernetes \u0026amp; Talos - Reddit, accesso eseguito il giorno gennaio 2, 2026, https://www.reddit.com/r/kubernetes/comments/1hs6bui/kubernetes_talos/ Talos with redundant routed networks via bgp : r/kubernetes - Reddit, accesso eseguito il giorno gennaio 2, 2026, https://www.reddit.com/r/kubernetes/comments/1iy411r/talos_with_redundant_routed_networks_via_bgp/ Installation :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 2, 2026, https://metallb.universe.tf/installation/ Kubernetes Homelab Series Part 3 - LoadBalancer With MetalLB \u0026hellip;, accesso eseguito il giorno gennaio 2, 2026, https://blog.dalydays.com/post/kubernetes-homelab-series-part-3-loadbalancer-with-metallb/ Unable to use MetalLB on TalosOS linux v.1.9.3 on Proxmox · Issue #2676 - GitHub, accesso eseguito il giorno gennaio 2, 2026, https://github.com/metallb/metallb/issues/2676 Unable to use MetalLB load balancer for TalosOS v1.9.3 · Issue #10291 · siderolabs/talos, accesso eseguito il giorno gennaio 2, 2026, https://github.com/siderolabs/talos/issues/10291 Configuration :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 2, 2026, https://metallb.universe.tf/configuration/ Implementing MAC Filtering for IPv4 in Proxmox Using Built-In Firewall Features, accesso eseguito il giorno gennaio 2, 2026, https://forum.proxmox.com/threads/implementing-mac-filtering-for-ipv4-in-proxmox-using-built-in-firewall-features.157726/ \\[SOLVED\\]- Allow MAC spoofing? - Proxmox Support Forum, accesso eseguito il giorno gennaio 2, 2026, https://forum.proxmox.com/threads/allow-mac-spoofing.84424/ Block incoming ARP requests if destination ip is not part of ipfilter-net \\[n\\], accesso eseguito il giorno gennaio 2, 2026, https://forum.proxmox.com/threads/block-incoming-arp-requests-if-destination-ip-is-not-part-of-ipfilter-net-n.144135/ Filter ARP request - Proxmox Support Forum, accesso eseguito il giorno gennaio 2, 2026, https://forum.proxmox.com/threads/filter-arp-request.118505/ Creating ExternalIPs in OpenShift with BGP and MetalLB | - Random Tech Adventures, accesso eseguito il giorno gennaio 2, 2026, https://xphyr.net/post/metallb_and_ocp_using_bgp/ Setting up a Talos kubernetes cluster with talhelper - beyondwatts, accesso eseguito il giorno gennaio 2, 2026, https://www.beyondwatts.com/posts/setting-up-a-talos-kubernetes-cluster-with-talhelper/ ","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/guides/talos-proxmox-metallb/","section":"Guide","summary":"","title":"Integrazione e Ottimizzazione di MetalLB su Cluster Kubernetes Talos OS in Ambienti Proxmox Virtual Environment","type":"guides"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/kube-vip/","section":"Tags","summary":"","title":"Kube-Vip","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/kvm/","section":"Tags","summary":"","title":"Kvm","type":"tags"},{"content":"Il successo di Kubernetes come standard de facto per l\u0026rsquo;orchestrazione dei container non risiede soltanto nella sua capacità di astrarre l\u0026rsquo;hardware o di gestire il networking, ma risiede in modo fondamentale nel suo modello operativo basato sui controller. In un sistema distribuito di scala massiva, la gestione manuale dei carichi di lavoro sarebbe impossibile; la stabilità è garantita invece da una miriade di cicli di controllo intelligenti che lavorano incessantemente per mantenere l\u0026rsquo;armonia tra ciò che l\u0026rsquo;utente ha dichiarato e ciò che effettivamente accade nei server fisici o virtuali.1 Un controller in Kubernetes è, nella sua essenza più pura, un ciclo infinito, un daemon che osserva lo stato condiviso del cluster attraverso l\u0026rsquo;API server e apporta le modifiche necessarie per far sì che lo stato attuale converga verso lo stato desiderato.1 Questo paradigma, mutuato dalla teoria dei sistemi e dalla robotica, trasforma la gestione delle infrastrutture da un approccio imperativo (fai questo) a uno dichiarativo (voglio che questo sia così).2\nFondamenti e meccanismi del loop di controllo # Per comprendere i controller partendo \u0026ldquo;da zero\u0026rdquo;, è necessario visualizzare il cluster non come un insieme statico di container, ma come un organismo dinamico regolato da un termostato intelligente. In una stanza, il termostato rappresenta il controller: l\u0026rsquo;utente imposta una temperatura desiderata (lo stato desiderato), il termostato rileva la temperatura attuale (lo stato attuale) e agisce accendendo o spegnendo il riscaldamento per eliminare la differenza.2 In Kubernetes, questo processo segue un pattern rigido denominato \u0026ldquo;Watch-Analyze-Act\u0026rdquo;.4\nIl primo pilastro, la fase di \u0026ldquo;Watch\u0026rdquo;, si affida all\u0026rsquo;API server come unica fonte di verità. I controller monitorano costantemente le risorse di loro competenza, sfruttando i meccanismi di notifica di etcd per reagire in tempo reale a ogni cambiamento.3 Quando un utente applica un manifesto YAML, l\u0026rsquo;API server memorizza la specifica (spec) in etcd, e il controller corrispondente riceve immediatamente un segnale.2\nNella fase di \u0026ldquo;Analyze\u0026rdquo;, il controller confronta la specifica con lo stato riportato nel campo status della risorsa. Se la specifica richiede tre repliche di un\u0026rsquo;applicazione ma lo stato ne riporta solo due, l\u0026rsquo;analisi identifica una discrepanza.2 Infine, nella fase \u0026ldquo;Act\u0026rdquo;, il controller non agisce direttamente sul container, ma invia istruzioni all\u0026rsquo;API server per creare nuovi oggetti (come un Pod) o rimuovere quelli esistenti.2 Altri componenti, come il kube-scheduler e il kubelet, eseguiranno poi le azioni fisiche necessarie.2 Questo disaccoppiamento garantisce che ogni componente sia specializzato e che il sistema possa tollerare guasti parziali senza perdere la coerenza globale.3\nIl Kube-Controller-Manager: il centro nevralgico # Logicamente, ogni controller è un processo separato, ma per ridurre la complessità operativa, Kubernetes raggruppa tutti i controller core in un unico binario chiamato kube-controller-manager.1 Questo demone viene eseguito sul piano di controllo (control plane) e gestisce la maggior parte dei cicli di controllo integrati.1 Per ottimizzare le prestazioni, il kube-controller-manager permette di configurare la concorrenza, ovvero il numero di oggetti che possono essere sincronizzati simultaneamente per ogni tipologia di controller.1\nController Parametro di Concorrenza Valore Predefinito Impatto sulle Prestazioni Deployment --concurrent-deployment-syncs 5 Velocità di aggiornamento delle applicazioni stateless StatefulSet --concurrent-statefulset-syncs Non specificato (globale) Gestione ordinata delle applicazioni con stato DaemonSet --concurrent-daemonset-syncs 2 Prontezza dei servizi infrastrutturali su nuovi nodi Job --concurrent-job-syncs 5 Capacità di elaborazione batch simultanea Namespace --concurrent-namespace-syncs 10 Velocità di pulizia e terminazione delle risorse ReplicaSet --concurrent-replicaset-syncs 5 Gestione del numero di repliche desiderato Questi parametri sono cruciali per gli amministratori di cluster di grandi dimensioni; aumentare questi valori può rendere il cluster più reattivo ma aumenta drasticamente il carico sulla CPU del piano di controllo e sul traffico di rete verso l\u0026rsquo;API server.1\nAnalisi dettagliata dei controller di carico di lavoro (Workload) # La gestione delle applicazioni in Kubernetes avviene attraverso astrazioni chiamate risorse di carico di lavoro, ognuna delle quali è governata da un controller specifico progettato per risolvere problemi di orchestrazione unici.9\nDeployment e ReplicaSet: lo standard stateless # Il controller Deployment è probabilmente il più utilizzato nell\u0026rsquo;ecosistema Kubernetes. Esso fornisce aggiornamenti dichiarativi per Pod e ReplicaSet.5 Quando si definisce un Deployment, il controller non crea direttamente i Pod, ma crea un ReplicaSet, il quale a sua volta garantisce che il numero esatto di Pod sia sempre in esecuzione.5\nLa vera potenza del Deployment risiede nella gestione delle strategie di aggiornamento, principalmente la \u0026ldquo;RollingUpdate\u0026rdquo;.11 Durante un rollout, il Deployment controller crea un nuovo ReplicaSet con la nuova versione dell\u0026rsquo;immagine e inizia a scalarlo verso l\u0026rsquo;alto, mentre contemporaneamente scala verso il basso il vecchio ReplicaSet.15 Questo meccanismo permette aggiornamenti senza downtime e facilita il rollback immediato tramite il comando kubectl rollout undo.18 I Deployment sono ideali per applicazioni web, API e microservizi dove i singoli Pod sono considerati effimeri e intercambiabili.9\nStatefulSet: l\u0026rsquo;identità nel caos distribuito # A differenza delle applicazioni stateless, molti sistemi (come database o code di messaggi) richiedono che ogni istanza abbia un\u0026rsquo;identità persistente e un ordine di avvio specifico.9 Il controller StatefulSet gestisce il deployment e lo scaling di un set di Pod fornendo garanzie di unicità.21\nOgni Pod riceve un nome derivato da un indice ordinale (es. $pod-0, pod-1, \\dots, pod-N-1$) che rimane costante anche se il Pod viene rischedulato su un altro nodo.17 Inoltre, lo StatefulSet garantisce la persistenza dello storage: ogni Pod viene associato a un PersistentVolume specifico tramite un volumeClaimTemplate.17 Se il Pod db-0 fallisce, il controller ne creerà uno nuovo chiamato db-0 e lo collegherà allo stesso volume di dati precedente, preservando lo stato applicativo.17\nDaemonSet: infrastruttura onnipresente # Il controller DaemonSet assicura che una copia di un Pod sia in esecuzione su tutti (o alcuni) nodi del cluster.5 Quando un nuovo nodo viene aggiunto al cluster, il DaemonSet controller vi aggiunge automaticamente il Pod specificato.9 Questo è fondamentale per servizi che devono risiedere su ogni macchina fisica, come raccoglitori di log (Fluentd, Logstash), agenti di monitoraggio (Prometheus Node Exporter) o componenti di rete (Calico, Cilium).9 È possibile limitare l\u0026rsquo;esecuzione a un sottoinsieme di nodi utilizzando selettori di etichette (label selectors) o affinità di nodo.22\nJob e CronJob: l\u0026rsquo;esecuzione a termine # Mentre i controller precedenti gestiscono servizi che dovrebbero essere eseguiti all\u0026rsquo;infinito, Job e CronJob gestiscono task che devono terminare con successo.9 Il Job controller crea uno o più Pod e garantisce che un numero specifico di essi termini correttamente.24 Se un Pod fallisce a causa di un errore del container o del nodo, il Job controller ne avvia uno nuovo fino al raggiungimento della quota di successi o del limite di tentativi (backoffLimit).24\nIl CronJob estende questa logica permettendo l\u0026rsquo;esecuzione di Job su base programmata, utilizzando il formato crontab standard di Unix.27 Questo è ideale per backup notturni, generazione di report periodici o attività di manutenzione del database.28\nCaratteristica Deployment StatefulSet DaemonSet Job Natura Workload Stateless Stateful Infrastrutturale Batch / Task unico Identità Pod Casuale (hash) Ordinale stabile Legata al nodo Temporanea Storage Condiviso o effimero Dedicato per replica Locale o specifico Effimero Ordine di avvio Casuale / Parallelo Sequenziale ordinato Parallelo su nodi Parallelo / Sequenziale Esempio Uso Nginx, Spring Boot MySQL, Kafka, Redis Fluentd, New Relic Migrazione DB I controller interni e l\u0026rsquo;integrità del sistema # Oltre ai controller visibili all\u0026rsquo;utente che gestiscono i Pod, il piano di controllo di Kubernetes esegue numerosi controller \u0026ldquo;di sistema\u0026rdquo; che garantiscono il funzionamento dell\u0026rsquo;infrastruttura stessa.5\nNode Controller # Il Node Controller è responsabile della gestione del ciclo di vita dei nodi all\u0026rsquo;interno del cluster.5 Le sue funzioni principali includono:\nRegistrazione e Monitoraggio: Tiene traccia dell\u0026rsquo;inventario dei nodi e del loro stato di salute.6 Rilevamento Guasti: Se un nodo smette di inviare segnali di heartbeat (segno di un guasto di rete o hardware), il Node Controller lo contrassegna come NotReady o Unknown.3 Evacuazione dei Pod: Se un nodo rimane non raggiungibile per un periodo prolungato, il controller avvia l\u0026rsquo;espulsione dei Pod gestiti da Deployment o StatefulSet affinché possano essere rischedulati su nodi sani.5 Namespace Controller # I namespace forniscono un meccanismo di isolamento logico all\u0026rsquo;interno di un cluster.12 Il Namespace Controller interviene quando un utente richiede la cancellazione di un namespace.5 Invece di una cancellazione istantanea, il controller avvia un processo di pulizia iterativo: si assicura che tutte le risorse associate (Pod, Service, Secret, ConfigMap) vengano rimosse correttamente prima di eliminare definitivamente l\u0026rsquo;oggetto Namespace dal database etcd.5\nEndpoints ed EndpointSlice Controller # Questi controller costituiscono il tessuto connettivo tra il networking e i carichi di lavoro. L\u0026rsquo;Endpoints Controller monitora costantemente i Service e i Pod; quando un Pod diventa \u0026ldquo;Ready\u0026rdquo; (secondo la sua readiness probe), il controller aggiunge l\u0026rsquo;indirizzo IP del Pod all\u0026rsquo;oggetto Endpoints corrispondente al Service.5 Questo permette a kube-proxy di instradare correttamente il traffico.3 L\u0026rsquo;EndpointSlice Controller è un\u0026rsquo;evoluzione più moderna e scalabile che gestisce raggruppamenti di endpoint più grandi in cluster con migliaia di nodi.5\nService Account e Token Controller # La sicurezza all\u0026rsquo;interno del cluster è mediata dai Service Account, che forniscono un\u0026rsquo;identità ai processi che girano nei Pod.12 I Service Account Controller creano automaticamente un account \u0026ldquo;default\u0026rdquo; per ogni nuovo namespace e generano i token segreti necessari affinché i container possano autenticarsi presso l\u0026rsquo;API server per operazioni di monitoraggio o automazione.8\nCloud Controller Manager (CCM): L\u0026rsquo;interfaccia con i provider # Nelle installazioni cloud (AWS, Azure, Google Cloud), Kubernetes deve interagire con risorse esterne come bilanciatori di carico o dischi gestiti.3 Il Cloud Controller Manager (CCM) separa la logica specifica del cloud dalla logica core di Kubernetes.6\nIl CCM esegue tre cicli di controllo principali:\nService Controller: Quando viene creato un Service di tipo LoadBalancer, questo controller interagisce con le API del cloud provider (es. AWS NLB/ALB) per istanziare un bilanciatore di carico esterno e configurarne i target verso i nodi del cluster.5 Route Controller: Configura le tabelle di routing dell\u0026rsquo;infrastruttura di rete del cloud per garantire che i pacchetti destinati ai Pod possano viaggiare tra i diversi nodi fisici.5 Node Controller (Cloud): Interroga il cloud provider per determinare se un nodo che ha smesso di rispondere sia stato effettivamente rimosso o terminato dalla console cloud, permettendo una pulizia più rapida delle risorse del cluster.5 Estendibilità estrema: Il pattern Operator e i Custom Controller # Uno dei punti di forza di Kubernetes è la sua capacità di essere esteso oltre le capacità native.7 Se i controller integrati gestiscono astrazioni generali (Pod, Service), il pattern Operator permette di gestire applicazioni complesse introducendo \u0026ldquo;conoscenza di dominio\u0026rdquo; direttamente nel piano di controllo.16\nAnatomia di un Operator # Un Operator è l\u0026rsquo;unione di due componenti:\nCustom Resource Definition (CRD): Estende l\u0026rsquo;API server permettendo la creazione di nuovi tipi di oggetti (es. un oggetto di tipo ElasticsearchCluster o PostgresBackup).7 Custom Controller: Un loop di controllo personalizzato che osserva queste nuove risorse e implementa la logica operativa specifica, come eseguire un backup prima di un aggiornamento del database o gestire il re-sharding dei dati.7 Gli Operator automatizzano compiti che normalmente richiederebbero un intervento umano esperto (un Site Reliability Engineer), come la gestione del quorum in un cluster distribuito o la migrazione di schemi database durante un upgrade applicativo.16\nStrumenti per lo sviluppo: Operator SDK e Kubebuilder # Lo sviluppo di un controller personalizzato da zero è complesso, poiché richiede la gestione di cache, code di lavoro (workqueues) e interazioni di rete a bassa latenza.34 Strumenti come Operator SDK (supportato da Red Hat) e Kubebuilder (progetto ufficiale Kubernetes SIGs) forniscono framework in linguaggio Go per generare boilerplate, gestire la serializzazione degli oggetti e implementare il ciclo di riconciliazione in modo efficiente.33\nStrumento Linguaggi Supportati Caratteristiche Principali Operator SDK Go, Ansible, Helm Integrazione con Operator Lifecycle Manager (OLM), ideale per integrazioni aziendali.33 Kubebuilder Go Basato su controller-runtime, fornisce astrazioni pulite per la generazione di CRD e Webhook.33 Client-Go Go Libreria di basso livello per il controllo totale, ma con curva di apprendimento molto ripida.33 Automazione Elastica: Horizontal Pod Autoscaler (HPA) # Il controller Horizontal Pod Autoscaler (HPA) automatizza lo scaling orizzontale, ovvero l\u0026rsquo;aggiunta o la rimozione di repliche dei Pod in risposta al carico.38\nIl funzionamento segue una formula matematica precisa per calcolare il numero di repliche desiderate:\n$R_{desiderate} = \\lceil R_{attuali} \\times \\frac{Valore_{attuale}}{Valore_{target}} \\rceil$\nL\u0026rsquo;HPA interroga il Metrics Server (o un adattatore per metriche personalizzate come Prometheus) per ottenere l\u0026rsquo;utilizzo medio delle risorse.38 Se l\u0026rsquo;utilizzo supera la soglia impostata (es. 70% di CPU), l\u0026rsquo;HPA aggiorna il campo replicas del Deployment o dello StatefulSet target.38 Questo permette al cluster di adattarsi a picchi di traffico imprevisti senza intervento manuale, ottimizzando al contempo i costi durante i periodi di bassa attività.38\nGuida Pratica: Installazione e Configurazione dei Controller # La maggior parte degli utenti interagisce con i controller attraverso file manifest YAML. Ecco come configurare e gestire i principali controller con esempi reali.\nConfigurazione di un Deployment con strategie di Rollout # Un Deployment ben configurato deve definire chiaramente come gestire gli aggiornamenti.\nYAML\napiVersion: apps/v1\nkind: Deployment\nmetadata:\nname: api-service\nlabels:\napp: api\nspec:\nreplicas: 4\nstrategy:\ntype: RollingUpdate\nrollingUpdate:\nmaxSurge: 25% # Numero di pod extra creati durante il rollout\nmaxUnavailable: 25% # Massimo numero di pod che possono essere offline\nselector:\nmatchLabels:\napp: api\ntemplate:\nmetadata:\nlabels:\napp: api\nspec:\ncontainers:\n- name: api-container\nimage: myrepo/api:v1.0.2\nports:\n- containerPort: 8080\nreadinessProbe: # Fondamentale per il controller Deployment\nhttpGet:\npath: /healthz\nport: 8080\n13\nPer gestire questo controller da CLI:\nVisualizzare lo stato: kubectl rollout status deployment/api-service.19 Vedere la cronologia: kubectl rollout history deployment/api-service.15 Eseguire il rollback: kubectl rollout undo deployment/api-service --to-revision=2.15 Installazione di un Operator tramite Operator SDK # Il processo di installazione di un Operator è più articolato rispetto a una risorsa nativa, poiché richiede la registrazione di nuovi tipi di API.36\nInstallazione delle CRD: kubectl apply -f deploy/crds/db_v1alpha1_mysql_crd.yaml. Questo insegna all\u0026rsquo;API server cosa sia un \u0026ldquo;MySQLDatabase\u0026rdquo;.34\nConfigurazione RBAC: kubectl apply -f deploy/role.yaml e kubectl apply -f deploy/role_binding.yaml. Questo dà al controller i permessi per creare Pod e Service.36\nDeployment del Controller: kubectl apply -f deploy/operator.yaml. Questo avvia il Pod che contiene il codice sorgente dell\u0026rsquo;Operator.36\nCreazione dell\u0026rsquo;istanza: Una volta che l\u0026rsquo;Operator è in esecuzione, l\u0026rsquo;utente crea una risorsa personalizzata per istanziare l\u0026rsquo;applicazione:\nYAML\napiVersion: db.example.com/v1alpha1\nkind: MySQLDatabase\nmetadata:\nname: production-db\nspec:\nsize: 3\nstorage: 100Gi\n7\nA questo punto, l\u0026rsquo;Operator prenderà in carico la richiesta e orchestrerà la creazione di StatefulSet, Service e backup necessari.7\nScelta del Controller: Decision Matrix per Architetti Cloud # Identificare il controller corretto è una decisione architettonica critica che influenza la resilienza e la manutenibilità dell\u0026rsquo;intero sistema.20\nScenari d\u0026rsquo;uso Controller da utilizzare Perché? API Gateway, Front-end Web, Microservizi stateless Deployment Massima velocità di scaling e facilità di aggiornamenti \u0026ldquo;rolling\u0026rdquo;.9 Database (PostgreSQL, MongoDB), Code (RabbitMQ), AI/ML con stato StatefulSet Garantisce che i dati rimangano accoppiati alle istanze corrette e gestisce il quorum.9 Monitoraggio, Log Forwarding, Proxy di Rete (Kube-proxy) DaemonSet Assicura che ogni nodo contribuisca all\u0026rsquo;osservabilità e alla connettività del cluster.20 Processamento dati massivo, Training di modelli ML, Migrazioni DB Job Gestisce task che devono girare fino al successo, con logica di retry integrata.23 Backup periodici, Pulizia cache, Rotazione log programmata CronJob Automazione basata su tempo, sostituisce il cron di sistema per un ambiente containerizzato.27 Software-as-a-Service (SaaS) complesso, Database managed-like Operator Quando la logica operativa richiede passi specifici (es. promozione leader) non coperti da StatefulSet.7 Best Practices Operative e Troubleshooting # Gestire correttamente i controller richiede la consapevolezza di alcune insidie comuni che possono destabilizzare l\u0026rsquo;ambiente di produzione.41\nL\u0026rsquo;importanza delle Probes: Liveness e Readiness # Il controller Deployment si fida delle informazioni fornite dai container. Se un container è \u0026ldquo;Running\u0026rdquo; ma l\u0026rsquo;applicazione all\u0026rsquo;interno è in stallo, il controller non interverrà a meno che non sia configurata una Liveness Probe.9 Allo stesso modo, una Readiness Probe è essenziale durante i rollout: essa informa il controller quando il nuovo Pod è effettivamente pronto a ricevere traffico, evitando che il rollout proceda se la nuova versione sta fallendo silenziosamente.9\nResource Requests e Limits: Il carburante dei Controller # Lo scheduler e i controller di autoscaling (HPA) dipendono interamente dalle dichiarazioni di risorse.41 Senza requests, lo scheduler potrebbe sovraffollare un nodo, portando a prestazioni degradate.9 Senza limits, un singolo Pod con un memory leak potrebbe consumare tutta la memoria del nodo, causando il riavvio forzato di Pod critici di sistema (OOM Killing).41\nLabels e Selectors: Il rischio del \u0026ldquo;Collisione\u0026rdquo; # I controller identificano le risorse di loro competenza tramite selettori di etichette.5 Un errore comune è l\u0026rsquo;utilizzo di etichette troppo generiche (es. app: web) in namespace condivisi. Se due Deployment diversi usano lo stesso selettore, i loro controller entreranno in conflitto, tentando ognuno di gestire i Pod dell\u0026rsquo;altro, portando a una continua creazione e cancellazione di container.47 È buona norma utilizzare etichette univoche e strutturate.\nGestione della cronologia e Rollback # Kubernetes mantiene una cronologia limitata dei rollout dei Deployment (per impostazione predefinita, 10 revisioni).21 È importante monitorare questi limiti per assicurarsi di poter tornare indietro a versioni stabili in caso di incidenti gravi.15 L\u0026rsquo;uso di strumenti di GitOps (come ArgoCD o Flux) che tracciano lo stato desiderato in un repository Git è la raccomandazione d\u0026rsquo;elezione per gestire configurazioni complesse senza errori manuali.14\nConclusioni: Verso l\u0026rsquo;autonomia del Cluster # Il modello dei controller in Kubernetes rappresenta il culmine dell\u0026rsquo;ingegneria dei sistemi distribuiti moderni. Comprendere come i diversi controller interagiscono tra loro — dal Node Controller che rileva un guasto, al Deployment che risponde rischedulando i Pod, fino all\u0026rsquo;HPA che scala le repliche — è ciò che differenzia un utilizzatore passivo di Kubernetes da un esperto di orchestrazione.2\nIl futuro di questa tecnologia si sta spostando verso una specializzazione sempre maggiore attraverso gli Operator, che permettono di gestire non solo i container, ma l\u0026rsquo;intero ciclo di vita del business logic, dai database AI-driven alle reti definite dal software. In questo ecosistema, il manifesto YAML non è più solo un file di configurazione, ma un contratto vivente che una schiera di controller intelligenti si impegna a onorare ogni secondo, garantendo che l\u0026rsquo;applicazione rimanga sempre disponibile, sicura e pronta a scalare.1\nBibliografia # kube-controller-manager - Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/ Controllers - Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/concepts/architecture/controller/ Kubernetes Control Plane: Ultimate Guide (2024) - Plural, accesso eseguito il giorno dicembre 31, 2025, https://www.plural.sh/blog/kubernetes-control-plane-architecture/ Kube Controller Manager: A Quick Guide - Techiescamp, accesso eseguito il giorno dicembre 31, 2025, https://blog.techiescamp.com/docs/kube-controller-manager-a-quick-guide/ A controller in Kubernetes is a control loop that: - DEV Community, accesso eseguito il giorno dicembre 31, 2025, https://dev.to/jumptotech/a-controller-in-kubernetes-is-a-control-loop-that-23d3 Basic Components of Kubernetes Architecture - Appvia, accesso eseguito il giorno dicembre 31, 2025, https://www.appvia.io/blog/components-of-kubernetes-architecture Understanding Custom Resource Definitions, Custom Controllers, and the Operator Framework in Kubernetes | by Damini Bansal, accesso eseguito il giorno dicembre 31, 2025, https://daminibansal.medium.com/understanding-custom-resource-definitions-custom-controllers-and-the-operator-framework-in-5734739e012d Kubernetes Components, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes-docsy-staging.netlify.app/docs/concepts/overview/components/ The Guide to Kubernetes Workload With Examples - Densify, accesso eseguito il giorno dicembre 31, 2025, https://www.densify.com/kubernetes-autoscaling/kubernetes-workload/ Workload Management - Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/concepts/workloads/controllers/ Deployment vs StatefulSet vs DaemonSet: Navigating Kubernetes Workloads, accesso eseguito il giorno dicembre 31, 2025, https://dev.to/sre_panchanan/deployment-vs-statefulset-vs-daemonset-navigating-kubernetes-workloads-190j Controllers :: Introduction to Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://shahadarsh.github.io/docker-k8s-presentation/kubernetes/objects/controllers/ Kubernetes Workload - Resource Types \u0026amp; Examples - Spacelift, accesso eseguito il giorno dicembre 31, 2025, https://spacelift.io/blog/kubernetes-workload Kubernetes Configuration Good Practices, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/blog/2025/11/25/configuration-good-practices/ How do you rollback deployments in Kubernetes? - LearnKube, accesso eseguito il giorno dicembre 31, 2025, https://learnkube.com/kubernetes-rollbacks Kubernetes Controllers vs Operators: Concepts and Use Cases \u0026hellip;, accesso eseguito il giorno dicembre 31, 2025, https://konghq.com/blog/learning-center/kubernetes-controllers-vs-operators Kubernetes StatefulSet vs. Deployment with Use Cases - Spacelift, accesso eseguito il giorno dicembre 31, 2025, https://spacelift.io/blog/statefulset-vs-deployment kubectl rollout undo - Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/reference/kubectl/generated/kubectl_rollout/kubectl_rollout_undo/ kubectl rollout - Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/reference/kubectl/generated/kubectl_rollout/ Kubernetes Deployments, DaemonSets, and StatefulSets: a Deep \u0026hellip;, accesso eseguito il giorno dicembre 31, 2025, https://www.professional-it-services.com/kubernetes-deployments-daemonsets-and-statefulsets-a-deep-dive/ StatefulSets - Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ Kubernetes DaemonSet: Examples, Use Cases \u0026amp; Best Practices - Groundcover, accesso eseguito il giorno dicembre 31, 2025, https://www.groundcover.com/blog/kubernetes-daemonset Mastering K8s Job Timeouts: A Complete Guide - Plural, accesso eseguito il giorno dicembre 31, 2025, https://www.plural.sh/blog/kubernetes-jobs/ What Are Kubernetes Jobs? Use Cases, Types \u0026amp; How to Run - Spacelift, accesso eseguito il giorno dicembre 31, 2025, https://spacelift.io/blog/kubernetes-jobs How to Configure Kubernetes Jobs for Parallel Processing - LabEx, accesso eseguito il giorno dicembre 31, 2025, https://labex.io/tutorials/kubernetes-how-to-configure-kubernetes-jobs-for-parallel-processing-414879 Understanding backoffLimit in Kubernetes Jobs | Baeldung on Ops, accesso eseguito il giorno dicembre 31, 2025, https://www.baeldung.com/ops/kubernetes-backofflimit CronJobs | Google Kubernetes Engine (GKE), accesso eseguito il giorno dicembre 31, 2025, https://docs.cloud.google.com/kubernetes-engine/docs/how-to/cronjobs CronJob in Kubernetes - Automating Tasks on a Schedule - Spacelift, accesso eseguito il giorno dicembre 31, 2025, https://spacelift.io/blog/kubernetes-cronjob CronJob - Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/ How to automate your tasks with Kubernetes CronJob - IONOS UK, accesso eseguito il giorno dicembre 31, 2025, https://www.ionos.co.uk/digitalguide/server/configuration/kubernetes-cronjob/ Service Accounts | Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/concepts/security/service-accounts/ Operator pattern - Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/concepts/extend-kubernetes/operator/ What Is The Kubernetes Operator Pattern? – BMC Software | Blogs, accesso eseguito il giorno dicembre 31, 2025, https://www.bmc.com/blogs/kubernetes-operator/ Ultimate Guide to Kubernetes Operators and How to Create New Operators - Komodor, accesso eseguito il giorno dicembre 31, 2025, https://komodor.com/learn/kubernetes-operator/ The developer\u0026rsquo;s guide to Kubernetes Operators | Red Hat Developer, accesso eseguito il giorno dicembre 31, 2025, https://developers.redhat.com/articles/2024/01/29/developers-guide-kubernetes-operators A complete guide to Kubernetes Operator SDK - Outshift | Cisco, accesso eseguito il giorno dicembre 31, 2025, https://outshift.cisco.com/blog/operator-sdk Build a Kubernetes Operator in six steps - Red Hat Developer, accesso eseguito il giorno dicembre 31, 2025, https://developers.redhat.com/articles/2021/09/07/build-kubernetes-operator-six-steps Kubernetes HPA \\[Horizontal Pod Autoscaler\\]Guide - Spacelift, accesso eseguito il giorno dicembre 31, 2025, https://spacelift.io/blog/kubernetes-hpa-horizontal-pod-autoscaler HPA with Custom GPU Metrics - Docs - Kubermatic Documentation, accesso eseguito il giorno dicembre 31, 2025, https://docs.kubermatic.com/kubermatic/v2.29/tutorials-howtos/hpa-with-custom-gpu-metrics/ Horizontal Pod Autoscaler (HPA) with Custom Metrics: A Guide - overcast blog, accesso eseguito il giorno dicembre 31, 2025, https://overcast.blog/horizontal-pod-autoscaler-hpa-with-custom-metrics-a-guide-0fd5cf0f80b8 7 Common Kubernetes Pitfalls (and How I Learned to Avoid Them), accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/blog/2025/10/20/seven-kubernetes-pitfalls-and-how-to-avoid/ kubectl rollout history - Kubernetes, accesso eseguito il giorno dicembre 31, 2025, https://kubernetes.io/docs/reference/kubectl/generated/kubectl_rollout/kubectl_rollout_history/ Install the Operator on Kubernetes | Couchbase Docs, accesso eseguito il giorno dicembre 31, 2025, https://docs.couchbase.com/operator/current/install-kubernetes.html The Kubernetes Compatibility Matrix Explained - Plural.sh, accesso eseguito il giorno dicembre 31, 2025, https://www.plural.sh/blog/kubernetes-compatibility-matrix/ A pragmatic look at the Kubernetes Threat Matrix | by Simon Elsmie | Beyond DevSecOps, accesso eseguito il giorno dicembre 31, 2025, https://medium.com/beyond-devsecops/a-pragmatic-look-at-the-kubernetes-threat-matrix-d58504e926b5 Tackle Common Kubernetes Security Pitfalls with AccuKnox CNAPP, accesso eseguito il giorno dicembre 31, 2025, https://accuknox.com/blog/avoid-common-kubernetes-mistakes 7 Common Kubernetes Pitfalls in 2023 - Qovery, accesso eseguito il giorno dicembre 31, 2025, https://www.qovery.com/blog/7-common-kubernetes-pitfalls ","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/guides/kubernetes-controllers-guide/","section":"Guide","summary":"","title":"L'architettura dei controller in Kubernetes: guida onnicomprensiva al motore dell'automazione cloud-native","type":"guides"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/load-balancing/","section":"Tags","summary":"","title":"Load-Balancing","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/lxc/","section":"Tags","summary":"","title":"Lxc","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/metallb/","section":"Tags","summary":"","title":"Metallb","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/production/","section":"Tags","summary":"","title":"Production","type":"tags"},{"content":"L’evoluzione delle infrastrutture digitali ha reso la virtualizzazione un elemento cardine non solo per i grandi data center aziendali, ma anche per i contesti di ricerca e i laboratori domestici (home lab). Proxmox Virtual Environment (VE) si distingue in questo panorama come una piattaforma di gestione della virtualizzazione di classe enterprise, completamente open-source, che integra in un’unica soluzione l\u0026rsquo;hypervisor KVM (Kernel-based Virtual Machine) e i container basati su LXC (Linux Containers).1 Questa trattazione esplora in profondità ogni aspetto della piattaforma, partendo dai concetti fondamentali fino alle configurazioni avanzate per la messa in produzione, fornendo al contempo un’analisi critica rispetto ai giganti del cloud pubblico come Amazon Web Services (AWS) e Google Cloud Platform (GCP).\nCapitolo 1: Fondamenti e Architettura di Sistema # Proxmox VE è un hypervisor di tipo 1, definito \u0026ldquo;bare metal\u0026rdquo;, poiché viene installato direttamente sull\u0026rsquo;hardware fisico senza la necessità di un sistema operativo sottostante preesistente.1 Questa architettura garantisce che le risorse della macchina — CPU, RAM, storage e connettività di rete — siano gestite direttamente dal software di virtualizzazione, riducendo drasticamente l\u0026rsquo;overhead e migliorando le prestazioni complessive.1\nIl Kernel e la Base Debian # La stabilità di Proxmox deriva dalla sua base Debian GNU/Linux, su cui viene applicato un kernel modificato per supportare le funzioni critiche di virtualizzazione e clustering.3 L\u0026rsquo;integrazione con Debian permette a Proxmox di beneficiare di un vasto ecosistema di pacchetti e di una gestione degli aggiornamenti tramite lo strumento APT (Advanced Package Tool), rendendo la manutenzione del sistema familiare per gli amministratori Linux.4\nI Pilastri della Gestione: pveproxy e pvedaemon # Il funzionamento di Proxmox è orchestrato da una serie di servizi specializzati che lavorano in concerto per offrire un\u0026rsquo;interfaccia di gestione fluida. Il servizio pveproxy funge da interfaccia web, operando sulla porta 8006 tramite protocollo HTTPS.1 Questo componente agisce come il punto di ingresso principale per l\u0026rsquo;utente, permettendo il controllo totale del datacenter tramite browser.1\nIl pvedaemon, invece, rappresenta il motore operativo che esegue i comandi impartiti dall\u0026rsquo;utente, come la creazione di macchine virtuali o la modifica delle impostazioni di rete.1 In un ambiente cluster, entra in gioco pve-cluster, un servizio che mantiene sincronizzate le configurazioni tra i nodi utilizzando un file system a cluster (pmxcfs).1 Questa architettura assicura che, qualora un amministratore apporti una modifica su un nodo, tale informazione sia istantaneamente disponibile in tutto il cluster, garantendo l\u0026rsquo;integrità operativa.1\nComponente Funzione Principale Dipendenze KVM Hypervisor per virtualizzazione completa Estensioni CPU (Intel VT-x / AMD-V) LXC Virtualizzazione leggera tramite container Condivisione kernel host QEMU Emulazione hardware per VM KVM per accelerazione pveproxy Server interfaccia Web (Porta 8006) Certificati SSL pvedaemon Esecuzione task amministrativi API di sistema pve-cluster Sincronizzazione multi-nodo Corosync (Porte 5404/5405) 1\nCapitolo 2: Tecnologie di Virtualizzazione: KVM vs LXC # La forza distintiva di Proxmox risiede nella sua capacità di offrire due tecnologie di virtualizzazione complementari sotto lo stesso tetto, permettendo agli amministratori di scegliere lo strumento più adatto in base al carico di lavoro specifico.1\nKVM e QEMU: Virtualizzazione Completa # Il binomio KVM/QEMU rappresenta la soluzione per la virtualizzazione completa. In questo scenario, ogni macchina virtuale si comporta come un computer fisico indipendente, dotato di un proprio BIOS/UEFI e di un kernel del sistema operativo autonomo.1 QEMU si occupa dell\u0026rsquo;emulazione dei componenti hardware — come controller disco, schede di rete e schede video — mentre KVM sfrutta le capacità hardware della CPU per eseguire il codice ospite a velocità quasi nativa.1\nQuesta tecnologia è indispensabile per eseguire sistemi operativi non Linux, come Microsoft Windows, o istanze Linux che richiedono kernel personalizzati o un isolamento totale per ragioni di sicurezza.1 Tuttavia, la virtualizzazione completa comporta un costo in termini di risorse: ogni VM richiede una porzione dedicata di RAM e CPU che non può essere facilmente condivisa con altre istanze, rendendola meno efficiente per servizi leggeri.1\nLXC: Efficienza e Velocità dei Container # I Linux Containers (LXC) offrono un approccio radicalmente diverso. Invece di emulare l\u0026rsquo;hardware, LXC isola i processi all\u0026rsquo;interno dell\u0026rsquo;ambiente host, condividendo il kernel del sistema operativo Proxmox.1 Questo elimina la necessità di avviare un intero kernel per ogni applicazione, riducendo i tempi di boot a pochi secondi e abbattendo drasticamente l\u0026rsquo;uso di memoria e CPU.1\nI container sono ideali per eseguire servizi Linux standard, come server web Nginx, database o istanze Docker annidate. La limitazione principale risiede nella compatibilità: un container può eseguire solo distribuzioni Linux e non può avere un kernel differente da quello dell\u0026rsquo;host.1 Nonostante ciò, per carichi di lavoro scalabili, LXC rappresenta la scelta d\u0026rsquo;elezione per ottimizzare la densità di servizi su un singolo mini PC.1\nAnalisi delle Performance: Casi di Studio # Studi comparativi indicano che LXC tende a superare KVM in compiti ad alta intensità di CPU e memoria, grazie al minor overhead.8 Tuttavia, sono stati rilevati casi anomali: in alcuni test relativi a carichi di lavoro Java o Elasticsearch, le VM KVM hanno mostrato prestazioni superiori rispetto agli LXC o addirittura all\u0026rsquo;hardware bare metal.9 Questo fenomeno è spesso attribuito a come il kernel ospite della VM gestisce la pianificazione dei processi e la cache della memoria in modo più aggressivo rispetto a quanto farebbe un processo isolato in un container, suggerendo che per applicazioni specifiche sia necessaria una validazione empirica prima della scelta definitiva.9\nCaratteristica KVM (Virtual Machine) LXC (Container) Isolamento Hardware (Massimo) Processo (Elevato) Kernel Indipendente Condiviso con l\u0026rsquo;host Sistemi Operativi Windows, Linux, BSD, ecc. Solo Linux Tempo di Avvio 30-60 secondi 1-5 secondi Uso RAM Riservata e fissa Dinamica e condivisa Overhead Moderato Minimo 1\nCapitolo 3: Lo Stack di Storage: Prestazioni e Integrità # La gestione dei dati in Proxmox è estremamente flessibile, supportando sia storage locale che distribuito. Per un utente homelab su un mini PC, la scelta tra ZFS e LVM è determinante per le prestazioni e la longevità dell\u0026rsquo;hardware.10\nZFS: Il Gold Standard per l\u0026rsquo;Integrità # ZFS è molto più di un semplice file system; è un gestore di volumi logici con funzionalità avanzate di protezione dei dati.10 La caratteristica più rilevante è il checksumming end-to-end, che permette di rilevare e correggere automaticamente la corruzione dei dati silente (bit rot).10 ZFS eccelle nella gestione degli snapshot e nella replicazione nativa, permettendo di sincronizzare i dischi delle VM tra diversi nodi Proxmox in pochi minuti.10\nTuttavia, ZFS è esigente in termini di risorse. Richiede l\u0026rsquo;accesso diretto ai dischi (HBA mode), rendendolo incompatibile con i controller RAID hardware tradizionali che dovrebbero essere evitati.10 Inoltre, ZFS utilizza la RAM come cache di lettura (ARC), raccomandando almeno 8-16 GB di memoria di sistema per operare in modo ottimale.10\nLVM e LVM-Thin: Velocità e Semplicità # LVM (Logical Volume Manager) è l\u0026rsquo;opzione tradizionale per la gestione dei dischi in Linux. Proxmox implementa LVM-Thin per permettere il \u0026ldquo;thin provisioning\u0026rdquo;, ovvero la possibilità di allocare virtualmente più spazio di quello fisicamente disponibile.10 LVM è estremamente veloce e ha un overhead di CPU e RAM quasi nullo, rendendolo ideale per mini PC con processori economici o poca memoria.10 Il rovescio della medaglia è la mancanza di protezione contro il bit rot e l\u0026rsquo;assenza di replicazione nativa tra i nodi del cluster.10\nStorage Distribuito: Ceph e Shared Storage # Per le configurazioni multi-nodo più ambiziose, Proxmox integra Ceph, un sistema di storage distribuito che trasforma i dischi locali di più server in un unico pool di archiviazione ridondante e altamente disponibile.11 Sebbene Ceph sia considerato lo standard per la produzione enterprise, la sua implementazione su mini PC richiede cautela: sono necessari almeno tre nodi (meglio cinque) e reti veloci (almeno 10GbE) per evitare colli di bottiglia e latenze inaccettabili.11\nTipo Storage Tipo Snapshot Replicazione Ridondanza ZFS Locale/Soft RAID Sì Sì Software RAID LVM-Thin Locale Sì No No (Richiede Hardware RAID) Ceph Distribuito Sì Sì Replica tra nodi NFS / iSCSI Condiviso (NAS) Dipende dal backend No Gestita dal NAS 10\nCapitolo 4: Networking e Segmentazione della Rete # La configurazione della rete in Proxmox si basa sull\u0026rsquo;astrazione dei componenti fisici in bridge virtuali, permettendo una gestione granulare del traffico tra le VM e il mondo esterno.16\nLinux Bridge e Naming Convention # Al momento dell\u0026rsquo;installazione, Proxmox crea un bridge predefinito chiamato vmbr0, che viene collegato alla scheda di rete fisica principale.1 Le moderne installazioni utilizzano nomi di interfaccia predittivi (come eno1 o enp0s3), che evitano cambiamenti di nome dovuti ad aggiornamenti del kernel o modifiche hardware.16 È possibile personalizzare questi nomi creando file .link in /etc/systemd/network/ per garantire una coerenza totale nelle configurazioni multi-nodo.16\nVLAN-Aware Bridge: La Guida alla Segmentazione # Per isolare il traffico in un home lab (ad esempio, separando le telecamere IP dai server di produzione), la tecnica consigliata è l\u0026rsquo;uso di bridge \u0026ldquo;VLAN-aware\u0026rdquo;.17 Invece di creare un bridge separato per ogni VLAN, un singolo bridge può gestire tag 802.1Q multipli. Una volta abilitata l\u0026rsquo;opzione nelle impostazioni del bridge, è sufficiente specificare il \u0026ldquo;VLAN Tag\u0026rdquo; nell\u0026rsquo;hardware di rete della VM.17\nQuesto approccio offre diversi vantaggi:\nSemplicità: Riduce la complessità dei file di configurazione /etc/network/interfaces.17 Flessibilità: Permette di cambiare la rete di una VM senza dover modificare l\u0026rsquo;infrastruttura di rete dell\u0026rsquo;host.17 Sicurezza: Abbinato a un firewall, impedisce il movimento laterale tra zone di sicurezza diverse.17 Il Ruolo di OpenVSwitch (OVS) # Per scenari di networking ancora più complessi, Proxmox supporta OpenVSwitch, uno switch virtuale multistrato progettato per operare in ambienti cluster su larga scala.19 OVS offre funzionalità avanzate di monitoraggio e gestione, ma richiede un\u0026rsquo;installazione separata (apt install openvswitch-switch) e una configurazione manuale che può risultare superflua per la maggior parte dei piccoli laboratori.19\nCapitolo 5: Dal Laboratorio alla Produzione: Manutenzione e Aggiornamenti # Trasformare un\u0026rsquo;installazione sperimentale in un sistema pronto per la produzione richiede il passaggio a pratiche di gestione più rigorose, specialmente per quanto riguarda la sicurezza e l\u0026rsquo;integrità del software.21\nGestione dei Repository: Enterprise vs No-Subscription # Proxmox offre diversi canali per gli aggiornamenti. Di default, il sistema è configurato con il repository \u0026ldquo;Enterprise\u0026rdquo;, che garantisce pacchetti estremamente stabili e testati, ma richiede una sottoscrizione a pagamento.3 Per gli utenti che non necessitano del supporto ufficiale, il repository \u0026ldquo;No-Subscription\u0026rdquo; è la scelta corretta.4\nPer passare al repository gratuito su Proxmox 8, è necessario modificare i file in /etc/apt/sources.list.d/. La procedura corretta prevede di commentare il repository enterprise e aggiungere la riga per il no-subscription, assicurandosi di includere anche il repository corretto per Ceph (anche se non utilizzato direttamente, alcuni pacchetti sono necessari) per evitare errori durante l\u0026rsquo;aggiornamento.5\nEsempio di configurazione per Proxmox 8 (Bookworm):\nBash\n# Disabilitare Enterprise\nsed -i \u0026rsquo;s/^deb/#deb/\u0026rsquo; /etc/apt/sources.list.d/pve-enterprise.list\n# Aggiungere No-Subscription\ncat \u0026gt; /etc/apt/sources.list.d/pve-no-subscription.list \u0026lt;\u0026lt; EOF\ndeb http://download.proxmox.com/debian/pve bookworm pve-no-subscription\nEOF\nÈ fondamentale utilizzare sempre il comando apt dist-upgrade invece di apt upgrade per assicurarsi che i nuovi pacchetti del kernel e le dipendenze strutturali di Proxmox siano installati correttamente.21\nHardening e Sicurezza dell\u0026rsquo;Accesso # La sicurezza di un sistema in produzione inizia dall\u0026rsquo;accesso amministrativo. Si raccomanda di:\nDisabilitare il login root via SSH: Utilizzare utenti non privilegiati dotati di sudo.22 Implementare il 2FA: Proxmox supporta nativamente TOTP e WebAuthn per l\u0026rsquo;accesso alla GUI.6 Certificati SSL: Sostituire il certificato auto-firmato con uno emesso da Let\u0026rsquo;s Encrypt tramite il protocollo ACME integrato.24 La configurazione ACME può essere effettuata direttamente dalla GUI sotto Datacenter \u0026gt; ACME. Se il Proxmox non è esposto pubblicamente, è possibile utilizzare le sfide DNS tramite plugin per fornitori come Cloudflare o DuckDNS, permettendo l\u0026rsquo;ottenimento di certificati validi anche in reti locali isolate.25\nCapitolo 6: Strategie di Backup con Proxmox Backup Server (PBS) # Un sistema senza un piano di backup non può essere considerato \u0026ldquo;in produzione\u0026rdquo;. Proxmox ha rivoluzionato questo aspetto con il lancio di Proxmox Backup Server (PBS), una soluzione dedicata che si integra perfettamente con Proxmox VE.28\nDeduplicazione e Integrità dei Dati # A differenza dei backup tradizionali (basati su file .vzdump), PBS opera a livello di blocchi. Questo significa che se dieci macchine virtuali eseguono lo stesso sistema operativo Linux, i blocchi di dati identici vengono salvati una sola volta sul server di backup.28 I vantaggi sono molteplici:\nRisparmio di spazio: Riduzione dello storage necessario anche del 90% in ambienti omogenei.29 Velocità: I backup incrementali trasferiscono solo i blocchi modificati, riducendo i tempi di esecuzione da ore a minuti.28 Verifica: PBS permette di programmare controlli periodici dell\u0026rsquo;integrità (Garbage Collection e Verification) per assicurarsi che i dati non siano corrotti.28 Implementazione di PBS # PBS può essere installato su hardware bare metal dedicato o, per test, come VM (anche se non raccomandato per la produzione reale dei backup critici dell\u0026rsquo;host che lo ospita).28 Una configurazione tipica prevede un programma di manutenzione rigoroso:\nPruning: Rimozione automatica dei vecchi backup in base a regole di conservazione (es. mantenere 7 giornalieri, 4 settimanali).28 Garbage Collection: Liberazione dello spazio fisico sul disco dopo che i blocchi sono stati marcati per la cancellazione dal pruning.28 Operazione Orario Consigliato Scopo Backup VM 02:00 Copia dati degli ospiti Pruning 03:00 Applicazione policy conservazione Garbage Collection 03:30 Recupero spazio fisico Verification 05:00 Controllo integrità blocchi 31\nCapitolo 7: Ottimizzazione per Mini PC e Risparmio Energetico # I mini PC sono popolari per gli home lab grazie al loro basso consumo, ma Proxmox è configurato di default per massimizzare le prestazioni, il che può portare a temperature elevate e sprechi energetici.32\nCPU Governor: Powersave vs Performance # Di default, Proxmox imposta il governor della CPU su performance, forzando i core alla massima frequenza.33 Per i mini PC, è consigliabile cambiare questa impostazione in powersave. Contrariamente al nome, nei processori moderni (specialmente Intel Core i5/i7 recenti), il governor powersave permette comunque alla CPU di accelerare istantaneamente sotto carico, ma la fa scendere a frequenze minime in idle, risparmiando anche 40-50W per nodo.33\nÈ possibile automatizzare questo cambiamento aggiungendo un comando al crontab dell\u0026rsquo;host:\nBash\n@reboot echo \u0026ldquo;powersave\u0026rdquo; | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor \u0026gt;/dev/null 2\u0026gt;\u0026amp;1\nQuesto garantisce che l\u0026rsquo;impostazione persista dopo ogni riavvio.33\nGestione Energetica Avanzata: Powertop e ASPM # Per ottimizzare ulteriormente il consumo, è possibile utilizzare strumenti come powertop per identificare i componenti che impediscono alla CPU di entrare negli stati di risparmio energetico profondi (C-states).32 Spesso, l\u0026rsquo;abilitazione dell\u0026rsquo;ASPM (Active State Power Management) nel BIOS o tramite parametri del kernel può dimezzare il consumo in idle dei mini PC dotati di NIC Intel o Realtek.33\nCriticità del Passthrough Hardware # Una sfida comune nei mini PC è il passthrough hardware, ad esempio passare un controller SATA o una iGPU a una VM specifica. È stato documentato che su alcuni modelli (come gli Aoostar), il passaggio del controller SATA può disabilitare le funzioni di gestione termica e di boosting della CPU dell\u0026rsquo;host, poiché il controller è integrato direttamente nel SoC.37 In questi casi, l\u0026rsquo;host perde la capacità di leggere i sensori di temperatura e, per protezione, blocca la CPU alla frequenza base, degradando le prestazioni generali.37\nCapitolo 8: Clustering e Alta Affidabilità (HA) # Sebbene Proxmox possa operare come nodo singolo, la sua vera potenza emerge in una configurazione cluster.\nLa Scienza del Quorum # In un cluster Proxmox, la stabilità è garantita dal concetto di \u0026ldquo;quorum\u0026rdquo;. Ogni nodo ha un voto e, affinché il cluster sia operativo, deve essere presente la maggioranza dei voti (50% + 1).38 Con due soli nodi, se uno fallisce, il cluster perde il quorum e i servizi si bloccano per evitare il fenomeno del \u0026ldquo;split-brain\u0026rdquo;.15\nLa soluzione ottimale è un cluster a tre nodi.38 Se non si dispone di tre mini PC identici, è possibile utilizzare un \u0026ldquo;Quorum Device\u0026rdquo; (QDevice). Un QDevice può essere un\u0026rsquo;istanza minima di Linux eseguita su un Raspberry Pi o persino in una piccola VM su un altro hardware, che fornisce il terzo voto necessario per mantenere il quorum in un setup a due nodi principali.15\nLive Migration e HA # Con uno storage condiviso (come un NAS via NFS) o tramite la replicazione ZFS, è possibile eseguire la \u0026ldquo;Live Migration\u0026rdquo; delle macchine virtuali da un host all\u0026rsquo;altro senza interruzioni di servizio.13 In caso di guasto hardware di un nodo, il gestore di alta affidabilità (HA Manager) di Proxmox rileverà l\u0026rsquo;assenza del nodo e riavvierà automaticamente le VM sugli host superstiti, minimizzando i tempi di fermo.15\nCapitolo 9: Proxmox vs. Cloud Pubblico (AWS e GCP) # Molti utenti si chiedono perché gestire un proprio server Proxmox invece di utilizzare servizi pronti all\u0026rsquo;uso come AWS o GCP. La risposta risiede in un equilibrio tra costi, controllo e apprendimento.\nAnalisi dei Costi (TCO) # AWS e GCP utilizzano un modello \u0026ldquo;pay-as-you-go\u0026rdquo; che può apparire economico inizialmente, ma i costi scalano rapidamente.40 Per un\u0026rsquo;istanza con 8 GB di RAM e 2 vCPU, il costo nel cloud può aggirarsi intorno ai 50-70 euro al mese.42 Un mini PC di fascia media per un home lab costa circa 300-500 euro; l\u0026rsquo;investimento iniziale si ripaga quindi in meno di un anno di utilizzo continuo.42 Inoltre, il cloud addebita costi per il traffico dati in uscita (egress), mentre nel proprio laboratorio l\u0026rsquo;unico limite è la larghezza di banda della propria connessione internet.45\nPrivacy e Sovranità dei Dati # Proxmox offre una privacy totale. I dati risiedono fisicamente nel mini PC dell\u0026rsquo;utente, non su server di terze parti soggetti a normative straniere o cambiamenti delle policy aziendali.44 Questo è fondamentale per la gestione di dati sensibili, backup personali o per chi desidera evitare il \u0026ldquo;vendor lock-in\u0026rdquo;.2\nComplessità Operativa e Learning Curve # AWS e GCP offrono migliaia di servizi gestiti (database, IA, networking globale) che Proxmox non può replicare facilmente.40 Tuttavia, imparare Proxmox significa comprendere i fondamenti dell\u0026rsquo;IT: hypervisor, file system, networking Linux e sicurezza di rete.1 Queste sono competenze universali che rimangono valide indipendentemente dal cloud provider utilizzato in futuro.38\nDimensione Proxmox VE AWS / GCP Controllo Hardware Totale Nessuno Costi Egress Zero Elevati Manutenzione Utente (Self-managed) Provider (Managed) Integrazione IA/ML Manuale Servizi nativi (Vertex AI, SageMaker) Scalabilità Hardware limitato Virtualmente infinita Proprietà dei Dati Utente Fornitore del servizio 40\nCapitolo 10: Conclusioni e Roadmap per l\u0026rsquo;Utente # Proxmox VE rappresenta il perfetto ponte tra la sperimentazione domestica e l\u0026rsquo;affidabilità professionale. Per un utente che parte da zero con un mini PC, il percorso verso la produzione segue tappe precise che trasformano un semplice hobby in un\u0026rsquo;infrastruttura resiliente.\nLa forza di questa piattaforma non risiede solo nelle sue capacità tecniche — come la velocità dei container LXC o l\u0026rsquo;integrità di ZFS — ma nella sua comunità e nella sua natura aperta. Mentre il cloud pubblico continuerà a dominare gli scenari di scala globale e le applicazioni \u0026ldquo;cloud-native\u0026rdquo;, Proxmox rimane la scelta d\u0026rsquo;elezione per chiunque cerchi indipendenza tecnologica, efficienza economica e un controllo granulare sul proprio ambiente digitale.\nImplementare Proxmox oggi significa investire in un sistema che cresce con le proprie esigenze, passando da una singola macchina a un cluster ridondante, protetto da un backup allo stato dell\u0026rsquo;arte e ottimizzato per consumare solo lo stretto necessario. Che si tratti di ospitare un server Home Assistant, un database per lo sviluppo o un\u0026rsquo;intera infrastruttura aziendale, Proxmox VE si conferma una delle soluzioni di virtualizzazione più complete e potenti disponibili sul mercato.\nBibliografia # Understanding the Proxmox Architecture: From ESXi to Proxmox VE 8.4 - Dev Genius, accesso eseguito il giorno dicembre 29, 2025, https://blog.devgenius.io/understanding-the-proxmox-architecture-from-esxi-to-proxmox-ve-8-4-0d41d300365a What Is Proxmox? Guide to Open Source Virtualization - CloudFire Srl, accesso eseguito il giorno dicembre 29, 2025, https://www.cloudfire.it/en/blog/proxmox-guida-virtualizzazione-open-source \\[SOLVED\\]- Explain please pve-no-subscription | Proxmox Support Forum, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/explain-please-pve-no-subscription.102743/ Package Repositories - Proxmox VE, accesso eseguito il giorno dicembre 29, 2025, https://pve.proxmox.com/wiki/Package_Repositories How to Setup Proxmox VE 8.4 Non-Subscription Repositories + \u0026hellip;, accesso eseguito il giorno dicembre 29, 2025, https://ecintelligence.ma/en/blog/how-to-setup-proxmox-ve-84-non-subscription-reposi/ Proxmox VE Port Requirements: The Complete Guide | Saturn ME, accesso eseguito il giorno dicembre 29, 2025, https://www.saturnme.com/proxmox-ve-port-requirements-the-complete-guide/ Firewall Ports Cluster Configuration - Proxmox Support Forum, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/firewall-ports-cluster-configuration.16210/ Proxmox VE: Performance of KVM vs. LXC - IKUS, accesso eseguito il giorno dicembre 29, 2025, https://ikus-soft.com/en_CA/blog/techies-10/proxmox-ve-performance-of-kvm-vs-lxc-75 Performance of LXC vs KVM - Proxmox Support Forum, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/performance-of-lxc-vs-kvm.43170/ Choosing the Right Proxmox Local Storage Format: ZFS vs LVM - Instelligence, accesso eseguito il giorno dicembre 29, 2025, https://www.instelligence.io/blog/2025/08/choosing-the-right-proxmox-local-storage-format-zfs-vs-lvm/ Proxmox VE Storage Options: Comprehensive Comparison Guide - Saturn ME, accesso eseguito il giorno dicembre 29, 2025, https://www.saturnme.com/proxmox-ve-storage-options-comprehensive-comparison-guide/ \\[SOLVED\\]- Performance comparison between ZFS and LVM - Proxmox Support Forum, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/performance-comparison-between-zfs-and-lvm.124295/ Proxmox with Local M.2 Storage: The Best Storage \u0026amp; Backup Strategy (No Ceph Needed), accesso eseguito il giorno dicembre 29, 2025, https://www.detectx.com.au/proxmox-with-local-m-2-storage-the-best-storage-backup-strategy-no-ceph-needed/ Mini PC Proxmox cluster with ceph, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/mini-pc-proxmox-cluster-with-ceph.156601/ HA Best Practice | Proxmox Support Forum, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/ha-best-practice.157253/ Network Configuration - Proxmox VE, accesso eseguito il giorno dicembre 29, 2025, https://pve.proxmox.com/wiki/Network_Configuration Proxmox VLAN Configuration | Bankai-Tech Docs, accesso eseguito il giorno dicembre 29, 2025, https://docs.bankai-tech.com/Proxmox/Docs/Networking/VLAN%20Configuration Proxmox VLAN Configuration: Linux Bridge Tagging, Management IP, and Virtual Machines, accesso eseguito il giorno dicembre 29, 2025, https://www.youtube.com/watch?v=stQzK0p59Fc Proxmox VLANs Demystified: Step-by-Step Network Isolation for Your Homelab - Medium, accesso eseguito il giorno dicembre 29, 2025, https://medium.com/@P0w3rChi3f/proxmox-vlan-configuration-a-step-by-step-guide-edc838cc62d8 Proxmox vlan handling - Homelab - LearnLinuxTV Community, accesso eseguito il giorno dicembre 29, 2025, https://community.learnlinux.tv/t/proxmox-vlan-handling/3232 How to Safely Update Proxmox VE: A Complete Guide - Saturn ME, accesso eseguito il giorno dicembre 29, 2025, https://www.saturnme.com/how-to-safely-update-proxmox-ve-a-complete-guide/ Proxmox server hardening document for compliance, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/proxmox-server-hardening-document-for-compliance.146961/ \\[SOLVED\\]- converting from no subscription repo to subscription - Proxmox Support Forum, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/converting-from-no-subscription-repo-to-subscription.164060/ How to Secure Your Proxmox VE Web Interface with Let\u0026rsquo;s Encrypt SSL - Skynats, accesso eseguito il giorno dicembre 29, 2025, https://www.skynats.com/blog/how-to-secure-your-proxmox-ve-web-interface-with-lets-encrypt-ssl/ Automate Proxmox SSL Certificates with ACME and Dynv6, accesso eseguito il giorno dicembre 29, 2025, https://bitingbytes.de/posts/2025/proxmox-ssl-certificate-with-dynv6/ Managing Certificates in Proxmox VE 8.1: A Step-by-Step Guide - BDRShield, accesso eseguito il giorno dicembre 29, 2025, https://www.bdrshield.com/blog/managing-certificates-in-proxmox-ve-8-1/ Step-by-step guide to configure Proxmox Web GUI/API with Let\u0026rsquo;s Encrypt certificate and automatic validation using the ACME protocol in DNS alias mode with DNS TXT validation redirection to Duck DNS. - GitHub Gist, accesso eseguito il giorno dicembre 29, 2025, https://gist.github.com/zidenis/e93532c0e6f91cb75d429f7ac7f66ba5 Proxmox Backup Server, accesso eseguito il giorno dicembre 29, 2025, https://homelab.casaursus.net/proxmox-backup-server/ Features - Proxmox Backup Server, accesso eseguito il giorno dicembre 29, 2025, https://www.proxmox.com/en/products/proxmox-backup-server/features How To: Proxmox Backup Server 4 (VM) Installation, accesso eseguito il giorno dicembre 29, 2025, https://www.derekseaman.com/2025/08/how-to-proxmox-backup-server-4-vm-installation.html Proxmox Backup Server - Our Home Lab, accesso eseguito il giorno dicembre 29, 2025, https://homelab.anita-fred.net/pbs/ Guide for Proxmox powersaving - Technologie Hub Wien, accesso eseguito il giorno dicembre 29, 2025, https://technologiehub.at/project-posts/tutorial/guide-for-proxmox-powersaving/ PSA How to configure Proxmox for lower power usage - Home Assistant Community, accesso eseguito il giorno dicembre 29, 2025, https://community.home-assistant.io/t/psa-how-to-configure-proxmox-for-lower-power-usage/323731 CPU power throttle back to save energy - Proxmox Support Forum, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/cpu-power-throttle-back-to-save-energy.27510/ gaming rig to run proxmox server - how do i lower my idle power? - Reddit, accesso eseguito il giorno dicembre 29, 2025, https://www.reddit.com/r/Proxmox/comments/1fwphxw/gaming_rig_to_run_proxmox_server_how_do_i_lower/ Powersaving tutorial : r/Proxmox - Reddit, accesso eseguito il giorno dicembre 29, 2025, https://www.reddit.com/r/Proxmox/comments/1nultme/powersaving_tutorial/ WTR Pro CPU throttling - Proxmox Support Forum, accesso eseguito il giorno dicembre 29, 2025, https://forum.proxmox.com/threads/wtr-pro-cpu-throttling.160039/ How to Set Up a Proxmox Cluster for Free – Virtualization Basics - freeCodeCamp, accesso eseguito il giorno dicembre 29, 2025, https://www.freecodecamp.org/news/set-up-a-proxmox-cluster-virtualization-basics/ Building a Highly Available (HA) two-node Home Lab on Proxmox - Jon, accesso eseguito il giorno dicembre 29, 2025, https://jon.sprig.gs/blog/post/2885 AWS Vs. GCP: Which Platform Offers Better Pricing? - CloudZero, accesso eseguito il giorno dicembre 29, 2025, https://www.cloudzero.com/blog/aws-vs-gcp/ AWS vs GCP vs Azure: Which Cloud Platform is Best for Mid-Size Businesses? - Qovery, accesso eseguito il giorno dicembre 29, 2025, https://www.qovery.com/blog/aws-vs-gcp-vs-azure What\u0026rsquo;s the Difference Between AWS vs. Azure vs. Google Cloud? - Coursera, accesso eseguito il giorno dicembre 29, 2025, https://www.coursera.org/articles/aws-vs-azure-vs-google-cloud I set up a tiny PC Proxmox cluster! : r/homelab - Reddit, accesso eseguito il giorno dicembre 29, 2025, https://www.reddit.com/r/homelab/comments/15gkr1r/i_set_up_a_tiny_pc_proxmox_cluster/ Compare Google Compute Engine vs Proxmox VE 2025 | TrustRadius, accesso eseguito il giorno dicembre 29, 2025, https://www.trustradius.com/compare-products/google-compute-engine-vs-proxmox-ve Cloud Comparison AWS vs Azure vs GCP – Networking \u0026amp; Security - Exeo, accesso eseguito il giorno dicembre 29, 2025, https://exeo.net/en/networking-security-cloud-comparison-aws-vs-azure-vs-gcp/ AWS vs GCP: Unraveling the cloud conundrum - Proxify, accesso eseguito il giorno dicembre 29, 2025, https://proxify.io/articles/aws-vs-gcp AWS vs GCP - Which One to Choose in 2025? - ProjectPro, accesso eseguito il giorno dicembre 29, 2025, https://www.projectpro.io/article/aws-vs-gcp-which-one-to-choose/477 AWS vs. GCP: A Developer\u0026rsquo;s Guide to Picking the Right Cloud - DEV Community, accesso eseguito il giorno dicembre 29, 2025, https://dev.to/shrsv/aws-vs-gcp-a-developers-guide-to-picking-the-right-cloud-59a1 ","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/guides/proxmox-complete-guide/","section":"Guide","summary":"","title":"Proxmox Virtual Environment: Architettura, Implementazione e Analisi Comparativa verso il Cloud Pubblico","type":"guides"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/static-site-generator/","section":"Tags","summary":"","title":"Static-Site-Generator","type":"tags"},{"content":"L\u0026rsquo;adozione di Talos OS come sistema operativo per i nodi Kubernetes rappresenta un cambio di paradigma verso l\u0026rsquo;immutabilità, la sicurezza e la gestione dichiarativa tramite API. Tuttavia, la natura minimalista e la mancanza di una shell tradizionale in Talos pongono sfide specifiche quando si tratta di configurare l\u0026rsquo;endpoint ad alta affidabilità (HA) per l\u0026rsquo;API server e l\u0026rsquo;esposizione dei servizi verso l\u0026rsquo;esterno. La scelta tra il Virtual IP (VIP) nativo di Talos, kube-vip e MetalLB non è puramente tecnica, ma dipende dalla scala del cluster, dai requisiti di latenza e dalla complessità dell\u0026rsquo;infrastruttura di rete sottostante.1 Una comprensione profonda di come questi componenti interagiscono con il kernel Linux e il piano di controllo di Kubernetes è essenziale per implementare una strategia di bilanciamento del carico che sia resiliente e scalabile.\nFondamenti dell\u0026rsquo;Alta Affidabilità del Control Plane in Talos OS # Il cuore di un cluster Kubernetes è il suo piano di controllo, che include componenti critici come etcd, kube-apiserver, kube-scheduler e kube-controller-manager. In Talos OS, questi componenti vengono eseguiti come pod statici gestiti direttamente dal kubelet.5 La sfida principale nell\u0026rsquo;architettura di un cluster HA consiste nel fornire ai client, come kubectl o i nodi worker, un unico endpoint stabile (un indirizzo IP o un URL) che possa raggiungere qualsiasi nodo del piano di controllo disponibile, garantendo la continuità operativa anche in caso di guasto di uno o più nodi.1\nTalos OS affronta questa sfida attraverso diverse metodologie, ognuna con implicazioni differenti in termini di velocità di failover e capacità di carico. L\u0026rsquo;approccio più immediato è l\u0026rsquo;utilizzo del VIP nativo integrato nel sistema operativo, ma man mano che il carico esterno sull\u0026rsquo;API server aumenta, emerge la necessità di soluzioni più sofisticate come bilanciatori di carico esterni o implementazioni basate su BGP.7\nIl Meccanismo del Virtual IP Nativo di Talos # Il VIP nativo di Talos è una funzionalità integrata progettata per semplificare la creazione di cluster HA senza richiedere risorse esterne come proxy inversi o bilanciatori di carico hardware.1 Questo meccanismo si basa sulla contesa dell\u0026rsquo;indirizzo IP condiviso tra i nodi del control plane attraverso un processo di elezione gestito da etcd.1\nDal punto di vista operativo, la configurazione richiede che tutti i nodi del control plane condividano una rete Layer 2. L\u0026rsquo;indirizzo VIP deve essere un indirizzo riservato e non utilizzato all\u0026rsquo;interno della stessa sottorete dei nodi.1 Un aspetto cruciale di questa implementazione è che il VIP non diventa attivo finché il cluster Kubernetes non è stato bootstrappato, poiché la sua gestione dipende direttamente dallo stato di salute di etcd.1\nCaratteristica del VIP Nativo Dettaglio Tecnico Requisito di Rete Connettività Layer 2 (stessa sottorete/switch) Meccanismo di Elezione Basato sul quorum di etcd Comportamento Failover Quasi istantaneo per shutdown graziosi; fino a 1 minuto per crash improvvisi Limitazione di Carico Solo un nodo riceve il traffico alla volta (Active-Passive) Dipendenza Bootstrap Attivo solo dopo la formazione del cluster etcd 1\nL\u0026rsquo;analisi dei tempi di failover rivela un\u0026rsquo;importante decisione progettuale dei creatori di Talos. Mentre una disconnessione ordinata permette un passaggio di consegne immediato, un guasto improvviso richiede che Talos attenda il timeout dell\u0026rsquo;elezione di etcd. Questo ritardo è intenzionale e serve a garantire che non si verifichino scenari di \u0026ldquo;split-brain\u0026rdquo;, in cui più nodi annunciano lo stesso IP contemporaneamente, una situazione che potrebbe corrompere le sessioni di rete e destabilizzare l\u0026rsquo;accesso all\u0026rsquo;API.1\nKubePrism: L\u0026rsquo;Eroe Silenzioso dell\u0026rsquo;Alta Affidabilità Interna # Spesso confuso con le soluzioni di VIP esterno, KubePrism è in realtà una funzionalità complementare e distinta.8 Mentre il VIP nativo o kube-vip servono principalmente per l\u0026rsquo;accesso esterno (come i comandi kubectl), KubePrism è progettato esclusivamente per l\u0026rsquo;accesso interno al cluster.7 Esso crea un endpoint di bilanciamento del carico locale su ogni nodo del cluster (solitamente su localhost:7445), che i processi interni come il kubelet utilizzano per comunicare con l\u0026rsquo;API server.8\nL\u0026rsquo;importanza di KubePrism risiede nella sua capacità di astrarre la complessità del piano di controllo dai nodi worker. Se il bilanciatore di carico esterno o il VIP dovessero fallire, KubePrism dispone di un meccanismo di fallback automatico che consente ai nodi di continuare a funzionare comunicando direttamente con i nodi del control plane.7 In architetture di produzione, è raccomandato mantenere KubePrism sempre abilitato per garantire che la salute interna del cluster non dipenda mai esclusivamente da un singolo endpoint di rete esterno.7\nAnalisi delle Strategie per il Bilanciamento del Carico dei Servizi # Oltre all\u0026rsquo;accesso all\u0026rsquo;API server, la gestione del traffico verso i carichi di lavoro (workload) richiede l\u0026rsquo;implementazione di servizi di tipo LoadBalancer. In ambienti bare-metal o virtualizzati dove Talos viene comunemente distribuito, questa funzionalità non è fornita automaticamente dal fornitore di cloud, rendendo necessaria l\u0026rsquo;installazione di controller specifici come MetalLB o kube-vip.3\nMetalLB: Lo Standard per i Servizi Bare-Metal # MetalLB è probabilmente la soluzione più matura e diffusa per fornire bilanciamento del carico in ambienti on-premise.3 Esso opera monitorando le risorse di tipo Service con spec.type: LoadBalancer e assegnando loro un indirizzo IP da un pool preconfigurato.3\nMetalLB supporta due modalità operative principali: Layer 2 e BGP. Nella modalità Layer 2, uno dei nodi del cluster viene eletto \u0026ldquo;leader\u0026rdquo; per un determinato indirizzo IP del servizio e risponde alle richieste ARP (per IPv4) o NDP (per IPv6).3 Sebbene sia estremamente semplice da configurare, questa modalità presenta il limite di convogliare tutto il traffico di un servizio attraverso un singolo nodo, creando un potenziale collo di bottiglia.4 Al contrario, la modalità BGP permette a ogni nodo di annunciare l\u0026rsquo;indirizzo IP del servizio ai router della rete, abilitando il bilanciamento del carico vero e proprio tramite ECMP (Equal-Cost Multi-Path).4\nKube-vip: Versatilità e Unificazione # Kube-vip si distingue per la sua capacità di gestire sia l\u0026rsquo;HA del control plane che il bilanciamento del carico dei servizi in un unico componente.2 A differenza del VIP nativo di Talos, kube-vip può essere configurato per utilizzare IPVS (IP Virtual Server) per distribuire il traffico dell\u0026rsquo;API server su tutti i nodi del control plane in modalità active-active, migliorando notevolmente le prestazioni sotto carico elevato.14\nKube-vip può funzionare come pod statico, il che lo rende ideale per scenari in cui l\u0026rsquo;endpoint HA deve essere disponibile fin dai primissimi istanti del bootstrap del cluster, prima ancora che il database etcd sia completamente formato.14 Tuttavia, la sua configurazione come bilanciatore di servizi è spesso considerata meno ricca di funzionalità rispetto a MetalLB, che offre una gestione più granulare dei pool di indirizzi e delle policy di annuncio.16\nConfronto tra le Strategie Richieste dall\u0026rsquo;Utente # La scelta della combinazione corretta di strumenti dipende dalla necessità di bilanciare semplicità operativa e scalabilità. Di seguito viene analizzato il confronto tra le tre strategie principali sollevate nel quesito.\nStrategia 1: VIP Nativo di Talos con MetalLB # Questa è la configurazione più comune e consigliata per cluster di dimensioni piccole e medie (fino a 10-20 nodi) in ambienti Layer 2.7\nVantaggi: Sfrutta la stabilità del sistema operativo per l\u0026rsquo;accesso critico all\u0026rsquo;API e utilizza MetalLB, che è lo standard di settore, per la gestione dei servizi applicativi. La separazione dei compiti rende il sistema facile da diagnosticare: i problemi dell\u0026rsquo;API sono legati alla configurazione di Talos, mentre i problemi delle applicazioni sono legati a MetalLB.17 Svantaggi: L\u0026rsquo;accesso all\u0026rsquo;API server è limitato alla capacità di un singolo nodo (active-passive), il che potrebbe non essere sufficiente per cluster con un\u0026rsquo;altissima frequenza di operazioni API (ad esempio, ambienti CI/CD massivi).7 Strategia 2: Kube-vip senza MetalLB # Questa strategia punta all\u0026rsquo;unificazione delle funzioni di rete sotto un unico controller.2\nVantaggi: Riduce il numero di componenti da gestire nel cluster. Kube-vip può gestire sia l\u0026rsquo;IP dell\u0026rsquo;API server che gli IP dei servizi LoadBalancer. Supporta IPVS per il bilanciamento reale dell\u0026rsquo;API.14 Svantaggi: Sebbene sia versatile, kube-vip può risultare più complesso da configurare correttamente per coprire tutte le casistiche di MetalLB, specialmente in reti BGP complesse. La perdita del pod kube-vip potrebbe, in teoria, interrompere contemporaneamente sia l\u0026rsquo;accesso al piano di controllo che a tutti i servizi del cluster.16 Strategia 3: Kube-vip con MetalLB # In questa configurazione, kube-vip viene utilizzato esclusivamente per l\u0026rsquo;alta affidabilità del control plane, mentre MetalLB gestisce i servizi applicativi.16\nVantaggi: Offre le migliori prestazioni per l\u0026rsquo;API server (grazie a IPVS o BGP ECMP forniti da kube-vip) mantenendo la flessibilità di MetalLB per le applicazioni.17 È una scelta eccellente per ambienti enterprise dove il control plane è sottoposto a forte stress. Svantaggi: È la configurazione più complessa da mantenere, richiedendo la gestione di due diversi controller di rete che potrebbero entrare in conflitto se non configurati con attenzione (ad esempio, entrambi che tentano di ascoltare sulla porta BGP 179).3 Caratteristica VIP Nativo + MetalLB Kube-vip (Solo) Kube-vip + MetalLB Complessità Bassa Media Alta Performance API Active-Passive Active-Active (IPVS) Active-Active (IPVS) Performance Servizi Elevata (L2/BGP) Media Elevata (L2/BGP) Standardizzazione Molto Comune Comune Professionale/Enterprise Uso Consigliato Homelab / PMI Sistemi Minimalisti Cluster ad alto carico 3\nStrategie Differenziate per Dimensione del Cluster # Il dimensionamento del cluster è un fattore determinante per la scelta della strategia di bilanciamento. Ciò che funziona per un piccolo server domestico potrebbe non essere adeguato per un data center distribuito.\nCluster Piccoli e Ambienti \u0026ldquo;Minecraft\u0026rdquo; # Per \u0026ldquo;configurazione Minecraft\u0026rdquo; si intende solitamente un cluster di dimensioni ridotte, spesso costituito da un solo nodo o da un piccolo set di nodi (3 o meno), tipico degli ambienti homelab o di test.21\nIn un cluster a singolo nodo, è fondamentale prestare attenzione a un dettaglio tecnico di Talos: per impostazione predefinita, i nodi del control plane sono etichettati per essere esclusi dai bilanciatori di carico esterni (node.kubernetes.io/exclude-from-external-load-balancers: \u0026ldquo;\u0026rdquo;).24 In un cluster multi-nodo, questo protegge i nodi master dal traffico applicativo, ma in un cluster a singolo nodo, impedisce a MetalLB o kube-vip di esporre correttamente i servizi.24 La soluzione consiste nel rimuovere o commentare questa etichetta nella configurazione della macchina.24\nPer questi piccoli cluster, la raccomandazione è la semplicità assoluta:\nControl Plane: Utilizzare il VIP nativo di Talos.7 Servizi: Utilizzare MetalLB in modalità Layer 2.10 Storage: Spesso accoppiato con Longhorn per la semplicità di gestione su pochi nodi.7 Cluster di Grandi Dimensioni (\u0026gt;100 Nodi) # In cluster di scala enterprise, le limitazioni della rete Layer 2 diventano evidenti. Il traffico di broadcast ARP per la gestione dei VIP può degradare le prestazioni della rete e la velocità di failover basata sull\u0026rsquo;elezione di etcd potrebbe non soddisfare i requisiti di disponibilità.4\nLe linee guida di Sidero Labs (gli sviluppatori di Talos) per i cluster ad alto carico suggeriscono di spostare la responsabilità del bilanciamento dell\u0026rsquo;API server all\u0026rsquo;esterno del cluster.6 L\u0026rsquo;uso di un bilanciatore di carico esterno (F5, Netscaler, o un\u0026rsquo;istanza HAProxy dedicata) che distribuisce le richieste a tutti i nodi del control plane sani è l\u0026rsquo;opzione più resiliente.6 Questo approccio scarica la CPU dei nodi master dalla gestione del traffico di rete e garantisce che l\u0026rsquo;accesso all\u0026rsquo;API sia indipendente dallo stato interno del piano di controllo Kubernetes.7\nPer i servizi, a questa scala è imperativo l\u0026rsquo;uso della modalità BGP.4 MetalLB o Cilium (che offre un piano di controllo BGP nativo basato su eBPF) diventano gli strumenti d\u0026rsquo;elezione.18 L\u0026rsquo;integrazione con router TOR (Top of Rack) permette una distribuzione del traffico realmente orizzontale, sfruttando l\u0026rsquo;infrastruttura di rete fisica per garantire la scalabilità.27\nAnalisi Tecnica dei Protocolli: ARP vs BGP # La decisione tra Layer 2 (ARP) e Layer 3 (BGP) è dettata dall\u0026rsquo;infrastruttura. È fondamentale comprendere il \u0026ldquo;costo\u0026rdquo; di ogni scelta.\nImplicazioni del Layer 2 e ARP # Il bilanciamento basato su ARP è fondamentalmente un meccanismo di failover, non di distribuzione del carico.12 Quando MetalLB o kube-vip operano in questa modalità, scelgono un nodo che risponde a tutte le richieste per un determinato IP.3 Il vantaggio è che funziona ovunque, anche su switch economici.29 Tuttavia, in caso di guasto del nodo leader, deve essere inviato un pacchetto ARP \u0026ldquo;gratuitous\u0026rdquo; per informare gli altri host che il MAC address associato a quell\u0026rsquo;IP è cambiato.12 Se i client o i router della rete hanno cache ARP persistenti e ignorano gli annunci gratuiti, si possono verificare interruzioni di connettività fino a 30-60 secondi.12\nImplicazioni del Layer 3 e BGP # BGP trasforma i nodi Kubernetes in veri e propri router.13 Ogni nodo annuncia i prefissi IP dei servizi a un peer BGP (solitamente il gateway predefinito). Questo permette il bilanciamento ECMP, dove il router distribuisce i pacchetti tra i nodi.4\nTuttavia, BGP su Kubernetes presenta una sfida nota come \u0026ldquo;churn\u0026rdquo; delle connessioni. Poiché i router tradizionali sono spesso \u0026ldquo;stateless\u0026rdquo; nel loro hashing ECMP, quando un nodo viene aggiunto o rimosso (ad esempio durante un aggiornamento di Talos), l\u0026rsquo;algoritmo di hashing del router potrebbe ricalcolare i percorsi, spostando sessioni TCP attive su nodi diversi.13 Se il nuovo nodo non conosce quella sessione (perché il traffico non è stato proxato correttamente), la connessione verrà interrotta con un errore \u0026ldquo;Connection Reset\u0026rdquo;.13 Per ovviare a questo, è necessario utilizzare router che supportano il \u0026ldquo;Resilient ECMP\u0026rdquo; o posizionare i servizi dietro un controller di Ingress che possa gestire la persistenza delle sessioni a livello applicativo.13\nGuida alla Configurazione: Dettagli e Avvertenze # La configurazione di queste strategie in Talos OS richiede l\u0026rsquo;uso di patch YAML applicate ai file di configurazione della macchina (machineconfig).\nConfigurazione del VIP Nativo # Un errore comune è utilizzare il VIP come endpoint nel file talosconfig.1 Poiché il VIP dipende dalla salute di etcd e del kube-apiserver, se questi componenti falliscono, non sarà possibile utilizzare talosctl tramite il VIP per riparare il nodo. La prassi corretta prevede l\u0026rsquo;inserimento dei singoli indirizzi IP fisici dei nodi master nella lista degli endpoint di talosconfig.6\nConflitti tra Kube-vip e MetalLB # Se si sceglie di utilizzare kube-vip per il control plane e MetalLB per i servizi, è vitale utilizzare le classi di bilanciamento (loadBalancerClass) introdotte in Kubernetes 1.24.17 Senza questa distinzione, entrambi i controller potrebbero tentare di \u0026ldquo;prendere in carico\u0026rdquo; lo stesso servizio, portando a una situazione di instabilità in cui l\u0026rsquo;indirizzo IP viene assegnato e rimosso continuamente.17\nInoltre, se entrambi i componenti vengono configurati per utilizzare BGP, è probabile che entrino in conflitto per l\u0026rsquo;uso della porta TCP 179.3 In Talos, una soluzione moderna consiste nell\u0026rsquo;utilizzare Cilium come CNI e affidare a lui l\u0026rsquo;intero piano di controllo BGP, eliminando la necessità di MetalLB e riducendo la complessità del sistema.18\nCasi d\u0026rsquo;Uso Particolari e Troubleshooting # Nelle installazioni reali, emergono spesso scenari non documentati che richiedono interventi specifici.\nProblemi di Routing Asimmetrico # Quando si utilizzano bilanciatori di carico software su Talos, può verificarsi il fenomeno del routing asimmetrico: il pacchetto entra nel cluster tramite il nodo A (che detiene il VIP) ma deve essere consegnato a un pod sul nodo B.32 Se il nodo B risponde direttamente al client tramite il proprio gateway predefinito, molti firewall bloccheranno il traffico considerandolo un attacco o un errore di protocollo.32\nPer mitigare questo problema, Talos e MetalLB raccomandano di abilitare la modalità \u0026ldquo;strict ARP\u0026rdquo; nel kube-proxy.31 Questo assicura che il traffico segua percorsi prevedibili. Un\u0026rsquo;altra opzione è l\u0026rsquo;uso di externalTrafficPolicy: Local nel servizio Kubernetes, che istruisce il bilanciatore di carico a inviare traffico solo ai nodi che ospitano effettivamente il pod del servizio, eliminando il salto interno tra i nodi e preservando l\u0026rsquo;indirizzo IP sorgente del client.13\nFailover e Impatto sui Workload # È fondamentale capire che il failover del VIP (sia esso nativo o gestito da kube-vip) influisce solo sull\u0026rsquo;accesso esterno al cluster (ad esempio, l\u0026rsquo;esecuzione di kubectl o le chiamate API esterne).1 All\u0026rsquo;interno del cluster, grazie a KubePrism e alla scoperta dei servizi, i carichi di lavoro continuano a comunicare normalmente e non risentono dello stato del VIP esterno.1 Tuttavia, le connessioni a lunga durata che passano attraverso il VIP (come i tunnel gRPC o le sessioni HTTP/2) verranno interrotte e richiederanno una logica di riconnessione lato client.1\nConclusioni e Raccomandazioni Strategiche # In base all\u0026rsquo;analisi dei dati raccolti e delle best practice di settore, la strategia corretta per un cluster Kubernetes su Talos OS può essere sintetizzata in tre percorsi principali, a seconda delle esigenze di scalabilità e della complessità della rete.\nPer la maggior parte degli utenti, la Strategia Consigliata è l\u0026rsquo;accoppiamento del VIP nativo di Talos con MetalLB in modalità Layer 2. Questa configurazione bilancia perfettamente la semplicità di gestione tipica di Talos con la flessibilità di MetalLB. È la scelta ideale per cluster che operano in una singola sala server o in un ambiente virtualizzato standard, garantendo un\u0026rsquo;alta affidabilità dell\u0026rsquo;API server senza aggiungere componenti critici che devono essere gestiti manualmente durante il bootstrap.\nPer le installazioni Enterprise o ad Alto Carico, la strategia ottimale vira verso il bilanciamento del carico esterno per il control plane e MetalLB o Cilium in modalità BGP per i servizi. Questa architettura elimina i colli di bottiglia tipici delle reti Layer 2 e sfrutta la potenza dei router fisici per distribuire il traffico, garantendo che il cluster possa scalare fino a centinaia di nodi senza degradi delle prestazioni di rete.\nInfine, per i Piccoli Cluster e Homelab (Minecraft Style), la parola d\u0026rsquo;ordine è minimalismo. L\u0026rsquo;uso del VIP nativo e di MetalLB (L2), avendo cura di configurare correttamente le etichette dei nodi per permettere l\u0026rsquo;esposizione dei servizi, fornisce un ambiente robusto e facile da mantenere, riducendo al minimo il \u0026ldquo;consumo\u0026rdquo; di risorse preziose da parte dei componenti di infrastruttura.\nIn sintesi, l\u0026rsquo;architetto di sistemi che opera con Talos OS deve sempre privilegiare l\u0026rsquo;abilitazione di KubePrism come fondamento della resilienza interna e selezionare il metodo di annuncio degli indirizzi IP (ARP vs BGP) non in base alla preferenza del software, ma in base alle capacità effettive dell\u0026rsquo;hardware di rete che ospita il cluster.\nBibliografia # Virtual (shared) IP - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 1, 2026, https://docs.siderolabs.com/talos/v1.8/networking/vip kube-vip: Documentation, accesso eseguito il giorno gennaio 1, 2026, https://kube-vip.io/ Setting Up MetalLB: Kubernetes LoadBalancer for Bare Metal Clusters | Talha Juikar, accesso eseguito il giorno gennaio 1, 2026, https://talhajuikar.com/posts/metallb/ MetalLB: A Load Balancer for Bare Metal Kubernetes Clusters | by 8grams - Medium, accesso eseguito il giorno gennaio 1, 2026, https://8grams.medium.com/metallb-a-load-balancer-for-bare-metal-kubernetes-clusters-ef8a9e00c2bd Control Plane - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 1, 2026, https://docs.siderolabs.com/talos/v1.9/learn-more/control-plane Production Clusters - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 1, 2026, https://docs.siderolabs.com/talos/v1.7/getting-started/prodnotes Kubernetes Cluster Reference Architecture with Talos Linux for 2025-05 - Sidero Labs, accesso eseguito il giorno gennaio 1, 2026, https://www.siderolabs.com/wp-content/uploads/2025/08/Kubernetes-Cluster-Reference-Architecture-with-Talos-Linux-for-2025-05.pdf difference VIP vs KubePrism (or other) · siderolabs talos · Discussion #9906 - GitHub, accesso eseguito il giorno gennaio 1, 2026, https://github.com/siderolabs/talos/discussions/9906 Installation - kube-vip, accesso eseguito il giorno gennaio 1, 2026, https://kube-vip.io/docs/installation/ Kubernetes Homelab Series Part 3 - LoadBalancer With MetalLB | Eric Daly\u0026rsquo;s Blog, accesso eseguito il giorno gennaio 1, 2026, https://blog.dalydays.com/post/kubernetes-homelab-series-part-3-loadbalancer-with-metallb/ Configuration :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 1, 2026, https://metallb.universe.tf/configuration/ MetalLB in layer 2 mode :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 1, 2026, https://metallb.universe.tf/concepts/layer2/ MetalLB in BGP mode :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 1, 2026, https://metallb.universe.tf/concepts/bgp/ Architecture | kube-vip, accesso eseguito il giorno gennaio 1, 2026, https://kube-vip.io/docs/about/architecture/ Static Pods | kube-vip, accesso eseguito il giorno gennaio 1, 2026, https://kube-vip.io/docs/installation/static/ What do you use for baremetal VIP ControlPane and Services : r/kubernetes - Reddit, accesso eseguito il giorno gennaio 1, 2026, https://www.reddit.com/r/kubernetes/comments/1nlnb1o/what_do_you_use_for_baremetal_vip_controlpane_and/ HA Kubernetes API server with MetalLB\u0026hellip;? - Reddit, accesso eseguito il giorno gennaio 1, 2026, https://www.reddit.com/r/kubernetes/comments/1o9t1j2/ha_kubernetes_api_server_with_metallb/ For those who work with HA onprem clusters : r/kubernetes - Reddit, accesso eseguito il giorno gennaio 1, 2026, https://www.reddit.com/r/kubernetes/comments/1j05ozt/for_those_who_work_with_ha_onprem_clusters/ Kubernetes Load-Balancer service - kube-vip, accesso eseguito il giorno gennaio 1, 2026, https://kube-vip.io/docs/usage/kubernetes-services/ metallb + BGP = conflict with kube-router? | TrueNAS Community, accesso eseguito il giorno gennaio 1, 2026, https://www.truenas.com/community/threads/metallb-bgp-conflict-with-kube-router.115690/ Talos Kubernetes in Five Minutes - DEV Community, accesso eseguito il giorno gennaio 1, 2026, https://dev.to/nabsul/talos-kubernetes-in-five-minutes-1p1h \\[Lab Setup\\]3-node Talos cluster (Mac minis) + MinIO backend — does this topology make sense? : r/kubernetes - Reddit, accesso eseguito il giorno gennaio 1, 2026, https://www.reddit.com/r/kubernetes/comments/1myb8xc/lab_setup_3node_talos_cluster_mac_minis_minio/ Getting back into the HomeLab game for 2024 - vZilla, accesso eseguito il giorno gennaio 1, 2026, https://vzilla.co.uk/vzilla-blog/getting-back-into-the-homelab-game-for-2024 Fix LoadBalancer Services Not Working on Single Node Talos Kubernetes Cluster, accesso eseguito il giorno gennaio 1, 2026, https://www.robert-jensen.dk/posts/2025/fix-loadbalancer-services-not-working-on-single-node-talos-kubernetes-cluster/ Deploy Talos Linux with Local VIP, Tailscale, Longhorn, MetalLB and Traefik - Josh\u0026rsquo;s Notes, accesso eseguito il giorno gennaio 1, 2026, https://notes.joshrnoll.com/notes/deploy-talos-linux-with-local-vip-tailscale-longhorn-metallb-and-traefik/ Kubernetes \u0026amp; Talos - Reddit, accesso eseguito il giorno gennaio 1, 2026, https://www.reddit.com/r/kubernetes/comments/1hs6bui/kubernetes_talos/ Advanced BGP configuration :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 1, 2026, https://metallb.universe.tf/configuration/_advanced_bgp_configuration/ Talos with redundant routed networks via bgp : r/kubernetes - Reddit, accesso eseguito il giorno gennaio 1, 2026, https://www.reddit.com/r/kubernetes/comments/1iy411r/talos_with_redundant_routed_networks_via_bgp/ MetalLB on K3s (using Layer 2 Mode) | SUSE Edge Documentation, accesso eseguito il giorno gennaio 1, 2026, https://documentation.suse.com/suse-edge/3.3/html/edge/guides-metallb-k3s.html Troubleshooting - Sidero Documentation - What is Talos Linux?, accesso eseguito il giorno gennaio 1, 2026, https://docs.siderolabs.com/talos/v1.9/troubleshooting/troubleshooting Installation :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 1, 2026, https://metallb.universe.tf/installation/ Analyzing Load Balancer VIP Routing with Calico BGP and MetalLB - AHdark Blog, accesso eseguito il giorno gennaio 1, 2026, https://www.ahdark.blog/analyzing-load-balancer-vip-routing/ Kubernetes Services : Achieving optimal performance is elusive | by CloudyBytes | Medium, accesso eseguito il giorno gennaio 1, 2026, https://cloudybytes.medium.com/kubernetes-services-achieving-optimal-performance-is-elusive-5def5183c281 Usage :: MetalLB, bare metal load-balancer for Kubernetes, accesso eseguito il giorno gennaio 1, 2026, https://metallb.universe.tf/usage/ ","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/guides/talos-vip-load-balancing/","section":"Guide","summary":"","title":"Strategie Architetturali per il Bilanciamento del Carico e l'Alta Affidabilità del Control Plane in Cluster Kubernetes basati su Talos OS","type":"guides"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/theme/","section":"Tags","summary":"","title":"Theme","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/virtualization/","section":"Tags","summary":"","title":"Virtualization","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/vpn/","section":"Tags","summary":"","title":"Vpn","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/web-development/","section":"Tags","summary":"","title":"Web-Development","type":"tags"},{"content":"","date":"7 gennaio 2026","externalUrl":null,"permalink":"/it/tags/wireguard/","section":"Tags","summary":"","title":"Wireguard","type":"tags"},{"content":"","date":"6 gennaio 2026","externalUrl":null,"permalink":"/tags/migration/","section":"Tags","summary":"","title":"Migration","type":"tags"},{"content":"","date":"6 gennaio 2026","externalUrl":null,"permalink":"/it/tags/migrazione/","section":"Tags","summary":"","title":"Migrazione","type":"tags"},{"content":" Introduzione: L\u0026rsquo;Illusione della Semplicitá # Oggi l\u0026rsquo;obiettivo sembrava banale: prendere un blog statico generato con Hugo, che attualmente gira pacificamente in un container Docker gestito tramite Compose, e spostarlo all\u0026rsquo;interno del cluster Kubernetes.\nSulla carta, è un\u0026rsquo;operazione da cinque minuti. Prendi il compose.yml, lo traduci in un Deployment e un Servizio, applichi, fatto. In realtà, questa migrazione si è trasformata in una masterclass sulla differenza tra la gestione dei volumi locali (Docker) e lo storage distribuito (Kubernetes/Longhorn), e su come i permessi dei file possano diventare il nemico pubblico numero uno.\nQuesta non è una guida \u0026ldquo;copia-incolla\u0026rdquo;. È la cronaca di come abbiamo sezionato il problema, analizzato i fallimenti e costruito una soluzione resiliente.\nEbbene sì, il blog che state leggendo ora gira su Kubernetes in self-hosting su Proxmox sul mio mini PC di casa!\nFase 1: Il Paradosso dello Storage # Il punto di partenza era un semplice docker-compose.yml che usavo per lo sviluppo locale:\nservices: hugo: image: hugomods/hugo:exts-non-root command: server --bind=0.0.0.0 --buildDrafts --watch volumes: - ./:/src # \u0026lt;--- IL COLPEVOLE Notate quella riga volumes. In Docker, stavo mappando la cartella corrente del mio host all\u0026rsquo;interno del container. È immediato: modifico un file sul mio laptop, Hugo se ne accorge e rigenera il sito.\nIl Problema Concettuale # Quando passiamo a Kubernetes, quel \u0026ldquo;mio laptop\u0026rdquo; non esiste più. Il Pod può essere schedulato su qualsiasi nodo del cluster. Non possiamo fare affidamento su file presenti sul filesystem dell\u0026rsquo;host (a meno di non usare hostPath, che però è un anti-pattern perché vincola il Pod a un nodo specifico, rompendo l\u0026rsquo;Alta Disponibilità).\nLa soluzione architetturale è usare un PersistentVolumeClaim (PVC) appoggiato a Longhorn. Longhorn replica i dati su più nodi, garantendo che se un nodo muore, i dati del blog sopravvivono e il Pod può ripartire altrove.\nMa qui sorge il paradosso: Un volume Longhorn nuovo è vuoto. Se avvio il Pod di Hugo attaccato a questo volume vuoto, Hugo crasherà istantaneamente perché non troverà il file config.toml.\nStrategia di Ingestione # Avevamo tre strade:\nGit-Sync Sidecar: Un container affiancato che clona costantemente il repo Git nel volume condiviso. Elegante, ma complesso per un blog personale. InitContainer: Un container che parte prima dell\u0026rsquo;app, clona il repo e muore. Copia One-Off: Avviare il Pod, aspettare che fallisca (o resti appeso) e copiare manualmente i dati una volta sola. Abbiamo optato per una variante ibrida. Dato che l\u0026rsquo;obiettivo era mantenere la modalità \u0026ldquo;watch\u0026rdquo; per editare i file live (magari tramite editor remoto in futuro), abbiamo deciso di trattare il volume come la \u0026ldquo;Single Source of Truth\u0026rdquo;.\nFase 2: L\u0026rsquo;Architettura del Manifesto # Perché un Deployment e non un StatefulSet?\nSpesso si associa lo StatefulSet alle applicazioni che hanno bisogno di stabilità dello storage. Tuttavia, Hugo (in modalità server) non ha bisogno di identità di rete stabili (come hugo-0, hugo-1). Ha solo bisogno dei suoi file. Un Deployment con strategia Recreate (per evitare che due pod scrivano contemporaneamente sullo stesso volume RWO) è sufficiente e più semplice da gestire.\nEcco il manifesto finale commentato:\napiVersion: apps/v1 kind: Deployment metadata: name: hugo-blog namespace: hugo-blog # Isolamento prima di tutto spec: replicas: 1 strategy: type: Recreate # Evita il lock del volume Longhorn selector: matchLabels: app: hugo-blog template: metadata: labels: app: hugo-blog spec: # IL SEGRETO DEI PERMESSI securityContext: fsGroup: 1000 containers: - name: hugo image: hugomods/hugo:exts-non-root args: - server - --bind=0.0.0.0 - --baseURL=https://blog.tazlab.net/ - --appendPort=false ports: - containerPort: 1313 volumeMounts: - name: blog-src mountPath: /src volumes: - name: blog-src persistentVolumeClaim: claimName: hugo-blog-pvc Deep Dive: fsGroup: 1000 # Questo è stato il momento critico dell\u0026rsquo;indagine. L\u0026rsquo;immagine hugomods/hugo:exts-non-root è costruita per girare, come dice il nome, senza privilegi di root (UID 1000). Tuttavia, quando Kubernetes monta un volume (specialmente con certi driver CSI come Longhorn), la directory di mount può appartenere a root per default.\nRisultato? Il container parte, prova a scrivere nella cartella /src (per la cache o file di lock) e riceve un Permission Denied.\nL\u0026rsquo;istruzione fsGroup: 1000 nel securityContext dice a Kubernetes: \u0026ldquo;Ehi, qualsiasi volume montato in questo Pod deve essere leggibile e scrivibile dal gruppo 1000\u0026rdquo;. Kubernetes applica ricorsivamente un chown o gestisce i permessi ACL al momento del mount, risolvendo il problema alla radice.\nFase 3: La Rete e il Discovery # Una volta che il Pod gira, deve essere raggiungibile. Qui entra in gioco Traefik, il nostro Ingress Controller.\napiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: hugo-blog-ingress annotations: # La magia di Let\u0026#39;s Encrypt traefik.ingress.kubernetes.io/router.tls.certresolver: myresolver spec: ingressClassName: traefik rules: - host: blog.tazlab.net http: paths: - path: / pathType: Prefix backend: service: name: hugo-blog port: number: 80 Durante il setup, ho dovuto verificare quale fosse il nome esatto del resolver configurato in Traefik. Un rapido controllo su traefik-values.yaml ha confermato che l\u0026rsquo;ID era myresolver. Senza questa corrispondenza esatta, i certificati SSL non verrebbero mai generati.\nUn dettaglio spesso trascurato: BaseURL. Hugo genera i link interni basandosi sulla sua configurazione. Se gira sulla porta interna 1313, tenderà a creare link tipo http://localhost:1313/post. Ma noi siamo dietro un Reverse Proxy (Traefik) che serve sulla porta HTTPS 443. L\u0026rsquo;argomento --baseURL=https://blog.tazlab.net/ e --appendPort=false forza Hugo a generare link corretti per il mondo esterno, a prescindere dalla porta su cui ascolta il container.\nFase 4: Operazione \u0026ldquo;Trapianto Dati\u0026rdquo; # Con il manifesto applicato, il Pod è andato in stato Running, ma serviva una pagina bianca o un errore, perché /src era vuota.\nQui abbiamo usato la forza bruta intelligente: kubectl cp.\n# Copia locale -\u0026gt; Pod remoto kubectl cp ./blog hugo-blog/hugo-blog-pod-xyz:/src Grazie al fsGroup configurato in precedenza, i file copiati hanno mantenuto i permessi corretti per essere letti dal processo Hugo. Immediatamente, il watcher di Hugo ha rilevato i nuovi file (config.toml, content/) e ha compilato il sito in pochi millisecondi.\nRiflessioni Post-Lab # Questa migrazione ha spostato il blog da un\u0026rsquo;entità \u0026ldquo;pet\u0026rdquo; (legata al mio computer) a \u0026ldquo;cattle\u0026rdquo; (parte del cluster).\nResilienza: Se il nodo dove gira Hugo muore, Longhorn ha replicato i dati su un altro nodo. Kubernetes rischedula il Pod, che si attacca alla replica dei dati e riparte. Tempo di downtime: secondi. Scalabilità: Non ne abbiamo bisogno ora, ma potremmo scalare a più repliche (rimuovendo la modalità --watch e usando Nginx per servire puramente gli statici). Sicurezza: Tutto gira in HTTPS, con certificati rinnovati automaticamente, e il container non ha privilegi di root. La lezione di oggi è che in Kubernetes, lo storage è un cittadino di prima classe. Non è più solo una cartella su disco; è una risorsa di rete con le sue regole di accesso, permessi e ciclo di vita. Ignorare questo aspetto è la via più veloce per un CrashLoopBackOff.\n","date":"6 gennaio 2026","externalUrl":null,"permalink":"/it/posts/hugo-blog-kubernetes-migration/","section":"Posts","summary":"","title":"Migrazione di un Blog Hugo su Kubernetes","type":"posts"},{"content":" Introduzione: Il Limite del \u0026ldquo;Basta che Funzioni\u0026rdquo; # Fino a ieri, il nostro cluster Kubernetes viveva in una sorta di limbo architettonico. L\u0026rsquo;Ingress Controller (Traefik) era configurato in modalità hostNetwork: true. In termini poveri, il Pod di Traefik dirottava l\u0026rsquo;intera interfaccia di rete del nodo su cui girava, ascoltando direttamente sulle porte 80 e 443 dell\u0026rsquo;IP fisico del Control Plane.\nFunziona? Sì. È una buona pratica? Assolutamente no. Questa configurazione crea un accoppiamento forte tra il servizio logico e l\u0026rsquo;infrastruttura fisica. Se il nodo muore, il servizio muore. Inoltre, blocca quelle porte per qualsiasi altra cosa. Nei cloud provider (AWS, GCP), questo problema si risolve con un click: \u0026ldquo;Create Load Balancer\u0026rdquo;. Ma noi siamo \u0026ldquo;on-premise\u0026rdquo; (o meglio, \u0026ldquo;on-homelab\u0026rdquo;), dove il lusso degli ELB (Elastic Load Balancer) non esiste.\nLa soluzione è MetalLB: un componente che simula un Load Balancer hardware all\u0026rsquo;interno del cluster, assegnando IP \u0026ldquo;virtuali\u0026rdquo; ai servizi. La missione di oggi era semplice sulla carta ma complessa nell\u0026rsquo;esecuzione: installare MetalLB, configurare una zona IP dedicata e migrare Traefik per farlo diventare un cittadino di prima classe del cluster.\nFase 1: MetalLB e la Danza dei Protocolli (Layer 2) # Per un cluster domestico dove non abbiamo router BGP costosi (come i Juniper o Cisco da datacenter), MetalLB offre la modalità Layer 2.\nConcetto Chiave: Layer 2 \u0026amp; ARP In questa modalità, uno dei nodi del cluster \u0026ldquo;alza la mano\u0026rdquo; e dice alla rete locale: \u0026ldquo;Ehi, l\u0026rsquo;IP 192.168.1.240 sono io!\u0026rdquo;. Lo fa inviando pacchetti ARP (Address Resolution Protocol). Se quel nodo muore, MetalLB elegge istantaneamente un altro nodo che inizia a urlare \u0026ldquo;No, ora sono io!\u0026rdquo;. È un meccanismo di failover semplice ma efficace.\nLa Sfida delle Tolleranze # Il primo ostacolo è stato architetturale. Di default, MetalLB installa dei pod chiamati \u0026ldquo;speaker\u0026rdquo; (quelli che \u0026ldquo;urlano\u0026rdquo; gli ARP) solo sui nodi Worker. Ma nel nostro cluster, il traffico entrava ancora prevalentemente dal Control Plane. Se non avessimo avuto uno speaker sul Control Plane, avremmo rischiato di avere un Load Balancer muto su metà dell\u0026rsquo;infrastruttura.\nAbbiamo dovuto forzare la mano a Helm con una configurazione di tolerations specifica, permettendo agli speaker di \u0026ldquo;sporcarsi le mani\u0026rdquo; anche sul nodo Master:\n# metallb-values.yaml speaker: tolerations: - key: \u0026#34;node-role.kubernetes.io/control-plane\u0026#34; operator: \u0026#34;Exists\u0026#34; effect: \u0026#34;NoSchedule\u0026#34; - key: \u0026#34;node-role.kubernetes.io/master\u0026#34; operator: \u0026#34;Exists\u0026#34; effect: \u0026#34;NoSchedule\u0026#34; controller: tolerations: - key: \u0026#34;node-role.kubernetes.io/control-plane\u0026#34; operator: \u0026#34;Exists\u0026#34; effect: \u0026#34;NoSchedule\u0026#34; Senza questo, gli speaker sarebbero rimasti in Pending sul control plane, rendendo il failover zoppo.\nFase 2: La Trappola del DHCP (Networking Surgery) # Configurare MetalLB richiede un pool di indirizzi IP da assegnare. E qui abbiamo rischiato il disastro.\nIl router domestico (uno Sky Hub) era configurato, come molti router consumer, per coprire l\u0026rsquo;intera subnet 192.168.1.x con il suo server DHCP (range .2 - .253).\nIl Pericolo del Conflitto IP Se avessimo detto a MetalLB \u0026ldquo;Usa il range .50-.60\u0026rdquo; senza toccare il router, avremmo creato una bomba a orologeria. Scenario:\nMetalLB assegna .50 a Traefik. Tutto funziona. Torno a casa, il mio telefono si connette al Wi-Fi. Il router, ignaro di MetalLB, assegna .50 al mio telefono. Risultato: Conflitto IP. Il cluster Kubernetes e il mio telefono iniziano a litigare per chi possiede l\u0026rsquo;indirizzo. I pacchetti si perdono, le connessioni cadono. Caos. La Soluzione: \u0026ldquo;DHCP Shrinking\u0026rdquo; Prima di applicare qualsiasi YAML, siamo intervenuti sul router. Abbiamo ridotto il range DHCP drasticamente: da .2-.120. Questo ha creato una \u0026ldquo;Terra di Nessuno\u0026rdquo; (da .121 a .254) dove il router non osa avventurarsi. È in questo spazio sicuro che abbiamo ritagliato il pool per MetalLB.\n# metallb-config.yaml apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: main-pool namespace: metallb-system spec: addresses: - 192.168.1.240-192.168.1.245 # Zona Sicura --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: l2-adv namespace: metallb-system spec: ipAddressPools: - main-pool Fase 3: Refactoring di Traefik (Il Grande Salto) # Con MetalLB pronto a servire IP, è arrivato il momento di staccare Traefik dall\u0026rsquo;hardware.\nLe modifiche al values.yaml di Traefik sono state radicali:\nVia hostNetwork: true: Il pod ora vive nella rete virtuale del cluster, isolato e sicuro. Via nodeSelector: Non obblighiamo più Traefik a girare sul Control Plane. Può (e deve) andare sui Worker. Service Type LoadBalancer: La chiave di volta. Chiediamo al cluster un IP esterno. Ma le migrazioni non sono mai indolori.\nFase 4: Cronaca di un Debugging (The Struggle) # Appena lanciato l\u0026rsquo;aggiornamento Helm, ci siamo scontrati con due problemi classici ma educativi.\n1. Il Deadlock del Volume (RWO) # Traefik usa un volume persistente (Longhorn) per salvare i certificati SSL (acme.json). Questo volume è di tipo ReadWriteOnce (RWO), il che significa che può essere montato da un solo nodo alla volta.\nQuando Kubernetes ha cercato di spostare Traefik dal Control Plane al Worker:\nHa creato il nuovo pod sul Worker. Il vecchio pod sul Control Plane era ancora in fase di spegnimento (Terminating). Il volume risultava ancora \u0026ldquo;agganciato\u0026rdquo; al vecchio nodo. Il nuovo pod è rimasto bloccato in ContainerCreating con l\u0026rsquo;errore Multi-Attach error. Soluzione: A volte Kubernetes è troppo gentile. Abbiamo dovuto forzare l\u0026rsquo;eliminazione del vecchio pod e scalare il deployment a 0 repliche per \u0026ldquo;sbloccare\u0026rdquo; il volume da Longhorn, permettendo poi al nuovo pod di montarlo pulito.\n2. La Guerra dei Permessi (Root vs Non-Root) # Nel processo di hardening, abbiamo deciso di far girare Traefik come utente non privilegiato (UID 65532), abbandonando root. Tuttavia, il file acme.json esistente nel volume era stato creato dal vecchio Traefik (che girava come root).\nRisultato? open /data/acme.json: permission denied\nL\u0026rsquo;utente 65532 guardava il file di proprietà di root e non poteva toccarlo. Il parametro fsGroup nel SecurityContext spesso non basta per file già esistenti su certi storage driver.\nSoluzione: Il Pattern \u0026ldquo;Init Container\u0026rdquo; Invece di tornare indietro e usare root (che sarebbe una sconfitta per la sicurezza), abbiamo implementato un Init Container. È un piccolo container effimero che parte prima di quello principale, esegue un comando, e muore.\nLo abbiamo configurato per girare come root (solo lui!), sistemare i permessi, e lasciare il campo libero a Traefik:\n# traefik-values.yaml snippet initContainers: - name: volume-permissions image: busybox:latest # Comando brutale ma efficace: \u0026#34;Questo è tutto tuo, utente 65532\u0026#34; command: [\u0026#34;sh\u0026#34;, \u0026#34;-c\u0026#34;, \u0026#34;chown -R 65532:65532 /data \u0026amp;\u0026amp; chmod 600 /data/acme.json || true\u0026#34;] securityContext: runAsUser: 0 # Root, necessario per chown volumeMounts: - name: data mountPath: /data Conclusioni # Oggi il cluster ha fatto un salto di qualità. Non è più un insieme di hack per far funzionare le cose in casa, ma un\u0026rsquo;infrastruttura che rispetta i pattern cloud-native.\nCosa abbiamo ottenuto:\nIndipendenza dal Nodo: Traefik può morire e rinascere su qualsiasi nodo; l\u0026rsquo;IP di servizio (192.168.1.240) lo seguirà grazie a MetalLB. Sicurezza: Traefik non ha più accesso all\u0026rsquo;intera rete dell\u0026rsquo;host e gira con un utente limitato. Ordine: Abbiamo separato chiaramente la responsabilità del router (DHCP domestico) da quella del cluster (Static IP Pool). La lezione principale? L\u0026rsquo;automazione (Helm) è potente, ma quando si tocca lo storage persistente (Stateful) e i permessi, l\u0026rsquo;intervento umano chirurgico e la comprensione dei log (permission denied, multi-attach error) rimangono insostituibili.\n","date":"4 gennaio 2026","externalUrl":null,"permalink":"/it/posts/metallb-traefik-config/","section":"Posts","summary":"","title":"Dal Caos di HostNetwork all'Eleganza di MetalLB","type":"posts"},{"content":" Introduzione: Il Paradosso della Persistenza # Nel paradigma Cloud Native, trattiamo i carichi di lavoro come bestiame, non come animali domestici. I Pod sono effimeri, sacrificabili e stateless. Tuttavia, la realtà operativa impone un vincolo ineludibile: lo stato deve risiedere da qualche parte. Che si tratti di un database, di log di sistema o, come nel nostro caso specifico, di certificati SSL generati dinamicamente da un Ingress Controller, la necessità di Block Storage persistente e distribuito è il primo vero ostacolo che trasforma un cluster \u0026ldquo;giocattolo\u0026rdquo; in un\u0026rsquo;infrastruttura di produzione.\nL\u0026rsquo;obiettivo di questa sessione non era banale: implementare Longhorn, il motore di storage distribuito di SUSE/Rancher, su un sistema operativo immutabile come Talos Linux. La sfida è duplice: Talos, per design, impedisce la modifica del filesystem di root e l\u0026rsquo;installazione di pacchetti a runtime. Questo rende l\u0026rsquo;installazione di driver di storage (come iSCSI) un\u0026rsquo;operazione che va pianificata a livello di architettura dell\u0026rsquo;immagine OS, non tramite semplici comandi apt o yum.\nQuesta cronaca documenta il processo di hardening dell\u0026rsquo;infrastruttura, il provisioning dello storage fisico su Proxmox, e una complessa sessione di troubleshooting legata ai permessi dei volumi persistenti durante l\u0026rsquo;integrazione con Traefik.\nFase 1: L\u0026rsquo;Ostacolo dell\u0026rsquo;Immutabilità e le System Extensions # La prima barriera tecnica incontrata riguarda la natura stessa di Longhorn. Per funzionare, Longhorn crea un device a blocchi virtuale su ogni nodo, che viene poi montato dal Pod. Questa operazione si basa pesantemente sul protocollo iSCSI (Internet Small Computer Systems Interface).\nIn una distribuzione Linux tradizionale (Ubuntu, CentOS), l\u0026rsquo;installazione di Longhorn verificherebbe la presenza di open-iscsi e, in caso negativo, l\u0026rsquo;amministratore lo installerebbe. Su Talos Linux, questo è impossibile. Il filesystem è in sola lettura; non esiste un package manager.\nAnalisi e Soluzione: Sidero Image Factory # Un controllo preliminare sul cluster ha rivelato la mancanza delle estensioni necessarie:\ntalosctl get extensions # Output: Nessuna estensione critica installata Senza iscsi-tools e util-linux-tools, i pod di Longhorn sarebbero rimasti indefinitamente nello stato ContainerCreating, incapaci di montare i volumi.\nLa soluzione architetturale adottata è stata l\u0026rsquo;uso della Sidero Image Factory. Invece di modificare il nodo esistente, abbiamo generato una nuova definizione di immagine OS (uno \u0026ldquo;schematic\u0026rdquo;) che includesse nativamente i driver richiesti.\nLe estensioni selezionate sono state:\nsiderolabs/iscsi-tools: Il demone e le utility user-space per iSCSI. siderolabs/util-linux-tools: Utility di gestione filesystem essenziali per la formattazione automatica dei volumi. siderolabs/qemu-guest-agent: Per migliorare l\u0026rsquo;integrazione con l\u0026rsquo;hypervisor Proxmox. L\u0026rsquo;aggiornamento è stato eseguito in modalità \u0026ldquo;rolling\u0026rdquo;, un nodo alla volta, garantendo che il cluster rimanesse operativo (o quasi) durante la transizione.\n# Esempio del comando di aggiornamento chirurgico talosctl upgrade --image factory.talos.dev/installer/[ID_SCHEMA]:v1.12.0 --preserve=true Questo passaggio sottolinea una lezione fondamentale del moderno DevOps: l\u0026rsquo;infrastruttura si gestisce dichiarativamente. Non si \u0026ldquo;patchano\u0026rdquo; i server; si sostituiscono le immagini che li governano.\nFase 2: Provisioning dello Storage Fisico (Proxmox \u0026amp; Talos) # Una volta abilitato il software a \u0026ldquo;parlare\u0026rdquo; con lo storage, dovevamo fornire lo storage fisico. Sebbene sia possibile utilizzare il disco principale del sistema operativo per i dati, questa è una pratica sconsigliata (anti-pattern) per diversi motivi:\nContesa di I/O: I log di sistema o le operazioni di etcd non devono competere con le scritture del database. Ciclo di vita: La reinstallazione del sistema operativo (es. un reset di Talos) potrebbe comportare la formattazione della partizione /var, cancellando i dati persistenti. La Strategia del Disco Dedicato # Abbiamo optato per l\u0026rsquo;aggiunta di un secondo disco virtuale (virtio-scsi o virtio-blk) su ogni VM Proxmox. Qui è emerso un rischio operativo critico: l\u0026rsquo;identificazione del device.\nSu Linux, i nomi dei device (/dev/sda, /dev/sdb, /dev/vda) non sono garantiti essere persistenti o deterministici, specialmente in ambienti virtualizzati dove l\u0026rsquo;ordine di boot può variare. Applicare una configurazione Talos che formatta /dev/sdb quando /dev/sdb è in realtà il disco di sistema porterebbe alla catastrofe (data loss totale).\nTecnica di Mitigazione: Identificazione tramite Dimensione # Per mitigare questo rischio, abbiamo adottato una tecnica di \u0026ldquo;flagging\u0026rdquo; hardware. Invece di creare dischi identici a quelli di sistema (34GB), abbiamo ridimensionato i nuovi dischi dati a 43GB.\n# Verifica pre-formattazione NODE DISK SIZE TYPE 192.168.1.127 sda 34 GB QEMU HARDDISK (OS) 192.168.1.127 vda 43 GB (Target Dati) Solo dopo aver confermato inequivocabilmente che /dev/vda era il disco da 43GB su tutti i nodi, abbiamo applicato la MachineConfig di Talos per partizionare, formattare in XFS e montare il disco su /var/mnt/longhorn.\nIl \u0026ldquo;Trick\u0026rdquo; del Kubelet Mount # Un dettaglio tecnico spesso trascurato è la visibilità dei mount. Il Kubelet gira all\u0026rsquo;interno di un container isolato. Montare un disco sull\u0026rsquo;host in /var/mnt/longhorn non lo rende automaticamente visibile al Kubelet.\nAbbiamo dovuto configurare esplicitamente extraMounts con propagazione rshared:\nkubelet: extraMounts: - destination: /var/lib/longhorn type: bind source: /var/mnt/longhorn options: - bind - rshared - rw Senza rshared, Longhorn avrebbe tentato di montare i volumi, ma il Kubelet non sarebbe stato in grado di passarli ai Pod, risultando in errori di \u0026ldquo;MountPropagation\u0026rdquo;.\nFase 3: Installazione e Configurazione di Longhorn # L\u0026rsquo;installazione tramite Helm è stata relativamente indolore, grazie alla preparazione meticolosa. Tuttavia, la configurazione di Longhorn in un ambiente a due nodi (un Control Plane e un Worker) richiede compromessi specifici.\nConfigurazione della Replica # Di default, Longhorn cerca di mantenere 3 repliche dei dati su nodi diversi per garantire l\u0026rsquo;alta affidabilità (HA). In un cluster a 2 nodi, questo requisito è impossibile da soddisfare (Hard Anti-Affinity).\nAbbiamo dovuto ridurre il numberOfReplicas a 2. Questo configura una situazione di \u0026ldquo;tolleranza al guasto minima\u0026rdquo;: se un nodo cade, i dati sono ancora accessibili sull\u0026rsquo;altro, ma la ridondanza è persa fino al ripristino. È un compromesso accettabile per un ambiente Homelab, ma critico da comprendere per la produzione.\nInoltre, abbiamo personalizzato il defaultDataPath per puntare a /var/lib/longhorn (il path interno al container del Kubelet che mappa il nostro disco dedicato), garantendo che i dati non toccassero mai il disco dell\u0026rsquo;OS.\nFase 4: L\u0026rsquo;Integrazione con Traefik e l\u0026rsquo;Incubo dei Permessi # La vera battaglia tecnica è iniziata quando abbiamo tentato di utilizzare questo nuovo storage per persistere i certificati SSL di Traefik (file acme.json).\nIl Problema: Init:CrashLoopBackOff # Dopo aver configurato Traefik per usare un PVC Longhorn, il pod è entrato in un ciclo di crash continuo. L\u0026rsquo;analisi dei log ha rivelato: chmod: /data/acme.json: Operation not permitted\nAnalisi della Root Cause # Il conflitto nasceva da tre vettori di sicurezza contrastanti:\nKubernetes fsGroup: Abbiamo istruito Kubernetes a montare il volume rendendolo scrivibile per il gruppo 65532 (l\u0026rsquo;utente non-root di Traefik). Questo imposta i permessi a 660 (Lettura/Scrittura per Utente e Gruppo). Let\u0026rsquo;s Encrypt / Traefik: Per sicurezza, Traefik esige che il file acme.json abbia permessi strettissimi: 600 (Solo l\u0026rsquo;utente proprietario può leggere/scrivere). Se i permessi sono più aperti (es. 660), Traefik si rifiuta di partire. HostNetwork \u0026amp; Porte Privilegiate: Poiché stiamo usando hostNetwork: true per esporre Traefik direttamente sull\u0026rsquo;IP del nodo, Traefik deve poter fare il bind sulle porte 80 e 443. In Linux, le porte sotto la 1024 richiedono privilegi di Root (o la capability NET_BIND_SERVICE). Il Loop Infinito del Troubleshooting # Inizialmente, abbiamo tentato di forzare i permessi con un initContainer. Fallito: l\u0026rsquo;initContainer non aveva i privilegi di root sul filesystem montato. Abbiamo poi provato a cambiare utente (runAsUser: 65532), ma questo impediva il binding sulla porta 80 (bind: permission denied).\nLa situazione era paradossale:\nSe giravamo come Root, potevamo aprire la porta 80, mas Kubernetes (tramite fsGroup) alterava i permessi del file a 660, facendo arrabbiare Traefik. Se giravamo come Non-Root, non potevamo aprire la porta 80. La Soluzione Definitiva: \u0026ldquo;Clean Slate\u0026rdquo; # La risoluzione ha richiesto un approccio radicale:\nRimozione di fsGroup: Abbiamo rimosso ogni direttiva fsGroup dal values.yaml di Helm. Questo dice a Kubernetes: \u0026ldquo;Monta il volume così com\u0026rsquo;è, non toccare i permessi dei file\u0026rdquo;. Esecuzione come Root (Temporanea): Abbiamo configurato Traefik per girare come runAsUser: 0 (Root). Questo risolve il problema del binding della porta 80. Reset del Volume: Poiché il file acme.json esistente era ormai \u0026ldquo;corrotto\u0026rdquo; dai tentativi precedenti (aveva permessi 660), Traefik continuava a fallire anche con la nuova configurazione. Abbiamo dovuto cancellare manualmente il file (rm /data/acme.json) dall\u0026rsquo;interno del pod. Al riavvio successivo, Traefik (girando come Root) ha creato un nuovo acme.json. Poiché non c\u0026rsquo;era fsGroup a interferire, il file è stato creato con i permessi di default corretti (600). Il log finale è stato una liberazione: Testing certificate renew... Register... providerName=myresolver.acme\nRiflessioni Post-Lab # L\u0026rsquo;implementazione di Longhorn su un cluster Kubernetes bare-metal (o virtualizzato low-level) è un\u0026rsquo;esercizio che espone la complessità nascosta dello storage distribuito. Non basta \u0026ldquo;installare il chart\u0026rdquo;. Bisogna comprendere come il sistema operativo gestisce i device, come il Kubelet gestisce i mount point e come i container gestiscono i permessi utente.\nLezioni Apprese:\nL\u0026rsquo;immutabilità richiede pianificazione: Su sistemi come Talos, le dipendenze kernel e userspace devono essere \u0026ldquo;bakerizzate\u0026rdquo; nell\u0026rsquo;immagine, non installate a posteriori. I permessi nello storage persistente sono insidiosi: Il meccanismo fsGroup di Kubernetes è utile per database standard, ma può essere distruttivo per applicazioni che richiedono permessi file paranoici (come Traefik/ACME o le chiavi SSH). Identificazione Hardware: Mai fidarsi dei nomi dei device (/dev/sda). Usare UUID o, in fase di provisioning, dimensioni disco univoche per evitare errori umani catastrofici. Il cluster ora possiede uno strato di persistenza resiliente. Il prossimo passo logico sarà rimuovere la dipendenza da hostNetwork e Root introducendo un Load Balancer BGP come MetalLB, permettendo a Traefik di girare come utente non privilegiato e completando l\u0026rsquo;architettura di sicurezza.\nGenerato tramite Gemini CLI\n","date":"2 gennaio 2026","externalUrl":null,"permalink":"/it/posts/longhorn-kubernetes-storage/","section":"Posts","summary":"","title":"Cronache del Lab: Costruire la Persistenza con Longhorn e Talos","type":"posts"},{"content":" Introduzione: Il fascino (apparente) della semplicità # Oggi ho affrontato una di quelle sessioni di laboratorio che iniziano con un obiettivo apparentemente semplice e finiscono per trasformarsi in una masterclass di architettura Kubernetes. L\u0026rsquo;obiettivo era chiaro: configurare un punto di ingresso solido (Ingress) per il mio cluster Talos Linux su Proxmox, esposto tramite un VIP (Virtual IP) nativo, e installare Traefik per gestire il traffico HTTPS con certificati automatici Let\u0026rsquo;s Encrypt.\nIl mantra della giornata era \u0026ldquo;Less is More\u0026rdquo;. Niente MetalLB (per ora). Niente Load Balancer esterni complessi. Volevo sfruttare le capacità native di Talos per gestire l\u0026rsquo;Alta Disponibilità di rete e far girare Traefik \u0026ldquo;sul ferro\u0026rdquo; (HostNetwork).\nQuella che segue non è un asettico tutorial, ma la cronaca fedele delle sfide, degli errori architettonici e delle soluzioni che hanno portato al successo.\nFase 1: Il VIP Nativo di Talos (Layer 2) # La prima sfida è stata garantire un indirizzo IP stabile (192.168.1.250) che potesse \u0026ldquo;fluttuare\u0026rdquo; tra i nodi, indipendentemente da quale macchina fisica fosse accesa.\nIl Ragionamento (Il Perché) # Perché un VIP nativo? In un ambiente Bare Metal (o VM su Proxmox), non abbiamo la comodità dei Load Balancer cloud (AWS ELB, Google LB) che ci forniscono un IP pubblico con un click. Le alternative classiche sono MetalLB (che annuncia gli IP via ARP/BGP) o Kube-VIP. Tuttavia, Talos Linux offre una funzionalità integrata per gestire VIP condivisi direttamente nella configurazione della macchina (machine config). Ho scelto questa strada per ridurre le dipendenze software: se il sistema operativo può farlo, perché installare un altro pod per gestirlo?\nL\u0026rsquo;Analisi e l\u0026rsquo;Errore # Ho iniziato identificando l\u0026rsquo;interfaccia di rete sui nodi (ens18) e creando una patch per annunciare l\u0026rsquo;IP 192.168.1.250.\n# vip-patch.yaml machine: network: interfaces: - interface: ens18 dhcp: true vip: ip: 192.168.1.250 L\u0026rsquo;applicazione della patch al nodo Control Plane (192.168.1.253) è stata un successo immediato. Il nodo ha iniziato a rispondere alle richieste ARP per il nuovo IP. Il problema è sorto quando ho tentato di applicare la stessa patch al nodo Worker (192.168.1.127) per garantire la ridondanza.\nErrore: virtual (shared) IP is not allowed on non-controlplane nodes\nAnalisi: Talos, per design, limita l\u0026rsquo;uso di VIP condivisi ai nodi Control Plane. Questo perché il caso d\u0026rsquo;uso primario è l\u0026rsquo;Alta Disponibilità dell\u0026rsquo;API Server (porta 6443), non il traffico utente generico. Impatto: Abbiamo dovuto accettare che il nostro VIP risiederà, per ora, solo sul Control Plane. È un Single Point of Failure? Sì, se il nodo CP muore, perdiamo l\u0026rsquo;IP. Ma per un home lab, è un compromesso accettabile che semplifica drasticamente lo stack.\nFase 2: Helm e la preparazione del terreno # Con il VIP attivo, serviva il \u0026ldquo;motore\u0026rdquo; per installare le applicazioni. Helm è lo standard de facto. L\u0026rsquo;installazione è stata banale tramite lo script ufficiale, ma essenziale. Helm ci permette di definire la nostra infrastruttura come codice (file Values) invece che come comandi imperativi lanciati a caso.\ncurl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 chmod 700 get_helm.sh \u0026amp;\u0026amp; ./get_helm.sh Fase 3: Traefik e l\u0026rsquo;inferno della configurazione # Qui è iniziata la vera battaglia. Volevamo Traefik configurato in un modo molto specifico:\nHostNetwork: Ascolto diretto sulle porte 80/443 del nodo (saltando il livello della rete overlay di K8s) per intercettare il traffico diretto al VIP. ACME (Let\u0026rsquo;s Encrypt): Generazione di certificati SSL validi. Persistenza: Salvataggio dei certificati su disco per evitare di rigenerarli a ogni riavvio (e colpire i rate-limit). Il primo muro: La sintassi Helm # Il chart di Traefik evolve rapidamente. La mia configurazione iniziale values.yaml usava sintassi deprecate per i redirect (redirectTo) e per l\u0026rsquo;esposizione delle porte. Helm rispondeva con errori criptici come got boolean, want object.\nSoluzione: Ho dovuto consultare la documentazione aggiornata (via Context7) e scoprire che la gestione globale dei redirect è ora più robusta se passata tramite additionalArguments piuttosto che cercare di incastrarla nella mappa delle porte.\nIl secondo muro: RollingUpdate vs HostNetwork # Una volta corretta la sintassi, Helm ha rifiutato l\u0026rsquo;installazione con un errore logico interessante:\nErrore: maxUnavailable should be greater than 0 when using hostNetwork\nDeep-Dive: Quando usi hostNetwork: true, un Pod occupa fisicamente la porta 80 del nodo. Kubernetes non può avviare un nuovo Pod (aggiornamento) sullo stesso nodo finché il vecchio non è morto, perché la porta è occupata. La strategia di default maxUnavailable: 0 (che cerca di non avere mai downtime) è matematicamente incompatibile con questo vincolo su un singolo nodo. Soluzione: Ho dovuto modificare la updateStrategy per permettere maxUnavailable: 1.\nupdateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1 maxSurge: 0 Il terzo muro: Pod Security Admission (PSA) # Superato l\u0026rsquo;ostacolo configurativo, i Pod non partivano. Rimanevano in stato CreateContainerConfigError o non venivano creati dal DaemonSet. Facendo il describe del DaemonSet (kubectl describe ds), è emersa la verità:\nErrore: violates PodSecurity \u0026quot;baseline\u0026quot;: host namespaces (hostNetwork=true)\nAnalisi: Talos e le versioni recenti di Kubernetes applicano standard di sicurezza rigorosi di default. Un Pod che richiede hostNetwork è considerato \u0026ldquo;privilegiato\u0026rdquo; perché può vedere tutto il traffico del nodo. Il namespace doveva essere esplicitamente autorizzato.\nSoluzione:\nkubectl label namespace traefik pod-security.kubernetes.io/enforce=privileged --overwrite Fase 4: Il paradosso della connessione # Tutto sembrava verde. Pod Running. VIP attivo. Ma provando a connettermi a http://192.168.1.250 (o al dominio tazlab.net), ricevevo un secco Connection Refused.\nL\u0026rsquo;indagine (Sherlock mode) # VIP: Il VIP 192.168.1.250 è sul nodo Control Plane (.253). Pod: Ho controllato dove girava il Pod di Traefik: kubectl get pods -o wide. Girava sul nodo Worker (.127). Il Buco Nero: Il traffico arrivava al nodo .253 (VIP), ma su quel nodo non c\u0026rsquo;era nessun Traefik in ascolto sulla porta 80! Il router mandava i pacchetti nel posto giusto, ma nessuno rispondeva. Perché Traefik non girava sul Control Plane? Deep-Dive: Taints \u0026amp; Tolerations. I nodi Control Plane hanno un \u0026ldquo;Taint\u0026rdquo; (una macchia) chiamata node-role.kubernetes.io/control-plane:NoSchedule. Questo dice allo scheduler: \u0026ldquo;Non piazzare alcun carico di lavoro qui, a meno che non sia esplicitamente tollerato\u0026rdquo;. Traefik, di default, non lo tollera.\nLa soluzione architettonica definitiva # Abbiamo dovuto prendere una decisione drastica per far lavorare tutto in armonia:\nAbbandonare il DaemonSet (che cerca di girare ovunque). Passare a un Deployment con 1 singola replica. Forzare questa replica a girare esclusivamente sul nodo Control Plane (dove risiede il VIP). Modifiche ai values.yaml:\n# 1. Tollerare il Taint del Control Plane tolerations: - key: \u0026#34;node-role.kubernetes.io/control-plane\u0026#34; operator: \u0026#34;Exists\u0026#34; effect: \u0026#34;NoSchedule\u0026#34; # 2. Forzare l\u0026#39;esecuzione sul nodo Control Plane nodeSelector: kubernetes.io/hostname: \u0026#34;talos-unw-ifc\u0026#34; # O usare label generiche # 3. Deployment a singola replica (Cruciale per ACME) deployment: kind: Deployment replicas: 1 Perché una sola replica? Perché la versione Community di Traefik non supporta la condivisione dei certificati ACME tra più istanze. Se avessimo due repliche, entrambe cercherebbero di rinnovare i certificati, andando in conflitto o venendo bannate da Let\u0026rsquo;s Encrypt.\nConclusioni e Stato Finale # Dopo aver applicato questa configurazione \u0026ldquo;chirurgica\u0026rdquo;, il sistema ha preso vita.\nIl router di casa inoltra le porte 80/443 al VIP 192.168.1.250. Il VIP porta il traffico al nodo Control Plane. Traefik (ora residente sul Control Plane) intercetta il traffico. Riconosce il dominio tazlab.net, richiede il certificato a Let\u0026rsquo;s Encrypt, lo salva in /data (volume hostPath montato) e serve l\u0026rsquo;applicazione whoami. Cosa abbiamo imparato? Che \u0026ldquo;semplice\u0026rdquo; non significa \u0026ldquo;facile\u0026rdquo;. Rimuovere strati di astrazione (come i Load Balancer esterni) ci costringe a capire profondamente come Kubernetes interagisce con la rete fisica sottostante. Abbiamo dovuto gestire a mano l\u0026rsquo;affinità dei nodi, la sicurezza dei namespace e le strategie di aggiornamento.\nIl risultato è un cluster snello, senza sprechi di risorse, perfetto per un Homelab, ma costruito con la consapevolezza di ogni singolo ingranaggio.\nProssimi passi: Configurare i backup dei certificati (perché ora sono su un solo nodo!) e iniziare a distribuire i servizi reali.\n","date":"30 dicembre 2025","externalUrl":null,"permalink":"/it/posts/talos-vip-traefik-setup/","section":"Posts","summary":"","title":"Cronache del Lab: VIP Nativo su Talos e Ingress Traefik","type":"posts"},{"content":"","date":"21 dicembre 2025","externalUrl":null,"permalink":"/it/tags/blog/","section":"Tags","summary":"","title":"Blog","type":"tags"},{"content":"Questo post descrive la configurazione dell\u0026rsquo;installazione di Hugo.\nConfigurazione di Docker Compose # Il sito Hugo è configurato tramite Docker Compose. Il file compose.yml definisce un servizio chiamato hugo che utilizza l\u0026rsquo;immagine Docker hugomods/hugo:exts-non-root. Questa immagine include la versione estesa di Hugo e viene eseguita come utente non-root, migliorando la sicurezza e fornendo funzionalità essenziali per un sito Hugo moderno.\nIl file compose.yml mappa anche la directory locale del progetto su /src all\u0026rsquo;interno del container, consentendo a Hugo di servire i contenuti dai file locali. La porta 1313 è esposta per accedere al server di sviluppo.\nservices: hugo: image: hugomods/hugo:exts-non-root container_name: hugo command: server --bind=0.0.0.0 --buildDrafts --buildFuture --watch volumes: - ./:/src ports: - \u0026#34;1313:1313\u0026#34; Installazione del tema Blowfish # Il tema Blowfish è stato installato utilizzando i sottomoduli Git. Questo metodo garantisce che il tema possa essere facilmente aggiornato e mantenuto insieme al progetto Hugo principale.\nInizializza il repository Git: ```bash git init ```\nAggiungi il tema Blowfish come sottomodulo: ```bash git submodule add -b main https://github.com/nunocoracao/blowfish.git themes/blowfish ```\nConfigura il tema: Il file hugo.toml predefinito è stato rimosso e i file di configurazione dalla directory config/_default/ del tema Blowfish sono stati copiati nella directory config/_default/ del sito. La riga theme = \u0026quot;blowfish\u0026quot; nel file config/_default/hugo.toml è stata decommentata per attivare il tema.\nQuesta configurazione fornisce un ambiente robusto e flessibile per lo sviluppo di un sito web Hugo con il tema Blowfish.\nAutomatizzare i Deploy con un Webhook # Dopo aver configurato il blog, il passo logico successivo per noi era automatizzare gli aggiornamenti. Effettuare manualmente il pull delle modifiche sul server ogni volta che veniva scritto un nuovo post sembrava macchinoso. Volevamo un classico flusso di lavoro GitOps: un git push sul ramo principale doveva aggiornare automaticamente il blog live.\nÈ qui che è iniziata la nostra avventura con i webhook, e si è trasformata in una vera e propria maratona di debugging!\nLo Strumento: webhook-receiver # Abbiamo deciso di utilizzare la popolare immagine Docker almir/webhook, uno strumento leggero che ascolta le richieste HTTP ed esegue script predefiniti. Il piano era semplice:\nGitHub invia una richiesta POST al nostro URL del webhook quando effettuiamo un push di un commit. Il servizio webhook verifica la richiesta utilizzando un segreto condiviso. Esegue quindi uno script, pull-blog.sh, che lancia git pull all\u0026rsquo;interno della directory del nostro progetto Hugo. Semplice, no? Be\u0026rsquo;\u0026hellip;\nIl Viaggio nell\u0026rsquo;Inferno dei Permessi # Quello che è seguito è stato un classico caso di \u0026ldquo;sulla mia macchina funziona\u0026rdquo; contro la dura realtà della sicurezza dei container Docker.\nProblema 1: git: not found Il primo trigger del webhook è fallito immediatamente. Ci siamo subito resi conto che l\u0026rsquo;immagine minimale almir/webhook non includeva git. La prima correzione è stata creare il nostro Dockerfile personalizzato basato sull\u0026rsquo;immagine e aggiungere git utilizzando il gestore di pacchetti di Alpine:\nFROM almir/webhook:latest USER root RUN apk add --no-cache git USER webhook Problema 2: Autenticazione GitHub Con git installato, l\u0026rsquo;errore successivo è stato fatal: could not read Username for 'https://github.com'. Il nostro git pull stava cercando di usare HTTPS e non aveva credenziali. Sebbene avremmo potuto usare un Personal Access Token (PAT), abbiamo optato per l\u0026rsquo;approccio più sicuro e standard per la comunicazione server-to-server: le Deploy Key SSH.\nQuesto ha comportato:\nGenerare una nuova coppia di chiavi SSH sul server. Aggiungere la chiave pubblica alla sezione \u0026ldquo;Deploy keys\u0026rdquo; del nostro repository GitHub (con accesso di sola lettura). Montare la chiave privata nel container webhook. Problema 3: La Saga del Permission denied È qui che le cose si sono complicate. Per quella che è sembrata un\u0026rsquo;eternità, ogni tentativo è stato accolto da un errore Permission denied (publickey) da parte di git. L\u0026rsquo;utente webhook all\u0026rsquo;interno del container non poteva accedere alla chiave SSH.\nIl nostro viaggio di debugging è andato più o meno così:\nTentativo A: Impostare i permessi del file della chiave dal Dockerfile. Fallito, perché non si può fare chmod/chown su un volume che viene montato a runtime during la fase di build dell\u0026rsquo;immagine. Tentativo B: Introdurre uno script entrypoint.sh per impostare i permessi all\u0026rsquo;avvio del container. Questo ci ha portato in un labirinto di problemi di cambio utente all\u0026rsquo;interno del container. Strumenti come su-exec, runuser e gosu sono tutti falliti con errori di Operation not permitted, anche dopo aver concesso le capabilities SETUID e SETGID al container. È stata una classica battaglia contro le immagini minimali di Alpine e le feature di sicurezza di Docker. La Svolta Dopo aver provato ogni combinazione di direttive user: e logica di entrypoint, abbiamo trovato il vero colpevole: la chiave privata veniva montata come sola lettura (read-only).\nI permessi di un file in sola lettura non possono essere cambiati. Il nostro script entrypoint.sh stava fallendo nel tentativo di chown della chiave all\u0026rsquo;utente webhook, ma falliva silenziosamente.\nLa Soluzione Finale e Funzionante La soluzione corretta, e molto più robusta, è stata:\nModificare compose.yml: Montare la chiave privata in una posizione temporanea e scrivibile (/tmp/id_rsa). Usare uno script entrypoint.sh: Questo script, eseguito come root prima dell\u0026rsquo;avvio dell\u0026rsquo;applicazione principale, fa quanto segue: Crea la directory .ssh nella home reale dell\u0026rsquo;utente webhook (/home/webhook/.ssh). Copia la chiave da /tmp/id_rsa a /home/webhook/.ssh/id_rsa. Imposta la proprietà (chown webhook:webhook) e i permessi (chmod 600) corretti sulla chiave copiata. Rimuove la chiave temporanea da /tmp. Aggiornare GIT_SSH_COMMAND: Assicurarsi che la variabile d\u0026rsquo;ambiente punti alla destinazione finale della chiave: /home/webhook/.ssh/id_rsa. Eseguire il container come utente webhook: Abbiamo impostato user: webhook in compose.yml per garantire che il processo venga eseguito con privilegi minimi dopo che l\u0026rsquo;entrypoint ha svolto il suo lavoro come root. L\u0026rsquo;Ultimo Mistero: I Log Vuoti Anche con tutto funzionante, il comando docker logs rimaneva ostinatamente vuoto. Il servizio webhook funzionava, ma inghiottiva tutto l\u0026rsquo;output del nostro script. L\u0026rsquo;ultimo pezzo del puzzle è stato aggiungere questa riga all\u0026rsquo;inizio del nostro script pull-blog.sh, che forza tutto l\u0026rsquo;output verso gli stream standard del container:\nexec \u0026gt;/proc/1/fd/1 2\u0026gt;/proc/1/fd/2 Con questo, abbiamo finalmente potuto vedere l\u0026rsquo;output di git pull nei log. Un lungo viaggio, ma una lezione preziosa sulle sfumature dei permessi Docker e della logica di runtime!\n","date":"21 dicembre 2025","externalUrl":null,"permalink":"/it/posts/hugo-installation/","section":"Posts","summary":"","title":"Dettagli sull'installazione di Hugo","type":"posts"},{"content":"","date":"21 dicembre 2025","externalUrl":null,"permalink":"/it/tags/docker-compose/","section":"Tags","summary":"","title":"Docker-Compose","type":"tags"},{"content":"","date":"20 dicembre 2025","externalUrl":null,"permalink":"/it/tags/biografia/","section":"Tags","summary":"","title":"Biografia","type":"tags"},{"content":"","date":"20 dicembre 2025","externalUrl":null,"permalink":"/tags/biography/","section":"Tags","summary":"","title":"Biography","type":"tags"},{"content":"Dagli esordi dei primi circuiti elettronici all\u0026rsquo;orchestrazione di Kubernetes, la mia costante è sempre stata una sola: non accettare mai che un sistema funzioni senza averne compreso a fondo la logica interna. Che si tratti di hardware o di riga di comando, il mio obiettivo è sempre stato quello di padroneggiare il sistema, comprenderne i meccanismi profondi e, dove possibile, spostarne i limiti un po\u0026rsquo; più in là.\nOggi molti parlano di Intelligenza Artificiale e Cloud come di novità assolute, ma chi ha vissuto l\u0026rsquo;evoluzione dei bit \u0026ldquo;sotto il cofano\u0026rdquo;, sta assistendo a una straordinaria convergenza. In questo post voglio raccontarvi come sono passato dal fischio del modem 56k all\u0026rsquo;orchestrazione di un cluster Kubernetes nel mio salotto.\nL\u0026rsquo;IA prima della potenza: L\u0026rsquo;era delle SVM (1999-2001) # Alla fine degli anni \u0026lsquo;90, l\u0026rsquo;Intelligenza Artificiale era una sfida contro i limiti della fisica. La potenza di calcolo di allora non permetteva alle reti neurali classiche di convergere in tempi ragionevoli.\nNella mia tesi di laurea in Fisica, ho affrontato questo limite utilizzando le Support Vector Machines (SVM) per la diagnosi precoce di tumori radiografici. Gli algoritmi erano scritti in C e C++ per la massima performance, con un\u0026rsquo;interfaccia in Java. Era un\u0026rsquo;epoca in cui l\u0026rsquo;IA si costruiva con la matematica pura.\nSlackware, RedHat e il fischio del modem # In quegli stessi anni, scoprivo Linux. Non c\u0026rsquo;erano tutorial su YouTube, c\u0026rsquo;erano i manuali e i newsgroup. Ho installato le prime versioni di RedHat e Slackware, configurando i kernel mentre il mondo fuori era connesso tramite il doppino telefonico e il fischio del modem 56k. Lì ho imparato come \u0026ldquo;pensa\u0026rdquo; un sistema operativo.\nDal Semantic Web alla Consulenza Enterprise # Dopo un Master in IT focalizzato sullo stack di internet e sul Semantic Web (l\u0026rsquo;idea di rendere i dati comprensibili alle macchine), sono approdato a Milano. Sono stati gli anni della consulenza \u0026ldquo;pesante\u0026rdquo; in Java, dove la modernità dei JavaBeans doveva dialogare con sistemi COBOL e database SQL critici. Una palestra incredibile per capire la gestione della complessità su larga scala.\nLa dualità: Insegnamento e Web Development # Il mio percorso è poi virato verso l\u0026rsquo;insegnamento di Matematica e Fisica. Ma non ho mai smesso di essere un developer. Durante l\u0026rsquo;abilitazione, ho lavorato come programmatore JavaScript e PHP, rimanendo connesso col mondo del web dinamico.\nIn questi anni ho capito che imparare un nuovo linguaggio (Python, Haskell, Rust, XML) è un processo velocissimo se ne comprendi i paradigmi. Una volta compresa la logica (funzionale, imperativa, a oggetti), la differenza è quasi solo sintassi.\nLa Scintilla: L\u0026rsquo;IA come compagno di studi # Il cerchio ha iniziato a chiudersi con l\u0026rsquo;avvento dei nuovi LLM. Inizialmente ho usato l\u0026rsquo;IA per scopi didattici: creare materiali personalizzati per i miei studenti e, soprattutto, insegnare loro come usare l\u0026rsquo;IA per migliorare l\u0026rsquo;apprendimento e non per \u0026ldquo;imbrogliare\u0026rdquo;.\nMa l\u0026rsquo;IA ha risvegliato in me la voglia di sperimentare. Avevo bisogno di un sistema Linux funzionante e, dopo 15 years, ho installato Linux Mint. Sono rimasto folgorato: il mondo Linux era diventato semplice, elegante, bellissimo. Grazie all\u0026rsquo;IA, non ho dovuto rileggere tonnellate di documentazione per driver e servizi; in due giorni avevo una workstation perfetta.\nL\u0026rsquo;Escalation: Dalla VM al Mini PC # A quel punto, la curiosità è diventata inarrestabile. Volevo servizi attivi 24/7. Ho affittato una VM, usandola come palestra con l\u0026rsquo;IA come \u0026ldquo;Senior Partner\u0026rdquo; sempre al mio fianco.\nMa i servizi crescevano: Docker, database SQL e NoSQL, database vettoriali per LLM, n8n per l\u0026rsquo;automazione, Tailscale per la rete\u0026hellip; Il server in affitto non bastava più. Invece di aumentare il canone mensile, ho fatto il salto: ho comprato un Mini PC con abbastanza ram, un dominio e ho iniziato a costruire il mio homelab.\nOltre la paura: Proxmox e Kubernetes # Con l\u0026rsquo;IA a supportarmi, la \u0026ldquo;paura della complessità\u0026rdquo; è svanita. Perché leggere 300 pagine di manuale quando l\u0026rsquo;IA può estrarre le 10 righe esatte che mi servono in quel momento?\nCosì ho installato Proxmox. Poi, mi sono detto: \u0026ldquo;Perché fermarsi?\u0026rdquo;. Ho affrontato la sfida definitiva: Kubernetes. Forse è esagerato per un laboratorio casalingo, ma l\u0026rsquo;argomento è troppo affascinante per essere ignorato. Oggi il mio Home Lab gira su un cluster Kubernetes con Linux Talos su Proxmox.\nConclusione: Un nuovo mondo da esplorare # Chi sono oggi? Un fisico che ha visto nascere l\u0026rsquo;IA e che oggi la usa per abbattere le barriere del tempo. Non so dove mi porterà questa strada fatta di High Availability, sicurezza e scalabilità, ma so che davanti a me c\u0026rsquo;è un mondo nuovo da esplorare.\nNon vedo l\u0026rsquo;ora di scoprire cosa c\u0026rsquo;è dietro il prossimo nodo.\nTi è piaciuto questo viaggio? Seguimi su blog.tazlab.net per scoprire come sto configurando il mio cluster e quali sono i prossimi esperimenti al confine tra fisica e cloud.\n","date":"20 dicembre 2025","externalUrl":null,"permalink":"/it/about/","section":"Roberto Tazzoli","summary":"","title":"Chi sono","type":"page"},{"content":"","date":"20 dicembre 2025","externalUrl":null,"permalink":"/tags/computer-science/","section":"Tags","summary":"","title":"Computer-Science","type":"tags"},{"content":"","date":"20 dicembre 2025","externalUrl":null,"permalink":"/it/tags/fisica/","section":"Tags","summary":"","title":"Fisica","type":"tags"},{"content":"","date":"20 dicembre 2025","externalUrl":null,"permalink":"/it/tags/ia/","section":"Tags","summary":"","title":"Ia","type":"tags"},{"content":"","date":"20 dicembre 2025","externalUrl":null,"permalink":"/it/tags/informatica/","section":"Tags","summary":"","title":"Informatica","type":"tags"},{"content":"","date":"20 dicembre 2025","externalUrl":null,"permalink":"/tags/physics/","section":"Tags","summary":"","title":"Physics","type":"tags"},{"content":"","externalUrl":null,"permalink":"/it/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/it/series/","section":"Series","summary":"","title":"Series","type":"series"}]