Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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-sdv pour automatiser les opérations DNS

Issuer vs ClusterIssuer

  • Issuer : Ressource namespacée, utilisable uniquement dans son namespace
  • ClusterIssuer : Ressource cluster-wide, utilisable depuis tous les namespaces

Staging vs Production

Let's Encrypt propose deux environnements :

EnvironnementUsageLimitesValidité certificat
StagingTests, développementAucune❌ Non reconnu par les navigateurs
ProductionProduction50 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 kubeconfig valide pointant vers votre cluster SdV
  • Une entrée DNS (A ou CNAME) pointant vers l'IP de l'Ingress privé 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 :

Terminal
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.

Terminal
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 :

Terminal
kubectl get pods -n cert-manager

Vous devriez voir 3 pods en état Running :

  • cert-manager-<id> : Contrôleur principal
  • cert-manager-webhook-<id> : Webhook de validation
  • cert-manager-cainjector-<id> : Injection de CA bundles

Vérifiez également les CRDs installées :

Terminal
kubectl get crd | grep cert-manager

Vous 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

Terminal
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-chart

Vérification du webhook

Vérifiez que le pod webhook SdV est opérationnel :

Terminal
kubectl get pods -n cert-manager -l app=cert-manager-webhook-sdv

Création des Issuers DNS01

Par défaut, le webhook SdV crée automatiquement deux ClusterIssuers :

Terminal
kubectl get clusterissuer

Sortie attendue :

NAME                                   READY   AGE
cert-manager-clusterissuer-staging     True    5m
cert-manager-clusterissuer-production  True    5m

Si vous préférez créer des Issuers namespacés, voici les manifestes :

issuer-dns01-staging.yaml
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
Terminal
# 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.yaml

Mise en œuvre dans un projet

Le processus se déroule en trois étapes :

  1. Déploiement de l'exemple
  2. Génération d'un certificat TLS provenant d'un Issuer de staging
  3. Génération d'un certificat TLS provenant d'un Issuer de 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 ConfigMap contenant notre page HTML
  • Un Deployment de nginx qui utilise le ConfigMap
  • Un Service pour permettre d'atteindre le serveur nginx
  • Un Ingress pour nous permettre d'accéder à nginx depuis le FQDN choisi
namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: letsencrypt-example
Terminal
kubectl apply -f namespace.yaml
kubectl apply -f service.yaml
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f ingress.yaml

Sortie attendue :

namespace/letsencrypt-example created
service/nginx-svc created
configmap/nginx-html created
deployment.apps/nginx created
ingress.networking.k8s.io/nginx-ingress created

Vérifiez que vous accédez bien à nginx de façon non sécurisée :

Terminal
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

certificate.yaml
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-tls

Déployez le certificat

Terminal
kubectl apply -f certificate.yaml
certificate.cert-manager.io/wildcard-certificate created

Modifier 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 :

Terminal
kubectl -n letsencrypt-example edit ingress nginx-ingress

Modifiez l'Ingress pour ajouter la section TLS :

ingress.yaml
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: 80

Vérifier la génération du certificat

Terminal
kubectl -n letsencrypt-example describe certificate

Sortie 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 issued

La dernière ligne confirme que le certificat TLS a bien été créé. On peut vérifier le Secret créé :

Terminal
kubectl -n letsencrypt-example get secret nginx-tls

Sortie :

nginx-tls   kubernetes.io/tls   2      2m23s

Validation 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

Terminal
kubectl -n letsencrypt-example edit certificate wildcard-certificate

Modifiez 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-tls

Sauvegardez et patientez quelques secondes.

Vérifier le nouveau certificat

Terminal
kubectl -n letsencrypt-example describe certificate

Sortie 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 issued

Il n'y a plus qu'à vérifier dans le navigateur : tout est désormais valide !