Technical Guide Intermediate 14 min read

Kubernetes cert-manager : le projet open source

cert-manager est le projet CNCF gradué qui transforme Kubernetes en plan de contrôle conscient des certificats. Il introduit des CRDs natifs (Issuer, ClusterIssuer, Certificate) qui permettent aux workloads de demander, renouveler et faire tourner leurs identités X.509 de manière déclarative. Ce guide couvre son architecture, ses backends, des exemples YAML et les garde-fous à mettre en production.

En bref

Type
Technical Guide
Niveau
Intermediate
Suivant
Gestion automatisée des certificats

Vue d'ensemble

cert-manager est le projet CNCF gradué qui transforme Kubernetes en plan de contrôle conscient des certificats. Il s'exécute comme un controller in-cluster qui observe un ensemble de ressources personnalisées — Issuer, ClusterIssuer, Certificate, CertificateRequest, Order, Challenge — et les réconcilie en demandant, stockant et renouvelant des certificats X.509. Les workloads consomment le Secret Kubernetes produit comme n'importe quel autre objet de configuration, et le renouvellement se déroule en arrière-plan.

Le projet prend en charge de nombreux backends d'émission. Une même ressource Certificate peut être adossée à un serveur ACME (Let's Encrypt, ZeroSSL ou un point d'accès ACME privé), à HashiCorp Vault PKI, à Venafi TPP / TLS Protect Cloud, à Google CAS, à AWS Private CA, à un CA Issuer ou self-signed in-cluster, ou à toute CA tierce accessible via le modèle de plugin External Issuer. cert-manager traite les différences de protocole comme un détail de backend et expose une API unique et uniforme aux équipes applicatives.

En pratique, il est devenu le mécanisme par défaut pour TLS, le mTLS et l'identité workload de courte durée dans Kubernetes. Les controllers Ingress, les service meshes (Istio, Linkerd, Cilium), les passerelles API et les opérateurs internes s'y intègrent tous. La référence canonique est cert-manager.io ; le code source vit sur github.com/cert-manager/cert-manager et le projet est gouverné par la CNCF depuis sa graduation en 2024.

Avant cert-manager, la gestion des certificats dans Kubernetes ressemblait à du bricolage. Une équipe plateforme scriptait `certbot` sur un bastion et recopiait les fichiers PEM résultants à la main dans un `Secret`. Une autre utilisait l'agent injector de Vault avec un sidecar qui réécrivait les fichiers sur disque. Une troisième montait des certificats statiques d'un an intégrés à un chart Helm, puis les oubliait jusqu'à ce qu'une panne vienne le leur rappeler. Le cluster ne savait rien de tout cela : les certificats étaient des blobs opaques venus de l'extérieur, qui expiraient sans que personne ne les surveille.

cert-manager a comblé cet écart en traitant les certificats comme des objets Kubernetes de première classe. Vous déclarez ce que vous voulez — un nom d'hôte, une durée, un Issuer, un algorithme de clé — en YAML. Un controller compare l'état déclaré à l'état du cluster, contacte le bon backend, écrit un `Secret`, et continue de réconcilier jusqu'à ce que le certificat vivant corresponde à la déclaration. Aux deux tiers de la durée de vie, le controller renouvelle automatiquement. Si la spec change, il réémet. C'est le même modèle que Kubernetes applique aux Pods, Deployments et Services, appliqué à X.509.

Le projet est né sous le nom de `kube-lego` chez Jetstack en 2016, a été renommé `cert-manager` en 2018, puis donné à la CNCF Sandbox en 2020, promu en Incubation en 2022, et a atteint le statut Graduated en septembre 2024. À ce moment-là il était déployé dans des dizaines de milliers de clusters, listé comme dépendance dure par les service meshes et les controllers Ingress, et de fait incontournable pour quiconque exécutait du TLS sur Kubernetes à l'échelle.

Ce que cert-manager résout

Le cycle de vie à l'intérieur du cluster est court à décrire et vaut la peine d'être posé avant d'aborder les backends et les cas limites. Chaque étape est une boucle de réconciliation prise en charge par un controller spécifique, et chacune produit une ou plusieurs ressources filles que vous pouvez inspecter avec `kubectl`.

La réconciliation ne s'arrête jamais. Si le `Secret` est supprimé, le controller réémet. Si la spec du Certificate change (un SAN ajouté, un algorithme de clé modifié), le controller réémet. Si l'on approche du `renewBefore`, le controller réémet. C'est ce qui rend cert-manager fondamentalement différent d'un `certbot` en cron : il n'y a pas de planning, seulement une boucle qui ferme l'écart entre déclaré et réel.

1

Déclarer un Issuer ou un ClusterIssuer

Un `Issuer` vit dans un espace de nommage et ne sert que les Certificates du même espace ; un `ClusterIssuer` est de portée cluster et peut être référencé depuis n'importe où. Les deux décrivent comment obtenir un certificat — quelle URL de directory ACME, quel chemin Vault, quel bundle de CA, quels identifiants — mais n'émettent rien par eux-mêmes. C'est l'objet de configuration que le controller lit lorsqu'une demande arrive.

2

Créer une ressource Certificate

L'équipe applicative (ou une abstraction plateforme par-dessus) crée un CRD `Certificate` qui déclare le sujet, les DNS names, la durée, la fenêtre de renouvellement, le nom du Secret et une référence à un Issuer ou ClusterIssuer. C'est le seul objet que la plupart des équipes workload manipulent directement.

3

Le controller produit un CertificateRequest

Le controller cert-manager observe le `Certificate`, génère une nouvelle clé privée in-cluster, construit un CSR et écrit une ressource `CertificateRequest` qui capture la demande de signature et la référence à l'Issuer. Cet objet est ce qu'un audit GitOps relirait, et c'est sur lui qu'un controller d'approbation déciderait si vous en aviez installé un.

4

Le controller spécifique à l'Issuer signe

Un second controller, propre au backend choisi (ACME, Vault, CA, Venafi, External Issuer), reprend le `CertificateRequest`, exécute l'échange protocolaire — pour ACME cela signifie créer des enfants `Order` et `Challenge` et résoudre HTTP-01, DNS-01 ou TLS-ALPN-01 — puis écrit le certificat signé sur le `CertificateRequest` une fois que la CA l'a renvoyé.

5

Le Secret est écrit, les workloads le consomment

Le controller sérialise le certificat, la chaîne et la clé privée dans un `Secret` Kubernetes de type `kubernetes.io/tls`, avec les clés `tls.crt`, `tls.key` et `ca.crt`. Les Pods le montent comme un volume de fichiers ou le lisent via l'environnement, et la boucle de renouvellement réécrit le même Secret sur place aux deux tiers de la durée de vie.

Backends d'émission pris en charge

L'abstraction Issuer est la partie qui rend cert-manager intéressant au niveau plateforme. Les équipes applicatives déclarent un Certificate ; l'équipe plateforme choisit quelle autorité s'occupe de la signature en coulisses. Remplacer un ClusterIssuer adossé à Let's Encrypt par un point d'accès ACME privé pointant vers une CA interne est, du point de vue de l'application, un non-événement.

Un pattern plateforme courant consiste à avoir un `ClusterIssuer` par environnement et par trust domain : un Issuer ACME public pour les hostnames d'ingress qui ont besoin de certificats reconnus par les navigateurs, et un Issuer ACME privé ou Vault pour tout ce qui est interne. L'`Issuer` à portée de namespace existe pour les cas où une application possède ses propres credentials et où l'équipe plateforme ne veut explicitement pas les exposer au niveau cluster.

ACME

Le backend le plus courant. Fonctionne avec n'importe quel directory RFC 8555 : Let's Encrypt (staging et production), ZeroSSL, Buypass, la CA publique de Google et, surtout, tout point d'accès ACME privé exposé par une CA d'entreprise. Prend en charge HTTP-01, DNS-01 (avec des solvers intégrés pour Route 53, Cloud DNS, Azure DNS, Cloudflare, RFC 2136 et un mécanisme de webhook pour les autres) et TLS-ALPN-01.

HashiCorp Vault

Dialogue avec un moteur Vault PKI via les méthodes d'authentification AppRole, ServiceAccount Kubernetes ou JWT/OIDC. Adapté aux environnements qui exécutent déjà Vault pour les secrets applicatifs et veulent que les certificats suivent le même modèle de gouvernance et d'audit.

Venafi

Intégration native avec Venafi TPP et Venafi TLS Protect Cloud, de manière à ce que les policy folders et workflows d'approbation existants restent la source d'autorité. Utile lorsque Venafi est le CLM corporate de référence et que cert-manager n'est que la couche de livraison côté cluster.

CA Issuer

Une CA in-cluster dont la clé vit dans un Secret. Pratique pour des CA intermédiaires de courte durée, des environnements de test éphémères ou des scénarios de bootstrap. Inadaptée comme racine durable — pas de protection offline, pas de HSM, pas de séparation des rôles.

External Issuers

Un contrat de plugin qui permet à toute CA de livrer un controller implémentant l'API CertificateRequest. Exemples publics : AWS Private CA, Google CAS, Step CA, SmallStep, GlobalSign Atlas et toute une longue traîne d'Issuers spécifiques aux fournisseurs. Du point de vue de l'utilisateur, un External Issuer ressemble exactement à un Issuer intégré.

Les CRDs en un coup d'œil

Les trois ressources que vous utiliserez au quotidien sont `Certificate`, `CertificateRequest` et la paire Issuer. Elles se ressemblent en YAML mais jouent des rôles très différents dans le cycle de vie.

Le pattern qui surprend les opérateurs débutants avec cert-manager, c'est le décalage de durée de vie entre `Certificate` et `CertificateRequest`. Un seul `Certificate` nommé `api-tls` peut produire des dizaines d'enfants `CertificateRequest` dans le temps, un par émission. L'historique est conservé (dans la limite de `revisionHistoryLimit`) afin que les auditeurs puissent répondre à « qui a renouvelé ceci, quand, contre quel Issuer, et qu'y avait-il dans le CSR ? » sans avoir besoin d'accéder aux logs de la CA.

Deux solvers sont déclarés et sélectionnés par zone DNS. Les hostnames publics sur `example.com` sont validés en HTTP-01 via le controller Ingress nginx. Les hostnames internes sur `internal.example.com` — qu'une CA publique ne pourrait pas joindre en HTTP — sont validés en DNS-01 contre Route 53. Le même `ClusterIssuer` sert les deux parce que cert-manager choisit le solver correspondant à chaque Certificate. Pour un point d'accès ACME privé adossé à une CA d'entreprise, seules l'URL `server` et les credentials changent ; le reste de la ressource reste identique.

C'est la forme d'un certificat d'identité workload moderne : durée de 24 heures, renouvellement déclenché au tiers de la durée de vie (8 heures avant expiration), clés ECDSA P-256, EKUs server-auth et client-auth pour mTLS dans les deux sens, et une URI SAN SPIFFE pour qu'un service mesh puisse utiliser le certificat comme identité portable. `rotationPolicy: Always` force une nouvelle clé privée à chaque renouvellement, ce qui est le réglage conservateur pour tout ce qui traverse une frontière réseau.

Le plugin `kubectl cert-manager` (installable via `krew`) mérite sa place dans la trousse de l'équipe plateforme. `cmctl status certificate` et `cmctl renew` économisent beaucoup de tâtonnements en incident, et impriment des chaînes d'erreur lisibles quand un renouvellement est bloqué.

CertificateCertificateRequestIssuer / ClusterIssuer
PortéeEspace de nommageEspace de nommageIssuer = namespace ; ClusterIssuer = cluster
AuteurÉquipe applicative ou abstraction plateformeController cert-manager (auto-généré)Équipe plateforme / PKI
Durée de vieLongue — même nom réutilisé à chaque renouvellementCourte — un par émission, conservé pour l'auditLongue — change rarement après configuration
Déclenche un renouvellementOui, via `renewBefore` ou un changement de specNon, c'est l'*artefact* d'un renouvellement, pas le déclencheurNon, c'est de la configuration
ClusterIssuer — ACME avec HTTP-01 yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
ingressClassName: nginx
selector:
dnsZones:
- example.com
- dns01:
route53:
region: eu-west-1
hostedZoneID: Z2KZENXMP3JV5Y
selector:
dnsZones:
- internal.example.com
Certificate — identité workload mTLS de courte durée yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: payments-api-mtls
namespace: payments
spec:
secretName: payments-api-mtls
secretTemplate:
annotations:
reloader.stakater.com/match: "true"
issuerRef:
name: internal-acme
kind: ClusterIssuer
commonName: payments-api.payments.svc.cluster.local
dnsNames:
- payments-api.payments.svc.cluster.local
- payments-api.payments
uris:
- spiffe://example.com/ns/payments/sa/payments-api
duration: 24h
renewBefore: 8h
privateKey:
algorithm: ECDSA
size: 256
rotationPolicy: Always
usages:
- server auth
- client auth
# Suivre toute la chaîne d'enfants d'un Certificate
kubectl -n payments get certificate,certificaterequest,order,challenge

# Inspecter ce qui a réellement été émis (contenu du Secret, pas le CRD)
kubectl -n payments get secret payments-api-mtls -o jsonpath='{.data.tls\.crt}' \
| base64 -d \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName,extendedKeyUsage

# Forcer un renouvellement sans toucher à la spec (utile en incident)
kubectl cert-manager renew -n payments payments-api-mtls

# Confirmer que le controller a vu la demande et produit un nouveau CertificateRequest
kubectl -n payments get certificaterequest \
--sort-by=.metadata.creationTimestamp \
-o custom-columns=NAME:.metadata.name,READY:.status.conditions[?(@.type=='Ready')].status,AGE:.metadata.creationTimestamp
cert-manager se trouve sur le chemin critique de tout workload qui termine TLS ou parle mTLS dans le cluster. Si le controller est en panne, les nouveaux pods finiront par ne plus démarrer (pas de Secret) et les renouvellements rateront leur fenêtre en silence. Traitez-le comme le controller Ingress : exécutez-le avec des PodDisruptionBudgets, plusieurs réplicas, des nœuds dédiés s'il le faut, et alertez sur `cert-manager_controller_sync_call_count` ainsi que sur la condition `Ready` de chaque Certificate des namespaces de production.

Considérations de production

cert-manager rend les cas faciles trivialement faciles. Les cas difficiles — flottes multi-cluster, points d'accès ACME privés, mélange de confiance publique et privée, obligations d'audit — sont ceux où les détails opérationnels comptent. La liste qui suit n'est pas exhaustive, mais elle reprend les points que la plupart des équipes apprennent à la dure.

Un Issuer par environnement, pas par application

La tentation, surtout dans les plateformes en self-service, est de laisser chaque équipe applicative créer son propre Issuer pointant vers ses propres credentials. Cela passe mal à l'échelle. L'Issuer est une configuration que l'équipe PKI doit gouverner : quelle CA, quel template de policy, quelle approbation, quelle destination d'audit. Le bon niveau de granularité est un `ClusterIssuer` par trust domain et par environnement (production-public, production-internal, staging-internal, dev-internal), référencé par nom, jamais copié. Les équipes applicatives choisissent un nom d'Issuer ; elles n'en configurent pas.

Réglez `renewBefore` à environ un tiers de `duration`

Le défaut de cert-manager renouvelle aux deux tiers de la durée de vie, ce qui laisse le dernier tiers comme marge de sécurité. Pour un certificat de 24 heures, cela donne 8 heures de marge — assez pour absorber une panne ACME, un délai de propagation DNS ou un redémarrage de controller. Des ratios plus courts ne gagnent rien de significatif et suppriment la marge ; des ratios plus longs vous font réémettre plus souvent que nécessaire et chargent inutilement la CA. La règle du tiers s'applique linéairement : un certificat de 90 jours renouvelle au jour 60, un certificat de 47 jours au jour 31, un certificat de 24 heures à la 16e heure.

La rotation des Secrets n'arrive pas instantanément aux pods

Quand le controller écrit un nouveau `Secret`, le kubelet de chaque nœud détecte le changement et met à jour le volume projeté — mais uniquement à son intervalle de synchronisation, soit environ 60 secondes par défaut. Les pods de longue durée qui ne lisent le certificat qu'au démarrage continueront à utiliser l'ancien jusqu'à leur redémarrage. Utilisez soit un sidecar comme `reloader` pour déclencher des restarts roulants sur changement de Secret, soit une bibliothèque (callback `tls.GetCertificate` en Go, abonnement SDS d'Envoy) qui relit le fichier à chaque connexion. Le piège est silencieux : le certificat est renouvelé, les dashboards sont au vert, et le workload présente toujours l'ancien.

Les rate limits ACME publics finiront par mordre

Let's Encrypt impose, à l'heure d'écriture, 300 certificats par domaine enregistré et par semaine (relevé de 50 en janvier 2024) et 5 certificats dupliqués par semaine (même ensemble exact de SAN). Un cluster qui fait tourner 200 microservices sur `*.example.com` avec rotation quotidienne atteindra la limite de doublons en deux jours et la limite par domaine peu après. Les options réalistes : un certificat wildcard à l'apex (un cert, plusieurs services), un point d'accès ACME privé sans ces limites, ou les deux — ACME public pour l'ingress, ACME privé pour tout ce qui vit à l'intérieur du cluster.

L'audit et la visibilité demandent quelque chose hors du cluster

cert-manager conserve les dernières révisions de `CertificateRequest` par Certificate, et cet historique suffit à un post-mortem in-cluster. Il ne suffit pas à un inventaire d'entreprise : il vit à l'intérieur d'un seul cluster, est borné par `revisionHistoryLimit`, et n'a aucune notion des certificats émis hors du cluster (load balancers, signature de code, IoT, ADCS). Un déploiement sérieux pousse chaque `CertificateRequest` et chaque événement `Certificate` vers une plateforme CLM externe, où ils côtoient le reste des certificats de l'organisation et peuvent être requêtés, alertés et reportés en un seul endroit.

Comment nous aidons

Evertrust & Kubernetes cert-manager : le projet open source

ACME privé pour cert-manager, prêt à l'emploiEvertrust PKI expose un point d'accès ACME conforme à la RFC 8555 adossé à votre CA privée — pointez n'importe quel ClusterIssuer cert-manager dessus. Aucun rate limit public, contrôle complet des politiques sur les types de clé, les durées, les SAN et les EKU, et identifiants gérés via votre IAM existant. Le même `ClusterIssuer` que vos équipes applicatives comprennent déjà pointe désormais vers une autorité que vous gouvernez réellement.

Visibilité unifiée multi-clusterEvertrust CLM ingère chaque événement d'émission de chaque instance cert-manager dans chaque cluster et le corrèle avec le reste des certificats de l'organisation (load balancers, signature de code, IoT, ADCS, CAs publiques). Un seul inventaire, une seule vue des expirations, un seul endroit pour répondre à « où ce CN est-il déployé ? » — ce que l'historique in-cluster de cert-manager ne peut pas vous donner seul.

Garde-fous de politique sur toute la plateformedéfinissez une fois pour toutes quelles CA, quels algorithmes de clé, quelles durées et quels motifs de SAN sont autorisés pour quels clusters, namespaces et trust domains. Les requêtes cert-manager qui violent la politique sont refusées à la frontière de la PKI, pas après coup en audit. Les équipes applicatives gardent leur workflow déclaratif ; l'équipe PKI garde les contrôles.