Logo de Béjean Développement

Déployer Traefik Proxy dans Kubernetes derrière Cloudflare en TLS Strict

Objectif

Dans cet article, nous allons voir comment installer, sous Kubernetes, Traefik Proxy 2.8 et comment le paramétrer pour qu'il fonctionne correctement avec CloudFlare en mode TLS Strict.

Préambule

Pour les besoins de l'article, j'ai déployé un cluster Kubernetes derrière un Load Balancer HaProxy. Ce point est une "contrainte" de mon infrastructure. Le trafic est donc transmis depuis Cloudflare au HaProxy pour être ensuite redirigé sur Traefik Proxy. J'utilise également MetalLB au sein de mon cluster Kubernetes dans le but d'exposer mes services sur mes différents nœuds.

Tutoriel

Avant de commencer, nous allons voir ensemble la configuration de Cloudflare. J'ai utilisé un compte gratuit pour réaliser le déploiement. Il faut faudra un enregistrement DNS proxied, un certificat Origin Server. Pensez à bien enregistrer la clé et à télécharger le fichier PEM car nous en aurons besoin pour la suite.

Configuration Cloudflare

La configuration de CloudFlare est assez rapide malgré qu'il y ait beaucoup de paramètres. Je vais donc vous indiquer les principaux. J'ai activé, dans la rubrique SSL/TLS -> Edge Certificates : Always Use HTTPS, TLS 1.3, Automatic HTTPS Rewrites et la version minimum de TLS est 1.2.

Aussi, j'ai également activé le mode Full (strict) dans la rubrique SSL/TLS -> Overview

Installation de Traefik

Pour installer simplement Traefik dans Kubernetes, nous allons utiliser Helm. Si ce n'est pas déjà fait, il faut ajouter le chart Traefik dans Helm, puis nous allons mettre à jour la base de données de Helm :

helm repo add traefik https://helm.traefik.io/traefik
helm repo update

Nous allons installer Traefik, dans le namespace default en utilisant la commande ci-dessous :

helm install traefik traefik/traefik

Si vous souhaitez déployer Traefik dans un namespace spécifique, il faut ajouter --namespace=traefik --create-namespace :

helm install traefik traefik/traefik --namespace=traefik --create-namespace

Pour contrôler que l'installation a réussie, vous pouvez lancer la commande kubectl get deploy -n traefik. Vous devriez avoir le résultat suivant :

NAME      READY   UP-TO-DATE   AVAILABLE   AGE
traefik   1/1     1            1           2m

Vous pouvez ajouter l'option -o wide pour obtenir plus de détail :

NAME      READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES          SELECTOR
traefik   1/1     1            1           2m   traefik      traefik:2.8.0   app.kubernetes.io/instance=traefik,app.kubernetes.io/name=traefik

Activation du dashboard

Par défaut, le dashboard n'est pas actif. Vous pouvez activer le dashboard en déployant une IngressRoute :

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: dashboard
  namespace: traefik
spec:
  entryPoints:
    - web
  routes:
    - match: (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
      kind: Rule
      services:
        - name: api@internal
          kind: TraefikService

Une fois déployé, avec la commande kubectl apply -f dashboard.yaml. Avant d'accéder au dashboard, il faut, avec la commande ci-dessous, récupérer l'adresse IP fournie par MetalLb et utilisée par Traefik :

kubectl get svc -n traefik

De mon côté, l'adresse IP est 172.16.0.1. Vous pouvez ouvrir votre navigateur et afficher le dashboard en ouvrant la page http://172.16.0.1/dashboard/. Le caractère / est très important à la fin, sans cela, vous aurez une page affichant 404 page not found.

Déploiement de Nginx

Pour les besoins du test, nous allons déployer Nginx dans un namespace spécifique et avec un service de type ClusterIP, pur ce faire, créer un fichier nommé nginx.yaml et coller le contenu ci-dessous :

apiVersion: v1
kind: Namespace
metadata:
  name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.21
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: nginx
spec:
  type: ClusterIP
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80

Puis, lancer la commande pour le déploiement :

kubectl apply -f nginx.yaml

Maintenant que nous avons déployé Traefik Proxy et Nginx, nous avons besoin de déployer une IngressRoute dans le but de router le trafic en direction du déploiement Nginx depuis Traefik et suivant un host défini, pour cela, nous allons ajouter le contenu ci-dessous dans le fichier ingress-nginx.yaml que nous allons créer :

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress-http
  namespace: nginx
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
  rules:
    - host: <votre-nom-de-domaine>
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx
                port:
                  number: 80

Dans ce fichier, nous définissons une annotation pour indiquer le point d'entrée que Traefik devra utiliser pour l'Ingress Route. Puis, dans le bloc spec, on indique le nom de domaine à utiliser ainsi que le service, puis le port du service.

Veillez bien à modifier la valeur de <votre-nom-de-domaine> avant de déployer.

Pour vérifier, vous pouvez tester le bon fonctionnement du déploiement à l'aide de la commande curl :

curl -I -H "Host: <votre-nom-de-domaine>" http://<ip-de-traefik>/

Vous devriez avoir une réponse avec un code 200.

Configuration de Haproxy

Comme je l'ai indiqué au début de l'article, j'utilise HaProxy comme point d'entrée de mon infrastructure. Je dois donc paramétrer Haproxy pour qu'il puisse router le trafic venant de CloudFlare vers Traefik.

Avant de paramétrer Haproxy, nous allons modifier la configuration de Traefik afin d'activer le healthcheck. Ceci permettra à Haproxy de réaliser un httpcheck sur Traefik. Nous allons créer un fichier values.yaml avec le contenu ci-dessous :

ports:
  traefik:
    healthchecksPort: 9000
    expose: true

La configuration est très simple, nous actions le healthcheck sur le port 9000 et nous exposons le port 9000 de Traefik. Pour déployer la configuration avec Helm sans avoir à désinstaller Traefik, il suffit de lancer la commande ci-dessous :

helm upgrade traefik traefik/traefik -f values.yaml

Lorsque vous souhaitez déployer Traefik avec la bonne configuration, vous devez lancer :

helm install --values=values.yaml traefik traefik/traefik

Si vous souhaitez connaître l'ensemble des valeurs que l'on peut configurer, vous pouvez lancer la commande :

helm show values traefik/traefik

Maintenant que le healthcheck est activé, nous allons remplacer le certificat par défaut de Traefik par le certificat Origin Server de Cloudflare. Tout d'abord, nous devons créer un secret Kubernetes avec le certificat de Cloudflare :

kubectl create secret generic default-certificate --from-file=tls.crt=cert.pem --from-file=tls.key=cert.key -n kube-system

En exécutant cette commande, nous créeons un secret nommé default-certificate comprenant les entrées tls.crt et tls.key dans le namespace kube-system avec les valeurs du certificat Cloudflare. Nous allons terminer cette partie avec la création du fichier certificate.yaml pour indiquer à Traefik le certificat à utiliser par défaut :

apiVersion: traefik.containo.us/v1alpha1
kind: TLSStore
metadata:
  name: default
  namespace: kube-system

spec:
  defaultCertificate:
    secretName: default-certificate

Pour déployer cette configuration, lancer la commande :

kubectl apply -f certificate.yaml

Passons désormais à la configuration de Haproxy, nous devons définir un frontend ainsi qu'un backend dans le fichier de configuration, voici ce que j'utilise :

frontend https_frontend
  mode http
  bind *:443 transparent ssl crt <chemin-vers-votre-certificat-origin-de-cloudflare> verify none

  acl kubernetes_acl hdr_dom(host) -i <votre-nom-de-domaine>
  use_backend kubernetes_backend if kubernetes_acl

backend kubernetes_backend
  mode http
  option forwardfor
  option httpchk GET /ping
  http-check expect status 200
  server s1 <ip-de-traefik>:80 check port 9000

Dans ce bloc d'instructions, vous devez modifier 3 variables :

  • <chemin-vers-votre-certificat-origin-de-cloudflare>
  • <votre-nom-de-domaine>
  • <ip-de-traefik>

Concernant la première variable <chemin-vers-votre-certificat-origin-de-cloudflare>, il faut vous assurer que le certificat Cloudflare est bien présent dans votre machine Haproxy, sans cela, vous aurez une erreur 525 qui sera renvoyée par Cloudflare lorsque vous souhaiterez accéder à votre page web depuis un navigateur. Autre précision, avec Cloudflare, vous ne pouvez pas faire du TLS Strict avec plusieurs niveaux de sous-domaines, vous devez utiliser un seul niveau, par exemple : nginx.kubernetes.domaine.com ne fonctionnera pas à l'inverse de nginx-kubernetes.domaine.com qui lui fonctionnera correctement.

Une fois déployé, Haproxy réalisera un healthcheck sur /ping et sur le port 9000 de Traefik.

Résultat

Vous pouvez visualiser la page en lançant votre navigateur à l'adresse : https://votre-nom-de-domaine. Vous devriez avoir une page HTML en HTTPS avec un certificat valide.