Blog | WeScale

Cilium CNI Deep Dive - Usages avancés

Rédigé par Stéphane Trébel | 03/05/2023

À propos

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 :

  • Le maillage de services permis par les politiques réseau de Cilium
  • La sécurisation du trafic réseau, et notamment l’inspection TLS des flux chiffrés
  • L’observabilité avancée offerte par des briques comme Prometheus/Grafana

Maillage de services

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.

Ingress

Référence Documentaire

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.

Gateway API

Référence Documentaire

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).

Gestion du trafic L7

Référence Documentaire

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 :

  • De l’équilibrage de charge entre services :
- "@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
  • De la réécriture de chemin lors des appels HTTP :
regex_rewrite:
    pattern:
        google_re2: { }
        regex: "^/foo.*$"
  • De toute autre fonctionnalité issue de plugins purement Envoy et qui serait activée au travers de la configuration fournie par Cilium au travers des CiliumEnvoyConfig et CiliumClusterwideEnvoyConfig :
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.

Sécurité du trafic

Contrôle du trafic entrant/sortant au niveau L7

Référence Documentaire

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 ! 😅

L’inspection des connexions TLS

Référence Documentaire

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 :

  • Intercepter la requête nouvellement créée par un endpoint Cilium. Cette requête sera générée en s’appuyant sur une autorité de certificat interne au cluster (et reconnue par Cilium).
  • Effectuer la terminaison TLS (et donc accéder au contenu de la requête)
  • Faire ce qu’il a à faire en termes de politique réseau, etc.
  • Recréer une nouvelle requête avec TLS en utilisant les autorités publiques de certificat (ou du moins celles approuvées pour un trafic en sortie de cluster dans votre organisation)
  • Émettre la requête HTTPS recréée vers la cible initiale, qui fera sa terminaison classiquement

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.

Observabilité à l’aide de Prometheus et Grafana

Référence Documentaire

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 :

  • des métriques internes à Cilium :

Métriques de base Cilium

  • des métriques spécifiques aux politiques réseau déployées :

Métriques des politiques réseau Cilium

  • Et bien d’autres métriques ! Sur les endpoints, sur l’eBPF lui-même, etc.

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 😉.

En conclusion

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 ! 💪