TLS et cert-manager
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/tlsSans 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-01 | DNS-01 | |
|---|---|---|
| Prérequis | domaine déjà résolu vers la Gateway, port 80 ouvert | accès API à la zone DNS |
Joker (*.) | non | oui |
| Risque | un blocage du port 80 ou une redirection | un jeton d'API puissant dans le cluster |
| Propagation | immédiate | dé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
tlsserverdé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: appsserver: 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. AvecgatewayHTTPRoute, 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 desallowedRoutesqui 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: appsD'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: 301Et 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_KEYgroupName 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: AlwaysLe 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-internetargetRefs 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
passerellelit la clé. Limitez le RBACget,listetwatchsur 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
CAAlimite 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
-kde 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-prodpour les sites publics, un émetteur interne (autorité propre, Vault) pour le TLS interne : mêmes objets, autreissuerRef. - 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
kubectl describe certificate signalements-tls -n passerelle: la conditionIssuinget les événements indiquent l'objet suivant à regarder.kubectl get certificaterequest,order -n passerellepuiskubectl 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).kubectl get challenge -n passerellepuiskubectl describe challenge <nom>: le champReasonindique l'étape. Un défi HTTP-01 qui échoue à l'auto-vérification pointe vers le DNS, le port 80 ou la route temporaire.kubectl get httproute -n passerelleet son statut : la route temporaire est-elleAccepted? Si non (NotAllowedByListeners), l'étiquette de namespace manque.kubectl logs -n cert-manager deploy/cert-manageren 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, avecconfig.gatewayAPI.enabledet 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.
Sources
- cert-manager, Gateway API
- cert-manager, ACME et défi HTTP-01
- cert-manager, Certificate (durée, renouvellement, rotation des clés)
- cert-manager, code source : types de la configuration du contrôleur (v1.21.2)
- Let's Encrypt, de 90 à 45 jours : calendrier de réduction des durées
- Let's Encrypt, limites de débit
- RFC 8555, Automatic Certificate Management Environment (ACME)
- scaleway/cert-manager-webhook-scaleway, README
- Gateway API, schéma de BackendTLSPolicy (CRD standard 1.6.2)