Skip to content

Istio Service Mesh & Kiali

Riferimento operativo per OpenShift Service Mesh (OSSM 2.x / Istio): architettura, mTLS, AuthorizationPolicy, audit da CLI e navigazione Kiali. Pensato per cluster enterprise con pattern zero-trust a namespace.

Architettura in 60 secondi

ComponenteRuolo
Istiod (control plane)Distribuisce config e certificati agli Envoy proxy
Envoy sidecar (data plane)Proxy iniettato in ogni pod: intercetta tutto il traffico, applica mTLS e policy
Ingress GatewayEnvoy standalone in istio-system: punto d’ingresso del traffico esterno
KialiConsole web: topologia, traffico, validazione config Istio
PrometheusSorgente delle metriche: il grafo Kiali è costruito da qui
JaegerDistributed tracing (trace e span delle richieste)
CONTROL PLANE · istio-system Istiod Ingress Gateway(Envoy) Kiali Prometheus Jaeger DATA PLANE · namespace applicativi Pod A app Envoysidecar Pod B Envoysidecar app mTLS 🔒 + AuthorizationPolicy config + certificati traffico esterno metriche

Concetti chiave:

  • Il mesh opera a livello rete: le feature (mTLS, authz, routing, resilienza) si ottengono senza modificare il codice applicativo.
  • In OSSM l’appartenenza al mesh è governata dal ServiceMeshMemberRoll (smmr): un namespace non membro è invisibile a Kiali e non riceve sidecar.
  • PeerAuthentication = chi sei (identità/mTLS). AuthorizationPolicy = cosa puoi fare (accesso). Vanno lette insieme: i principals funzionano solo con mTLS attivo.

Inventario mesh

Terminal window
oc get smcp -n istio-system
Terminal window
oc get smmr -n istio-system default -o jsonpath='{.status.members}' | tr ',' '\n'
Terminal window
oc get pods -n istio-system
Terminal window
oc get route -n istio-system kiali -o jsonpath='https://{.spec.host}{"\n"}'
Terminal window
oc get route -n istio-system jaeger -o jsonpath='https://{.spec.host}{"\n"}'

Verifica sidecar iniettato in un namespace (2/2 = app + istio-proxy):

Terminal window
oc get pods -n <ns> -o custom-columns='POD:.metadata.name,CONTAINERS:.spec.containers[*].name'

mTLS & PeerAuthentication

Cos’è. PeerAuthentication (PA) governa come i sidecar Envoy si autenticano tra loro: decide se il traffico service-to-service dentro il mesh deve viaggiare in mTLS (mutual TLS) o può restare in chiaro. È la risposta alla domanda “chi sei?” a livello di trasporto — distinta e complementare rispetto ad AuthorizationPolicy, che risponde a “cosa puoi fare?”.

Perché conta. Con mTLS attivo ogni pod ottiene da Istiod un certificato con un’identità SPIFFE derivata dalla sua ServiceAccount (spiffe://cluster.local/ns/<ns>/sa/<sa>). Due effetti: il traffico interno è cifrato end-to-end tra sidecar, e le AuthorizationPolicy possono basarsi sui principals (l’identità verificata), non su IP falsificabili. Ecco perché PA e AuthorizationPolicy si leggono sempre insieme: un from.source.principals funziona solo se l’mTLS è attivo, altrimenti non c’è identità da verificare.

Come si applica. Una PA senza selector vale per tutto il namespace; nel root namespace del mesh (istio-system) vale per tutto il mesh. Con selector si restringe a un workload. La precedenza è workload > namespace > mesh.

Modalità

ModeComportamento
PERMISSIVEAccetta sia plaintext che mTLS. È il default e serve in migrazione: i client non ancora nel mesh continuano a funzionare mentre quelli con sidecar passano a mTLS
STRICTSolo mTLS: il plaintext viene rifiutato. È l’obiettivo zero-trust, ma va introdotto con cautela (un client senza sidecar viene tagliato fuori)
DISABLEmTLS disattivato. Usato per eccezioni mirate: scrape Prometheus, collector esterni, health check di componenti non-mesh

Estrazione

Terminal window
oc get peerauthentication -A
Terminal window
oc get peerauthentication,requestauthentication -A -o yaml > all-istio-auth.yaml

Esempi

Mesh-wide STRICT (nel root namespace del mesh):

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT

Override a livello porta (es. escludere una porta dal wrapping mTLS):

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: app-mtls
namespace: my-ns
spec:
selector:
matchLabels:
app: my-app
mtls:
mode: STRICT
portLevelMtls:
"9090":
mode: DISABLE

Gotcha classici

  • STRICT lato server ≠ mTLS garantito lato client. La PA STRICT dice al server di pretendere mTLS, ma è la DestinationRule che dice al client di originare connessioni mTLS. Se manca una DR mesh-wide, Kiali segnala KIA0401 (“Mesh-wide DestinationRule enabling mTLS is missing”). Nella pratica l’auto-mTLS di Istio spesso compensa — i client con sidecar negoziano mTLS da soli e il traffico viaggia cifrato lo stesso — ma la config è incompleta e fragile. Verifica sempre nel Graph di Kiali che gli archi in-mesh mostrino il lucchetto e, cliccando l’arco, mTLS Enabled con i principals SPIFFE.
  • DestinationRule che disabilita mTLS. Al contrario, una DR con trafficPolicy.tls.mode: DISABLE rompe l’mTLS anche con PA STRICT: il client smette di cifrare, il server STRICT rifiuta → connessioni fallite. Kiali flagga il conflitto in Istio Config.
  • Precedenza PA: workload > namespace > mesh. Una PA con selector su un workload vince su quella di namespace, che vince su quella mesh-wide in istio-system.
  • Traffico TCP non-HTTP (es. Hazelcast, database): Envoy lo wrappa comunque in mTLS come connessione opaca, salvo portLevelMtls. Nel Graph appare come arco blu (salute non valutabile), ma il lucchetto c’è.
PA con selector workload — vince sempre PA senza selector nel ns namespace PA default in istio-system mesh-wide — fallback - Traffico TCP non-HTTP: Envoy lo tratta come connessione opaca e la wrappa comunque in mTLS (salvo `portLevelMtls`).

AuthorizationPolicy

Modello mentale

Tre campi decidono tutto:

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: example
namespace: my-ns
spec:
selector: # A CHI si applica (pod destinazione); assente = tutto il ns
matchLabels:
app: my-app
action: ALLOW # ALLOW | DENY | AUDIT
rules: # CHI / COSA è permesso (from / to / when)
- from:
- source:
principals: ["cluster.local/ns/other-ns/sa/my-sa"]
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/*"]

Regole di precedenza (9 troubleshooting su 10 partono da qui):

  1. DENY vince sempre su ALLOW (i DENY si valutano prima).
  2. Se esiste almeno una ALLOW su un workload → tutto ciò che non matcha è negato implicitamente.
  3. Nessuna policy sul workload → tutto permesso (default allow di Istio).
  4. selector assente → policy su tutto il namespace; nel root namespace (istio-system) → tutto il mesh.
  5. rules: [] con action ALLOW → deny-all (non matcha nulla). Attenzione al duale: rules: [{}] (regola vuota) → allow-all.
  6. source.principals richiede mTLS attivo; source.namespaces fida chiunque nel namespace sorgente.
Richiesta Matcha una policy DENY? 403 · negato RBAC: access denied no Esistono ALLOW sul workload? no Permesso default allow di Istio Una ALLOW matcha? Permesso no → deny implicito

Pattern zero-trust a namespace (i 4 template)

Baseline tipica ripetuta su ogni namespace applicativo:

PolicyFunzione
allow-nothingrules: [] → default deny: da sola nega tutto
allow-from-nsConsente il traffico intra-namespace (source.namespaces = stesso ns)
allow-gateway-saConsente la ServiceAccount del micro-gateway (ns -mgw gemello)
allow-istio-ingressgatewayConsente l’ingress gateway di istio-system
Ingress Gateway istio-system ns applicativo -fe allow-nothing (baseline) allow-from-ns ⟳ ns applicativo -be allow-nothing (baseline) allow-from-ns ⟳ ns -mgw allow-nothing allow-from-ns ⟳ allow-istio-ingressgateway allow-istio-ingressgateway allow-gateway-sa allow-gateway-sa ⟳ = traffico intra-namespace consentito · tutto ciò che non ha una freccia è negato da allow-nothing
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-nothing
namespace: my-ns
spec: {} # nessuna rule = deny-all
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-from-ns
namespace: my-ns
spec:
action: ALLOW
rules:
- from:
- source:
namespaces: ["my-ns"]
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-istio-ingressgateway
namespace: my-ns
spec:
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"]

Il diagramma dei flussi risultanti — utente e sistemi esterni entrano da due gateway distinti (frontend per gli utenti, backend per il M2M), i micro-gateway fanno da dorsale interna:

ESTERNO ISTIO-SYSTEM NAMESPACE APP Utentibrowser/app Sistemi extM2M istio-ingressgwgateway frontend backend-ingressgwgateway M2M app-fesolo da gateway FE+ intra-ns app-befe / mgw / gw-bewildcard *-mgw app-mgwsolo proprio triofe/be/mgw altri *-mgwmicro-gw di altre app flusso ammesso da policy esterno verso gateway wildcard *-mgw (vedi KIA0101)

Nota: se source.namespaces contiene * o *-mgw, Istio lo tratta come nome letterale e la regola non matcha nulla (Kiali segnala KIA0101). Il wildcard cross-app va quindi verificato, non dato per funzionante.

Audit da CLI (i comandi core)

Estrazione raw

Terminal window
oc get authorizationpolicy -A
Terminal window
oc get authorizationpolicy -A -o yaml > all-istio-authz.yaml
Terminal window
oc get networkpolicy -A -o yaml > all-netpol.yaml

Matrice “chi parla con chi” (CSV) ⭐

Inventario rapido (quali/dove):

Terminal window
oc get authorizationpolicy -A

Export YAML integrale (backup/analisi offline):

Terminal window
oc get authorizationpolicy -A -o yaml > istio-authz.yaml

Estrae namespace, policy, action e sorgenti ammesse per tutte le AuthorizationPolicy:

Terminal window
oc get authorizationpolicy -A -o json | jq -r '.items[] | [.metadata.namespace, .metadata.name, (.spec.action // "ALLOW"), ((.spec.rules // []) | if length == 0 then "DENY-ALL" else map((.from // []) | map(.source | ((.principals // []) + (.namespaces // []) | join(","))) | join(";")) | join(" | ") end)] | @csv' > authz-matrix.csv

Versione arricchita — aggiunge il selector (pod target) e le operation (metodi/porte/path permessi). È la vista human-readable completa sorgente → destinazione → operazioni:

Terminal window
oc get authorizationpolicy -A -o json | jq -r '.items[] | [.metadata.namespace, .metadata.name, (.spec.action // "ALLOW"), ((.spec.selector.matchLabels // {}) | if . == {} then "ALL-PODS-IN-NS" else tostring end), ((.spec.rules // []) | if length == 0 then "DENY-ALL" else map(((.from // []) | map(.source | ((.principals // []) + (.namespaces // []) + (.ipBlocks // []) | join(","))) | join(";")) + (if .to then " -> " + ((.to // []) | map(.operation | ((.methods // []) + (.ports // []) + (.paths // []) | join(","))) | join(";")) else "" end)) | join(" | ") end)] | @csv' > authz-matrix-full.csv

Versione a video (tabellare):

Terminal window
oc get authorizationpolicy -A -o json | jq -r '.items[] | [.metadata.namespace, .metadata.name, (.spec.action // "ALLOW"), ((.spec.rules // []) | if length == 0 then "DENY-ALL (no rules)" else map((.from // []) | map(.source | ((.principals // []) + (.namespaces // []) | join(","))) | join(";")) | join(" | ") end)] | @tsv' | column -t -s $'\t'

Deduplica template

Con centinaia di policy, verifica quante varianti distinte esistono davvero (nome + spec identica):

Terminal window
oc get authorizationpolicy -A -o json | jq -r '.items[] | "\(.metadata.name)\t\(.spec | tostring)"' | sort | uniq -c | sort -rn

Se escono 4-6 righe → è un template stampato ovunque: leggi 4 YAML e conosci l’intero cluster. Le varianti in minoranza sono le eccezioni da investigare.

Inventario NetworkPolicy (per confronto L3/L4)

Terminal window
oc get netpol -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,POD-SELECTOR:.spec.podSelector.matchLabels,TYPES:.spec.policyTypes'

Matrice NetworkPolicy (CSV) — l’equivalente L3/L4

Namespace, policy, pod target, policyTypes e sorgenti ingress ammesse (podSelector / namespaceSelector / ipBlock):

Terminal window
oc get networkpolicy -A -o json | jq -r '.items[] | [.metadata.namespace, .metadata.name, ((.spec.podSelector.matchLabels // {}) | if . == {} then "ALL-PODS-IN-NS" else tostring end), ((.spec.policyTypes // []) | join("+")), ((.spec.ingress // []) | if length == 0 then (if ((.spec.policyTypes // []) | index("Ingress")) then "DENY-ALL-INGRESS" else "-" end) else map(((.from // []) | if length == 0 then "ANY" else map(if .podSelector then "pod:"+((.podSelector.matchLabels // {})|tostring) elif .namespaceSelector then "ns:"+((.namespaceSelector.matchLabels // {})|tostring) elif .ipBlock then "cidr:"+.ipBlock.cidr else "?" end) | join(",") end)) | join(" | ") end)] | @csv' > netpol-matrix.csv

Regola d’oro dell’incrocio

Un flusso A → B funziona solo se passa entrambi i livelli. Diagnosi rapida: timeout / connection refused → guarda le NetworkPolicy (L3/L4); HTTP 403 RBAC: access denied → guarda le AuthorizationPolicy (L7). Se un flusso è permesso in una matrice ma assente nell’altra, hai trovato l’incoerenza.

Certificazione coverage (audit)

Un namespace è scoperto se è membro del mesh (ha la NetworkPolicy istio-mesh-basic generata da Maistra per ogni membro del ServiceMeshMemberRoll) ma non contiene alcuna AuthorizationPolicy → Istio applica il default-allow, raggiungibile a L7 da qualsiasi workload autenticato del mesh.

Lista certificabile (fonte primaria: API server, non la console):

Terminal window
comm -23 <(oc get netpol -A -o jsonpath='{range .items[?(@.metadata.name=="istio-mesh-basic")]}{.metadata.namespace}{"\n"}{end}' | sort -u) <(oc get authorizationpolicy -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\n"}{end}' | sort -u)

Confezionamento come evidenza datata + hash per l’integrità:

Terminal window
{ echo "=== AUDIT AuthZ coverage — $(date -u +%FT%TZ) ==="; echo "Cluster: $(oc whoami --show-server)"; comm -23 <(oc get netpol -A -o jsonpath='{range .items[?(@.metadata.name=="istio-mesh-basic")]}{.metadata.namespace}{"\n"}{end}' | sort -u) <(oc get authorizationpolicy -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\n"}{end}' | sort -u); } | tee authz-coverage-$(date +%Y%m%d).txt
Terminal window
sha256sum authz-coverage-$(date +%Y%m%d).txt > authz-coverage-$(date +%Y%m%d).sha256

Codici di validazione Kiali (KIA)

Kiali fa validazione semantica: dice cosa Istio fa davvero, non cosa sembra dire lo YAML. I codici incontrati sul campo:

CodiceSignificatoInsidia
KIA0101Namespace not found for this rulesource.namespaces tratta il valore come nome letterale, non glob: - '*' o - '*-mgw' non matchano nulla. Una regola pensata come apertura è in realtà inefficace.
KIA0401Mesh-wide DestinationRule enabling mTLS is missingPeerAuthentication STRICT lato server senza la DR che fa originare mTLS ai client. Spesso l’auto-mTLS compensa (traffico cifrato lo stesso), ma la config è incompleta — verifica nel Graph che gli archi abbiano il lucchetto.
KIA1106More than one Virtual Service for same hostPiù VirtualService sullo stesso host → routing non deterministico.

Per verificare l’effetto reale di una policy sospetta, la controprova è sempre istioctl x authz check (sotto) o il flag mTLS sull’arco nel Graph.

istioctl / Envoy debugging

Quali policy Envoy applica effettivamente a un pod (tabella per policy con action e match):

Terminal window
istioctl x authz check <pod-name> -n <ns>

Validazione config del mesh (stesso motore dei warning Kiali):

Terminal window
istioctl analyze -n <ns>

Stato di sincronizzazione dei proxy col control plane:

Terminal window
istioctl proxy-status

Config RBAC grezza nell’Envoy del pod (fallback se istioctl non è disponibile):

Terminal window
istioctl proxy-config listener <pod-name> -n <ns> -o json | grep -A5 rbac

Log Envoy del pod, cercando i deny:

Terminal window
oc logs <pod-name> -n <ns> -c istio-proxy | grep RBAC

La firma di un blocco authz è HTTP 403 con flag RBAC: access denied.

Osservabilità (OSSM 3.x)

L’osservabilità del mesh si appoggia allo stack di monitoring di OpenShift: Kiali non ha un proprio database, correla metriche Prometheus con la config Istio. Se Prometheus non scrapa, gran parte di Kiali (grafo, health, tassi) resta vuota.

Componenti

ComponenteRuolo
PrometheusScrapa le metriche dai sidecar Envoy e dai container applicativi. È la sorgente del Traffic Graph
ThanosAggrega, gestisce la retention e l’API di query sopra Prometheus
AlertmanagerGestisce e instrada gli alert generati da Prometheus
Grafana TempoBackend di distributed tracing; sostituisce Jaeger nelle versioni recenti di OSSM
OpenTelemetryAPI vendor-neutral per instrumentare il codice e inviare trace/span ai backend
Kiali / OSSMCVisualizza topologia, traffico, health; l’OSSMC integra le viste Kiali nella console OpenShift

Pipeline dati di Kiali

Il flusso in 4 passi: (1) Prometheus scrapa le metriche da Envoy e app, (2) Kiali interroga le API OpenShift/Istio per la config, (3) il Kiali server correla metriche e config, (4) OSSMC presenta i dati nella console. Il tracing è un layer a parte: i proxy Envoy generano gli span iniziali anche per servizi non instrumentati, li inoltrano al collector, e Kiali mostra i trace con link alla UI di tracing.

Onboarding di un namespace nell’osservabilità

Due azioni, da cluster-admin. Primo, abilitare il user workload monitoring (crea uno stack Prometheus/Thanos/Alertmanager dedicato in openshift-user-workload-monitoring):

apiVersion: v1
kind: ConfigMap
metadata:
name: cluster-monitoring-config
namespace: openshift-monitoring
data:
config.yaml: |
enableUserWorkload: true

Secondo, etichettare il namespace per l’iniezione del sidecar (senza questo, niente Envoy e niente metriche):

Terminal window
oc label namespace <namespace> istio-injection=enabled

La Kiali Custom Resource

Il Kiali operator installa una CR kiali.io/v1alpha1.Kiali nello stesso namespace della CR Istio. Con più mesh sullo stesso cluster puoi avere una console Kiali per mesh; con un mesh multi-cluster, una sola vista consolidata.

apiVersion: kiali.io/v1alpha1
kind: Kiali
metadata:
name: kiali
spec:
istio_namespace: istio-system
auth:
strategy: openshift # integra l'auth con OpenShift OAuth
external_services:
prometheus: {} # sorgente metriche (essenziale)
grafana: {}
tracing: {} # Tempo/Jaeger

URL della console:

Terminal window
echo "https://$(oc get routes -n istio-system kiali -o jsonpath='{.spec.host}')"

OSSMC vs console Kiali esterna

L’OpenShift Service Mesh Console plug-in (OSSMC) porta tutte le viste Kiali dentro la console OpenShift (menu Service Mesh nella admin view, più un tab Service Mesh su Deployment/Service). Unico limite rispetto alla console Kiali standalone: l’OSSMC visualizza un solo mesh alla volta.

Distributed tracing

Il tracing segue una singola richiesta dall’ingresso attraverso tutti i servizi che attraversa. Ogni servizio aggiunge uno span (info contestuale); la collezione di span di una richiesta forma il trace completo. Serve instrumentare le app (via OpenTelemetry), ma il data plane genera gli span iniziali anche senza instrumentazione completa. I trace compaiono nel side panel del nodo/edge in Kiali, se il tracing è configurato.

Kiali

VistaCosa ci trovi
OverviewCard per namespace: salute, warning config, membri del mesh
Traffic GraphTopologia col traffico reale (da Prometheus)
Istio ConfigInventario oggetti Istio con validazione inline
WorkloadsDettaglio Deployment: policy associate, tab Logs (incluso istio-proxy)
Services / ApplicationsAggregazioni per Service e per label app

Leggere il Graph

Geografia a tre fasce: grigio sinistra = sorgenti esterne al ns selezionato → bianco centro = il tuo namespace → grigio destra = destinazioni fuori dal mesh (egress, ServiceEntry, PassthroughCluster).

SORGENTI ESTERNE NAMESPACE SELEZIONATO FUORI DAL MESH ingress gateway ns -fe / -mgw Service ▲ Workload ● cache (TCP) APM / collector 🔑 PassthroughCluster HTTP sano HTTP errori (click → response code!) TCP puro (salute non valutata)
ElementoSignificato
TriangoloService Kubernetes
CerchioWorkload (Deployment/pod)
QuadratoApp (aggregazione per label app)
Icona chiaveDestinazione esterna via egress
PassthroughClusterTraffico verso destinazioni non registrate nel mesh (no ServiceEntry)
Arco verdeHTTP sano
Arco arancioHTTP degradato (errori sopra soglia warning)
Arco rossoHTTP con errori sopra soglia critica
Arco bluTCP puro: Kiali non ne giudica la salute
LucchettomTLS attivo sull’arco (Display → Security)

Tipi di grafo: App graph per la visione d’insieme (meno nodi), Workload graph per il debug puntuale. Con Display → Service Nodes disattivato si dimezzano i nodi.

I quattro rendering disponibili:

TipoCosa mostra
App graphAggrega i workload per label applicativa — vista logica
Service graphAlto livello: traffico aggregato per Service Kubernetes
Versioned app graphCome App graph ma separa le versioni (traffico version-specific)
Workload graphDettaglio massimo: comunicazione workload-to-workload

Interazioni sul grafo

  • Single-click su un nodo → lo seleziona e apre il side panel; double-click → drill-down nel dettaglio di quel nodo, con il traffico visto dalla prospettiva del suo proxy.
  • Il side panel (nodo/edge/box) riporta: grafici di request rate / error rate / response time, health e stato, response code e host breakdown, link alle risorse del mesh, e i trace correlati se il tracing è configurato.
  • Le icone punto esclamativo sul grafo sono cliccabili e danno l’help contestuale sul campo vicino.
  • Edge labels: request rate, error rate, response time
  • Security: lucchetto mTLS, missing sidecar, health badge
  • Istio config overlays: VirtualService, DestinationRule, Gateway, ServiceEntry
  • Resilience: circuit breaker e fault injection
  • Node visibility: service nodes, application nodes, operation nodes, idle nodes/edges
  • Protocol badge: HTTP, gRPC, TCP

Traffic animation: cerchi animati = richieste HTTP riuscite, rombi rossi = errori; densità ∝ volume, velocità ∝ response time. Il traffico TCP appare come cerchi sfalsati la cui velocità riflette il throughput.

Replay: rigioca il traffico di una finestra temporale passata. Lo stato del grafo (replay incluso) è bookmarkable — puoi condividere una vista precisa con un collega via URL.

Find / Hide (domare il caos)

name *= activegate
name *= activegate or name = PassthroughCluster
httpout > 0

Occhio alla finestra temporale (in alto a destra): il grafo mostra traffico reale, non configurazione — un workload senza traffico recente sparisce. Grafo vuoto ≠ mesh rotto: quasi sempre è finestra troppo corta, zero traffico, o Prometheus del mesh con problemi.

Workflow di audit sicurezza

  1. Graph → Display → Security: tutti gli archi attivi in-mesh devono avere il lucchetto (con PA STRICT mesh-wide). Archi senza lucchetto = da investigare.
  2. Istio Config → filtro PeerAuthentication: verifica la policy mesh-wide in istio-system e che gli override (namespace/workload) siano voluti.
  3. Istio Config → filtro AuthorizationPolicy: per ogni policy controlla selector, sorgenti (principals/namespaces) e operation. I warning tipici: selector che non matcha nessun workload (policy morta), conflitti PA/DestinationRule, riferimenti a SA/namespace inesistenti.
  4. Servizi scoperti: incrocia i servizi del namespace con le policy — senza policy (e senza deny-all a livello ns) un servizio è raggiungibile da tutto il mesh.
  5. Zero warning: l’audit è completo quando tutte le risorse passano la validazione Kiali.

Troubleshooting 403 / RBAC

Flusso rapido quando qualcosa viene bloccato:

1 · Graph arco rosso → 403 2 · Logs istio-proxy RBAC: access denied 3 · istioctl x authz check policy applicate al pod 4 · Verifica policy ALLOW? DENY? mTLS? SA?
  1. Graph: arco rosso → click → pannello a destra → response code 403.
  2. Workloads → pod destinazione → Logs → istio-proxy: cerca RBAC: access denied.
  3. istioctl x authz check <pod> -n <ns>: elenca le policy effettivamente applicate.
  4. Verifica in ordine: esiste una ALLOW che matcha la sorgente? C’è una DENY che vince? La sorgente arriva in mTLS (i principals non matchano il plaintext)? La ServiceAccount nel principal è quella giusta (cluster.local/ns/<ns>/sa/<sa>)?

Gotchas enterprise

  • Kiali mostra solo il mesh: le NetworkPolicy native (OVN-K) non le vede. Authz Istio = L7/identità; NetworkPolicy = L3/L4. Sono complementari, servono entrambe.
  • Il muro di archi rossi verso collector esterni (es. agent APM/ActiveGate) spesso non è un incendio: TLS su porte non dichiarate o classificazioni Envoy. Click sull’arco e leggi i codici prima di allarmarti.
  • Label app/version non curate → App graph illeggibile (nodi aggregati con nomi generici).
  • Policy vecchie di anni con warning “no matching workloads” = debito tecnico da ripulire, non da ignorare.
  • Aggiungere la prima ALLOW a un workload finora senza policy nega implicitamente tutto il resto: pianifica le sorgenti prima di applicare.
  • Dopo ogni upgrade del mesh, rifai il giro mTLS nel Graph per verificare che nulla si sia rotto.