
Le but de l'article est de présenter au lecteur les bases de la mise en réseau et de la gestion des politiques réseau dans Kubernetes, ainsi que le plugin tiers Calico qui étend les capacités standard. En cours de route, la facilité de configuration et certaines fonctionnalités seront démontrées à l'aide d'exemples réels tirés de notre expérience d'exploitation.
Une introduction rapide Ă lâappliance rĂ©seau Kubernetes
Un cluster Kubernetes ne peut ĂȘtre imaginĂ© sans rĂ©seau. Nous avons dĂ©jĂ publiĂ© des documents sur leurs bases : «"Et"».
Dans le cadre de cet article, il est important de noter que K8s lui-mĂȘme n'est pas responsable de la connectivitĂ© rĂ©seau entre les conteneurs et les nĆuds : pour cela, divers Plugins CNI (Interface rĂ©seau de conteneurs). Pour en savoir plus sur ce concept, nous .
Par exemple, le plus courant de ces plugins est â fournit une connectivitĂ© rĂ©seau complĂšte entre tous les nĆuds du cluster en Ă©levant des ponts sur chaque nĆud et en lui attribuant un sous-rĂ©seau. Toutefois, une accessibilitĂ© complĂšte et non rĂ©glementĂ©e nâest pas toujours bĂ©nĂ©fique. Pour assurer une sorte d'isolation minimale dans le cluster, il est nĂ©cessaire d'intervenir dans la configuration du pare-feu. Dans le cas gĂ©nĂ©ral, il est placĂ© sous le contrĂŽle du mĂȘme CNI, c'est pourquoi toute intervention de tiers dans iptables peut ĂȘtre mal interprĂ©tĂ©e ou totalement ignorĂ©e.
Et un « prĂȘt Ă l'emploi » pour organiser la gestion des politiques de rĂ©seau dans un cluster Kubernetes est fourni . Cette ressource, rĂ©partie sur des espaces de noms sĂ©lectionnĂ©s, peut contenir des rĂšgles permettant de diffĂ©rencier les accĂšs d'une application Ă l'autre. Il permet Ă©galement de configurer l'accessibilitĂ© entre des pods, des environnements (espaces de noms) ou des blocs d'adresses IP spĂ©cifiques :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978Ce nâest pas lâexemple le plus primitif de pourrait une fois pour toutes dĂ©courager le dĂ©sir de comprendre la logique du fonctionnement des politiques de rĂ©seau. Cependant, nous essaierons toujours de comprendre les principes et mĂ©thodes de base de traitement des flux de trafic Ă l'aide des politiques de rĂ©seau...
Il est logique qu'il existe 2 types de trafic : entrant dans le pod (Ingress) et sortant de celui-ci (Egress).

En fait, la politique est divisée en ces 2 catégories en fonction de la direction du mouvement.
Le prochain attribut requis est un sĂ©lecteur ; celui Ă qui la rĂšgle sâapplique. Il peut s'agir d'un pod (ou d'un groupe de pods) ou d'un environnement (c'est-Ă -dire un espace de noms). Un dĂ©tail important : les deux types de ces objets doivent contenir une Ă©tiquette (Ă©tiquette dans la terminologie Kubernetes) - ce sont ceux avec lesquels les politiciens opĂšrent.
En plus d'un nombre fini de sélecteurs unis par une sorte d'étiquette, il est possible d'écrire des rÚgles comme « Autoriser/refuser tout/tout le monde » dans différentes variantes. A cet effet, des constructions de la forme sont utilisées :
podSelector: {}
ingress: []
policyTypes:
- Ingressâ dans cet exemple, tous les pods de l'environnement sont bloquĂ©s du trafic entrant. Le comportement inverse peut ĂȘtre obtenu avec la construction suivante :
podSelector: {}
ingress:
- {}
policyTypes:
- IngressDe mĂȘme pour les sortants :
podSelector: {}
policyTypes:
- Egress- pour l'éteindre. Et voici ce qu'il faut inclure :
podSelector: {}
egress:
- {}
policyTypes:
- EgressRevenant sur le choix d'un plugin CNI pour un cluster, il convient de noter que tous les plugins réseau ne prennent pas en charge NetworkPolicy. Par exemple, Flannel déjà mentionné ne sait pas comment configurer les politiques réseau, ce qui dans le dépÎt officiel. Une alternative y est également mentionnée - un projet Open Source , qui élargit considérablement l'ensemble standard des API Kubernetes en termes de politiques réseau.

Apprendre à connaßtre Calico : théorie
Le plugin Calico peut ĂȘtre utilisĂ© en intĂ©gration avec Flannel (sous-projet ) ou indĂ©pendamment, couvrant Ă la fois la connectivitĂ© rĂ©seau et les capacitĂ©s de gestion de la disponibilitĂ©.
Quelles opportunitĂ©s offre lâutilisation de la solution « en boĂźte » K8s et de lâensemble dâAPI de Calico ?
Voici ce qui est intégré à NetworkPolicy :
- les politiciens sont limitĂ©s par lâenvironnement ;
- les stratĂ©gies sont appliquĂ©es aux pods marquĂ©s dâĂ©tiquettes ;
- les rĂšgles peuvent ĂȘtre appliquĂ©es aux pods, aux environnements ou aux sous-rĂ©seaux ;
- les rÚgles peuvent contenir des protocoles, des spécifications de port nommés ou symboliques.
Voici comment Calico étend ces fonctions :
- les politiques peuvent ĂȘtre appliquĂ©es Ă nâimporte quel objet : pod, conteneur, machine virtuelle ou interface ;
- les rÚgles peuvent contenir une action spécifique (interdiction, autorisation, journalisation) ;
- la cible ou la source des rĂšgles peut ĂȘtre un port, une plage de ports, des protocoles, des attributs HTTP ou ICMP, une IP ou un sous-rĂ©seau (4e ou 6e gĂ©nĂ©ration), des sĂ©lecteurs quelconques (nĆuds, hĂŽtes, environnements) ;
- De plus, vous pouvez réguler le passage du trafic à l'aide des paramÚtres DNAT et des politiques de transfert de trafic.
Les premiers commits sur GitHub dans le rĂ©fĂ©rentiel Calico remontent Ă juillet 2016, et un an plus tard, le projet a pris une position de leader dans l'organisation de la connectivitĂ© rĂ©seau Kubernetes - comme en tĂ©moignent, par exemple, les rĂ©sultats de l'enquĂȘte, :

De nombreuses grandes solutions gérées avec K8, telles que , , et d'autres ont commencé à en recommander l'utilisation.
CĂŽtĂ© performances, tout est super ici. En testant son produit, l'Ă©quipe de dĂ©veloppement de Calico a dĂ©montrĂ© des performances astronomiques, exĂ©cutant plus de 50000 500 conteneurs sur 20 nĆuds physiques avec un taux de crĂ©ation de XNUMX conteneurs par seconde. Aucun problĂšme nâa Ă©tĂ© identifiĂ© concernant la mise Ă lâĂ©chelle. De tels rĂ©sultats dĂ©jĂ Ă l'annonce de la premiĂšre version. Des Ă©tudes indĂ©pendantes axĂ©es sur le dĂ©bit et la consommation des ressources confirment Ă©galement que les performances de Calico sont presque aussi bonnes que celles de Flannel. :

Le projet se développe trÚs rapidement, il prend en charge le travail dans les solutions populaires gérées K8s, OpenShift, OpenStack, il est possible d'utiliser Calico lors du déploiement d'un cluster utilisant , il y a des références à la construction de réseaux Service Mesh ( utilisé conjointement avec Istio).
EntraĂźnez-vous avec Calico
Dans le cas général d'utilisation de Kubernetes vanilla, l'installation de CNI revient à utiliser le fichier calico.yaml, , en utilisant kubectl apply -f.
La version actuelle du plugin est généralement compatible avec les deux ou trois derniÚres versions de Kubernetes ; la compatibilité avec les versions antérieures n'est ni testée ni garantie. D'aprÚs les développeurs, Calico s'exécute au niveau du noyau. Linux au-dessus de 3.10 sous contrÎle CentOS 7, Ubuntu 16 ou Debian 8, via iptables ou IPVS.
Isolement dans l'environnement
Pour une compréhension générale, examinons un cas simple pour comprendre en quoi les politiques de réseau dans la notation Calico diffÚrent des politiques standard et comment l'approche de création de rÚgles simplifie leur lisibilité et leur flexibilité de configuration :

Il y a 2 applications web déployées dans le cluster : en Node.js et PHP, dont une utilise Redis. Pour bloquer l'accÚs à Redis depuis PHP, tout en conservant la connectivité avec Node.js, appliquez simplement la stratégie suivante :
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: allow-redis-nodejs
spec:
podSelector:
matchLabels:
service: redis
ingress:
- from:
- podSelector:
matchLabels:
service: nodejs
ports:
- protocol: TCP
port: 6379Essentiellement, nous avons autorisĂ© le trafic entrant vers le port Redis depuis Node.js. Et ils nâont clairement rien interdit dâautre. DĂšs que NetworkPolicy apparaĂźt, tous les sĂ©lecteurs qui y sont mentionnĂ©s commencent Ă ĂȘtre isolĂ©s, sauf indication contraire. Toutefois, les rĂšgles d'isolement ne s'appliquent pas aux autres objets non couverts par le sĂ©lecteur.
L'exemple utilise apiVersion Kubernetes prĂȘt Ă l'emploi, mais rien ne vous empĂȘche de l'utiliser . La syntaxe y est plus dĂ©taillĂ©e, vous devrez donc réécrire la rĂšgle pour le cas ci-dessus sous la forme suivante :
apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
name: allow-redis-nodejs
spec:
selector: service == 'redis'
ingress:
- action: Allow
protocol: TCP
source:
selector: service == 'nodejs'
destination:
ports:
- 6379 Les constructions mentionnées ci-dessus pour autoriser ou refuser tout le trafic via l'API NetworkPolicy standard contiennent des constructions avec des parenthÚses difficiles à comprendre et à mémoriser. Dans le cas de Calico, pour changer la logique d'une rÚgle de pare-feu en sens inverse, il suffit de changer action: Allow sur action: Deny.
Isolement par environnement
Imaginez maintenant une situation dans laquelle une application génÚre des métriques commerciales à collecter dans Prometheus et à analyser plus en profondeur à l'aide de Grafana. Le téléchargement peut contenir des données sensibles, qui sont à nouveau visibles publiquement par défaut. Cachons ces données aux regards indiscrets :

Prometheus, en rÚgle générale, est placé dans un environnement de service distinct - dans l'exemple, il s'agira d'un espace de noms comme celui-ci :
apiVersion: v1
kind: Namespace
metadata:
labels:
module: prometheus
name: kube-prometheus Champ metadata.labels cela sâest avĂ©rĂ© nâĂȘtre pas un hasard. Comme mentionnĂ© ci-dessus, namespaceSelector (aimer podSelector) fonctionne avec des Ă©tiquettes. Par consĂ©quent, pour permettre aux mĂ©triques d'ĂȘtre extraites de tous les pods sur un port spĂ©cifique, vous devrez ajouter une sorte d'Ă©tiquette (ou en extraire des Ă©tiquettes existantes), puis appliquer une configuration telle que :
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-metrics-prom
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
module: prometheus
ports:
- protocol: TCP
port: 9100Et si vous utilisez les politiques Calico, la syntaxe sera la suivante :
apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
name: allow-metrics-prom
spec:
ingress:
- action: Allow
protocol: TCP
source:
namespaceSelector: module == 'prometheus'
destination:
ports:
- 9100En général, en ajoutant ce type de politiques pour des besoins spécifiques, vous pouvez vous protéger contre les interférences malveillantes ou accidentelles dans le fonctionnement des applications du cluster.
La meilleure pratique, selon les créateurs de Calico, est l'approche « Bloquez tout et ouvrez explicitement ce dont vous avez besoin », documentée dans (d'autres suivent une approche similaire - notamment dans ).
Utilisation d'objets Calico supplémentaires
Permettez-moi de vous rappeler que grĂące Ă l'ensemble Ă©tendu d'API Calico, vous pouvez rĂ©guler la disponibilitĂ© des nĆuds, sans se limiter aux pods. Dans l'exemple suivant, en utilisant GlobalNetworkPolicy la possibilitĂ© de transmettre des requĂȘtes ICMP dans le cluster est fermĂ©e (par exemple, les pings d'un pod vers un nĆud, entre pods ou d'un nĆud vers un pod IP) :
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: block-icmp
spec:
order: 200
selector: all()
types:
- Ingress
- Egress
ingress:
- action: Deny
protocol: ICMP
egress:
- action: Deny
protocol: ICMP Dans le cas ci-dessus, il est toujours possible pour les nĆuds du cluster de « se contacter » via ICMP. Et ce problĂšme est rĂ©solu au moyen GlobalNetworkPolicy, appliquĂ© Ă une entitĂ© HostEndpoint:
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: deny-icmp-kube-02
spec:
selector: "role == 'k8s-node'"
order: 0
ingress:
- action: Allow
protocol: ICMP
egress:
- action: Allow
protocol: ICMP
---
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
name: kube-02-eth0
labels:
role: k8s-node
spec:
interfaceName: eth0
node: kube-02
expectedIPs: ["192.168.2.2"]Le cas du VPN
Enfin, je donnerai un exemple trÚs réel d'utilisation des fonctions Calico dans le cas d'une interaction quasi-cluster, lorsqu'un ensemble standard de politiques ne suffit pas. Pour accéder à l'application Web, les clients utilisent un tunnel VPN, et cet accÚs est étroitement contrÎlé et limité à une liste spécifique de services dont l'utilisation est autorisée :

Les clients se connectent au VPN via le port UDP standard 1194 et, une fois connectés, reçoivent des routes vers les sous-réseaux de cluster de pods et de services. Des sous-réseaux entiers sont poussés afin de ne pas perdre de services lors des redémarrages et des changements d'adresse.
Le port dans la configuration est standard, ce qui impose quelques nuances sur le processus de configuration de l'application et de son transfert vers le cluster Kubernetes. Par exemple, dans le mĂȘme AWS LoadBalancer pour UDP est apparu littĂ©ralement Ă la fin de l'annĂ©e derniĂšre dans une liste limitĂ©e de rĂ©gions, et NodePort ne peut pas ĂȘtre utilisĂ© en raison de son transfert sur tous les nĆuds du cluster et il est impossible de faire Ă©voluer le nombre d'instances de serveur pour Ă des fins de tolĂ©rance aux pannes. De plus, vous devrez modifier la plage de ports par dĂ©faut...
Suite à la recherche de solutions possibles, les solutions suivantes ont été retenues :
- Les pods avec VPN sont planifiĂ©s par nĆud dans
hostNetwork, c'est-à -dire à l'adresse IP réelle. - Le service est affiché à l'extérieur via
ClusterIP. Un port est physiquement installĂ© sur le nĆud, accessible de l'extĂ©rieur avec de lĂ©gĂšres rĂ©serves (prĂ©sence conditionnelle d'une vĂ©ritable adresse IP). - DĂ©terminer le nĆud sur lequel la gousse a poussĂ© dĂ©passe le cadre de notre histoire. Je dirai simplement que vous pouvez « clouer » Ă©troitement le service sur un nĆud ou Ă©crire un petit service side-car qui surveillera l'adresse IP actuelle du service VPN et modifiera les enregistrements DNS enregistrĂ©s auprĂšs des clients - celui qui a assez d'imagination.
Du point de vue du routage, nous pouvons identifier de maniÚre unique un client VPN par son adresse IP émise par le serveur VPN. Vous trouverez ci-dessous un exemple primitif de restriction de l'accÚs d'un tel client aux services, illustré sur le Redis mentionné ci-dessus :
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
name: vpnclient-eth0
labels:
role: vpnclient
environment: production
spec:
interfaceName: "*"
node: kube-02
expectedIPs: ["172.176.176.2"]
---
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: vpn-rules
spec:
selector: "role == 'vpnclient'"
order: 0
applyOnForward: true
preDNAT: true
ingress:
- action: Deny
protocol: TCP
destination:
ports: [6379]
- action: Allow
protocol: UDP
destination:
ports: [53, 67]Ici, la connexion au port 6379 est strictement interdite, mais en mĂȘme temps le fonctionnement du service DNS est prĂ©servĂ©, dont le fonctionnement souffre bien souvent lors de l'Ă©laboration des rĂšgles. Parce que, comme mentionnĂ© prĂ©cĂ©demment, lorsqu'un sĂ©lecteur apparaĂźt, la politique de refus par dĂ©faut lui est appliquĂ©e, sauf indication contraire.
Les résultats de
Ainsi, grùce à l'API avancée de Calico, vous pouvez configurer de maniÚre flexible et modifier dynamiquement le routage dans et autour du cluster. En général, son utilisation peut ressembler à tirer des moineaux avec un canon, et implémenter un réseau L3 avec des tunnels BGP et IP-IP semble monstrueux dans une simple installation Kubernetes sur un réseau plat... Cependant, sinon l'outil semble tout à fait viable et utile .
Il n'est pas toujours possible d'isoler un cluster pour répondre aux exigences de sécurité, et c'est là que Calico (ou une solution similaire) vient à la rescousse. Les exemples donnés dans cet article (avec des modifications mineures) sont utilisés dans plusieurs installations de nos clients dans AWS.
PS
A lire aussi sur notre blog :
- «";
- "Un guide illustré de la mise en réseau dans Kubernetes" : , ;
- «».
Source: habr.com
