Networking

AKS : diagnostiquer un flux bloqué par une NetworkPolicy avant de desserrer le trafic

Un runbook de production pour prouver si un trafic AKS est refusé par une policy Kubernetes ou Cilium, avec sélecteurs, deux sens du flux, DNS, preuves bornées et rollback.

15 sept. 2026 azureakskubernetesnetwork-policyciliumacnsobservabilitysecurityrunbookrollbackproduction

Un déploiement AKS se termine, les pods sont prêts, mais un service ne parvient plus à en appeler un autre. Le contournement le plus rapide consiste souvent à ajouter une policy allow-all, supprimer le default-deny ou élargir un sélecteur de namespace. La requête peut repartir, au prix de la frontière que le cluster devait justement faire respecter.

Le cas d’usage est un pod orders-api du namespace apps-prod qui appelle payments-api dans payments-prod sur TCP 8443. La panne apparaît après une release qui a modifié des labels et des policies réseau. Ce runbook doit déterminer si l’enforcement est en cause, quel sens du flux manque d’autorisation et si une correction étroite peut être promue ou doit être rollbackée.

Figer un seul flux en échec

Décrivez la connectivité comme un tuple, pas comme « le service est inaccessible ». Relevez le workload source réel, la destination, le port, le protocole et la fenêtre UTC. Conservez si possible un flux témoin fonctionnel depuis la même source.

yaml network-policy-incident.yml
cluster: aks-platform-prod
source:
namespace: apps-prod
workload: deployment/orders-api
pod_label: app=orders-api
destination:
namespace: payments-prod
service: payments-api
port: 8443
protocol: TCP
incident_window: 2026-09-15T05:40:00Z/2026-09-15T06:10:00Z

change_context:
- application release
- label change
- NetworkPolicy change

decision:
promote_only_if: exact-flow-allowed-and-unrelated-flow-still-denied
rollback_on: selector-expands-or-required-boundary-cannot-be-proven

Conservez le commit applicatif, celui des manifests de policy et le digest de l’image déployée. Un timeout n’identifie pas à lui seul une policy : DNS, endpoints du Service, readiness, TLS et listener applicatif peuvent produire le même symptôme.

Prouver le data plane et les types de policy actifs

Lisez la configuration du cluster avant de choisir les outils. AKS peut utiliser plusieurs combinaisons de réseau et de moteur de policy. Les commandes Cilium et les flow logs ACNS ne sont utiles que si ce data plane et les fonctions d’observabilité correspondantes sont réellement activés.

bash 01-read-aks-network-profile.sh
RG="rg-platform-prod"
CLUSTER="aks-platform-prod"

az aks show --resource-group "$RG" --name "$CLUSTER" --query '{networkPlugin:networkProfile.networkPlugin,networkPluginMode:networkProfile.networkPluginMode,networkPolicy:networkProfile.networkPolicy,networkDataplane:networkProfile.networkDataplane}' --output yaml

kubectl get networkpolicy -A
kubectl get ciliumnetworkpolicy,ciliumclusterwidenetworkpolicy -A 2>/dev/null || true

Ne supposez pas qu’une NetworkPolicy Kubernetes standard constitue la seule couche d’enforcement. Sur un cluster Cilium, des policies Cilium limitées à un namespace ou globales peuvent coexister avec les policies standard. Les NSG de subnet, UDR et firewalls Azure restent des contrôles distincts ; ne les modifiez qu’après avoir qualifié la décision dans le cluster.

Reconstruire les deux sens de l’autorisation

Les NetworkPolicies s’additionnent ; il n’existe pas d’ordre « première règle gagnante » à parcourir. Un pod sélectionné devient isolé séparément pour l’ingress et l’egress. Pour un flux entre pods, l’egress de la source et l’ingress de la destination doivent tous deux l’autoriser lorsque les deux extrémités sont isolées dans ces directions.

text policy-decision-model.txt
Egress du pod source
Quelles policies selectionnent orders-api ?
Leurs regles egress cumulees autorisent-elles payments-api sur TCP 8443 ?
Le DNS est-il autorise avant la resolution du nom de service ?

Ingress du pod destination
Quelles policies selectionnent payments-api ?
Leurs regles ingress cumulees autorisent-elles les labels du namespace et du pod source ?
Le targetPort du Service correspond-il au listener ?

Resultat
Autorise seulement si chaque direction isolee permet le tuple
L'ordre des policies et le nom des manifests ne creent aucune priorite

Ce modèle évite une erreur fréquente : corriger l’ingress de destination alors que l’egress source refuse toujours le flux, puis conclure à une incohérence du moteur de policy.

Comparer les sélecteurs aux labels déployés

Les policies agissent sur les labels présents, pas sur les labels attendus. Inspectez exactement les pods impliqués et les labels de namespace utilisés par les sélecteurs.

bash 02-inventory-selectors.sh
kubectl get pod -n apps-prod -l app=orders-api --show-labels -o wide
kubectl get pod -n payments-prod -l app=payments-api --show-labels -o wide
kubectl get namespace apps-prod payments-prod --show-labels

kubectl get networkpolicy -n apps-prod -o yaml > apps-prod-networkpolicies.yml
kubectl get networkpolicy -n payments-prod -o yaml > payments-prod-networkpolicies.yml

kubectl get service payments-api -n payments-prod -o yaml
kubectl get endpointslice -n payments-prod -l kubernetes.io/service-name=payments-api -o wide

Comparez podSelector, namespaceSelector, policyTypes, ports et protocoles. Portez une attention particulière à une release qui renomme les labels app, component, team ou d’environnement. Un EndpointSlice vide ou un targetPort incohérent décrit une panne de Service, pas la preuve d’un refus réseau.

Rejouer avec le vrai contexte de sécurité

Exécutez le test minimal depuis un pod touché. Un pod de debug portant d’autres labels peut ne pas être sélectionné par les mêmes policies et produire un faux succès.

bash 03-replay-flow.sh
SOURCE_POD=$(kubectl get pod -n apps-prod -l app=orders-api -o jsonpath='{.items[0].metadata.name}')

kubectl exec -n apps-prod "$SOURCE_POD" -- getent hosts payments-api.payments-prod.svc.cluster.local
kubectl exec -n apps-prod "$SOURCE_POD" -- sh -c 'nc -vz -w 3 payments-api.payments-prod.svc.cluster.local 8443'

# Conserver un temoin avec le meme pod et le meme protocole.
kubectl exec -n apps-prod "$SOURCE_POD" -- sh -c 'nc -vz -w 3 health-api.shared.svc.cluster.local 8443'

Séparez résolution du nom, connexion TCP et réponse applicative. Si le DNS échoue après l’ajout d’un default-deny egress, vérifiez l’autorisation vers le DNS du cluster ou LocalDNS avant de modifier la policy du service métier.

Lire les preuves de flux quand ACNS est disponible

Avec un data plane Cilium et Advanced Container Networking Services configuré, une observation Hubble à la demande ou des container network logs bornés peuvent exposer source, destination, protocole et verdict. Vérifiez d’abord l’état des fonctions : aucune ligne ne signifie pas « refusé » si la capture n’était pas active ou ne sélectionnait pas le flux.

text flow-evidence-checklist.txt
Pour le tuple en incident
Identites des pods source et destination
Namespaces source et destination
Port et protocole de destination
Verdict FORWARDED, DROPPED ou policy-denied
Node qui porte le pod source
Policy ou regle quand cette information est exposee

Si aucun flux n'est visible
Confirmer le data plane Cilium
Confirmer ACNS et le mode de capture
Confirmer que les filtres couvrent le tuple
Rejouer une requete bornee et horodatee
Revenir aux selecteurs, endpoints et preuves applicatives

Les logs stockés ne permettent une analyse historique que si la collecte existait pendant l’incident. Pour un rejeu en direct, préférez une observation à la demande filtrée au plus juste. N’activez pas une capture globale en pleine panne sans budget de volume et de rétention.

Construire la correction minimale

Ajoutez l’autorisation requise par l’identité et le port au lieu de supprimer l’isolation. L’exemple cible un seul workload source et un seul workload destination, avec un label de namespace explicite. Adaptez les clés aux labels réellement garantis par vos déploiements et contrôles d’admission.

yaml allow-orders-to-payments.yml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-orders-api
namespace: payments-prod
spec:
podSelector:
  matchLabels:
    app: payments-api
policyTypes:
- Ingress
ingress:
- from:
  - namespaceSelector:
      matchLabels:
        kubernetes.io/metadata.name: apps-prod
    podSelector:
      matchLabels:
        app: orders-api
  ports:
  - protocol: TCP
    port: 8443

Si l’egress source est isolé, ajoutez-y l’autorisation symétrique dans une règle distincte et relue. Avec Azure CNI Powered by Cilium, ne comptez pas sur ipBlock pour sélectionner les IP de pods ou de nodes ; utilisez les identités de namespace et de pod pour les workloads internes au cluster.

Valider avec des canaris positifs et négatifs

Appliquez le candidat depuis le dépôt de configuration vers un namespace canari ou un workload borné. La réussite du flux autorisé ne couvre que la moitié du test. Confirmez qu’une source étrangère reste refusée et que DNS, health probes et trafic de plateforme requis fonctionnent encore.

text policy-canary-gates.txt
Canari positif
orders-api resout payments-api
TCP 8443 se connecte
La requete applicative renvoie le statut attendu
Aucun nouveau probleme DNS ou reset

Canari negatif
Un workload etranger reste refuse sur TCP 8443
orders-api reste refuse sur un port non approuve
Les flux inter-namespaces hors contrat restent bloques

Gate d'exploitation
Manifest relu et versionne
Preuves de flux conformes aux identites attendues
Manifest de rollback et owner enregistres

Observez au moins un cycle de requêtes représentatif après promotion. Si la policy était correcte et qu’une dérive de labels a causé la panne, décidez si le correctif durable appartient au workload, au sélecteur ou aux contrôles d’admission. Ne laissez pas indéfiniment un label de compatibilité ajouté dans l’urgence.

Décider, promouvoir ou rollbacker

yaml network-policy-decision.yml
promote_narrow_allowance:
when:
- failed_tuple_is_reproduced
- selected_policies_explain_the_deny
- positive_and_negative_canaries_pass

fix_workload_or_service:
when:
- labels_drifted_from_the_deployment_contract
- endpoints_or_target_port_are_wrong
- application_or_tls_fails_after_tcp_connects

hold_change:
when:
- active_policy_engine_or_selector_scope_is_unclear
- no_negative_canary_proves_the_boundary

rollback:
action:
- revert_the_candidate_manifest_commit
- reapply_the_previous_policy_set
- repeat_the_same_positive_and_negative_tests
- confirm_unrelated_traffic_did_not_gain_access

Conclusion

Un flux AKS bloqué ne justifie pas de desserrer le cluster. La preuve utile relie un tuple au data plane actif, aux policies qui sélectionnent les deux extrémités, aux labels déployés, aux endpoints du Service et, quand la télémétrie le permet, à un verdict observé.

Ne promouvez que l’autorisation minimale qui passe les canaris positifs et négatifs. Si la policy n’est pas en cause, conservez la frontière de sécurité et poursuivez sur DNS, service discovery, TLS ou l’application. Dans les deux cas, fermez avec une validation rejouable et un chemin de retour versionné.