Cet article est le troisième et dernier d’une série qui consiste à plonger en profondeur dans Cilium, l’outil novateur de gestion de réseau d’un cluster Kubernetes. Les deux premiers articles nous ont permis d’installer Cilium, de paramétrer Hubble et de configurer des entités fondamentales de Cilium.
Nous allons cette fois-ci aller encore un peu plus loin en abordant :
Cilium permet d’adresser un certain nombre de cas d’usage qui relèvent de ce qu’on appelle le maillage de services (« Service Mesh »). Nous allons voir dans cette section ce qu’il en est, comment configurer tout ça dans Cilium, et l’intérêt de gérer ces éléments à travers l’eBPF de Cilium.
Cilium peut servir d'Ingress Controller, et donc de gestionnaire d'Ingress s’il a été configuré comme tel (la documentation détaille les différents paramètres à activer selon vos contraintes).
Déployer les capacités de Cilium en tant qu’Ingress Controller permet de définir une Ingress comme avec n’importe quel autre Ingress Controller. Il suffit d’indiquer dans la spécification ingressClassName: cilium pour se reposer sur Cilium.
Pour aller plus loin que la simple Ingress, Cilium est également capable de satisfaire à la relativement nouvelle Gateway API.
La Kubernetes Gateway API est une spécification de différentes ressources Kubernetes permettant d’étendre les possibilités d’un cluster en proposant une interface qui peut être implémentée différemment par les différents fournisseurs de services K8s, comme Cilium.
C’est donc un standard à respecter pour permettre une interopérabilité facilitée des services externes avec un cluster Kubernetes.
Une fois le service activé dans le cluster et dans le Chart Helm de Cilium (--set gatewayAPI.enabled=true) il est alors possible de créer des ressources satisfaisant donc à la spécification Gateway API (Gateway, HTTPRoute, etc.) :
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: tls-gateway
spec:
gatewayClassName: cilium
listeners:
- name: https-1
protocol: HTTPS
port: 443
hostname: "bookinfo.cilium.rocks"
tls:
certificateRefs:
- kind: Secret
name: demo-cert
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: https-app-route-1
spec:
parentRefs:
- name: tls-gateway
hostnames:
- "bookinfo.cilium.rocks"
rules:
- matches:
- path:
type: PathPrefix
value: /details
backendRefs:
- name: details
port: 9080
Ainsi, la Gateway nouvellement créée va générer le Service de type LoadBalancer correspondant, qui va donc à son tour aller piocher une adresse IP dans un pool configuré (cf. notre article précédent qui traite de ce sujet).
De cette manière, chaque requête HTTPS à destination du FQDN bookinfo.cilium.rocks et vers le chemin /details verra sa terminaison TLS effectuée en entrée de la Gateway et le trafic déchiffré automatiquement transmis vers le backend details sur son port 9080. C’est exactement le genre de chose qu’on retrouvait sur les Ingress et qui est maintenant servi par le couple Gateway/HTTPRoute,et ce avec davantage de configurations possibles (autres protocoles que HTTP, par exemple).
Cilium, grâce à une utilisation sous-jacente d’Envoy est capable d’implémenter des fonctionnalités de maillage de services avancés, notamment la gestion du trafic au niveau de la couche L7 du modèle OSI.
Cela signifie qu’il est possible de définir des configurations d’Envoy que ce soit à l’intérieur d’un namespace avec les CiliumEnvoyConfig (raccourci : cec) ou carrément au niveau global avec les CiliumClusterwideEnvoyConfig (raccourci : ccec).
En définissant une de ces configurations, et en activant les fonctionnalités concernées au sein de Cilium (--set-string extraConfig.enable-envoy-config=true et --set loadBalancer.l7.backend=envoy), il devient possible de créer un proxy Envoy qui va permettre :
- "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
name: lb_route
virtual_hosts:
- name: "lb_route"
domains: [ "*" ]
routes:
- match:
prefix: "/"
route:
weighted_clusters:
clusters:
- name: "default/echo-service-1"
weight: 50
- name: "default/echo-service-2"
weight: 50
regex_rewrite:
pattern:
google_re2: { }
regex: "^/foo.*$"
apiVersion: cilium.io/v2
kind: CiliumClusterwideEnvoyConfig
metadata:
name: envoy-lb-listener
spec:
services:
<snip>
resources:
- "@type": type.googleapis.com/envoy.config.listener.v3.Listener
<snip>
- "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
<snip>
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
<snip>
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
<snip>
Sur ces différents points, Cilium n’essaie donc pas de réinventer la roue et passe le relai au proxy le plus connu des environnements Kubernetes.
La sécurité est un sujet capital de toutes les architectures logicielles. Cilium doit en conséquence, en tant que CNI, être en mesure de nous offrir un niveau de sécurité élevé sans devoir sacrifier à une expérience de configuration simple et expressive.
C’est pourquoi la sécurité se traduit avant tout par des politiques réseaux que nous avons déjà croisées avec les CiliumNetworkPolicy, qui peuvent permettre de restreindre le trafic entrant à partir de sources pré-approuvées :
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "fqdn"
spec:
endpointSelector:
matchLabels:
org: myorg
egress:
- toFQDNs:
- matchName: "secure.my-corp.com"
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
"k8s:k8s-app": kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"
Ici, on restreint le trafic sortant vers les endpoints (au sens Cilium) étiquetés org=myorg au seul FQDN secure.my-corp.com. Aucun autre trafic ne pourra partir de ces endpoints vers un autre FQDN à l’extérieur (car —rappelons le— dès qu’on applique une politique de trafic réseau sur la sortie/egress, l’endpoint passe de Default Allow à Default Deny).
La section concernant kube-system/kubedns et le port 53 est nécessaire afin que Cilium s’assure que les pods de l’endpoint passent par une requête DNS. Comme évoqué ci-dessus, dès qu’on appliquera cette politique, l’endpoint va passer en Default Deny. Ceci implique donc qu’il faut explicitement indiquer à Cilium que l’endpoint a « le droit » d’envoyer des requêtes DNS, sinon la règle de limitation par FQDN n’aurait pas de sens : la source ne serait même pas capable de faire une résolution DNS ! 😅
Le chiffrement des données à l’aide de TLS est aujourd’hui omniprésent dans nos échanges, à travers notamment HTTPS. Les garanties de sécurité offertes par ce genre de protocole sont un vrai facteur de sûreté au quotidien des données parfois précieuses qui nous sont confiées.
On pourrait croire que le chiffrement serait en opposition avec une gestion saine du trafic et son observabilité, car il rend les connexions opaques, mais nous allons voir ici qu’il n’en est rien, grâce au mécanisme de l'inspection TLS qui se rapproche beaucoup —dans son principe, évidemment— d’une « attaque » Man In The Middle orchestrée par Cilium.
C'est-à-dire qu’au lieu d’avoir un flux d’egress classique :
Schéma d'un pod k8s émettant une requête HTTPS, interceptée puis déchiffrée par Cilium, qui ensuite en recrée une nouvelle
Dit autrement, Cilium va être capable de :
Une fois que tout ça est bien configuré, on peut voir dans le log de Cilium les requêtes HTTP passer d’un endpoint à un autre :
▶ kubectl -n kube-system exec ds/cilium -- cilium monitor -t l7
Listening for events on 16 CPUs with 64x4096 of shared memory
-> Request http from 721 ([<snip>]) to 0 ([<snip>]), identity 24825->16777232, verdict Forwarded GET https://artii.herokuapp.com/fonts_list => 0
-> Response http to 721 ([<snip>]) from 0 ([<snip>]), identity 16777232->24825, verdict Forwarded GET https://artii.herokuapp.com/fonts_list => 404
Il va sans dire que cette méthode n’est pas sans inconvénients ni sans risques. Notons par exemple le fait de devoir gérer les certificats/CA dans les pods clients, de devoir gérer les secrets Kubernetes concernés, etc.
On ne présente plus ni Prometheus ni Grafana, le couple le plus connu dans le milieu de l’observabilité.
Cilium, là aussi, va s’appuyer sur ces solutions standards du marché pour offrir une observabilité de pointe en exposant une large quantité de métriques, récupérées par Prometheus pour ensuite être consultées dans des tableaux de bord Grafana.
La liste des métriques est effectivement assez longue, car elle recouvre :
Métriques de base Cilium
Métriques des politiques réseau Cilium
Nul doute que vous serez à même de définir des tableaux de bord taillés pour votre besoin avec une telle armada de données 😉.
Cilium, en adoptant le paradigme de l’eBPF, a su se donner tous les moyens d’avoir une vue en profondeur de la couche réseau d’un cluster Kubernetes et adresse ainsi une multitude de cas d’usage auxquels nous pouvons être confrontés au quotidien.
Nous avons pu voir, au cours de ces trois articles, que c’est un outil très puissant qui peut parfois sembler un peu vertigineux au premier abord. Mais c’est sans compter une expérience d’utilisation qui privilégie la simplicité et n’hésite pas à se reposer sur d’autres standards du marché afin de se concentrer sur sa valeur cardinale : gérer des politiques réseaux, simplement, et puissamment !
Merci de nous avoir suivis pendant toute la durée de cette « immersion » dans Cilium. Nous espérons que vous avez apprécié cette visite. N’hésitez pas à nous partager vos exemples d’utilisation de Cilium pour voir ce que vous en avez retenu ! 💪