Aller au contenu

TLS et cert-manager

À la fin, vous saurez

  • Expliquer où le TLS est terminé entre le navigateur et le pod, et ce que cela implique pour les clés
  • Installer cert-manager avec l'intégration Gateway API et créer un ClusterIssuer ACME de staging puis de production
  • Obtenir un certificat par l'annotation d'une Gateway, et vérifier chaque étape de l'émission
  • Choisir entre un défi HTTP-01 et un défi DNS-01, et configurer le webhook Scaleway
  • Régler la durée et le renouvellement d'un Certificate en tenant compte de la baisse annoncée des durées de vie
  • Chiffrer le dernier tronçon jusqu'au pod avec une BackendTLSPolicy

Prérequis

Testé avec cert-manager 1.21 gateway-api 1.6.2 kubernetes 1.36 traefik 3.7 traefik-helm-chart 41.6.1 , vérifié le 5 octobre 2026

Pourquoi

Jusqu'ici, Signalements répond en HTTP clair. Un navigateur moderne l'affiche comme « non sécurisé », les cookies de session ne devraient pas y circuler, et tout équipement placé entre l'utilisateur et le répartiteur de charge peut lire et modifier les échanges. HTTPS n'est pas une option.

Obtenir le certificat est la partie facile : une autorité en délivre un, une fois, après avoir vérifié que vous contrôlez le nom de domaine. La partie difficile est le temps. Les certificats expirent, et un certificat expiré, c'est un site inaccessible avec un avertissement rouge. Renouveler à la main une fois par an, c'est une tâche qu'on oublie. Renouveler tous les 45 jours, ce que le secteur s'apprête à imposer (voir plus bas), c'est impossible à tenir sans automatisation.

Dans Kubernetes, cette automatisation a un nom : cert-manager, un contrôleur qui demande, stocke et renouvelle des certificats, en déclarant l'intention dans des objets de l'API. Cette leçon le met en place derrière la Gateway de la leçon précédente. Les fondements du TLS et de l'infrastructure à clés publiques sont ceux du cours TLS et PKI ; on s'en tient ici à ce qu'il faut pour exploiter un cluster.

Les concepts

Où le TLS se termine

Entre le navigateur et le pod de l'API, il y a plusieurs tronçons. Le choix de l'endroit où le chiffrement s'arrête est le choix d'architecture central.

    flowchart LR
  N["Navigateur"] -- "HTTPS (1)" --> G["Traefik<br/>(Gateway)"]
  G -- "HTTP ou HTTPS (2)" --> P["Pod<br/>signalements"]
  P -- "TLS (3)" --> B["PostgreSQL"]
  
  • Terminaison à la Gateway (mode Terminate, le défaut) : Traefik déchiffre, lit la requête HTTP, applique le routage de la leçon 6, puis envoie la requête, en clair ou rechiffrée, au pod. Seul Traefik détient la clé privée du site. C'est le cas courant.
  • Passage direct (mode Passthrough) : la Gateway ne déchiffre pas et transmet le flux TLS tel quel, en choisissant la destination d'après le nom présenté dans le message d'ouverture de la connexion (le SNI). Elle ne voit alors rien du HTTP : pas de filtres, pas de correspondance de chemin. Il faut une TLSRoute.
  • Rechiffrement : la Gateway termine le TLS du client, puis ouvre une nouvelle connexion TLS vers le pod, qu'elle authentifie. On le fait pour que le trafic ne circule pas en clair sur le réseau du cluster (voir la fin de la leçon).

Le Secret kubernetes.io/tls

Le certificat et sa clé privée vivent dans un Secret d'un type particulier, kubernetes.io/tls, qui doit contenir deux clés : tls.crt (le certificat, suivi de la chaîne intermédiaire) et tls.key (la clé privée). Le listener HTTPS de la Gateway désigne ce Secret :

listeners:
  - name: signalements-https
    protocol: HTTPS
    port: 8443
    hostname: signalements.apps.example.com
    tls:
      mode: Terminate
      certificateRefs:
        - name: signalements-tls    # un Secret de type kubernetes.io/tls

Sans cert-manager, vous créeriez ce Secret vous-même avec kubectl create secret tls. Avec lui, vous ne le créez pas : il apparaît, et se met à jour.

Les objets de cert-manager

cert-manager ajoute quelques définitions de ressources :

  • Issuer et ClusterIssuer : qui signe. Un Issuer est limité à un namespace, un ClusterIssuer sert tout le cluster. Les deux décrivent une autorité (ACME, une autorité interne, Vault...). Pour Let's Encrypt, le type est acme.
  • Certificate : quoi obtenir. Noms de domaine, durée, Secret de destination, émetteur.
  • CertificateRequest, Order, Challenge : les objets intermédiaires que cert-manager crée lui-même pour mener la demande. On ne les écrit pas, mais on les lit en cas de panne : ils portent l'état précis de chaque étape.

Le cheminement est le suivant : un Certificate est créé, cert-manager génère une clé privée et une demande de signature (CertificateRequest) ; l'émetteur ACME crée un Order auprès de l'autorité ; l'autorité exige de prouver le contrôle du domaine, ce qui produit un Challenge ; une fois le défi réussi, l'autorité signe, et cert-manager écrit le certificat dans le Secret.

ACME, ou la preuve de contrôle

ACME (Automatic Certificate Management Environment, RFC 8555) est le protocole par lequel un client automatise ce dialogue avec l'autorité. Let's Encrypt est l'autorité ACME publique la plus utilisée. Pour délivrer un certificat, elle veut une preuve que vous contrôlez le nom. Deux défis nous intéressent.

HTTP-01 : l'autorité demande de publier un jeton à l'adresse http://<nom>/.well-known/acme-challenge/<jeton>, sur le port 80, puis elle l'interroge depuis plusieurs points d'Internet. Simple, aucun accès à la zone DNS requis. Mais il faut que le domaine pointe déjà vers votre Gateway, que le port 80 soit joignable depuis Internet, et il ne délivre pas de certificat à joker (*.apps.example.com).

DNS-01 : l'autorité demande de publier un enregistrement TXT _acme-challenge.<nom>. cert-manager le crée par l'API de votre fournisseur DNS. Cela fonctionne sans port ouvert (serveur interne, sans accès public), et c'est la seule méthode pour les jokers. En contrepartie, cert-manager détient un identifiant d'API qui peut modifier votre zone DNS. Pour Scaleway, cert-manager n'a pas de solveur intégré : on installe un webhook externe, cert-manager-webhook-scaleway, que le dépôt de Scaleway décrit.

HTTP-01DNS-01
Prérequisdomaine déjà résolu vers la Gateway, port 80 ouvertaccès API à la zone DNS
Joker (*.)nonoui
Risqueun blocage du port 80 ou une redirectionun jeton d'API puissant dans le cluster
Propagationimmédiatedépend du DNS (secondes à minutes)

Les limites de Let's Encrypt, et son environnement de test

Let's Encrypt applique des limites de débit, pour que quelques déploiements fautifs n'épuisent pas le service. Celles de la documentation actuelle : jusqu'à 50 certificats par domaine enregistré et par période de 7 jours ; jusqu'à 5 certificats pour exactement le même ensemble de noms par 7 jours (la limite dite des doublons) ; jusqu'à 5 échecs de validation par nom et par heure pour un compte ; jusqu'à 300 nouvelles commandes par compte toutes les 3 heures. Les renouvellements faits avec les informations de renouvellement ACME (ARI) sont exemptés de la limite des nouvelles commandes.

La limite des doublons est celle qu'on atteint en pratique : un déploiement qui crée et supprime un Certificate en boucle, ou une Gateway recréée plusieurs fois, épuise 5 certificats identiques en une journée, et le sixième essai échoue pendant une semaine. D'où l'environnement de staging de Let's Encrypt (https://acme-staging-v02.api.letsencrypt.org/directory), aux limites nettement plus hautes, qui signe avec une autorité non reconnue par les navigateurs : on y valide toute la chaîne, puis on passe en production en changeant l'URL.

Des certificats de plus en plus courts

Les durées de vie baissent. D'après l'annonce de Let's Encrypt « De 90 à 45 jours » :

  • depuis le 13 mai 2026, le profil tlsserver délivre des certificats de 45 jours, sur demande ;
  • le 10 février 2027, le profil par défaut (classic) passera à 64 jours ;
  • le 16 février 2028, il passera à 45 jours ; la durée pendant laquelle une validation de domaine peut être réutilisée tombe en parallèle de 30 jours à 7 heures.

Un profil de 6 jours (shortlived) existe aussi, mais l'annonce n'en dit presque rien. Deux conséquences pour vous : l'automatisation n'est plus facultative, et une validation de domaine qui dépendait d'une intervention (un enregistrement à ajouter à la main) ne tiendra plus. cert-manager sait demander un profil d'après le champ profile de l'émetteur ACME depuis sa version 1.18, d'après sa documentation.

En pratique

Les manifestes sont validés comme du YAML ; leur effet sur un cluster est décrit, non reproduit. Les domaines sont des sous-domaines de example.com, domaine réservé : sur un vrai cluster, utilisez le vôtre, déjà pointé vers l'adresse de la Gateway.

1. Installer cert-manager avec l'intégration Gateway API

Les CRD de Gateway API doivent être présentes avant le démarrage de cert-manager : sinon l'intégration ne s'active pas, et il faut redémarrer le déploiement après les avoir installées. Fichier de valeurs :

# cert-manager-values.yaml (chart v1.21.2)
crds:
  enabled: true
config:
  apiVersion: controller.config.cert-manager.io/v1alpha1
  kind: ControllerConfiguration
  gatewayAPI:
    enabled: true     # active la lecture des annotations sur les Gateways
$ helm install cert-manager oci://quay.io/jetstack/charts/cert-manager \
    --version v1.21.2 --namespace cert-manager --create-namespace \
    -f cert-manager-values.yaml
$ kubectl get pods -n cert-manager

Trois pods doivent devenir Running : le contrôleur, l'injecteur de CA (cainjector) et le webhook d'admission. Le code source de cert-manager 1.21 précise que l'intégration exige aussi le feature gate ExperimentalGatewayAPISupport, activé par défaut depuis la version 1.15.

2. Les émetteurs : staging, puis production

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: plateforme@example.com
    privateKeySecretRef:
      name: letsencrypt-staging-compte      # la clé du compte ACME, créée par cert-manager
    solvers:
      - http01:
          gatewayHTTPRoute:
            parentRefs:
              - name: entree
                namespace: passerelle
                kind: Gateway
                sectionName: apps
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: plateforme@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-compte
    solvers:
      - http01:
          gatewayHTTPRoute:
            parentRefs:
              - name: entree
                namespace: passerelle
                kind: Gateway
                sectionName: apps
  • server : l'URL du répertoire ACME de l'autorité.
  • email : l'adresse de contact du compte.
  • privateKeySecretRef : cert-manager enregistre un compte ACME et range sa clé dans ce Secret. Un par émetteur.
  • solvers : comment prouver le contrôle. Avec gatewayHTTPRoute, cert-manager crée, le temps du défi, une HTTPRoute temporaire attachée au parent indiqué, qui sert le jeton sur le port 80, puis la supprime. D'après la documentation de cert-manager, la Gateway doit avoir un listener sur le port 80 et des allowedRoutes qui acceptent cette route.

3. Le listener HTTPS annoté

On reprend la Gateway entree de la leçon 6 et on y ajoute l'annotation et un listener HTTPS :

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: entree
  namespace: passerelle
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
  gatewayClassName: traefik
  listeners:
    - name: apps
      protocol: HTTP
      port: 8000
      hostname: "*.apps.example.com"
      allowedRoutes:
        kinds:
          - kind: HTTPRoute
        namespaces:
          from: Selector
          selector:
            matchLabels:
              acces-passerelle: apps
    - name: signalements-https
      protocol: HTTPS
      port: 8443
      hostname: signalements.apps.example.com
      tls:
        mode: Terminate
        certificateRefs:
          - name: signalements-tls
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              acces-passerelle: apps

D'après la documentation, pour qu'un listener donne lieu à un certificat, il faut un hostname non vide, un tls.mode: Terminate et un certificateRefs[].name non vide. cert-manager observe la Gateway, lit l'annotation, et crée un Certificate dans le namespace de la Gateway, avec le nom de domaine du listener, qui écrit son résultat dans le Secret signalements-tls. Comme la route temporaire du défi est créée elle aussi dans passerelle, ce namespace doit porter l'étiquette acces-passerelle: apps, sans quoi le listener apps la refuse :

$ kubectl label namespace passerelle acces-passerelle=apps
$ kubectl apply -f gateway.yaml
$ kubectl get certificate,certificaterequest,order,challenge -n passerelle

Observez la suite des objets : un Certificate à READY: False, une CertificateRequest, un Order, puis un Challenge dans l'état pending ; au bout de quelques dizaines de secondes, tout passe à True ou valid et les Challenge disparaissent. Avec l'émetteur de staging, le certificat est valide mais signé par une autorité de test : curl le refusera, sauf avec -k. C'est le comportement attendu.

4. Basculer en production

Quand le staging fonctionne, on change l'annotation (letsencrypt-prod) et on supprime le Secret de staging pour forcer une nouvelle émission :

$ kubectl annotate gateway entree -n passerelle \
    cert-manager.io/cluster-issuer=letsencrypt-prod --overwrite
$ kubectl delete secret signalements-tls -n passerelle
$ kubectl wait certificate/signalements-tls -n passerelle --for=condition=Ready --timeout=120s
$ curl -sv https://signalements.apps.example.com/sante 2>&1 | grep -E "issuer|subject|expire"

La dernière commande montre l'émetteur du certificat servi : en production, une autorité Let's Encrypt reconnue ; avec le staging, un nom d'autorité contenant « STAGING ». Le certificat changeant d'émetteur, c'est bien la preuve qu'on a tout basculé.

5. La redirection de HTTP vers HTTPS

Une route sur le listener HTTP redirige tout vers HTTPS. Le défi ACME n'est pas gêné : la route temporaire de cert-manager a un chemin exact vers /.well-known/acme-challenge/<jeton>, plus prioritaire d'après les règles de correspondance de la leçon 6 que le préfixe / de la redirection.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: signalements-vers-https
  namespace: signalements
spec:
  parentRefs:
    - name: entree
      namespace: passerelle
      sectionName: apps
  hostnames:
    - signalements.apps.example.com
  rules:
    - filters:
        - type: RequestRedirect
          requestRedirect:
            scheme: https
            port: 443
            statusCode: 301

Et la route applicative de la leçon 6 se rattache, elle, au listener signalements-https. Le port: 443 de la redirection est le port public : celui du Service Traefik, pas du pod.

6. Un joker par DNS-01 chez Scaleway

Si la zone example.com est gérée par Scaleway, un seul certificat *.apps.example.com couvre toutes les applications, sans port 80. Installez d'abord le webhook (cert-manager doit déjà être là), d'après son README :

$ helm repo add scaleway https://helm.scw.cloud/
$ helm repo update
$ helm install scaleway-certmanager-webhook scaleway/scaleway-certmanager-webhook \
    --namespace cert-manager

Puis un Secret avec les deux variables du webhook, SCW_ACCESS_KEY et SCW_SECRET_KEY, créé hors du dépôt Git, et un émetteur :

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod-dns
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: plateforme@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-dns-compte
    solvers:
      - dns01:
          webhook:
            groupName: acme.scaleway.com
            solverName: scaleway
            config:
              accessKeySecretRef:
                name: scaleway-secret
                key: SCW_ACCESS_KEY
              secretKeySecretRef:
                name: scaleway-secret
                key: SCW_SECRET_KEY

groupName et solverName sont les identifiants du webhook ; les deux références pointent vers le Secret. Le jeton que vous y mettez doit appartenir à une application IAM dédiée, à qui l'on ne donne que les droits sur les zones DNS nécessaires (voir la section Sécurité). Le listener HTTPS utilisera alors hostname: "*.apps.example.com", avec un seul certificateRefs.

7. Un Certificate explicite

L'annotation couvre le cas simple. Pour fixer la durée, la rotation des clés ou le renouvellement, on écrit le Certificate soi-même :

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: signalements
  namespace: passerelle
spec:
  secretName: signalements-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - signalements.apps.example.com
  duration: 1080h        # 45 jours
  renewBefore: 360h      # renouveler 15 jours avant l'expiration
  privateKey:
    algorithm: ECDSA
    size: 256
    rotationPolicy: Always

Le délai de renouvellement par défaut est de deux tiers de la durée écoulée, d'après la documentation ; renewBefore le fixe en valeur absolue. Le champ rotationPolicy: Always génère une nouvelle clé à chaque renouvellement ; c'est le défaut depuis cert-manager 1.18. Notez que la durée demandée n'est qu'une demande : l'autorité décide de la durée délivrée, et un profil de 45 jours ou de 64 jours l'emporte sur duration.

8. TLS jusqu'au pod : la BackendTLSPolicy

Pour rechiffrer entre Traefik et l'API, il faut un pod qui sert du TLS (Signalements écouterait alors en HTTPS sur son port, avec un certificat émis pour son nom de Service par une autorité interne, par exemple un second ClusterIssuer de type ca). La Gateway valide ce certificat grâce à une BackendTLSPolicy, ressource du canal standard depuis la version 1.4 :

apiVersion: gateway.networking.k8s.io/v1
kind: BackendTLSPolicy
metadata:
  name: signalements-tls-interne
  namespace: signalements
spec:
  targetRefs:
    - group: ""
      kind: Service
      name: signalements
  validation:
    hostname: signalements.signalements.svc.cluster.local
    caCertificateRefs:
      - group: ""
        kind: ConfigMap
        name: autorite-interne

targetRefs désigne le Service ; validation.hostname est le nom que la Gateway attend dans le certificat du pod ; caCertificateRefs pointe vers un ConfigMap qui contient l'autorité qui l'a signé (la clé ca.crt). Les champs subjectAltNames et wellKnownCACertificates offrent d'autres façons de valider. La documentation de Traefik 3.7 indique que BackendTLSPolicy est prise en charge ; vérifiez-le pour votre version.

Sous le capot

Le contrôleur de Gateway de cert-manager (le gateway-shim) est une boucle de réconciliation qui surveille les Gateways. Pour chaque listener qui remplit les conditions, il construit un Certificate et le fait créer ou mettre à jour..

Le défi HTTP-01 pas à pas. cert-manager crée un pod serveur du jeton, un Service, et la HTTPRoute temporaire qui les relie à la Gateway. Il fait d'abord une auto-vérification : il requête lui-même l'URL du jeton depuis l'intérieur du cluster ; tant qu'elle échoue, il n'avertit pas l'autorité. Cela évite de gaspiller une tentative. Puis il signale à l'autorité qu'elle peut valider. L'autorité interroge depuis plusieurs lieux, et si le jeton est servi à chaque fois, le défi réussit. Les statuts des objets (Challenge) indiquent en toutes lettres l'étape où cela se bloque.

Le renouvellement. À chaque réconciliation, cert-manager compare l'expiration du Secret au moment de renouvellement (deux tiers de la durée ou renewBefore). Quand il est atteint, il émet une nouvelle CertificateRequest et remplace le Secret en place ; Traefik, qui observe les Secrets, recharge le certificat sans redémarrer, et les nouvelles connexions utilisent le nouveau certificat.

La chaîne de confiance. Le Secret tls.crt contient le certificat feuille puis l'intermédiaire. Si l'intermédiaire manque, certains clients (cURL récent, navigateurs) le complètent, d'autres échouent avec une erreur de vérification. cert-manager écrit la chaîne complète que renvoie l'autorité ; ne remplacez pas le fichier à la main.

Pièges courants

Les doublons de certificats. Supprimer le Certificate et le Secret pour « repartir de zéro » consomme une émission à chaque fois. Cinq fois de suite sur les mêmes noms, et Let's Encrypt refuse pendant plusieurs jours avec une erreur de limite de débit (too many certificates (5) already issued for this exact set of identifiers). Travaillez en staging, et sauvegardez le Secret avant de le détruire.

Le défi bloqué en pending. Les causes se classent par ce que dit le Challenge (kubectl describe challenge) : Waiting for HTTP-01 challenge propagation suivi d'un échec de l'auto-vérification (le nom ne résout pas vers la Gateway, le port 80 est filtré, une redirection globale HTTP vers HTTPS intercepte la requête) ; ou un listener qui refuse la route temporaire (NotAllowedByListeners) faute de l'étiquette sur le namespace. Le scénario 6 de la leçon 10 le traite.

La redirection posée au mauvais endroit. Une redirection globale configurée sur le point d'entrée de Traefik (propre à Traefik) s'applique avant tout routage, donc au défi aussi. La redirection au niveau de la route, comme dans la section 5, ne le bloque pas.

Le Secret absent au démarrage. Tant que signalements-tls n'existe pas, le listener HTTPS est en erreur : sa condition ResolvedRefs est à False, raison InvalidCertificateRef, et la Gateway peut ne pas être programmée pour ce listener. Cela se résout à l'émission du certificat ; c'est normal pendant la première minute.

Un certificat de staging oublié. Un site qui affiche un avertissement après la mise en service a sans doute encore un certificat signé par l'autorité de test. L'émetteur du Secret se lit avec kubectl get secret signalements-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -issuer.

Les CRD arrivées après cert-manager. Les annotations sont ignorées sans message. Redémarrez le déploiement cert-manager après avoir installé Gateway API.

Sécurité

  • La clé privée du site est dans un Secret. Quiconque lit les Secrets du namespace passerelle lit la clé. Limitez le RBAC get, list et watch sur les Secrets (la liste donne le contenu), et chiffrez-les au repos dans etcd (cours Sécurité de Kubernetes).
  • Le jeton DNS-01 est puissant. Il peut modifier votre zone : rediriger le domaine, ou obtenir des certificats pour tous ses noms. Créez une application IAM dédiée avec les droits minimaux, rangez le Secret dans un namespace restreint, faites-le tourner, et préférez, quand c'est possible, de déléguer le défi à une zone séparée par un enregistrement CNAME.
  • Les Gateways qui lisent des Secrets d'autres namespaces exigent une ReferenceGrant (leçon 6) : c'est voulu, ne la remplacez pas par un droit large.
  • CAA. Un enregistrement DNS CAA limite les autorités qui ont le droit de délivrer pour votre domaine (0 issue "letsencrypt.org"), ce qui réduit l'impact d'une validation frauduleuse chez une autre autorité.
  • Ne coupez pas la validation. Les options -k de curl ou l'ignorance du certificat dans le code d'un client « pour que ça marche en staging » finissent en production. Installez l'autorité de staging dans le magasin de confiance du client de test.
  • mTLS. TLS peut authentifier le client aussi (mutual TLS). La Gateway standard 1.6 propose la validation de certificats client au niveau du listener, et les maillages de services généralisent l'idée entre pods (leçon 9). On ne l'active pas sur un site public destiné à des navigateurs.

En production

  • Un émetteur par rôle. letsencrypt-prod pour les sites publics, un émetteur interne (autorité propre, Vault) pour le TLS interne : mêmes objets, autre issuerRef.
  • Alerter sur l'expiration. cert-manager expose des métriques Prometheus, dont l'expiration de chaque certificat : une alerte à 14 jours avant l'échéance (7 pour des certificats de 45 jours) attrape un renouvellement bloqué silencieusement.
  • Anticiper les 45 jours. Un renouvellement toutes les 3 semaines environ multiplie les occasions d'échec. Testez le renouvellement en forçant l'expiration d'un certificat de staging, et gardez les limites de débit en tête : plusieurs environnements qui demandent les mêmes noms les épuisent.
  • Un Certificate par hôte, ou un joker ? Le joker simplifie (un seul), mais sa clé protège tous les sous-domaines ; en cas de fuite, tout est compromis. Pour les domaines des clients, un certificat par client.
  • Kapsule. Le Load Balancer Scaleway créé par le Service de Traefik transmet les connexions TCP : c'est Traefik qui termine le TLS, et les certificats ne se gèrent qu'à cet endroit.
  • GitOps. Les émetteurs et les Gateways sont dans le dépôt ; les Secrets de compte et de jeton DNS, jamais en clair (cours GitOps, gestion des secrets).

Exercices

Exercice 1 : lire une émission

Après kubectl apply de la Gateway annotée, le Certificate reste à READY: False depuis dix minutes. Listez les commandes, dans l'ordre, qui vous mènent à la cause, et ce que vous cherchez dans chacune.

Solution
  1. kubectl describe certificate signalements-tls -n passerelle : la condition Issuing et les événements indiquent l'objet suivant à regarder.
  2. kubectl get certificaterequest,order -n passerelle puis kubectl describe order <nom> : l'état de l'Order (pending, invalid, errored) et, en cas d'erreur, le message de l'autorité (limite de débit par exemple).
  3. kubectl get challenge -n passerelle puis kubectl describe challenge <nom> : le champ Reason indique l'étape. Un défi HTTP-01 qui échoue à l'auto-vérification pointe vers le DNS, le port 80 ou la route temporaire.
  4. kubectl get httproute -n passerelle et son statut : la route temporaire est-elle Accepted ? Si non (NotAllowedByListeners), l'étiquette de namespace manque.
  5. kubectl logs -n cert-manager deploy/cert-manager en dernier recours.

On descend de l'objet de plus haut niveau vers le plus bas, comme dans le cours précédent pour un pod.

Exercice 2 : choisir le défi

Lyneko héberge pour un client une application accessible uniquement depuis un réseau privé, sur interne.client.example.com, et veut aussi un certificat unique pour *.interne.client.example.com. HTTP-01 ou DNS-01 ? Pourquoi ? Quelle précaution prendre avec le jeton ?

Solution

DNS-01, pour deux raisons : le port 80 n'est pas joignable depuis Internet, donc l'autorité ne pourrait pas faire le défi HTTP-01 ; et un joker ne peut être délivré que par DNS-01. Il faut que cert-manager puisse écrire dans la zone du client : un jeton d'API limité à cette zone (ou, mieux, la délégation du seul sous-domaine _acme-challenge par un CNAME vers une zone que Lyneko contrôle, pour ne jamais détenir d'accès à la zone du client). Ce jeton est conservé dans un Secret du namespace cert-manager, avec un RBAC restreint.

Exercice 3 : planifier 2027

Le profil par défaut de Let's Encrypt passe à 64 jours le 10 février 2027. Votre Certificate demande duration: 2160h (90 jours) sans renewBefore. Que se passe-t-il, et que changez-vous ?

Solution

La durée demandée est une demande, pas une garantie : l'autorité délivre 64 jours, et cert-manager calcule le renouvellement à partir de la durée réellement délivrée pour le défaut (deux tiers de la durée, soit après 43 jours environ), donc le renouvellement continue de fonctionner sans changer. Ce qui peut casser : une valeur de renewBefore en absolu supérieure à la durée délivrée (cert-manager exige que la durée dépasse renewBefore), ou un processus qui suppose 90 jours (alerte à 30 jours avant expiration qui sonnerait en permanence). On corrige en retirant duration si elle n'a pas de raison d'être, en passant à renewBeforePercentage (relatif) plutôt qu'à renewBefore, et en vérifiant les seuils d'alerte.

Récapitulatif

  • Le TLS se termine à la Gateway (le plus courant), passe tel quel (TLSRoute), ou est rechiffré jusqu'au pod (BackendTLSPolicy).
  • Un listener HTTPS lit un Secret kubernetes.io/tls (tls.crt, tls.key).
  • cert-manager automatise la demande et le renouvellement : Issuer ou ClusterIssuer (qui), Certificate (quoi), CertificateRequest, Order et Challenge (les étapes, utiles au diagnostic).
  • ACME : HTTP-01 (port 80, pas de joker) ou DNS-01 (accès à la zone, jokers possibles ; webhook pour Scaleway).
  • L'intégration Gateway API se déclenche par l'annotation cert-manager.io/cluster-issuer, avec config.gatewayAPI.enabled et des CRD installées avant le contrôleur.
  • Toujours passer par le staging de Let's Encrypt, à cause de la limite de 5 doublons par semaine.
  • Les durées baissent : 45 jours en option depuis le 13 mai 2026, 64 jours par défaut le 10 février 2027, 45 jours le 16 février 2028.
  • La clé privée est dans un Secret ; le jeton DNS-01 est un accès à la zone : à restreindre.

Pour aller plus loin

  • Gateway API en production, pour les listeners et les ReferenceGrants dont cette leçon dépend.
  • Les NetworkPolicies : le trafic entre la Gateway et l'application, une fois chiffré, doit encore être autorisé.
  • Le cours TLS et PKI, pour les certificats, les chaînes de confiance et la révocation.
  • La documentation de cert-manager : les pages sur l'ACME, le webhook DNS-01 et les métriques.
  • L'annonce de Let's Encrypt sur les durées et les profils, pour suivre le calendrier.
  • Le cours Sécurité de Kubernetes, pour le chiffrement des Secrets au repos.
  • Glossaire : cert-manager, ACME, Gateway API.
Voir ma constellation →

Sources