Aller au contenu

Les NetworkPolicies

300 Concevoir ⏱ 1 h 30 kubernetescilium

À la fin, vous saurez

  • Expliquer pourquoi tout pod d'un cluster peut joindre tous les autres tant qu'aucune politique ne le sélectionne
  • Écrire un refus par défaut pour un namespace et les autorisations minimales de Signalements
  • Autoriser la résolution DNS en sortie sans ouvrir davantage
  • Distinguer, d'après l'indentation du YAML, une règle en ET d'une règle en OU
  • Dire si le plugin réseau d'un cluster applique les NetworkPolicies, et comment le vérifier
  • Tester qu'une politique autorise ce qu'elle doit et bloque ce qu'elle doit

Prérequis

Testé avec cilium 1.20 kind 0.33.0 kubernetes 1.36 , vérifié le 5 octobre 2026

Pourquoi

Dans un cluster Kubernetes sans politique, tout pod peut joindre tout autre pod, dans n'importe quel namespace, sur n'importe quel port. C'est la conséquence directe du modèle réseau (leçon 1, Le modèle réseau de Kubernetes) : chaque pod a une adresse joignable par tous les autres, sans traduction d'adresses. Ce modèle est très commode pour les applications, et très dangereux pour la sécurité.

Imaginez que le tableau de bord d'un outil interne, hébergé dans un autre namespace du même cluster que Signalements, soit compromis. Sans politique, l'attaquant peut ouvrir une connexion vers PostgreSQL sur le port 5432, et tenter les mots de passe. Il peut aussi joindre l'API de Kubernetes, le service de métadonnées, ou n'importe quel service interne sans authentification, parce que ces services ont été écrits en supposant que « le réseau du cluster est de confiance ».

Une NetworkPolicy décrit, pour un ensemble de pods, quelles connexions sont autorisées. Elle transforme un réseau plat en un réseau où chaque flux est explicite : la passerelle parle à l'API, l'API parle à la base, et rien d'autre. Elle est la première ligne de défense latérale, celle qui limite les dégâts d'un pod compromis. Elle n'est pas un pare-feu applicatif : elle filtre des adresses, des ports et des protocoles, pas des requêtes HTTP.

Les concepts

La sémantique exacte

Une NetworkPolicy est un objet de namespace, qui contient :

  • podSelector : les pods auxquels la politique s'applique (dans son namespace). Un sélecteur vide, {}, désigne tous les pods du namespace ;
  • policyTypes : Ingress (connexions entrantes), Egress (connexions sortantes), ou les deux ;
  • ingress : une liste de règles from + ports : qui a le droit de se connecter à ces pods ;
  • egress : une liste de règles to + ports : où ces pods ont le droit de se connecter.

Cinq règles de lecture gouvernent tout le reste, d'après la documentation de Kubernetes :

  1. Sans politique, tout est permis. Un pod que aucune politique ne sélectionne n'est pas filtré.
  2. Dès qu'une politique sélectionne un pod pour une direction, tout ce qui n'est pas explicitement autorisé dans cette direction est refusé. C'est le « refus par défaut implicite ». Une politique Ingress sur un pod ne touche pas à ses connexions sortantes.
  3. Les politiques s'additionnent. Elles ne sont jamais en conflit : le trafic autorisé pour un pod est l'union de ce que permettent toutes les politiques qui le sélectionnent. Il n'y a pas de règle « interdire » ni d'ordre : on ne peut qu'autoriser.
  4. Les deux extrémités comptent. Pour qu'une connexion du pod A vers le pod B passe, il faut que la politique d'egress de A (si A est sélectionné en egress) et la politique d'ingress de B (si B est sélectionné en ingress) l'autorisent.
  5. Les réponses sont autorisées. Les politiques s'appliquent à l'ouverture de la connexion ; les paquets de retour d'une connexion autorisée passent (le suivi de connexion de la leçon 3 le permet).

La documentation précise une exception : le trafic de et vers le nœud où tourne le pod est toujours autorisé. C'est ce qui permet aux sondes du kubelet de continuer à fonctionner. Elle précise aussi qu'une NetworkPolicy ne fait rien si le plugin réseau ne la met pas en œuvre (voir plus bas).

Les sélecteurs de pairs

Dans from et to, un pair peut être décrit de trois façons :

  • podSelector : des pods par étiquettes, dans le namespace de la politique s'il est seul ;
  • namespaceSelector : tous les pods des namespaces dont les étiquettes correspondent. Depuis Kubernetes 1.21, chaque namespace porte l'étiquette kubernetes.io/metadata.name, égale à son nom, ce qui permet de désigner un namespace par son nom sans l'étiqueter soi-même ;
  • ipBlock : une plage d'adresses CIDR, avec des exceptions (except). On l'utilise pour ce qui est hors du cluster (un réseau d'entreprise, un service externe) ; pour des pods, on préfère les sélecteurs, car leurs adresses changent.

Ports

Un champ ports liste des couples protocole et port (TCP, UDP ou SCTP). Un port peut être un numéro ou le nom d'un port de conteneur, ce qui évite de répéter 8000 partout. endPort désigne une plage, de port à endPort incluse, stable depuis Kubernetes 1.25. Une règle sans ports autorise tous les ports.

Le piège de l'indentation : ET ou OU

C'est l'erreur la plus répandue. Comparez :

# Version 1 : OU. Deux éléments dans from.
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: traefik
      - podSelector:
          matchLabels:
            app.kubernetes.io/name: traefik
# Version 2 : ET. Un seul élément, avec deux sélecteurs.
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: traefik
        podSelector:
          matchLabels:
            app.kubernetes.io/name: traefik

La différence est un tiret et un niveau d'indentation. La version 1 contient deux éléments : « tous les pods du namespace traefik », ou « les pods app.kubernetes.io/name: traefik du namespace de la politique ». Ce second élément ne dit rien du namespace traefik : il désigne, dans signalements, des pods étiquetés comme Traefik, probablement aucun. Mais le premier élément est déjà beaucoup trop large : n'importe quel pod du namespace traefik est autorisé. La version 2 contient un élément : les pods étiquetés traefik dans le namespace traefik, ce qu'on voulait dire. Aucun outil ne signale la différence : les deux sont valides.

Warning

Un tiret devant podSelector change le sens de la règle du tout au tout. Relisez toute politique qui contient un namespaceSelector et un podSelector en vous demandant : « un élément, ou deux ? ».

En pratique

Ce que l'on construit : un namespace signalements où tout est refusé par défaut, puis les trois flux nécessaires. Les manifestes sont validés comme du YAML ; ce qu'on observe sur un cluster est décrit, non reproduit. Les étiquettes sont celles du déploiement du cours précédent (Déployer Signalements).

1. Vérifier que le plugin applique les politiques

Avant d'écrire une ligne : un plugin qui ne les applique pas rend toute politique décorative, sans message d'erreur. Sur Kapsule, le plugin réseau par défaut est Cilium, qui les applique. Sur kind, le plugin par défaut est kindnet. Les notes de la documentation de kind parlent de « routes simples » et de masquerade, sans mentionner les politiques ; le code source de kindnetd, lui, les met en œuvre : à partir de la version de kind de ce cours (0.33.0), il embarque la bibliothèque kube-network-policies du projet Kubernetes, qui évalue les NetworkPolicies standard et filtre les paquets avec nftables. Son code le configure en mode FailOpen: true : si le composant de filtrage défaille, il laisse passer. Dans tous les cas, testez (section 7), et ne vous fiez pas à la seule présence de l'objet.

2. Le refus par défaut

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: refus-par-defaut
  namespace: signalements
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]

podSelector: {} sélectionne tous les pods du namespace, policyTypes couvre les deux directions, et il n'y a ni ingress ni egress : rien n'est autorisé. Appliquez-la, et Signalements cesse de fonctionner : l'API ne résout plus la base (le DNS est refusé), ni n'y accède, et Traefik ne l'atteint plus. C'est le point de départ voulu : on ajoute maintenant ce dont on a besoin.

3. Autoriser le DNS en sortie

Le piège classique : après le refus par défaut, l'application échoue avec une erreur de résolution de noms (could not translate host name "postgresql" to address: Temporary failure in name resolution, avec le client PostgreSQL). Chaque pod interroge CoreDNS (leçon 5, Le DNS du cluster), qui vit dans kube-system.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: autorise-dns
  namespace: signalements
spec:
  podSelector: {}
  policyTypes: [Egress]
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

C'est un seul élément to (ET) : les pods k8s-app: kube-dns de kube-system. On autorise UDP et TCP, car le DNS bascule sur TCP quand la réponse est tronquée. k8s-app: kube-dns est l'étiquette que portent les pods de CoreDNS dans la plupart des distributions, même quand le Deployment s'appelle coredns ; vérifiez-la sur votre cluster avec kubectl get pods -n kube-system --show-labels.

Note

Quand le cluster utilise NodeLocal DNSCache, les pods interrogent une adresse locale au nœud, et cette politique doit viser cette adresse (voir la leçon 5). Un ipBlock sur l'adresse du cache est alors le plus simple.

4. La Gateway vers l'API

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-depuis-passerelle
  namespace: signalements
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: signalements
      app.kubernetes.io/component: api
  policyTypes: [Ingress]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: traefik
          podSelector:
            matchLabels:
              app.kubernetes.io/name: traefik
      ports:
        - protocol: TCP
          port: http          # le port nommé du conteneur, 8000

La politique s'applique aux pods de l'API (deux étiquettes, comme le Service), et autorise en entrée les pods de Traefik, et eux seuls, sur le port nommé http. Souvenez-vous que Traefik envoie les requêtes directement aux pods, pas à l'adresse du Service (cours précédent) : la source est donc un pod de traefik, et la politique le voit comme tel. Sans cette règle, l'API recevrait des erreurs 504 de Traefik, qui n'atteindrait plus les pods.

5. L'API vers PostgreSQL

Il faut deux règles, une de chaque côté : l'egress de l'API et l'ingress de la base.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-vers-postgresql
  namespace: signalements
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: signalements
  policyTypes: [Egress]
  egress:
    - to:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: postgresql
      ports:
        - protocol: TCP
          port: 5432
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: postgresql-depuis-api
  namespace: signalements
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: postgresql
  policyTypes: [Ingress]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: signalements
      ports:
        - protocol: TCP
          port: 5432

La première s'applique aux pods d'étiquette app.kubernetes.io/name: signalements : l'API et les pods de la migration (qui portent la même étiquette de nom, voir le cours précédent), qui ont besoin de la base eux aussi. La seconde protège la base : seuls ces pods, sur le port 5432. Ici, podSelector seul désigne des pods du namespace de la politique, ce qu'on veut.

Si l'une des deux règles manque, la connexion échoue, et l'erreur est déroutante : un délai de connexion (timeout), pas un refus, parce que le paquet est silencieusement abandonné. Un refus explicite (Connection refused) viendrait du pod lui-même, pas d'une politique.

6. Ce qui sort vers Internet

Signalements n'appelle aucun service externe ; si c'était le cas (un service de notification), on l'autoriserait avec un ipBlock, en excluant les plages du cluster :

egress:
  - to:
      - ipBlock:
          cidr: 0.0.0.0/0
          except:
            - 10.0.0.0/8
            - 172.16.0.0/12
            - 192.168.0.0/16
    ports:
      - protocol: TCP
        port: 443

« Tout Internet sauf les plages privées », sur le port 443 : l'API peut appeler des services publics mais pas le réseau interne. Les plages privées sont à adapter à votre réseau (celles du VPC Scaleway et du cluster). Pour un nom précis plutôt qu'une plage, une NetworkPolicy standard ne sait rien faire : voir Cilium plus bas. Pour une plage de ports, endPort :

ports:
  - protocol: TCP
    port: 30000
    endPort: 32767

7. Tester

Un pod de test dans un autre namespace montre ce qui est bloqué. Le namespace signalements impose le profil restricted, où une image d'outils réseau qui tourne en root serait refusée : on teste donc depuis un namespace d'essai.

$ kubectl create namespace test-reseau
$ kubectl run -n test-reseau diag --rm -it --image=nicolaka/netshoot --restart=Never -- sh
# depuis le pod de test :
$ nc -zv -w 3 postgresql.signalements.svc.cluster.local 5432
$ curl -s -m 3 http://signalements.signalements.svc.cluster.local/sante

Avec les politiques en place, les deux commandes doivent échouer par délai d'attente : la base et l'API ne sont plus joignables depuis un pod qui n'est ni Traefik ni l'API. Retirez les politiques, et elles aboutissent. Testez aussi le sens inverse, ce qui doit passer, depuis un pod de l'API : kubectl exec dans un pod signalements, qui n'a pas forcément nc, ou kubectl debug avec un conteneur éphémère de profil restreint. La vérification n'est complète que si les deux sens sont testés : une politique trop stricte casse l'application, trop large elle ne protège rien.

Avec Cilium, Hubble donne la vue de chaque décision :

$ hubble observe --namespace signalements --verdict DROPPED --follow

Chaque ligne indique la source, la destination, le port et le verdict. Sans Hubble, il n'y a pas de journal des refus dans une NetworkPolicy standard : l'absence de trace est la raison pour laquelle on teste.

Sous le capot

Les politiques sont des objets de l'API, appliquées par le plugin réseau. Kubernetes stocke l'objet et ne fait rien d'autre. C'est le plugin (CNI, leçon 2, Les plugins CNI) qui le traduit en règles de filtrage sur chaque nœud, pour les pods qui s'y trouvent. D'où le piège de la section 1 : un cluster sans plugin compatible accepte les objets, et ne filtre rien.

Chez Cilium, le filtrage se fait dans le noyau avec eBPF. Les pods reçoivent une identité numérique dérivée de leurs étiquettes de sécurité, et les règles sont exprimées sur ces identités, pas sur des adresses IP : un pod qui change d'adresse garde son identité, et la table de règles ne change pas. À chaque paquet, un programme eBPF cherche l'identité source, l'identité destination et le port dans une table (une map), et décide. C'est ce qui rend Cilium efficace avec beaucoup de politiques.

Chez kindnet, la bibliothèque kube-network-policies s'appuie sur nftables et sur une file d'attente de paquets : les paquets qui nécessitent une décision sont envoyés à un processus en espace utilisateur (NFQUEUE) qui évalue les politiques, puis renvoie un verdict au noyau. C'est plus simple et moins rapide, bien adapté à un cluster de formation.

L'ordre avec les Services. Les politiques s'évaluent sur l'adresse du pod réellement visée, après la traduction d'adresse du Service (kube-proxy et ses alternatives). On écrit donc podSelector et port du pod, jamais l'adresse virtuelle d'un Service.

Pièges courants

La politique inerte. Plugin sans prise en charge : toutes les politiques sont acceptées par l'API et n'ont aucun effet. Test de non-régression à garder : un pod de test qui doit échouer.

Le DNS oublié. Un refus d'egress sans règle DNS donne des erreurs de résolution, pas de connexion refusée. Premier réflexe devant « ça marchait avant la politique » : nslookup depuis un pod de test.

L'ET devenu OU. Voir plus haut : un tiret de trop ouvre le namespace entier. À relire à chaque revue.

Des politiques d'ingress seules. Elles ne limitent que l'entrée : un pod compromis peut quand même sortir vers n'importe où. Une défense de profondeur a les deux directions.

Les sondes. D'après la documentation, le trafic du nœud est autorisé, donc les sondes du kubelet passent. Une implémentation peut cependant se comporter autrement : si un pod passe soudain à « non prêt » après une politique, vérifiez que l'implémentation laisse passer le trafic du nœud (avec Cilium, l'entité host).

Le namespace de Traefik oublié. Si traefik a lui aussi un refus par défaut, Traefik doit pouvoir parler au serveur d'API (pour lire les routes), au DNS, et aux pods des applications. Le refus par défaut d'un namespace d'infrastructure est une opération à mener avec un plan de rollback.

Les pods en hostNetwork. Ils partagent l'adresse du nœud, et leur traitement par les politiques dépend de l'implémentation : la documentation de Kubernetes l'indique comme point à vérifier. Évitez de compter dessus.

Les politiques ne voient pas le HTTP. « Autoriser GET /sante seulement » est impossible avec une NetworkPolicy standard.

Sécurité

  • Refus par défaut dans chaque namespace applicatif, créé avec le namespace (Kustomize, Helm, ou un contrôleur comme Kyverno qui l'ajoute à la création). Un namespace sans politique est un namespace ouvert.
  • Qui peut modifier les politiques ? Le droit create ou update sur les NetworkPolicy d'un namespace permet de les assouplir. Il est à réserver à l'équipe plateforme, ou à valider par revue dans Git (RBAC : cours Sécurité de Kubernetes).
  • Pensez au service de métadonnées et à l'API de Kubernetes. Un pod ordinaire n'a pas à joindre l'adresse du service de métadonnées du cloud, ni l'API du cluster : un egress minimal (DNS, base, services nommés) les exclut d'office. C'est une des protections les plus utiles après une compromission.
  • Étiquettes comme contrôle d'accès. namespaceSelector sur une étiquette libre donne l'accès à qui peut l'ajouter. Préférez kubernetes.io/metadata.name, qu'on ne peut pas modifier.
  • La politique ne remplace pas l'authentification. Elle limite qui peut tenter ; PostgreSQL exige toujours son mot de passe, et l'identité entre services s'obtient par le mTLS d'un maillage (leçon 9).
  • Les politiques ne protègent pas les nœuds. Elles filtrent les pods ; l'accès aux nœuds, aux ports NodePort et au kubelet relève des groupes de sécurité du fournisseur et des politiques d'hôte de votre plugin.

En production

  • Cilium et ses extensions. Sur Kapsule, où Cilium est le plugin par défaut, la CiliumNetworkPolicy offre ce que la politique standard n'a pas : des règles L7 (HTTP : méthode, chemin ; DNS), des règles par nom de domaine avec toFQDNs, des entités (host, world, kube-apiserver) et le refus explicite. Le prix est une dépendance à Cilium. Exemple : autoriser la sortie vers un domaine précis, en observant les réponses DNS pour apprendre ses adresses.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-vers-scaleway
  namespace: signalements
spec:
  endpointSelector:
    matchLabels:
      app.kubernetes.io/name: signalements
  egress:
    - toEndpoints:
        - matchLabels:
            k8s:io.kubernetes.pod.namespace: kube-system
            k8s:k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: ANY
          rules:
            dns:
              - matchPattern: "*.scaleway.com"
    - toFQDNs:
        - matchPattern: "*.scaleway.com"
      toPorts:
        - ports:
            - port: "443"
              protocol: TCP

La règle DNS laisse CoreDNS répondre et permet à Cilium de voir quelles adresses correspondent aux noms ; la règle toFQDNs autorise ensuite ces adresses. Sans la première, la seconde ne connaît aucune adresse.

  • Politiques d'administration. La NetworkPolicy standard appartient à l'équipe de l'application, et rien n'empêche cette équipe de la relâcher. Le groupe SIG Network travaille à des politiques de cluster, posées par la plateforme. Les anciennes ressources AdminNetworkPolicy et BaselineAdminNetworkPolicy (groupe policy.networking.k8s.io, version v1alpha1) sont, d'après le site du projet, désormais classées parmi les APIs « précédentes » : la ressource ClusterNetworkPolicy, en v1alpha2 à ce jour, les remplace, avec deux niveaux (administration, avant les politiques de l'application, et base, après). Elles sont expérimentales : vérifiez la prise en charge de votre plugin avant de vous y appuyer, et préférez pour l'instant les politiques propres au plugin pour les règles de cluster.
  • Écrire les politiques depuis l'observation. Hubble (ou la CLI cilium) permet de relever les flux réels d'une application avant d'écrire ses politiques, ce qui évite la phase d'essai-erreur sur un cluster en service.
  • Introduire progressivement. Namespace par namespace, en commençant par des environnements de pré-production, avec des tests de connectivité dans l'intégration continue.
  • Documenter les flux. Un diagramme des flux autorisés par application, tenu avec les manifestes, sert de contrat entre plateforme et équipes.

Exercices

Exercice 1 : lire une politique

Que fait cette politique, appliquée dans signalements ? Qui peut joindre le port 5432 de PostgreSQL, en plus de l'API ?

spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: postgresql
  policyTypes: [Ingress]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/name: signalements
        - namespaceSelector:
            matchLabels:
              role: outils
      ports:
        - protocol: TCP
          port: 5432
Solution

Il y a deux éléments dans from (OU) : les pods app.kubernetes.io/name: signalements du namespace signalements, ou tous les pods de tous les namespaces portant l'étiquette role: outils. Donc en plus de l'API et de la migration, n'importe quel pod d'un namespace étiqueté role: outils peut ouvrir une connexion à la base. Si l'on voulait seulement l'API d'un namespace d'outils, il faudrait un seul élément qui combine les deux sélecteurs (sans tiret devant podSelector).

Exercice 2 : le délai qui n'explique rien

Après avoir ajouté refus-par-defaut, autorise-dns et api-vers-postgresql, l'API démarre mais /sante répond par une erreur de base de données. Les journaux de l'API affichent un délai de connexion vers postgresql:5432. Quelle règle manque, et comment le confirmez-vous sans rien modifier ?

Solution

Il manque postgresql-depuis-api : l'ingress de la base. Le DNS passe (le nom est résolu, sinon l'erreur serait une résolution de nom), l'egress de l'API passe, mais le refus par défaut s'applique aussi à l'entrée de la base. La confirmation : hubble observe --namespace signalements --verdict DROPPED (avec Cilium) montre un paquet abandonné de l'API vers le port 5432 de la base, direction ingress. Sans Hubble, un test depuis un pod de test avec nc -zv -w 3 donne le même délai, et la lecture des politiques qui sélectionnent la base (kubectl get networkpolicy -n signalements) montre qu'aucune ne l'ouvre en entrée.

Exercice 3 : laisser passer Prometheus

Prometheus (pods app.kubernetes.io/name: prometheus, namespace monitoring) doit relever les métriques de l'API sur son port nommé metrics. Écrivez la politique qui l'autorise, et dites ce qui change si vous oubliez que les deux sélecteurs doivent être dans le même élément.

Solution
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-depuis-prometheus
  namespace: signalements
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: signalements
      app.kubernetes.io/component: api
  policyTypes: [Ingress]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
          podSelector:
            matchLabels:
              app.kubernetes.io/name: prometheus
      ports:
        - protocol: TCP
          port: metrics

Les politiques s'additionnent : celle-ci ouvre un second flux, sans toucher à api-depuis-passerelle. Avec un tiret devant podSelector, la règle deviendrait « tout pod du namespace monitoring », ou « un pod prometheus de signalements » : le namespace de supervision entier pourrait joindre le port des métriques, ce qui ouvre bien plus que voulu.

Récapitulatif

  • Sans politique, le réseau du cluster est plat : tout pod joint tout pod.
  • Une NetworkPolicy sélectionne des pods ; dès qu'elle en sélectionne un pour une direction, tout ce qui n'est pas autorisé dans cette direction est refusé. Les politiques s'additionnent, il n'y a pas de règle de refus.
  • Pour qu'une connexion passe, l'egress de la source et l'ingress de la destination doivent l'autoriser ; le trafic de retour suit.
  • Départ de sécurité : un refus par défaut par namespace, puis le DNS en sortie, puis chaque flux nécessaire (Traefik, API, base).
  • ET ou OU : un namespaceSelector et un podSelector dans le même élément (ET), ou dans deux éléments (OU, ouvre tout le namespace).
  • ipBlock pour ce qui est hors du cluster ; ports par numéro ou nom, endPort pour une plage.
  • Il faut un plugin qui les applique : Cilium oui ; kindnet (kind 0.33.0) aussi, d'après son code, en mode FailOpen. Toujours tester.
  • CiliumNetworkPolicy pour le L7 et les noms de domaine ; ClusterNetworkPolicy (alpha) pour les règles de cluster à venir.

Pour aller plus loin

  • Diagnostiquer le réseau d'un cluster, où une NetworkPolicy qui bloque le DNS est l'un des six scénarios.
  • IPv6, double pile et maillage de services, pour l'identité entre services (mTLS) que les politiques d'adresses ne donnent pas.
  • La page Network Policies de la documentation de Kubernetes, et la tâche Declare Network Policy.
  • Le projet Network Policy API de SIG Network, pour suivre la ClusterNetworkPolicy.
  • La documentation de Cilium sur les politiques L3, L4 et L7, et sur Hubble.
  • Le cours Sécurité de Kubernetes, pour le RBAC, l'admission et le durcissement qui complètent le cloisonnement réseau.
  • Glossaire : NetworkPolicy, Pod, Namespace Kubernetes.
Voir ma constellation →

Sources