NetworkPolicies — micro-segmentation est-ouest (audit P6 #22)
NetworkPolicies — micro-segmentation est-ouest (audit P6 #22)
default-deny par namespace + allow ciblés sur les workloads maison
(default = WordPress/MySQL, rstudio, registry). Durcissement
defense-in-depth, pas une correction de faille : l’absence de NetworkPolicy
était un compromis assumé du mono-tenant
(ADR 0012). Ces policies
posent une barrière est-ouest pour qu’un pod compromis ne puisse pas balayer
librement le cluster.
Portée
- Couverts :
default,rstudio,registry. - NON couverts (volontairement) :
rook-ceph— infra Ceph/CSI vendored ; verrouiller ses flux (mon/osd/rgw) casserait le stockage. Hors périmètre de ce premier jet.kubernetes-dashboard— déployé par chart Helm, pas de manifeste natif ici.kube-system— composants système (CoreDNS, Cilium).
Principe de chaque namespace
00-default-deny.yaml: unNetworkPolicyqui sélectionne tous les pods (podSelector: {}) et nie ingress + egress. À partir de là, plus rien ne passe sauf ce qui est explicitement ré-autorisé.allow-dns: indispensable — sans lui, le deny-all egress coupe la résolution DNS (CoreDNS,kube-system) et casse tout. Autorise UDP/TCP 53 verskube-system.- Allow métier ciblés (ingress/egress strictement nécessaires).
⚠️ Pré-requis label sur kube-system. Les règles DNS ciblent
kube-systemvianamespaceSelector. K8s ≥ 1.21 pose automatiquement le label immuablekubernetes.io/metadata.name: kube-system, utilisé ici — aucune action requise. (Vérifiable :kubectl get ns kube-system --show-labels.)
Flux autorisés (résumé)
| Namespace | Ingress | Egress |
|---|---|---|
default | wordpress:80 depuis n’importe où ; mysql:3306 ⬅ wordpress uniquement | DNS ; wordpress → mysql:3306 ; API server :6443 |
rstudio | rstudio:8787 depuis n’importe où (Service) | DNS ; API server :6443 |
registry | registry:5000 depuis n’importe où (pulls de tout le cluster) | DNS ; API server :6443 |
Volontairement permissif sur l’ingress des Services exposés (wordpress, rstudio, registry) : ils sont conçus pour être joignables (LoadBalancer). La valeur de ces policies est ailleurs : isoler MySQL (seul WordPress y accède), bloquer tout egress non listé (un pod compromis ne peut pas exfiltrer ni scanner), et poser un cadre pour durcir plus finement ensuite.
Note Ceph : l’accès au stockage RBD/CephFS passe par le kubelet (montage noyau), pas par le réseau pod — il n’est donc PAS bloqué par ces policies et n’a pas besoin d’allow explicite. Idem health/readiness probes (kubelet → pod), exemptées par Cilium.
Egress Internet (rare, borné par ports)
Deux policies seulement ouvrent un egress vers 0.0.0.0/0, et restreint par
ports (jamais en grand) : argocd/allow-egress.yaml (repo-server → git,
443/22/80…) et dagster/allow-internet-egress.yaml (run workers → store objet
public pour le sync d’un snapshot de données ouvert, 443/80 — cf.
issue #256). On ne
filtre pas par plages IP du fournisseur : sous Cilium un ipBlock exclut déjà
les entités réservées/pods du cluster, et les plages publiques (AWS
ip-ranges.json) ont un churn régulier — la borne, ce sont les ports.
Application
# Dans l'ordre : le default-deny d'abord, puis les allow.kubectl apply -f platform/network-policies/default/kubectl apply -f platform/network-policies/rstudio/kubectl apply -f platform/network-policies/registry/Validation (banc multi-nœuds)
À exécuter sur le banc Lima bench/lima/ avant la
prod :
kubectl get netpol -A→ les policies présentes par namespace.- WordPress répond toujours (ingress :80) et lit/écrit MySQL (DNS +
wordpress → mysql:3306OK). - Un pod de test dans
defaultne peut pas joindremysql:3306(seul le podtier: frontendest autorisé) ni un egress arbitraire. - DNS fonctionne depuis chaque namespace (
nslookup kubernetes.default). - Le provisionnement/montage des PVC reste OK (preuve que Ceph n’est pas impacté).
Cf. SAFEGUARDS.md — toute policy réseau passe par le banc avant la prod.
Résultat de validation (banc multi-node, 2026-06-01)
Policies du namespace default appliquées sur le banc 3 nœuds (cluster réel K8s
1.34 + Cilium 1.19, WordPress/MySQL déployés) :
| Test | Attendu | Obtenu |
|---|---|---|
WordPress → MySQL :3306 (flux autorisé) | passe | ✅ OK |
| DNS depuis WordPress (CoreDNS) | passe | ✅ OK |
WordPress → MySQL :22 (port non listé) | bloqué | ✅ DENY |
WordPress → 1.1.1.1:443 (egress non listé) | bloqué | ✅ DENY |
Le default-deny mord (egress non listé bloqué), le flux métier et le DNS sont
préservés. rstudio/registry n’étaient pas déployés sur le banc (mêmes
patrons, plus simples) — validés par revue + kubeconform.