Étape 8: Mise en place de l'Ingress Controller (Traefik)

Introduction

Pour finaliser le cluster Kubernetes, nous allons y déployer un Ingress Controller.

De base, K8S propose l’objet Ingress dont le rôle est de gérer les accès externes aux services hébergés au sein du cluster. Il définit les règles de routage pour diriger le trafic vers les services appropriés en fonction du chemin de l’URL appelée ou d’autres paramètres.

L’Ingress Controller va permettre la surveillance de ces objets et va les exploiter pour fournir un service de reverse proxy.

Il existe plusieurs Ingress Controller, chacun apportant son lot de fonctionnalités. On peut citer NGINX, ou encore HAProxy que nous utilisons déjà en tant que load balancer externe.

Pour cet article, c’est Traefik que l’on va retenir.

Conçu par Émile Vaugue, fondateur de la société française "Containous" devenu "Traefik Labs", le projet a été initié en 2015 pour répondre aux besoins de gestion du trafic dans des environnements dynamiques, notamment les conteneurs.

Disponible dans une version gratuite et à l’heure de cet article dans sa troisième version, Traefik connait une adoption grandissante et s’intègre parfaitement dans une architecture Kubernetes.

Traefik se déploie sous forme de conteneurs directement sur le cluster Kubernetes.

Il est important de rappeler que dans notre cas, nous allons avoir besoin de déployer deux instances constituées de plusieurs itérations de Traefik. Une dédiée aux applications exposées dans le LAN, l’autre pour les applications exposées en DMZ.

En effet, l’architecture que je vous propose exploite les capacités de segmentation logique d’un cluster Kubernetes pour héberger au sein d’un même cluster des applications destinées à être accessibles à travers deux zones réseau.

N’oubliez pas qu’on n’a précédemment déployé deux serveurs HAproxy externes, le premier prdk8snlb501 configuré pour renvoyer vers les control planes, et prdk8snlb511 configuré pour renvoyer vers les nodes en DMZ.

Comme K8S autorise plusieurs class d’Ingress Controller, nous allons tous simplement installer une classe traefik-lan et une classe traefik-dmz.

Chacune de ces classes dispose de son propre compte de service.

C’est ainsi que Traefik dialogue avec l’API de K8S , surveille les objets Ingress provisionnés et applique les règles de routage en conséquence.

Résumé de l'infrastructure à déployer

Cliquez sur l'image pour l'agrandir.

Enfin, il est important de noter que si Traefik peut se contenter des objets Ingress basiques, il fournit également ses propres objets qu’il est possible d’installer ou pas.

Ces objets additionnels étendent l’API Kubernetes et permettent à Traefik de proposer des fonctions supplémentaires, comme la réécriture d’URL ou la protection d’accès par mot de passe.

S’il est souvent conseillé de rester au plus proche des objets natifs de l’API, pour s’assurer une réversibilité maximum, nous aurons également à traiter avec les objets Traefik pour certains besoins.

Installation

Avant de démarrer, on s’assure toujours de la santé du cluster avec la commande:

kubectl get pod
Controle des nodes

Cliquez sur l'image pour l'agrandir.

Si tous les nœuds sont opérationnels, on peut poursuivre.

Déploiement de l'instance LAN

En prérequis, nous allons labéliser nos control plane avec l’étiquette traefik-lan=yes. Ceci afin de permettre le déploiement des pods Traefiks dédié au LAN automatiquement et seulement sur ces deniers.

kubectl label nodes prdk8sctp501 traefik-lan=yes
kubectl label nodes prdk8sctp502 traefik-lan=yes
kubectl label nodes prdk8sctp503 traefik-lan=yes
Labélisation des control plane pour le support de traefik en LAN

Cliquez sur l'image pour l'agrandir.

Il est d’usage de ne pas donner plus de rôle qu’ils n’ont déjà dans un cluster aux Control Plane.

Mais pour une petite infrastructure comme la mienne, ne pas optimiser les ressources attribuées à ces nodes serait dommage.

Comme affiché lors de la définition de l’architecture, les control plane serviront également de points d’entrée pour l’instance Traefik en LAN (et pas en DMZ bien sûr).

On poursuit avec la déclaration d’un namespace dédié.

kubectl create ns inf-traefik-lan
Création du namespace

Cliquez sur l'image pour l'agrandir.

Puis on y inscrit le compte de service réservé à cette instance de Traefik.

Pour cela on se place dans le dossier ~/kub.coolcorp.priv/traefik du premier control plane créé lors du lancement du playbook Ansible et dans lequel ont été provisionnés tous les fichiers de configuration de Traefik.

Emplacement des fichiers

Cliquez sur l'image pour l'agrandir.

Le compte de service est décrit dans le fichier svc-traefik-lan.yaml:

  
  apiVersion: v1
  kind: ServiceAccount
  metadata:
    name: "svc-prd-traefik"
    namespace: "inf-traefik-lan"
    

C’est très basique, on n’a besoin de préciser que très peu de choses en dehors du nom du compte de service svc-prd-traefik.

On applique le compte avec la commande:

kubectl apply -f svc-traefik-lan.yaml
Création du compte de service pour le LAN

Cliquez sur l'image pour l'agrandir.

Ensuite, on enregistre le repo Helm de Traefik, puisque c’est de de cette manière qu’on va installer le produit.

Pour rappel Helm est un gestionnaire de package pour Kubernetes, on n’a déployé le binaire Helm automatiquement avec Ansible.

Les commandes à passer sont les suivantes:

helm repo add traefik https://helm.traefik.io/traefik
helm repo update
Ajout du repo helm Traefik

Cliquez sur l'image pour l'agrandir.

C’est maintenant que l’on procède à l’installation de l’instance Traefik du LAN avec la commande:

helm install --namespace=inf-traefik-lan traefik-lan traefik/traefik -f traefik-config-lan.yaml --set nodeSelector.traefik-lan=yes --set tolerations[0].key=node-role.kubernetes.io/control-plane --set tolerations[0].operator=Exists --set tolerations[0].effect=NoSchedule
Installation de Traefik en LAN

Cliquez sur l'image pour l'agrandir.

La commande permet de préciser le namespace et le fichier de configuration traefik-config-lan.yaml à utiliser pour surcharger les paramètres par défaut.

Le reste des arguments permet le déploiement sur les control plane. L’usage du label n’est pas suffisant, car par défaut les control plane sont marqués avec certaines caractéristiques qui vont devoir être tolérées par les pods pour s’exécuter sur les serveurs associés.

Le contenu du fichier traefik-config-lan.yaml est le suivant:

 
image:
  name: traefik
  tag: "3.0.1"
  pullPolicy: IfNotPresent
deployment:
  enabled: true
  kind: DaemonSet
  labels:
    network: lan
  podLabels:
    network: lan
providers:
  kubernetesIngress:
    labelSelector: network=lan
  kubernetesCRD:
    enabled: true
    allowCrossNamespace: false
    allowExternalNameServices: false
    allowEmptyServices: false
    namespaces:
       - inf-traefik-lan
serviceAccount:
  name: "svc-prd-traefik"
logs:
    level: ERROR
hostNetwork: true
ports:
  web:
    port: 80
    ports.web.redirectTo.port: websecure
  websecure:
    port: 443
securityContext:
  capabilities:
    drop: [ALL]
    add: [NET_BIND_SERVICE]
  readOnlyRootFilesystem: true
  runAsGroup: 0
  runAsNonRoot: false
  runAsUser: 0
updateStrategy:
  rollingUpdate:
    maxSurge: 0
    maxUnavailable: 1
  type: RollingUpdate
additionalArguments:
  - "--providers.kubernetesingress.ingressclass=traefik-lan"
  - "--serversTransport.insecureSkipVerify=true"

Rappelez vous que son contenu a été généré depuis un template via Ansible.

Je ne vais pas détailler chaque ligne, mais certains points sont importants à comprendre.

  • tag: "3.0.1": C’est la version de Traefik à déployer

  • kind: DaemonSet: Un DeamonSet est un type de Deployment spécifique. Cela correspond pour le cluster à exécuter une instance d’un pod sur chaque node qui répond aux caractéristiques demandées. Dans notre cas, un control plane. Cela permet d’ajouter automatiquement un pod Traefik sur un nouveau control plane qui serait ajouté au cluster par la suite.

  • labelSelector: network=lan: C’est ici qu’on indique à traefik de considérer tout objet de type Ingress qui sera labélisé network=lan. Si ce n’est pas le cas, il l’ignorera.

  • kubernetesCRD: enabled: true: CRD pour Custom Ressource Definition. On active l’usage des objets additionnels de Traefik en demandant à les inscrire dans l’API K8S.

  • name: "svc-prd-traefik": Le nom du compte de service à utiliser que l'on a déployé précédemment.

  • hostNetwork: true: Indique qu’on va mapper Traefik au réseau du serveur. Les pods Traefik sont effectivement, hors exceptions, les seuls pod à pouvoir être exposés directement à travers les ports du serveur.

  •  
          ports:
          web:
            port: 80
            ports.web.redirectTo.port: websecure
          websecure:
            port: 443
          

    Ces lignes sont importantes. Elles définissent tous les points d’entrée sur lesquels Traefik doit écouter. Ici le point web sur le port 80 et le point websecure sur le port 443. On n’en profite pour mettre une redirection automatique du port 80 vers le port 443 via l’option ports.web.redirectTo.port: websecure

    Taefik n’est pas limité au port HTTP et HTTPS, il peut également travailler sur des ports plus exotiques et sur la couche trois du modèle OSI. On peut donc ajouter autant de ports que l’on souhaite et les nommer à notre convenance (web et websecure ont été choisis de manière totalement arbitraire, mais c’est souvent ainsi que vous les retrouverez dans les exemples Traefik)

  • - "--providers.kubernetesingress.ingressclass=traefik-lan": Permet de nommer la classe d’Ingress Controller rattachée à cette instance Traefik.

Si tout va bien, on peut vérifier le statut du déploiement avec la commande:

kubectl get pod -n inf-traefik-lan -o wide
Controle du déploiement

Cliquez sur l'image pour l'agrandir.

Chaque control plane dispose d’un pod Traefik (déploiement via DaemonSet).

Avant de passer à la DMZ, on va ajouter à Traefik la capacité d’exposer un dashboard de suivi.

C’est une option bien pratique. Le dashboard ne permet aucune action, mais affiche le statut de Traefik et surtout décrit le routage proposé par ce dernier.

Pour des raisons de sécurité, on va souhaiter protéger l’accès au dashboard par un mot de passe. Ce qui va nous permettre d’exploiter un premier objet custom de Traefik : le Middelware.

celui-ci va se baser sur un objet Secret contenant un login/mot de passe au format apache pour demander une authentification avant l’affichage de la page.

Vous pouvez utiliser l’outil htpasswd pour générer le nom d’utilisateur et le password.

Par exemple en passant par votre instance WSL et votre terminal Windows.

Sinon il existe des sites en ligne pour cela (mais attention ce n’est jamais bon d’utiliser un site web pour générer des password critiques…sans être parano, vous n’êtes jamais certains de l’usage qui en ai fait derrière).

La commande de l’outil est:

htpasswd -Bbn vote_login votre_password
Génération des informations d'authentification avec htpasswd

Cliquez sur l'image pour l'agrandir.

Il suffit de placer le contenu de la sortie de cette commande dans un fichier sec-traefik-dashboard-lan-password au niveau du premier control plane.

Génération des informations d'authentification avec htpasswd

Cliquez sur l'image pour l'agrandir.

Puis de lancer la commande suivante pour générer un Secret à partir du fichier.

kubectl create secret generic sec-traefik-dashboard-lan-password --from-file=traefik-dashboard-lan-password --namespace inf-traefik-lan
Génération du secret

Cliquez sur l'image pour l'agrandir.

Il est conseillé de supprimer le fichier ensuite.

Le dashboard étant accessible via HTTPS, il est également nécessaire de lui associer un certificat.

Plusieurs démarches sont possibles, et tout va dépendre de ce que vous avez à disposition pour générer des certificats.

Me concernant, j’ai ma propre PKI interne (sous Windows) intégrée à mon domaine, qui me permet de générer des certificats reconnus sur l’ensemble de mes devices internes.

Vous pouvez vous inspirer de ce que je propose et l’adapter à vos usages, l’idée n’étant pas ici de faire un tutoriel sur les certificats.

Mais dans l’ensemble j’utilise la logique standard:

  • 1 : génération d’une clef privée
  • 2 : génération d’une demande de certificat (un csr pour Certificate Siging Request) incluant le hostname et nom du DNS qui devra être rattaché à mon certificat.
  • 3 : je soumets mon certificat à une PKI (ici une PKI Windows).
  • 4 : j’obtiens un certificat.

Depuis mon instance WSL sur mon poste de travail, je génère ma clef privée (sur 4096 bits) et ma demande de certificat (un csr), à l’aide de openssl.

openssl req -new -nodes -sha256 -keyout traefik-k8s-lan.coolcorp.priv.key -out traefik-k8s-lan.coolcorp.priv.csr -newkey rsa:4096 -subj "/C=FR/ST=Ile-de-France/L=Paris/O=OFI/OU=Infrastructure/CN=traefik-k8s-lan.coolcorp.priv" -reqexts SAN -config <(printf "[req]\ndistinguished_name = req_distinguished_name\n[req_distinguished_name]\n[SAN]\nsubjectAltName=DNS:traefik-k8s-lan.coolcorp.priv")
Usage de WSL

Cliquez sur l'image pour l'agrandir.

Génération de la clef et du csr

Cliquez sur l'image pour l'agrandir.

Je copie mon csr sur ma PKI et je demande à cette dernière l’obtention d’un certificat calqué sur un template TPL-SRV-WEB-DEFAULT (regroupe les caractéristiques d’un certificat utilisé par un service web)

Copie du CSR sur la PKI

Cliquez sur l'image pour l'agrandir.

certreq -attrib "CertificateTemplate:TPL-SRV-WEB-DEFAULT" -submit traefik-k8s-lan.coolcorp.priv.csr
Génération du certificat à partir du CSR

Cliquez sur l'image pour l'agrandir.

J’obtiens à la fin un certificat et une clef privée.

Ensemble des fichiers clef et certificat

Cliquez sur l'image pour l'agrandir.

Ces deux composants vont être copiés sur le premier control plane (leur contenu est copiable sans difficulté, c’est du texte).

Recopie du contenu des fichiers associés à la clef et au certificat

Cliquez sur l'image pour l'agrandir.

Recopie du contenu des fichiers associés à la clef et au certificat

Cliquez sur l'image pour l'agrandir.

Ces deux fichiers vont servir à créer un autre Secret.

kubectl create secret generic sec-certif-traefik-k8s-lan --from-file=tls.crt=traefik-k8s-lan.coolcorp.priv.cer --from-file=tls.key=traefik-k8s-lan.coolcorp.priv.key --namespace inf-traefik-lan
Génération du secret qui va stocker les informations du certificat (et la clef privée)

Cliquez sur l'image pour l'agrandir.

Maintenant qu’on dispose d’un secret sec-traefik-dashboard-lan-password contenant les informations d’authentification pour l’accès au dashboardet d’un secret sec-certif-traefik-k8s-lan pour le certificat, on peut lancer la commande suivante:

kubectl apply -f traefik-lan-dashboard.yaml
Application du yaml associé au dashboard

Cliquez sur l'image pour l'agrandir.

On se base sur le fichier traefik-lan-dashboard.yaml dont le contenu est le suivant:

 
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-lan-dashboard
  namespace: "inf-traefik-lan"
spec:
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(`traefik-k8s-lan.coolcorp.priv`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
      services:
        - name: api@internal
          kind: TraefikService
      middlewares:
        - name: traefik-dashboard-lan-auth # Referencing the BasicAuth middleware
          namespace: inf-traefik-lan
  tls:
     secretName: sec-certif-traefik-k8s-lan
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: traefik-dashboard-lan-auth
  namespace: inf-traefik-lan
spec:
  basicAuth:
    secret: sec-traefik-dashboard-lan-password
  

On y retrouve deux objets. Tous les deux propres à Traefik et ajoutés à l’API K8S dans la branche traefik.io/v1alpha1

Le premier est nommé IngressRoute. C’est un peu un Ingress amélioré. On y’ retrouve le point d’entrée attendu (websecure) et l’URL associée (traefik-k8s-lan.coolcorp.priv) avec l’obligation d’y ajouter le préfix /api ou /dashboard.

Si une requête venant de l’extérieur répond à ces règles, alors elle sera redirigée vers un service interne à Traefik api@internal, soit le dashboard.

On n’y trouve également la référence au middleware pour l’accès sécurisé ainsi qu’au secret sec-certif-traefik-k8s-lan dans la section TLS pour l’association du certificat.

Le second objet est le fameux Middleware lui-même référencé dans l’objet précédent. Il prend simplement comme entrée le secret contenant le login/password.

Si on résume, lorsqu’on va taper https://traefik-k8s-lan.coolcorp.priv/dashboard/ on va arriver sur l’entrée websecure, et l’URL va matcher avec la règle de routage.

Traefik va y appliquer le certificat contenu dans le secret sec-certif-traefik-k8s-lan, mais avant d’exposer le dashboard, il va d’abord faire appel au Middleware qui lui, va demander une authentification.

Si l’authentification correspond aux informations de connexion présente dans le secret sec-traefik-dashboard-lan-password, alors, la requête est redirigée vers le service api@internal.

Voyons si cela se produit réellement. Bien entendu avant, il faudra s’assurer d’avoir un alias DNS qui renvoie le nom traefik-k8s-lan.coolcorp.priv vers le loabalancer HAProxy du LAN (prdk8snlb501), qui lui-même relayera vers l’un ou l’autre des control plane pour être ensuite intercepté par l’instance Traefik qui s’y exécute.

Controle de l'enregistrement DNS (ici sur un DNS Server windows)

Cliquez sur l'image pour l'agrandir.

Pour rappel HAproxy a été configuré lors d'une étape précédente

On n’a bien un prompt d’authentification, et lorsque qu’on rentre le bon login et le bon password, on n’arrive bien sur le dashboard Traefik.

Prompt d'authentification

Cliquez sur l'image pour l'agrandir.

Dashboard Traefik

Cliquez sur l'image pour l'agrandir.

Si on descend un peu dans l’affichage, on retrouve bien l’activation des CRD dans la liste des providers, en plus des objets Ingress natif (KubernetesIngress). Traefik va donc pouvoir s'appuyer sur les deux types d'objets, natifs et custom pour gérer le routage. On n’aura l’occasion de parcourir davantage ce dashboard un peu plus loin dans l’article.

Liste des providers activés

Cliquez sur l'image pour l'agrandir.

Déploiement de l'instance DMZ

Concernant l’installation de l’instance en DMZ, on va répéter exactement les mêmes opérations. Je ne vais pas donc forcément redonner le même niveau de détail.

On démarre de la même manière, à savoir labéliser les nodes qui exécuteront les pods traefik.

Je vous invite à revoir la définition de l’infrastructure, mais pour la DMZ ce sont les deux workers qui y sont présents qui vont avoir cette charge.

On les labélise donc de la manière suivante

kubectl label nodes prdk8snod511 traefik-dmz=yes
kubectl label nodes prdk8snod512 traefik-dmz=yes
Labélisation des nodes en DMZ pour supporter l'instance Traefik en DMZ

Cliquez sur l'image pour l'agrandir.

Puis on créer le namespace dédié

kubectl create ns inf-traefik-dmz
Création du namespace

Cliquez sur l'image pour l'agrandir.

Toujours sur le premier control plane dans le dossier ~/kub.coolcorp.priv/traefik on trouve le fichier svc-traefik-dmz.yaml généré durant le playbook Ansible.

Son contenu est le suivant:

 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: "svc-prd-traefik-dmz"
  namespace: "inf-traefik-dmz"

Comme pour le LAN, Traefik va utiliser un compte de service, mais différent de celui créé précédemment.

On applique le yaml avec la commande suivante:

kubectl apply -f svc-traefik-dmz.yaml
Application du compte de service pour la DMZ

Cliquez sur l'image pour l'agrandir.

On passe à l’installation de Traefik, toujours via Helm. Les repos étant déjà ajoutés, il faut utiliser la commande suivante:

helm install --namespace=inf-traefik-dmz traefik-dmz traefik/traefik -f traefik-config-dmz.yaml --set nodeSelector.traefik-dmz=yes --set tolerations[0].key=node-role.kubernetes.io/worker-dmz --set tolerations[0].operator=Exists --set tolerations[0].effect=NoSchedule
Installation de Traefik en DMZ

Cliquez sur l'image pour l'agrandir.

On joue sur la notion de label, pour faire en sorte que cette fois-ci les pods ne s’exécutent plus sur les control plane, mais bien sûr les workers en DMZ.

Le fichier de configuration utilisé pour surcharger les paramètres, et créer via un template au moment de l’exécution d’Ansible, est pratiquement identique à celui utilisé pour le LAN.

 
image:
  name: traefik
  tag: "3.0.1"
  pullPolicy: IfNotPresent
deployment:
  enabled: true
  kind: DaemonSet
  labels:
    network: dmz
  podLabels:
    network: dmz
providers:
  kubernetesIngress:
    labelSelector: network=dmz
  kubernetesCRD:
    enabled: true
    allowCrossNamespace: false
    allowExternalNameServices: false
    allowEmptyServices: false
    namespaces:
       - inf-traefik-dmz
serviceAccount:
  name: "svc-prd-traefik-dmz"
logs:
    level: ERROR
hostNetwork: true
ports:
  web:
    port: 80
    ports.web.redirectTo.port: websecure
  websecure:
    port: 443
securityContext:
  capabilities:
    drop: [ALL]
    add: [NET_BIND_SERVICE]
  readOnlyRootFilesystem: true
  runAsGroup: 0
  runAsNonRoot: false
  runAsUser: 0
updateStrategy:
  rollingUpdate:
    maxSurge: 0
    maxUnavailable: 1
  type: RollingUpdate
additionalArguments:
  - "--providers.kubernetesingress.ingressclass=traefik-dmz"
  - "--serversTransport.insecureSkipVerify=true"

Hormis le namespace d’exécution, le nom du compte de service utilisé et l’usage du mot clef « dmz » pour définir à la fois la classe de l’Ingress Controller et le label des Ingress à contrôler, c’est exactement la même logique que pour le LAN.

On peut vérifier l’installation via la commande suivante:

kubectl get pod -n inf-traefik-dmz -o wide
Controle de l'installation en DMZ

Cliquez sur l'image pour l'agrandir.

Cette fois-ci les pod Traefik sont bien déployés sur les worker en DMZ.

Toujours dans la même logique que précédemment, on va déployer le dashboard.

On commence donc par créer une combinaison login/password avec la commande htpasswd qu’on pourra retrouver sur son installation WSL.

htpasswd -Bbn votre_login votre_password
Génération du login/password

Cliquez sur l'image pour l'agrandir.

On exploite la sortie de la commande pour la recopier sur le premier control plane dans un fichier nommé traefik-dashboard-dmz-password

Récupération de la sortie pour création du fichier

Cliquez sur l'image pour l'agrandir.

Puis on génère le secret à partir de ce fichier via la commande:

kubectl create secret generic sec-traefik-dashboard-dmz-password --from-file=traefik-dashboard-dmz-password --namespace inf-traefik-dmz
Génération du secret pour stocker les informations d'authentification

Cliquez sur l'image pour l'agrandir.

Un nouveau certificat est nécessaire et on rejoue donc la logique de création de clef, de génération d’un CSR et de l’obtention d’un certificat auprès d’une PKI.

openssl req -new -nodes -sha256 -keyout traefik-k8s-dmz.coolcorp.priv.key -out traefik-k8s-dmz.coolcorp.priv.csr -newkey rsa:4096 -subj "/C=FR/ST=Ile-de-France/L=Paris/O=OFI/OU=Infrastructure/CN=traefik-k8s-dmz.coolcorp.priv" -reqexts SAN -config <(printf "[req]\ndistinguished_name = req_distinguished_name\n[req_distinguished_name]\n[SAN]\nsubjectAltName=DNS:traefik-k8s-dmz.coolcorp.priv")
Génération de la clef privée et du CSR

Cliquez sur l'image pour l'agrandir.

certreq -attrib "CertificateTemplate:TPL-SRV-WEB-DEFAULT" -submit traefik-k8s-dmz.coolcorp.priv.csr
Récupération du certificat

Cliquez sur l'image pour l'agrandir.

Clef et certificat

Cliquez sur l'image pour l'agrandir.

La clef privée et le certificat doivent être recopiés sur le premier control plane, puis à partir de ces deux fichiers on génère le secret sec-certif-traefik-k8s-dmz.

Récopie de la clef et du certificat sur le premier control plane

Cliquez sur l'image pour l'agrandir.

Récopie de la clef et du certificat sur le premier control plane

Cliquez sur l'image pour l'agrandir.

kubectl create secret generic sec-certif-traefik-k8s-dmz --from-file=tls.crt=traefik-k8s-dmz.coolcorp.priv.cer --from-file=tls.key=traefik-k8s-dmz.coolcorp.priv.key --namespace inf-traefik-dmz
Génération du secret associé au certificat

Cliquez sur l'image pour l'agrandir.

Il ne reste plus qu’à jouer le yaml traefik-dmz-dashboard.yaml généré par Ansible dans ~/kub.coolcorp.priv/traefik sur le premier control plane pour déployer le dashboard.

kubectl apply -f traefik-dmz-dashboard.yaml
Application du dashboard en DMZ

Cliquez sur l'image pour l'agrandir.

Le contenu est le suivant:

 
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-dmz-dashboard
  namespace: "inf-traefik-dmz"
spec:
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(`traefik-k8s-dmz.coolcorp.priv`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
      services:
        - name: api@internal
          kind: TraefikService
      middlewares:
        - name: traefik-dashboard-dmz-auth # Referencing the BasicAuth middleware
          namespace: inf-traefik-dmz
  tls:
     secretName: sec-certif-traefik-k8s-dmz
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: traefik-dashboard-dmz-auth
  namespace: inf-traefik-dmz
spec:
  basicAuth:
    secret: sec-traefik-dashboard-dmz-password
  

Hormis les références à la DMZ plutôt qu’au LAN, le fichier est exactement le même que pour le LAN.

On peut tester l’accès au dashboard via l’URL https://traefik-k8s-dmz.coolcorp.priv/dashboard/.

Bien entendu, la aussi il faut un alias DNS correctement renseigné, mais qui cette fois ci va pointer l’IP du LoadBalancer HAProxy en DMZ prdk8snlb511 (paramétré dans une étape précédente pour renvoyer les requêtes vers l’un ou l’autre des nœuds en DMZ).

Entrée DNS

Cliquez sur l'image pour l'agrandir.

Comme pour le dashboard en LAN, on n'a d'abord un prompt d'authentification puis ensuite on n'accède au dashboard, mais cette fois-ci propre à l'instance traefik en DMZ.

Prompt d'authentification

Cliquez sur l'image pour l'agrandir.

Accès au dashboard en DMZ

Cliquez sur l'image pour l'agrandir.

Tests

Comme nous l’avons fait lors du déploiement de la couche de storage persistant via le CSI vSphere, nous allons tester le bon fonctionnement de nos opérations avec une application de test.

Toujours comme j’ai l’habitude de le faire, je créer un dossier du nom du namespace que je vais utiliser pour y stocker mes fichiers.

Création du dossier de travail

Cliquez sur l'image pour l'agrandir.

Je créer ce même namespace dev-demotraefik-lan de manière déclarative via la commande:

kubectl create ns dev-demotraefik-lan
Création du namespace pour les tests

Cliquez sur l'image pour l'agrandir.

Le premier fichier YAML est 01-deploy-demotraefik-front.yaml.

 
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: deploy-demotraefik-front
    namespace: dev-demotraefik-lan
    labels:
      environment: dev
      network: lan
      application: demotraefik
  spec:
    strategy:
      type: Recreate
    selector:
      matchLabels:
        environment: dev
        network: lan
        application: demotraefik
    template:
      metadata:
        labels:
          environment: dev
          network: lan
          application: demotraefik
      spec:
        nodeSelector:
          network: lan
        containers:
        - name: demo-nginx-traefik-lan
          image: nginx:1.27
          resources:
            requests:
              memory: "8Mi"
              cpu: "250m"
            limits: 
              memory: "256Mi"
              cpu: 1  
          livenessProbe:
            initialDelaySeconds: 5 
            periodSeconds: 5 
            exec:
             command:
             - ls /usr/share/nginx/html
          ports:
          - containerPort: 80
        

Il permet la création d’un objet Deployment nommé deploy-demotraefik-front qui va lui-même s’occuper de déployer un pod demo-nginx-traefik-lan basé sur l’image nginx 1.27.

Le pod va donc contenir un seul conteneur qui écoute sur le port 80.

Une fois le fichier appliqué avec la commande:

kubectl apply -f 01-deploy-demotraefik-front.yaml
Déploiement de l'application de test

Cliquez sur l'image pour l'agrandir.

On peut vérifier que le déploiement est correcte avec la commande:

kubectl get pod -n dev-demotraefik-lan -o wide
Vérification du déploiement

Cliquez sur l'image pour l'agrandir.

On peut passer au second fichier 02-svc-demotraefik-front.yaml.

---
  kind: Service
  apiVersion: v1
  metadata:
    name: svc-demotraefik-front
    namespace: dev-demotraefik-lan
    labels:
      environment: dev
      network: lan
      application: demotraefik
  spec:
    ports:
      - name: http
        port: 80
        protocol: TCP
        targetPort: 80
    selector:
      environment: dev
      network: lan
      application: demotraefik
    

C’est un objet Service qui y décrit. Je ne rentre pas dans le détail à ce niveau, mais cet objet va permettre d’exposer au sein du cluster, le pod créé précédemment, et donc le conteneur NGINX qui s’y trouve.

Pour cela le service relaye les requêtes qu’il reçoit sur le port 80, vers le port 80 de tout pod qui correspond au label contenu dans la section selector.

Si vous revenez sur le fichier précédent, vous verrez qu’il s’agit des labels positionnés sur le pod rattaché au Deployment.

On exécute la création du service avec la commande:

kubectl apply -f 02-svc-demotraefik-front.yaml
Application du service

Cliquez sur l'image pour l'agrandir.

On controle que le service est bien créé avec la commande:

kubectl get svc -n dev-demotraefik-lan -o wide
Application du service

Cliquez sur l'image pour l'agrandir.

Avant de passer au dernier fichier, on va d’avoir à nouveau générer un certificat. Notre but est de tester l’exposition du conteneur NGINX à l’extérieur du cluster via une URL de test. Il faut donc un certificat si l’on veut exposer l’application en HTTPS.

Je ne réexplique pas la logique, mais comme pour les dashboards en DMZ et en LAN, on génère une clef, avec un CSR, on soumet le CSR à une PKI et on récupère un certificat.

openssl req -new -nodes -sha256 -keyout demotraefik-lan.coolcorp.priv.key -out demotraefik-lan.coolcorp.priv.csr -newkey rsa:4096 -subj "/C=FR/ST=Ile-de-France/L=Paris/O=OFI/OU=Infrastructure/CN=demotraefik-lan.coolcorp.priv" -reqexts SAN -config <(printf "[req]\ndistinguished_name = req_distinguished_name\n[req_distinguished_name]\n[SAN]\nsubjectAltName=DNS:demotraefik-lan.coolcorp.priv")
Génération de la clef privée et du CSR

Cliquez sur l'image pour l'agrandir.

certreq -attrib "CertificateTemplate:TPL-SRV-WEB-DEFAULT" -submit demotraefik-lan.coolcorp.priv.csr
Génération du certificat

Cliquez sur l'image pour l'agrandir.

La clef et le certificat sont copiés sur le premier control plane:

Récupération de la clef et du certificat

Cliquez sur l'image pour l'agrandir.

Récupération de la clef et du certificat

Cliquez sur l'image pour l'agrandir.

On génère le secret associé avec la commande:

kubectl create secret generic sec-certificate-demotraefik-lan --from-file=tls.crt=demotraefik-lan.coolcorp.priv.cer --from-file=tls.key=demotraefik-lan.coolcorp.priv.key --namespace dev-demotraefik-lan
Création du secret pour stocker le certificat

Cliquez sur l'image pour l'agrandir.

On peut maintenant passer au dernier fichier, 03-ing-demotraefik-front.yaml.

Son contenu est le suivant:

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ing-nginxnfs-default
  namespace: dev-demotraefik-lan
  labels:
      environment: dev
      network: lan
      application: demotraefik
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
    ingressClassName: traefik-lan
    traefik.ingress.kubernetes.io/router.tls: "true"
spec:
  ingressClassName: traefik-lan
  tls:
  - hosts:
    - demotraefik-lan.coolcorp.priv
    secretName: sec-certificate-demotraefik-lan
  rules:
  - host: demotraefik-lan.coolcorp.priv
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc-demotraefik-front
            port:
              number: 80
            

Contrairement aux dashboards Traefik, on n’utilise cette fois-ci un objet Ingress natif à l’API K8S.

En effet, il n’y a pas besoin de fonctions avancées comme précédemment et on ne redirige pas vers l’API interne de Traefik.

L’objet Ingress est donc suffisant.

C’est grâce à ce qui figure dans les annotations que Traefik va pouvoir savoir comment se comporter.

Pour le reste du fichier, on reste sur la définition d’un Ingress telle que décrite dans l’API Kubernetes.

On spécifie la classe d’Ingress Controller qu’on souhaite exploiter, ici traefik-lan.

Puis on donne l’URL rattachée, le service à utiliser ainsi que le port vers lequel renvoyer les requêtes.

Dans cet exemple, on n’a donc un Ingress ing-nginxnfs-default faisant appel à l’Ingress Controller traefik-lan qui va permettre de rediriger tous flux reçu avec l’URL demotraefik-lan.coolcorp.priv vers le port 80 du service svc-demotraefik-front en y appliquant le certificat contenu dans le secret sec-certificate-demotraefik-lan.

On applique la configuration avec la commande:

kubectl apply -f 03-ing-demotraefik-front.yaml
Déploiement de l'Ingress

Cliquez sur l'image pour l'agrandir.

Comme toute objet K8S on veut vérifier le statut du déploiement de l'Ingress:

kubectl get ing -n dev-demotraefik-lan
Controle de l'Ingress

Cliquez sur l'image pour l'agrandir.

Vérifions maintenant que le site est accessible. Pour cela, il faut qu’un alias DNS existe afin que l’URL de test renvoi vers l’IP du loadbalancer du LAN, afin que celui-ci relaye sur l’un ou l’autre des contrôles plane pour l’instance Traefik qui s’y trouve puisse traiter la demande.

Vérification de l'entrée DNS

Cliquez sur l'image pour l'agrandir.

Tentons un accès.

On n’a bien un certificat rattaché et reconnu.

Résultat du test

Cliquez sur l'image pour l'agrandir.

Si maintenant on se connecte sur le dashboard Traefik de l’instance du LAN, on va pouvoir retrouver dans le menu HTTP, le détail de notre configuration.

Utilisation du dashboard Traefik pour vérifier le routage

Cliquez sur l'image pour l'agrandir.

Tout le chemin de routage est visible. La présence du bouclier indique qu’on n’est bien en TLS avec un certificat.

Résumé du routage

Cliquez sur l'image pour l'agrandir.

Détail du routage

Cliquez sur l'image pour l'agrandir.

Notez que cet exemple tire parti du Traefik en LAN, mais on pourrait très bien appliquer la même chose pour la DMZ, il suffirait juste de changer les références à traefik-lan vers traefik-dmz .

On pourrait même se passer dans ce cas du certificat, car en DMZ, l’applicatif est accessible depuis le WEB.

Ce qui implique qu’on peut utiliser des services externes de demandes automatiques de certificat basésur le protocole ACME et supporté par Traefik, comme Let’s encrypt par exemple.

Dans ce cas, tout se fait automatiquement, mais nous verrons ce point dans un autre article quand nous détaillerons vraiment le déploiement d’une application. (Et finalement même en LAN, on pourrait arriver à ce niveau d’automatisation…mais on verra ça plus tard).

Conclusion

Nous voila arrivé à la fin de cet article. J’espère qu’il vous aura permis de comprendre comment déployer un Ingress Controller, principalement Traefik.

L’idée était aussi de vous montrer comment exploiter à la fois un objet Ingress classique et des objets customisés ajoutés à l’API K8S.

Encore une fois, rester au plus proche des objets de base est une bonne pratique, mais parfois, on n’est obligé d’étendre l’API pour bénéficier de fonctionnalités avancées.

À noter que les objets Ingress tendent à être remplacés par des objets Gateway qui peuvent reprendre des fonctions justement assurées par des objets custom de Traefik. À la date de cet article, je n’ai pas suffisamment d’expérience pour en parler davantage, mais je ne manquerais pas d’écrire sur le sujet une fois que j’en saurais un peu plus de mon côté.

Par contre on peut maintenant dire que le cluster est pleinement opérationnel et capable de répondre à de nombreux usages.

Il ne reste plus qu’à déployer une véritable application…mais ça, c’est pour l’article final de ce Cookbook. Mais avant, je vous propose de parler Certificat.