cert-manager : Let's Encrypt depuis K8S - DNS01
Ce guide détaille la mise en œuvre de certificats TLS automatisés via Let's Encrypt et cert-manager sur un cluster Kubernetes SdV, en utilisant la méthode de validation DNS01. Il s'adresse aux équipes DevOps/Ops/SRE souhaitant sécuriser leurs applications web avec HTTPS, en particulier pour les Ingress privés et les certificats wildcard.
Introduction
Let's Encrypt est une autorité de certification (CA) gratuite, automatisée et ouverte, permettant de générer des certificats TLS pour sécuriser les communications HTTPS. Sur Kubernetes, cert-manager automatise l'ensemble du cycle de vie des certificats : demande, validation, renouvellement et révocation.
Concepts clés
Méthode de validation DNS01
La validation DNS01 repose sur la création temporaire d'un enregistrement DNS TXT pour prouver le contrôle du domaine :
_acme-challenge.[votre-domaine] IN TXT "<token-validation>"Let's Encrypt vérifie que vous contrôlez le domaine en interrogeant le DNS. Cette méthode nécessite :
- Un compte avec accès API au fournisseur DNS (ici : API SdV)
- Un Token d'API SdV avec les droits de gestion DNS
- Le webhook
cert-manager-webhook-sdvpour automatiser les opérations DNS
Issuer vs ClusterIssuer
Issuer: Ressource namespacée, utilisable uniquement dans son namespaceClusterIssuer: Ressource cluster-wide, utilisable depuis tous les namespaces
Staging vs Production
Let's Encrypt propose deux environnements :
| Environnement | Usage | Limites | Validité certificat |
|---|---|---|---|
| Staging | Tests, développement | Aucune | ❌ Non reconnu par les navigateurs |
| Production | Production | 50 certificats/domaine/semaine | ✅ Valide et reconnu |
Le mode staging permet de valider votre configuration sans risquer d'atteindre les limites de production.
Certificats wildcard
Un certificat wildcard (*.example.com) couvre tous les sous-domaines de premier niveau :
- ✅
app.example.com,api.example.com,test.example.com - ❌
sub.app.example.com(sous-domaine de niveau 2)
Prérequis
Avant de démarrer, assurez-vous de disposer de :
- Helm 3.x (guide d'installation)
- kubectl configuré avec un
kubeconfigvalide pointant vers votre cluster SdV - Une entrée DNS (A ou CNAME) pointant vers l'IP de l'
Ingressprivé du cluster (pour ingress privés) ou public - Un Token d'API SdV avec les droits de gestion DNS pour votre zone
- Droits d'administration sur le cluster (création de namespaces, CRDs, ClusterRoles)
Installation de cert-manager
cert-manager est un composant central pour la gestion automatisée des certificats TLS sur Kubernetes. Il s'installe via Helm et déploie plusieurs contrôleurs, CRDs (Custom Resource Definitions) et webhooks.
Étape 1 : Ajout du chart Helm
Ajoutez le repository Jetstack à votre configuration Helm :
helm repo add jetstack https://charts.jetstack.io --force-update
helm repo updateÉtape 2 : Déploiement de cert-manager
Nous déployons cert-manager dans un namespace dédié cert-manager avec les CRDs incluses.
cat <<EOF > values.yaml
# Installation des CRDs (Custom Resource Definitions)
# Obligatoire pour utiliser les ressources Certificate, Issuer, ClusterIssuer
installCRDs: true
# Désactivation de Prometheus (optionnel)
# Activez si vous utilisez Prometheus pour la supervision
prometheus:
enabled: false
# Ressources recommandées pour la production
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
cpu: 100m
memory: 128Mi
EOF
helm upgrade --install \
--namespace cert-manager \
--create-namespace \
--version "v1.14.2" \
--values values.yaml \
cert-manager \
jetstack/cert-managerÉtape 3 : Vérification de l'installation
Après quelques instants, vérifiez que tous les Pods cert-manager sont opérationnels :
kubectl get pods -n cert-managerVous devriez voir 3 pods en état Running :
cert-manager-<id>: Contrôleur principalcert-manager-webhook-<id>: Webhook de validationcert-manager-cainjector-<id>: Injection de CA bundles
Vérifiez également les CRDs installées :
kubectl get crd | grep cert-managerVous devriez voir plusieurs CRDs dont certificates.cert-manager.io, issuers.cert-manager.io, clusterissuers.cert-manager.io.
Installation du webhook SdV
Le webhook cert-manager-webhook-sdv est un composant spécifique SdV qui permet à cert-manager d'interagir avec l'API DNS de SdV pour créer automatiquement les enregistrements TXT nécessaires à la validation DNS01.
Déploiement du webhook
cat <<EOF > values.webhook.yaml
# Email de contact pour Let's Encrypt
contactEmail: admin@example.com
# Token d'API SdV pour la gestion DNS
applicationToken: "VOTRE_TOKEN_SDP_ICI"
EOF
helm upgrade --install \
--namespace cert-manager \
--create-namespace \
--version "0.8.0" \
--values values.webhook.yaml \
cert-manager-webhook-sdv \
oci://sdv-registry-ext.sdv.fr/devops/cert-manager-webhook-sdv-chartVérification du webhook
Vérifiez que le pod webhook SdV est opérationnel :
kubectl get pods -n cert-manager -l app=cert-manager-webhook-sdvCréation des Issuers DNS01
Par défaut, le webhook SdV crée automatiquement deux ClusterIssuers :
kubectl get clusterissuerSortie attendue :
NAME READY AGE
cert-manager-clusterissuer-staging True 5m
cert-manager-clusterissuer-production True 5mSi vous préférez créer des Issuers namespacés, voici les manifestes :
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-dns-staging
namespace: letsencrypt-example
spec:
acme:
# Serveur ACME de staging Let's Encrypt
server: https://acme-staging-v02.api.letsencrypt.org/directory
# Email de contact pour les notifications
email: admin@example.com
# Secret pour stocker la clé privée du compte ACME
privateKeySecretRef:
name: letsencrypt-dns-staging
# Solver DNS01 avec webhook SdV
solvers:
- dns01:
webhook:
groupName: sdv
solverName: sdv
config:
apiKeySecretRef:
name: sdv-webhook-credentials
key: applicationToken# Créer d'abord le secret avec le token SdV
kubectl create secret generic sdv-webhook-credentials \
--namespace letsencrypt-example \
--from-literal=applicationToken='VOTRE_TOKEN_SDP_ICI'
# Déployer les Issuers
kubectl apply -f issuer-dns01-staging.yaml
kubectl apply -f issuer-dns01-production.yamlMise en œuvre dans un projet
Le processus se déroule en trois étapes :
- Déploiement de l'exemple
- Génération d'un certificat TLS provenant d'un
Issuerde staging - Génération d'un certificat TLS provenant d'un
Issuerde production
Déploiement de l'exemple
À titre d'exemple, nous allons déployer un serveur nginx qui sert une simple page statique.
Les ressources à déployer sont :
- Un
Namespace - Un
ConfigMapcontenant notre page HTML - Un
Deploymentde nginx qui utilise leConfigMap - Un
Servicepour permettre d'atteindre le serveur nginx - Un
Ingresspour nous permettre d'accéder à nginx depuis le FQDN choisi
apiVersion: v1
kind: Namespace
metadata:
name: letsencrypt-examplekubectl apply -f namespace.yaml
kubectl apply -f service.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f ingress.yamlSortie attendue :
namespace/letsencrypt-example created
service/nginx-svc created
configmap/nginx-html created
deployment.apps/nginx created
ingress.networking.k8s.io/nginx-ingress createdVérifiez que vous accédez bien à nginx de façon non sécurisée :
curl http://[FQDN]/Sortie :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html lang="en"><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><title>Letsencrypt</title></head><body bgcolor="#a3d9ff"></body></html>Génération d'un certificat TLS provenant d'un Issuer de staging
Pour que cert-manager puisse traiter vos demandes de génération de certificats TLS, il faut créer des Issuers ou ClusterIssuers.
Let's Encrypt permet la génération de deux catégories de certificats TLS :
- Certificats de production : chaîne de signature complète, avec restrictions sur le nombre de certificats générés par jour
- Certificats de staging : chaîne de signature incomplète, sans restriction sur le nombre de certificats générés par jour
Contrairement à la méthode de validation HTTP-01, la méthode DNS-01 permet la génération de certificats TLS pour des domaines wildcards. Pour pouvoir les générer, nous devrons créer un Certificate plutôt que d'utiliser l'annotation dans l'Ingress.
Créez un manifeste Certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-certificate
namespace: letsencrypt-example
spec:
commonName: "*.[FQDN]"
dnsNames:
- [FQDN]
- "*.[FQDN]"
issuerRef:
kind: Issuer
name: letsencrypt-dns-staging
secretName: nginx-tlsDéployez le certificat
kubectl apply -f certificate.yamlcertificate.cert-manager.io/wildcard-certificate createdModifier l'Ingress
Il faut maintenant modifier l'Ingress pour ajouter le nœud tls qui précise :
- Le(s) FQDN concerné(s) par la génération du certificat TLS
- Le nom du Secret Kubernetes qui sera créé et qui contiendra les clés privée/publique
Éditez l'Ingress :
kubectl -n letsencrypt-example edit ingress nginx-ingressModifiez l'Ingress pour ajouter la section TLS :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
namespace: letsencrypt-example
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: public
tls:
- secretName: nginx-tls
hosts:
- [FQDN]
rules:
- host: [FQDN]
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc
port:
number: 80Vérifier la génération du certificat
kubectl -n letsencrypt-example describe certificateSortie complète
Name: wildcard-certificate
Namespace: letsencrypt-example
Labels: <none>
Annotations: API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Creation Timestamp: 2022-04-28T08:44:35Z
Generation: 1
Resource Version: 230554098
Self Link: /apis/cert-manager.io/v1/namespaces/letsencrypt-example/certificates/wildcard-certificate
UID: b8b35833-0c1d-4246-9cea-404c80748b38
Spec:
Common Name: *.[FQDN]
Dns Names:
[FQDN]
*.[FQDN]
Issuer Ref:
Kind: Issuer
Name: letsencrypt-dns-staging
Secret Name: nginx-tls
Status:
Conditions:
Last Transition Time: 2022-04-28T08:58:38Z
Message: Certificate is up to date and has not expired
Observed Generation: 2
Reason: Ready
Status: True
Type: Ready
Not After: 2022-07-27T07:58:36Z
Not Before: 2022-04-28T07:58:37Z
Renewal Time: 2022-06-27T07:58:36Z
Revision: 1
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 2m18s cert-manager Issuing certificate as Secret does not exist
Normal Generated 2m17s cert-manager Stored new private key in temporary Secret resource "wildcard-certificate-vq2ms"
Normal Requested 2m17s cert-manager Created new CertificateRequest resource "wildcard-certificate-tt25h"
Normal Issuing 2m15s cert-manager The certificate has been successfully issuedLa dernière ligne confirme que le certificat TLS a bien été créé. On peut vérifier le Secret créé :
kubectl -n letsencrypt-example get secret nginx-tlsSortie :
nginx-tls kubernetes.io/tls 2 2m23sValidation dans le navigateur
Pour finir de valider le processus de génération, testez avec un navigateur en accédant au FQDN déclaré.
Génération d'un certificat TLS provenant d'un Issuer de production
Générons désormais le certificat par notre Issuer de production en éditant notre Certificate et modifiant le nœud spec.issuerRef.name pour pointer vers letsencrypt-dns-production.
Éditez le Certificate
kubectl -n letsencrypt-example edit certificate wildcard-certificateModifiez la référence de l'Issuer pour utiliser la production :
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-certificate
namespace: letsencrypt-example
spec:
commonName: "*.[FQDN]"
dnsNames:
- [FQDN]
- "*.[FQDN]"
issuerRef:
kind: Issuer
name: letsencrypt-dns-production
secretName: nginx-tlsSauvegardez et patientez quelques secondes.
Vérifier le nouveau certificat
kubectl -n letsencrypt-example describe certificateSortie complète
Name: wildcard-certificate
Namespace: letsencrypt-example
Labels: <none>
Annotations: API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Creation Timestamp: 2022-04-28T08:44:35Z
Generation: 2
Resource Version: 230556933
Self Link: /apis/cert-manager.io/v1/namespaces/letsencrypt-example/certificates/wildcard-certificate
UID: b8b35833-0c1d-4246-9cea-404c80748b38
Spec:
Common Name: *.k8s-priv2.gretel.com
Dns Names:
k8s-priv2.gretel.com
*.k8s-priv2.gretel.com
Issuer Ref:
Kind: Issuer
Name: letsencrypt-dns-production
Secret Name: nginx-tls
Status:
Conditions:
Last Transition Time: 2022-04-28T08:58:38Z
Message: Certificate is up to date and has not expired
Observed Generation: 2
Reason: Ready
Status: True
Type: Ready
Not After: 2022-07-27T07:58:36Z
Not Before: 2022-04-28T07:58:37Z
Renewal Time: 2022-06-27T07:58:36Z
Revision: 2
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 16m cert-manager-certificates-trigger Issuing certificate as Secret does not exist
Normal Generated 16m cert-manager-certificates-key-manager Stored new private key in temporary Secret resource "wildcard-certificate-b8mnt"
Normal Requested 16m cert-manager-certificates-request-manager Created new CertificateRequest resource "wildcard-certificate-lp7ph"
Normal Issuing 4m37s cert-manager-certificates-trigger Issuing certificate as Secret was previously issued by Issuer.cert-manager.io/letsencrypt-dns-staging
Normal Reused 4m37s cert-manager-certificates-key-manager Reusing private key stored in existing Secret resource "nginx-tls"
Normal Requested 4m37s cert-manager-certificates-request-manager Created new CertificateRequest resource "wildcard-certificate-zmp5b"
Normal Issuing 2m1s (x2 over 13m) cert-manager-certificates-issuing The certificate has been successfully issuedIl n'y a plus qu'à vérifier dans le navigateur : tout est désormais valide !