Kubernetes NetworkPolicy è una di quelle funzionalità che sembrano semplici finché non si prova ad applicarle in un cluster molto utilizzato. Nel 2026 resta un elemento fondamentale per il networking “least privilege”, ma è facile configurarlo male perché l’applicazione dipende dalla CNI, i selettori si basano sulle etichette e ciò che “intendevi” non sempre coincide con ciò che l’API fa davvero. Questa guida si concentra sugli errori che causano incidenti e sugli schemi che i team usano più spesso per segmentare il traffico senza compromettere la produzione.
Il caso più frequente del “non funziona” non è un problema di YAML: NetworkPolicy è un’API, non un firewall di per sé. Il cluster deve usare una soluzione di rete che implementi l’enforcement delle NetworkPolicy; altrimenti le regole possono esistere e non avere alcun effetto. Anche con l’enforcement attivo, il comportamento può variare in base alla CNI per casi limite, logging e osservabilità: “supportato” va verificato, non dato per scontato.
In secondo luogo, bisogna essere precisi sulla semantica dell’isolamento. Per impostazione predefinita i pod non sono isolati e accettano traffico da ovunque. Un pod diventa isolato per ingress e/o egress solo quando viene selezionato da una policy che copre quella direzione (tramite policyTypes o comportamenti impliciti). Da quel momento le regole sono allow-list: il traffico è consentito solo se almeno una policy applicabile lo permette, e tutto il resto viene negato per la direzione isolata.
Infine, è importante ricordare il livello a cui opera Kubernetes NetworkPolicy. La NetworkPolicy “classica” lavora soprattutto a livello L3/L4: IP, namespace, pod e porte. Se l’esigenza è “consentire solo questo percorso HTTP” o “solo questo nome DNS”, spesso servono estensioni specifiche della CNI o API di policy più recenti, invece di forzare quei requisiti dentro NetworkPolicy di base.
Affidarsi a una policy in un cluster dove l’enforcement non è attivo (o non è attivo su ogni node pool) resta uno degli errori principali. Il YAML sembra corretto, la CI passa, non cambia nulla, e il team se ne accorge solo durante una revisione di sicurezza o dopo un incidente. Inserisci un controllo esplicito nei runbook: quale CNI applica le policy e come validare l’enforcement in un namespace di test.
Lasciare policyTypes implicito e dare per scontato che copra entrambe le direzioni crea buchi difficili da vedere. Spesso si scrive una policy di ingress pensando che limiti anche l’uscita, poi si scopre che l’egress è rimasto completamente aperto. Rendi esplicita la direzione ogni volta che l’accesso in uscita conta (quasi sempre).
Un altro fraintendimento ricorrente è credere che gli oggetti Service siano “target” della policy. NetworkPolicy seleziona i pod, non i Service. Puoi progettare regole che corrispondono ai pod dietro un Service, ma il nome del Service non è un selettore. Quando qualcuno dice “ho permesso il Service”, spesso ha ignorato le label reali dei pod, e la regola finisce per non matchare nulla.
Uno schema di segmentazione che regge nel tempo parte da confini che hanno significato: namespace per tenancy e ciclo di vita, label per identità e ruolo dell’applicazione, e un elenco ridotto di servizi condivisi (DNS, logging, metriche, ingress, gateway di service mesh) che molti workload devono raggiungere. Se queste basi sono incoerenti, le NetworkPolicy diventano fragili e i team finiscono per disattivarle o aggiungere eccezioni troppo ampie.
Un approccio pratico è standardizzare un contratto minimo di label tra i workload: app.kubernetes.io/name, app.kubernetes.io/part-of, app.kubernetes.io/component e una label di ambiente o tenant a livello di namespace. In questo modo i selettori restano leggibili e prevedibili, riducendo il debugging del tipo “perché non matcha?” causato da label improvvisate.
I servizi condivisi meritano una gestione dedicata. Il DNS è la causa più comune di outage autoindotti durante i rollout: si abilita il default-deny sull’egress e poi ci si dimentica di consentire l’uscita verso i pod DNS (e talvolta verso l’indirizzo node-local DNS, se usato). Tratta DNS, eventuale sincronizzazione oraria e i proxy di uscita obbligatori come dipendenze di base e modellali esplicitamente nello schema iniziale.
Schema 1: “Baseline del namespace + affinamento per applicazione”. Inizia con un default-deny a livello di namespace per l’ingress (e per l’egress dove serve), poi aggiungi piccole regole di allow specifiche per app. L’obiettivo non è creare una matrice perfetta subito, ma costruire un perimetro controllato e iterare in modo sicuro mentre emergono i flussi legittimi.
Schema 2: “Allow-list dei servizi condivisi”. Mantieni un set ristretto di policy (spesso gestite dal team piattaforma/SRE) che consenta a tutti i namespace applicativi di raggiungere servizi essenziali: DNS, componenti dell’ingress controller (quando necessario), agent di osservabilità e endpoint admission/webhook utili al funzionamento del cluster. Mantieni l’elenco breve e revisionato, perché ogni eccezione “condivisa” può diventare una scorciatoia per il movimento laterale se cresce troppo.
Schema 3: “Livelli basati su label dentro un namespace”. Per chi preferisce meno namespace, usa tier come role=frontend, role=backend, role=db e definisci i flussi consentiti: frontend → backend, backend → db, negando tutto il resto. È un modello vicino al modo in cui si pensa l’architettura e riduce la frammentazione rispetto a selettori one-off.

Nel 2026 la strategia più sicura resta l’enforcement progressivo. Si parte documentando i flussi previsti (anche una tabella semplice è sufficiente), poi si introduce il default-deny in uno scope controllato e si aggiungono regole di allow in modo iterativo monitorando i risultati. L’approccio “big bang” — attivare default-deny ovunque e correggere i guasti in diretta — quasi sempre genera un incidente rumoroso e un rollback.
I test devono includere casi positivi e negativi. Non basta verificare che “il servizio A può chiamare il servizio B”: serve anche un test di regressione che dimostri che “il servizio C non può chiamare il servizio B”. Senza test negativi, nel tempo le policy tendono a diventare troppo permissive perché nessuno nota i percorsi extra che si sono aperti.
Per il troubleshooting serve un metodo coerente. Inizia verificando se il pod è selezionato da qualche policy (e per quale direzione), poi elenca tutte le policy del namespace che possono matchare, e solo dopo analizza le regole. Se la tua CNI fornisce visibilità sui flussi o log di verdetto delle policy, usali subito: spesso riducono il debugging da ore a minuti mostrando quale regola ha consentito o negato una connessione.
L’egress è dove nasce la maggior parte dei problemi reali: API esterne, mirror di pacchetti, identity provider e webhook trasformano rapidamente un “deny by default” in una lunga allow list. Due mitigazioni aiutano molto: instradare l’uscita tramite un percorso di egress controllato (proxy o gateway) e consentire l’egress principalmente verso quel punto controllato, invece che verso decine di destinazioni esterne. Così le policy restano più piccole e gli audit più semplici.
I selettori di namespace falliscono spesso quando i namespace non sono etichettati in modo coerente. Si scrivono regole che si basano su namespaceSelector, poi si scopre che i namespace di sistema o quelli “storici” non hanno label, quindi la regola non può matchare. La correzione è anche organizzativa: definisci uno standard minimo di labeling dei namespace e applicalo (i controlli policy-as-code funzionano bene).
Infine, tieni d’occhio l’evoluzione dell’ecosistema di policy. Il gruppo di lavoro upstream sulle policy di rete continua a far progredire risorse di policy a livello “admin” e cluster-wide come CRD, con cambiamenti di naming e versioning nel tempo. Se ti servono guardrail globali o baseline policy tra namespace, valuta queste API di livello amministrativo nel tuo ambiente invece di cercare di imitare una “policy di cluster” usando solo oggetti NetworkPolicy per namespace.