Kuma Service Mesh – Parte 1
Concetti fondamentali e configurazione iniziale
Pascal Carone
7/4/202511 min read
Indice:
Introduzione
Architettura di Kuma: Control Plane e Data Plane
Sidecar injection e transparent proxying
Creazione di una mesh e abilitazione di mTLS
MeshTrafficPermission: politiche di accesso zero-trust
Osservabilità con MeshMetric, MeshTrace e MeshAccessLog
Conclusione
Introduzione
In Kubernetes, la complessità delle comunicazioni fra servizi aumenta notevolmente. Garantire connettività affidabile, sicurezza end-to-end e osservabilità del traffico richiede soluzioni dedicate. I service mesh rispondono a questa esigenza introducendo un livello di infrastruttura trasparente che si occupa di queste funzionalità, permettendo agli sviluppatori di concentrarsi sulla logica applicativa.
Kuma è un service mesh open-source (progetto CNCF Sandbox creato originariamente da Kong) progettato per essere semplice da usare e adatto sia a Kubernetes che ad ambienti tradizionali (VM, bare metal). In questo primo articolo di due dedicati a Kuma su Kubernetes, esamineremo i concetti fondamentali del suo funzionamento e la configurazione iniziale di una service mesh. Tratteremo il modello Control Plane / Data Plane, il meccanismo di sidecar injection e transparent proxying, la definizione delle risorse principali (come Mesh e MeshTrafficPermission) con l’abilitazione della mutual TLS (mTLS) per una postura zero-trust. Accenneremo infine alle funzionalità di base per l’osservabilità (risorse MeshMetric, MeshTrace e MeshAccessLog) offerte da Kuma. Il tono sarà tecnico e riflessivo, pensato per chi gestisce infrastrutture Kubernetes e vuole comprendere il perché oltre al come di Kuma.
Architettura di Kuma: Control Plane e Data Plane
Kuma adotta il classico modello architetturale dei service mesh basato su Control Plane (CP) e Data Plane (DP). Il Control Plane è il cervello della mesh: un servizio centralizzato (il processo kuma-cp) che gestisce la configurazione e distribuisce politiche di traffico, sicurezza e osservabilità. Il Data Plane è costituito da una rete di proxy sidecar (basati su Envoy) che affiancano ogni istanza di servizio applicativo e applicano sul campo le regole ricevute dal CP. In pratica, ogni pod Kubernetes con un'applicazione contiene anche un container sidecar proxy che intercetta tutto il traffico in ingresso e in uscita per quel servizio. Questi proxy, essendo out-of-process, si occupano di funzionalità trasversali come instradamento (routing), retry/failover, cifratura mTLS, controllo accessi e raccolta metriche/log.
Il Control Plane e i Data Plane comunicano tipicamente tramite API di controllo (es. xDS di Envoy). I proxy sidecar si registrano al CP e ottengono configurazioni dinamiche: questo significa che il CP non è sul percorso dei dati runtime, ma distribuisce aggiornamenti e policy ai proxy in modo centralizzato. I sidecar Envoy vengono così configurati in tempo reale (ad esempio, quali servizi possono comunicare, quali percorsi di traffico usare, ecc.), mentre il traffico applicativo vero e proprio scorre tra i proxy. Il risultato è un’architettura service mesh in cui la rete dei servizi è resa più sicura e controllabile senza modificare il codice delle applicazioni.


Nell'illustrazione: il Kuma CP controlla una serie di sidecar Envoy (DP) uno per ogni servizio/pod (A, B, C). Ogni sidecar proxy affianca un servizio applicativo e intercetta il traffico da e verso di esso, applicando le policy definite.
Questa architettura fornisce una base per implementare il principio zero-trust e altri pattern avanzati. Invece di affidarsi alla rete “aperta” del cluster, ogni comunicazione passa attraverso proxy che autenticano, crittografano e autorizzano le richieste in base a regole centralizzate. Kuma, in qualità di control plane, semplifica notevolmente questa gestione centralizzata: esso è un singolo componente facile da deployare su Kubernetes usa l’API server come datastore, mentre in ambienti “universal” (VM) utilizza per default un database come PostgreSQL.
Sidecar injection e transparent proxying
Per attivare il data plane di Kuma su Kubernetes, è necessario inserire automaticamente il container sidecar Envoy in ogni pod applicativo. Kuma fornisce un mechanism di sidecar injection automatica tramite un webhook di ammissione Mutating: in pratica, quando abilitiamo Kuma su un namespace, ogni nuovo pod in quel namespace viene modificato on-the-fly per includere il container del proxy sidecar (oltre a un init container). L’attivazione avviene tipicamente aggiungendo una label al namespace (o ai pod specifici). Ad esempio, per abilitare Kuma su un namespace di nome my-namespace possiamo applicare:
Con la label kuma.io/sidecar-injection: enabled presente, Kuma sa già che deve effettuare l’injection del proxy sidecar in tutti i pod di quel namespace. Questa operazione aggiunge due container addizionali a ciascun pod applicativo:
kuma-init: un init container (eseguito all’avvio del pod) che configura le regole di rete (iptables) per il transparent proxyng. In sostanza, kuma-init reindirizza automaticamente il traffico destinato ai service mesh (es. verso altre service identity interne) affinché passi tramite il proxy Envoy invece di andare direttamente all’esterno. Se Kuma CNI è installato, si può usare il plugin CNI al posto dell’init container per configurare il routing trasparente.
kuma-sidecar: il container vero e proprio del data plane proxy, contenente il processo kuma-dp che avvia Envoy. Questo sidecar si avvia dopo il successo di kuma-init (che termina una volta impostate le regole di routing). Il binario kuma-dp esegue Envoy e lo registra presso il Control Plane, instaurando un collegamento persistente. Da quel momento, Envoy (sidecar) riceverà dal CP la configurazione attuale della mesh (liste di servizi e loro indirizzi, certificati mTLS, policy di traffico, ecc.).
Una volta in esecuzione, il sidecar Envoy aggancia tutto il traffico del pod: richieste in uscita dal container applicativo vengono intercettate e inviate da Envoy verso la destinazione appropriata; simmetricamente, il traffico in ingresso destinato al servizio passa prima per Envoy e poi viene inoltrato al container applicativo locale. Questo proxying trasparente avviene senza che l’applicazione debba essere consapevole del proxy – dal punto di vista del codice nulla cambia nelle chiamate di rete, ma dietro le quinte il traffico viene dirottato via sidecar. Il flusso tipico di una richiesta tra due servizi attraverso Kuma è illustrato di seguito:


Nell'illustrazione: Service A invia una richiesta che viene automaticamente catturata dal sidecar Envoy locale, il quale la critta e inoltra attraverso la rete (mTLS) verso il sidecar del Service B. Il sidecar di B decifra e verifica la richiesta, applica eventuali policy (es. controllo accessi) e infine la consegna al Service B. Tutto questo avviene in modo trasparente per i servizi A e B (che credono di comunicare direttamente tra loro sull’host e porta usuali).
Questo meccanismo offre vari benefici: nessuna modifica applicativa, sicurezza end-to-end con mutual TLS trasparente, possibilità di introdurre policy di routing o fault injection e così via, il tutto gestito centralmente da Kuma. Prima di vedere come abilitare la mTLS e le policy di sicurezza, introduciamo la risorsa fondamentale che definisce una mesh in Kuma.
Creazione di una mesh e abilitazione di mTLS
In Kuma, una mesh è un dominio logico di isolamento che raccoglie un insieme di servizi e definisce le policy di rete comuni (ad esempio, abilitazione di mTLS, regole di default, etc.). Su Kubernetes, le mesh sono rappresentate dalla risorsa custom Mesh (CRD di gruppo mesh). Quando installiamo Kuma, viene creata di default una mesh chiamata “default” (ma è possibile definirne di multiple per multi-tenancy). In scenari base useremo la mesh di default, tenendo presente che più mesh isolate possono coesistere nello stesso cluster se necessario.
La risorsa Mesh ci permette di configurare impostazioni globali, la più importante delle quali è l’abilitazione del mutual TLS. Per impostazione predefinita, subito dopo l’installazione, la mesh default non ha mTLS attivo: il traffico tra servizi quindi non è cifrato né autenticato da Kuma (ovvero si comporta come la normale rete Kubernetes). Per implementare lo zero-trust, abilitiamo il mTLS sulla mesh in modo che ogni connessione tra sidecar Envoy venga cifrata e accompagnata da identificazione reciproca tramite certificati.
Abilitare il mTLS in Kuma è molto semplice. Possiamo utilizzare un CA integrato (builtin) fornito out-of-the-box, oppure specificare un nostro CA esterno (modalità “provided”). Nel caso più semplice, configuriamo la mesh per usare il CA builtin. Ecco un esempio di manifest YAML compatibile con Kuma 2.13.x per abilitare mTLS nella mesh default:
Questo manifest definisce che nella mesh default è attivo il backend CA chiamato ca-1 di tipo builtin. Kuma quindi genera automaticamente un’autorità di certificazione interna e distribuisce a tutti i data plane proxy (sidecar Envoy) un certificato X.509 univoco (con identità SPIFFE) firmato da tale CA. La configurazione sopra indica anche che i certificati dei proxy verranno ruotati ogni 24 ore (dpCert.rotation.expiration: 24h) e che la chiave del CA è RSA 2048 bit con validità 10 anni. Subito dopo l’applicazione di questa risorsa, Kuma provvede dietro le quinte a creare il CA e rilasciare i certificati a tutti i sidecar già connessi (e a quelli che si connetteranno in futuro). Non importa se abbiamo pochi pod o migliaia di servizi: l’operazione è automatica e scalabile – è sufficiente questa singola configurazione per ottenere mTLS ovunque (“data planes across multiple services, this is how easy it is to use zero-trust security with Kuma”).
A questo punto tutto il traffico interno alla mesh è cifrato e autenticato in modo trasparente dai proxy Envoy. Tuttavia, Kuma adotta un principio secure-by-default: quando il mTLS è abilitato, per impostazione predefinita nessun servizio può comunicare con altri finché non vengono definite esplicitamente le permission. Questo comportamento garantisce che non ci siano vie di comunicazione non autorizzate: abbiamo creato un circuito cifrato, ma ora dobbiamo accendere gli interruttori giusti affinché certi servizi possano parlarsi. In altre parole, con mTLS attivo Kuma imposta un default-deny su tutto il traffico interno, implementando una politica zero-trust a tutti gli effetti. Nel prossimo paragrafo vedremo come definire tali permessi tramite la risorsa MeshTrafficPermission.
(Nota: se il mTLS non fosse attivo, di default Kuma permette il traffico libero tra servizi all’interno della mesh il controllo degli accessi dettagliato richiede infatti l’identificazione reciproca fornita da mTLS. Ma abilitando mTLS senza ulteriori configurazioni, tale “default allow” viene rimosso e subentra il default deny).
MeshTrafficPermission: politiche di accesso zero-trust
Per ristabilire la comunicazione desiderata tra servizi in una mesh con mTLS, occorre creare delle policy di autorizzazione esplicite. Kuma 2.13 introduce la risorsa MeshTrafficPermission (in breve MTP) proprio per questo scopo: definire quali servizi/client sono autorizzati a comunicare con un dato servizio destinatario all’interno di una mesh. Possiamo immaginare questa policy come delle regole firewall L7 basate sull’identità dei servizi (certificate SPIFFE) invece che sugli IP.
Come funziona in pratica? Ogni sidecar Envoy, avendo un certificato con identità, può essere configurato per accettare solo connessioni in ingresso provenienti da certi servizi/identità attese. La MeshTrafficPermission è la configurazione di alto livello che Kuma tradurrà in regole per i proxy Envoy. Vediamo un esempio concreto.
Supponiamo di avere due servizi nella mesh default: demo-app e kv (come nel classico esempio applicativo di Kuma). Vogliamo consentire che demo-app possa chiamare il servizio kv. Definiamo dunque una MeshTrafficPermission rivolta al servizio di destinazione kv e autorizziamo il traffico in ingresso da parte di demo-app. Il manifest YAML seguente mostra come configurarlo (nell’ipotesi che entrambi i servizi risiedano nel namespace Kubernetes kuma-demo):
In questa policy dichiariamo che per il Dataplane con label app: kv (cioè il proxy del servizio KV) è permesso accettare traffico da qualunque MeshSubset di dataplane che abbia tag app: demo-app nel namespace kuma-demo. In altre parole: “il servizio kv può ricevere richieste dai servizi con label demo-app nello namespace kuma-demo”. L’azione di default per tale combinazione è Allow (consenti). Qualsiasi altro servizio non menzionato rimane non autorizzato a chiamare kv. Dopo aver applicato questo YAML, il sidecar Envoy del servizio KV inizierà ad accettare connessioni dai sidecar dei pod aventi label app: demo-app (nel namespace specificato), rifiutando invece altri tentativi. Nel nostro esempio, demo-app tornerà a funzionare potendo contattare kv con successo, mentre se un terzo servizio non autorizzato provasse a chiamare kv, la connessione verrebbe negata dal proxy di KV.
La flessibilità di MeshTrafficPermission consente di definire regole di accesso molto granulari. Possiamo autorizzare interi gruppi (es: tutti i servizi in un certo namespace “observability” possono accedere ovunque) oppure specifici servizi verso specifici altri. Possiamo anche inserire eccezioni di deny esplicito per bloccare certi client, e persino una modalità shadow per testare regole (allow con shadow deny) loggando tentativi bloccati. Tutto questo concorre a implementare un modello zero-trust in cui il default è “nego tutto” e solo ciò che è autorizzato esplicitamente viene permesso. Vale la pena notare che Kuma associa un’identità SPIFFE ad ogni dataplane proxy (es. qualcosa come spiffe://<mesh>.<zone>.mesh.local/ns/<namespace>/sa/<serviceAccount>). Le MeshTrafficPermission usano tali identità sotto il cofano per effettuare i match; la sintassi mostrata sopra sfrutta tag come app: demo-app poiché in Kubernetes Kuma etichetta automaticamente i dataplane con attributi comprensibili (app, namespace, servizio, etc.). In modalità avanzata si potrebbero usare direttamente prefissi SPIFFE ID nelle regole di allow/deny, ma nel contesto di Kubernetes è più comune ragionare in termini di tag di servizio come abbiamo fatto.
Riassumendo, abilitando mTLS e configurando opportune MeshTrafficPermission, Kuma ci consente di costruire reti di servizio interne al cluster fortemente sicure: ogni servizio parla con gli altri su canali cifrati e autenticati, e solo le comunicazioni autorizzate passano. Questo riduce drasticamente la superficie di attacco e previene movimenti laterali non controllati all’interno dell’infrastruttura.
Osservabilità con MeshMetric, MeshTrace e MeshAccessLog
Oltre a sicurezza e controllo del traffico, un service mesh efficace fornisce osservabilità integrata sul comportamento dei servizi. Kuma, poggiando su Envoy, è in grado di estrarre moltissime metriche, log e tracce di ogni chiamata. Per gestire queste funzionalità in modo declarativo, Kuma mette a disposizione tre risorse principali di osservabilità:
MeshMetric – per configurare la raccolta ed esposizione delle metriche di traffico dei proxy (ad esempio, integrazione con Prometheus o OpenTelemetry metric collector).
MeshTrace – per abilitare il distributed tracing delle richieste, inviando gli span a backend come Zipkin, Jaeger o Datadog.
MeshAccessLog – per gestire i log di accesso delle richieste e connessioni, ad esempio definendo formati custom e destinazioni (file locali, output standard, o aggregatori via TCP/OTLP).
Queste risorse permettono di collegare Kuma con il proprio stack di monitoring/tracing esistente. Ad esempio, si può definire un MeshMetric con backend Prometheus che farà esporre ad ogni Envoy sidecar un endpoint metrics scrapiabile; oppure un MeshTrace che invia le tracce a un collector Jaeger tramite API HTTP; oppure configurare MeshAccessLog perché i proxy spediscano log in tempo reale a Loki. Kuma semplifica la vita creando per default oltre 50 metriche out-of-the-box (golden signals) sugli Envoy, e supporta immediatamente l’integrazione con strumenti CNCF noti. Una demo di Kuma include uno stack observability completo (Prometheus, Grafana, Jaeger, Loki) che si può installare con un solo comando (kumactl install observability) per vedere in azione queste funzionalità. In contesti reali, l’operatore può utilizzare le risorse di osservabilità sopra citate per indirizzare i dati verso sistemi enterprise già in uso.
Vale la pena sottolineare che l’osservabilità fornita dalla mesh è pervasiva: poiché ogni chiamata transita via Envoy, è possibile collezionare metriche a livello L7 (HTTP) dettagliate, includere informazioni di tracing distribuito automaticamente nelle richieste, e avere log uniformi di tutte le interazioni servizio-servizio. Questo aiuta enormemente chi gestisce l’infrastruttura a monitorare performance, individuare colli di bottiglia e diagnosticare problemi senza dover strumentare manualmente ogni microservizio.
Nota: approfondiremo la configurazione specifica di metriche, logging e tracing avanzato in un secondo momento. Per ora è importante sapere che esistono queste risorse (MeshMetric, MeshTrace, MeshAccessLog) che, se abilitate, istruiscono Kuma a raccogliere ed esportare i dati di osservabilità attraverso i sidecar proxy.
Conclusione
In questo articolo abbiamo esplorato i fondamenti di Kuma come service mesh su Kubernetes, aggiornati alla versione 2.13.x. Abbiamo visto come Kuma implementa un’architettura Control Plane / Data Plane in cui un control plane centralizzato (Kuma CP) gestisce una flotta di proxy Envoy sidecar distribuiti accanto ai servizi applicativi. Tramite il meccanismo di sidecar injection automatica e transparent proxying, Kuma si inserisce nella comunicazione tra pod senza richiedere modifiche alle applicazioni, permettendoci di applicare policy di rete avanzate in modo trasparente.
Abbiamo poi affrontato la configurazione iniziale di una mesh Kubernetes con Kuma: abilitare la mutual TLS con pochi parametri per ottenere comunicazioni cifrate e identificate automaticamente, e utilizzare la risorsa MeshTrafficPermission per definire in modo esplicito chi può parlare con chi in ottica zero-trust. Questo modello eleva notevolmente la sicurezza intracluster, riducendo i rischi in caso di compromissione di un servizio. Infine, abbiamo introdotto le capacità di osservabilità integrate di Kuma – metriche, tracce e log centralizzati – che forniscono una visibilità completa sul traffico dei microservizi con un minimo sforzo di configurazione.
Dal punto di vista di chi gestisce infrastrutture Kubernetes, Kuma emerge come uno strumento potente ma relativamente semplice da adottare: offre policy dichiarative, sicurezza zero-trust e telemetry out-of-the-box, il tutto con un footprint leggero (un singolo control plane e sidecar Envoy ben ottimizzati) e supporto nativo sia a K8s che ambienti ibridi. Nella Parte 2 di questa serie esploreremo funzionalità più avanzate di Kuma – ad esempio traffic routing e failover, scenari multi-cluster (multi-zone), integrazione con ingress/gateway e altre policy di livello applicativo – continuando il nostro percorso verso una gestione sempre più raffinata del traffico tra servizi containerizzati.
