In molti contesti enterprise la parola d’ordine è una sola: zero vulnerabilità di severità “IMPORTANT” in produzione.
Sulla carta è una scelta condivisibile. Nella pratica, però, può trasformarsi in un paradosso tecnico difficile da gestire.
In questo articolo vediamo un caso reale (generalizzato e anonimizzato) che coinvolge:
- immagini Docker UBI9 (Red Hat)
- PHP
- scanner di sicurezza agganciati alla pipeline CI/CD
- vulnerabilità 0-day senza remediation
…e una pipeline che si rifiuta ostinatamente di far passare la build.
Il contesto: policy rigide e immagini “enterprise”
Immagina un’azienda strutturata, con processi maturi e tanta voglia di fare le cose “come si deve”.
Prima decisione: si definisce una policy molto chiara. Nessun rilascio può andare in produzione se contiene vulnerabilità classificate come IMPORTANT o superiori. La pipeline di build ha un controllo di sicurezza che analizza immagini, dipendenze, runtime; se trova qualcosa sopra una certa soglia, semplicemente boccia la build.
Seconda decisione: per garantire sicurezza, supporto e compliance, si scelgono solo immagini UBI9 di Red Hat come base Docker. Immagini enterprise, aggiornate regolarmente, pensate per girare su Kubernetes e supportate da un vendor solido. Una scelta più che sensata: meno immagini “strane” in circolazione, più standardizzazione, più controllo.
Terza componente del quadro: una pipeline CI/CD ben configurata, dove a ogni build viene eseguito uno scanner di sicurezza su tutto lo stack. Dalla base image alle librerie di sistema, passando per il runtime PHP e le sue estensioni, tutto viene passato al setaccio.
Fin qui sembra un caso da manuale di “security by design”: policy chiare, immagini ufficiali, automazione spinta. Cosa potrebbe mai andare storto?
Il giorno in cui il vendor ti mette in crisi (senza avere torto)
Poi succede una cosa normalissima nel mondo reale: Red Hat rilascia una nuova versione dell’immagine PHP su UBI9, il classico tag latest. È l’immagine che dovresti usare, quella ufficiale, quella consigliata.
C’è solo un dettaglio: nel frattempo sono state scoperte sei vulnerabilità di severità IMPORTANT legate a quella versione. Parliamo di 0-day, quindi problemi appena identificati, ancora in fase di analisi. Il vendor fa quello che deve fare: pubblica un advisory, spiega che al momento non esiste remediation, non c’è ancora una patch installabile, ma dà una serie di indicazioni pratiche su come usare quella versione in sicurezza. Suggerisce best practice, configurazioni, limitazioni d’uso: in sostanza ti dice “puoi continuare a usarla, ma fallo così, così e così”.
Dal punto di vista del vendor, è una gestione responsabile: ti informa, ti mette in guardia, ti indica delle mitigazioni. Non può fare di più finché non c’è una fix upstream.
Entra in scena la pipeline: “Build FAILED” ❌
Tu, diligentemente, aggiorni la base image e fai partire la build con l’ultima versione rilasciata. Dopo il pull dell’immagine, entra in azione lo scanner di sicurezza.
Risultato: individua esattamente quelle sei vulnerabilità IMPORTANT citate nell’advisory. La pipeline non è interessata al contesto, alle mitigazioni, al fatto che sia un 0-day senza soluzione: vede solo una cosa, una regola configurata tempo prima:
“Se trovi anche una sola vulnerabilità di severità IMPORTANT, la build deve fallire.”
E infatti la build fallisce. Non una volta sola: tutte le pipeline che usano quella base image cominciano a rompersi in serie. Non hai nessuna immagine più sicura verso cui migrare, perché quella è l’ultima e l’unica ufficialmente supportata. Non hai una patch da applicare. Non hai un workaround tecnico lato codice che elimini la voce dal report.
Hai solo un paradosso: stai usando esattamente l’immagine enterprise raccomandata, seguendo le linee guida del vendor, e il tuo stesso processo di sicurezza ti impedisce di rilasciare.
Il classico cane che si morde la coda. E noi, in questa storia, siamo proprio la bocca del cane. 🐶
Il vero nodo: quando la sicurezza diventa un semplice “checkbox”
Questo tipo di situazione nasce quasi sempre dalla tentazione di ridurre la sicurezza a un numero su un report: sopra una certa soglia è rosso, sotto è verde. Una soglia rigida del tipo “IMPORTANT = blocco” sembra razionale finché non la vivi sulla tua pelle.
Il problema è che una regola del genere ignora completamente il contesto. Non tutte le vulnerabilità IMPORTANT sono uguali per il tuo caso specifico: alcune richiedono determinate condizioni per essere sfruttate, altre interessano componenti che nel tuo container neanche esponi. Inoltre, non distingue tra una vulnerabilità per cui esiste già una versione corretta e una per cui lo stesso vendor ti dice apertamente che al momento una patch non c’è.
In questo scenario, la pipeline si comporta come un guardiano cieco: applica la regola in modo binario, senza sapere se hai implementato mitigazioni, se il servizio è effettivamente esposto, se c’è un piano condiviso di remediation. Di fatto blocca il lavoro, ma non è detto che stia aumentando davvero il livello di protezione.
Cosa si potrebbe fare meglio (senza abbassare il livello di sicurezza)
La soluzione non è togliere i controlli di sicurezza, né “abbassare la severità” per magia. La questione è far evolvere il modo in cui li integriamo nel flusso di lavoro.
Un primo passo è distinguere tra ciò che deve bloccare e ciò che deve semplicemente essere monitorato. Invece di dire “blocca tutto ciò che è IMPORTANT”, si potrebbe decidere di bloccare quando esiste già una versione corretta della libreria o dell’immagine, o quando la vulnerabilità è chiaramente sfruttabile nel nostro contesto (ad esempio perché tocca direttamente un servizio esposto). In tutti gli altri casi, il problema va comunque tracciato, notificato, discusso con il team di sicurezza, ma non necessariamente deve impedire ogni build.
Un altro elemento fondamentale è introdurre un processo di risk acceptance esplicito. Per le vulnerabilità 0-day senza remediation puoi prevedere eccezioni temporanee, documentate, con una scadenza chiara. La pipeline può continuare a segnalare il problema come “critico” ma consentire il passaggio se esiste un ticket approvato, che elenca la advisory del vendor, le mitigazioni applicate e una data entro cui riesaminare la situazione.
C’è poi il tema, annoso ma importantissimo, dell’uso cieco di :latest. Se le tue build dipendono sempre e solo dall’ultima immagine disponibile, ti esponi a sorprese del genere. Tag versionati, passaggi controllati da una release all’altra, un minimo di revisione prima di aggiornare la base image: tutto questo riduce le possibilità che un semplice docker pull faccia esplodere mezza infrastruttura CI.
Dell’uso (da evitare quando possibile) di un’immagine
:latestne ho parlato proprio la settimana scorsa al Golem durante una sessione informativa sull’utilizzo di Docker.
Infine, le mitigazioni suggerite dal vendor non dovrebbero restare relegate a una pagina di advisory. Dovrebbero entrare nelle tue policy, nei template Helm, nei Dockerfile, nelle linee guida interne. Se Red Hat ti dice “puoi continuare a usare questa versione, ma con queste precauzioni”, allora queste precauzioni vanno trasformate in configurazioni obbligatorie e controlli automatici, non solo in una nota a margine.
Conclusione: la sicurezza è molto più di una soglia nel report
La storia delle sei vulnerabilità IMPORTANT senza patch è un esempio concreto di cosa succede quando applichiamo la sicurezza come se fosse un interruttore on/off. I controlli automatici sono fondamentali, ma senza contesto rischiano di bloccare il lavoro proprio quando stai cercando di fare la cosa giusta, seguendo il vendor e adottando buone pratiche.
Una pipeline di sicurezza matura non si limita a dire “rosso” o “verde”:
ti aiuta a valutare il rischio, a tracciare le eccezioni, a verificare l’applicazione delle mitigazioni, a dare priorità agli interventi quando finalmente la patch arriverà.
Altrimenti restiamo lì, a guardare il cane che si morde la coda, con la build rossa, l’immagine ufficiale, e la sensazione un po’ amara che sì, la sicurezza è importante… ma senza buon senso finiamo davvero per far la parte della bocca del cane. 😅
Rispondi