Écrire des politiques réseau : des règles L3/L4 aux règles L7
Kubernetes part du principe que chaque Pod peut joindre tous les autres Pods ; CiliumNetworkPolicy permet de restreindre cela, jusqu'à la méthode HTTP si nécessaire.
Par défaut, le réseau Kubernetes n’a aucune restriction : n’importe quel Pod peut joindre n’importe quel autre Pod du cluster, sur n’importe quel port. Pour un cluster avec la moindre multi-tenance ou une exigence de sécurité allant au-delà de « tout faire confiance en interne », ce comportement par défaut est généralement inadapté. CiliumNetworkPolicy permet de le restreindre, des simples listes d’autorisation jusqu’aux règles qui inspectent la requête HTTP elle-même.
Le point de départ : tout est ouvert
Confirmez le comportement par défaut avant de modifier quoi que ce soit :
kubectl run client --image=curlimages/curl --command -- sleep infinity
kubectl exec client -- curl -s http://some-other-service.default.svc.cluster.local
Sans aucune politique en place, cette requête réussit quels que soient client et ce que fait some-other-service. Dès que n’importe quelle politique sélectionne un Pod donné pour une direction donnée (ingress ou egress), le trafic de ce Pod dans cette direction passe en refus par défaut : seul ce que la politique autorise explicitement passe. Un Pod qu’aucune politique ne sélectionne reste entièrement ouvert. C’est le point le plus important à comprendre avant d’écrire votre première politique : en écrire une trop étroitement ciblée peut couper silencieusement du trafic que vous n’aviez pas l’intention de toucher, pour tout Pod qu’elle sélectionne.
L3/L4 : restreindre par identité et par port
La forme de politique la plus courante restreint quels Pods peuvent joindre quels Pods, sur quels ports, en utilisant l’identité basée sur les labels de Cilium (présentée dans le guide de cette série sur la manière dont Cilium utilise eBPF) plutôt que des adresses IP :
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: default
spec:
endpointSelector:
matchLabels:
app: api
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
Lecture : elle s’applique aux Pods labellisés app: api (endpointSelector), et n’autorise le trafic entrant (ingress) que depuis les Pods labellisés app: frontend, uniquement sur le port TCP 8080. Tout autre Pod, et tout autre port, est désormais refusé vers les Pods api, puisque l’existence de cette politique les place en refus par défaut pour l’ingress.
L’egress fonctionne de la même façon, dans l’autre sens
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-egress-to-database
namespace: default
spec:
endpointSelector:
matchLabels:
app: api
egress:
- toEndpoints:
- matchLabels:
app: database
toPorts:
- ports:
- port: "5432"
protocol: TCP
Les Pods api ne peuvent désormais initier des connexions sortantes que vers les Pods database sur le port 5432. Combinez les règles d’ingress et d’egress dans la même politique, ou gardez-les séparées, selon ce qui se lit le plus clairement dans votre cas ; fonctionnellement, des endpointSelector identiques dans des politiques distinctes pour les mêmes Pods se composent toujours correctement.
Autoriser le trafic vers ou depuis l’extérieur du cluster
fromEndpoints/toEndpoints font correspondre des Pods par label. Pour le trafic vers ou depuis quelque chose d’extérieur au réseau de Pods du cluster, ce sont des règles basées sur le DNS ou sur des CIDR qui s’appliquent :
egress:
- toFQDNs:
- matchName: "api.external-service.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
toFQDNs est une extension propre à Cilium par rapport à la NetworkPolicy Kubernetes standard : elle permet d’autoriser l’egress vers un domaine externe nommé plutôt que de devoir connaître et figer à l’avance ses adresses IP, ce qui compte pour tout service externe dont vous ne maîtrisez pas les IP et qui peuvent changer sans préavis.
L7 : une politique qui lit la requête réelle
Tout ce qui précède opère en L3/L4 : identités et ports, sans regarder à l’intérieur du trafic. Cilium peut aller plus loin et prendre des décisions basées sur le contenu des protocoles L7 qu’il comprend, HTTP étant le plus courant :
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-read-only-for-reporting
namespace: default
spec:
endpointSelector:
matchLabels:
app: api
ingress:
- fromEndpoints:
- matchLabels:
app: reporting
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "GET"
path: "/reports/.*"
Ceci autorise le service reporting à joindre api sur le port 8080, mais uniquement via des requêtes GET dont le chemin correspond à /reports/.*. Un POST vers le même port, ou un GET vers un chemin non listé, est refusé, alors qu’il s’agit des mêmes Pods sur le même port que des règles L3/L4 seules auraient autorisés en bloc. Ceci est appliqué par l’Envoy embarqué de Cilium pour les Pods auxquels la règle s’applique ; la politique L7 a un coût de performance réel mais généralement modeste par rapport à l’application purement L3/L4 en eBPF, à garder en tête pour les chemins à très haut débit où une restriction L3/L4 suffit à votre exigence de sécurité réelle.
Tester avant de faire confiance
Une politique mal appliquée échoue en mode fermé, en refusant du trafic et non en l’autorisant, ce qui est le mode de défaillance le plus sûr mais reste une panne s’il s’agit du mauvais trafic. Avant d’appliquer une nouvelle politique restrictive à quoi que ce soit qui sert du trafic réel :
kubectl apply --dry-run=server -f policy.yaml
Appliquez-la ensuite d’abord dans un environnement inférieur, générez du trafic qui devrait être autorisé et du trafic qui devrait être refusé, et confirmez que les deux se comportent comme prévu, avant de promouvoir la même politique vers tout ce que sert la production.
Partir de « tout autoriser », puis restreindre délibérément
Un parcours d’adoption raisonnable pour un cluster sans politiques existantes : n’essayez pas d’écrire un jeu de politiques complet et correct dès le premier jour. Commencez par observer les schémas de trafic réels (Hubble, présenté sur la page d’accueil de cette technologie, est conçu exactement pour cela), écrivez des politiques qui correspondent à ce qui est observé comme légitime, appliquez-les d’abord dans un environnement hors production, et seulement ensuite restreignez le trafic de production en conséquence. Écrire des politiques restrictives à partir de schémas de trafic supposés plutôt qu’observés est une façon courante de provoquer une panne qui ressemble à un problème de Cilium mais qui est en réalité une politique qui n’a pas pris en compte un schéma légitime auquel personne n’avait pensé.
Guides populaires
-
Comment Cilium utilise eBPF : identité, politique et sockets Ce qui se passe réellement dans le noyau quand un Pod démarre, communique avec un autre Pod ou fait l'objet d'une décision de politique sur un paquet, et pourquoi c'est rapide. -
Préparer un cluster pour Cilium : désactiver d'abord le CNI par défaut Cilium remplace entièrement le réseau de votre cluster, ce qui rend l'ordre des opérations plus important qu'avec la plupart des add-ons. -
Exposer des Services avec la prise en charge de Gateway API de Cilium GatewayClass, Gateway et HTTPRoute : routage par nom d'hôte, correspondance de chemins, réécriture d'URL et TLS, sans Pods de passerelle dédiés.