Comme beaucoup sans doute, j’ai été désappointé lorsque le projet Let’s Encrypt a annoncé en janvier 2018 qu’il suspendait définitivement la méthode de création de certificat dite « challenge SNI » - la plus facile à mettre en œuvre - suite à la découverte d’une faille de sécurité conceptuelle (cf. « Update Regarding ACME TLS-SNI and Shared Hosting Infrastructure »). Pas le choix, pour générer de nouveaux certificats TLS/SSL, je devais me rabattre sur une des deux méthodes restantes : le « challenge HTTP » ou le « challenge DNS ».

Le « challenge HTTP » implique qu’un serveur web tourne sur la machine cible (ce qui n’est pas toujours le cas puisque les certificats TLS/SSL peuvent être utilisés à d’autres fins que le web) et que vous traitiez de manière spécifique l’url /.well-known/ (ce qui peut être compliqué avec les serveurs applicatifs).

Le « challenge DNS » est plus complexe à mettre en œuvre mais il me semble plus générique. J’ai donc opté pour celui-ci.

Les zones et serveurs DNS des plateformes que j’administre sont gérés par OVH ou Gandi et je m’appuie sur Certbot pour générer et renouveler les certificats TLS/SSL. Après avoir effectué bien des recherches, avoir suivi bien des tutoriels et avoir même ouvert un ticket sur le forum de Let’s Encrypt, j’ai fini par trouver une méthode basée sur Certbot et le « challenge DNS », qui fonctionne aussi bien avec OVH qu’avec Gandi, au prix de quelques ajustements spécifiques imposés par leur API et le mode de mise à jour de leurs serveurs DNS.

La méthode

Je me suis appuyé sur le tutoriel « Easy SSL Auto-Renewal via DNS using Certbot with Lexicon » qui se veut générique. Son contenu s’adapte au fournisseur de service DNS sélectionné dans la copieuse liste déroulante. Malgré cet effort remarquable, les scripts proposés nécessitent quelques ajustements pour OVH et Gandi.

Les messages d’erreur renvoyés par Certbot pouvant être sibyllins, j’aurais sans doute cherché pendant longtemps la cause du problème que je rencontrais sans l’aide de la communauté Let’s Encrypt. Un grand merci à elle.

Pour faire court, le challenge DNS est basé sur l’ajout d’enregistrements temporaires dans la zone DNS. Plusieurs conditions sont nécessaires à sa réussite :

  • Pour pouvoir automatiser la procédure, le fournisseur du service DNS doit exposer une API.

  • L’utilisation de cette API étant naturellement restreinte, nous devons demander au fournisseur une clé nous autorisant à modifier notre zone DNS via cette API.

  • Entre l’ajout de l’enregistrement temporaire et le contrôle d’existence de cet enregistrement par les serveurs du projet Let’s Encrypt, nous devons laisser le temps à l’information de se propager aux serveurs DNS. L’auteur du tutoriel introduit donc une pause finale de 30 secondes dans son script. L’expérience montre que ce n’est pas assez avec OVH et Gandi. Il est nécessaire d’attendre 2 minutes (120 secondes) avec OVH et 25 minutes (1500 secondes) avec Gandi pour éviter tout échec.

Rendez-vous sur les pages ci-dessous pour obtenir une clé d’API :

Concernant Gandi, l’obtention d’une clé d’API opérationnelle (i.e. « de production ») passe par l’activation préliminaire de l’API sur la plateforme de test (OT&E). Vous obtiendrez dans les deux cas un jeton d’authentification de 24 caractères alphanumériques que nous nommerons AUTH_TOKEN dans notre script. Vous pouvez faire des tests avec le jeton OT&E, mais seul le jeton de production sera nécessaire par la suite.

Concernant OVH, les champs « Script name » et « Script description » du formulaire sont à renseigner à votre guise. Vous devez par contre veiller à associer les droits suffisants à cette clé d’API :

  • GET : /domain/zone/*
  • POST : /domain/zone/*
  • PUT : /domain/zone/*
  • DELETE : /domain/zone/*/record/*

Après validation du formulaire, OVH fournit un jeu de trois clés nommées « Application Key », « Application Secret » et « Consumer Key » que nous nommerons respectivement AUTH_APPLICATION_KEY, AUTH_APPLICATION_SECRET et AUTH_CONSUMER_KEY dans notre script. Une quatrième information sera nécessaire pour OVH, c’est le point d’entrée, que nous nommerons AUTH_ENTRYPOINT dans notre script et dont la valeur est « ovh-eu » en Europe.

Je ne m’attarde pas sur l’installation de Certbot, ce n’est pas l’objet de cet article. Si vous utilisez une Debian Stretch, sachez simplement que la dernière version de Certbot est disponible dans le dépôt « stretch-backports » :

apt update
apt install -t stretch-backports certbot

Pour exploiter les API de Gandi et d’OVH, nous allons avoir besoin de l’outil dns-lexicon. Nous devons l’installer via le gestionnaire de paquets Python Pip car il n’est pas disponible sous la forme de paquet Debian :

apt install python3-pip
pip3 install dns-lexicon

Ceci étant fait, nous devons fournir à Certbot un script lui permettant d’ajouter et de supprimer des enregistrements de la zone DNS. Pour rendre le script générique, nous stockerons les clés d’API dans un fichier séparé :

Pour OVH :

cd /etc/letsencrypt
touch lexicon-ovh.sh ovh-api-keys
chmod 700 lexicon-ovh.sh
chmod 600 ovh-api-keys
editor lexicon-ovh.sh
editor ovh-api-keys

Pour Gandi :

cd /etc/letsencrypt
touch lexicon-gandi.sh gandi-api-keys
chmod 700 lexicon-gandi.sh
chmod 600 gandi-api-keys
editor lexicon-gandi.sh
editor gandi-api-keys

NB : Les permissions d’accès aux fichiers ovh-api-keys et gandi-api-keys doivent être drastiquement limitées car ils contiennent des informations sensibles.

Voici le contenu des fichiers :

Pour OVH :

  • /etc/letsencrypt/ovh-api-keys :

    AUTH_ENTRYPOINT="ovh-eu"
    AUTH_APPLICATION_KEY="xxxxxxxxxxxxxxxx"
    AUTH_APPLICATION_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    AUTH_CONSUMER_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    
  • /etc/letsencrypt/lexicon-ovh.sh :

    #!/bin/bash
    
    . /etc/letsencrypt/ovh-api-keys
    
    if [ "$1" != "list" ]; then
        NAME_VALUE="_acme-challenge.${CERTBOT_DOMAIN}"
        CONTENT_VALUE="${CERTBOT_VALIDATION}"
    else
        NAME_VALUE=""
        CONTENT_VALUE=""
    fi
    
    /usr/local/bin/lexicon ovh \
                           --auth-entrypoint "${AUTH_ENTRYPOINT}" \
                           --auth-application-key "${AUTH_APPLICATION_KEY}" \
                           --auth-application-secret "${AUTH_APPLICATION_SECRET}" \
                           --auth-consumer-key "${AUTH_CONSUMER_KEY}" \
                           --name "${NAME_VALUE}" \
                           --content "${CONTENT_VALUE}" \
                           "$1" "${CERTBOT_DOMAIN}" TXT \
            || exit 255
    
    if [ "$1" == "create" ]; then
      sleep 120
    fi
    

Pour Gandi :

  • /etc/letsencrypt/gandi-api-keys :

    AUTH_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxx"
    
  • /etc/letsencrypt/lexicon-gandi.sh :

    #!/bin/bash
    
    . /etc/letsencrypt/gandi-api-keys
    
    if [ "$1" != "list" ]; then
        NAME_VALUE="_acme-challenge.${CERTBOT_DOMAIN}"
        CONTENT_VALUE="${CERTBOT_VALIDATION}"
    else
        NAME_VALUE=""
        CONTENT_VALUE=""
    fi
    
    /usr/local/bin/lexicon gandi \
                           --auth-token="${AUTH_TOKEN}" \
                           --name "${NAME_VALUE}" \
                           --content "${CONTENT_VALUE}" \
                           "$1" "${CERTBOT_DOMAIN}" TXT \
        || exit 255
    
    if [ "$1" == "create" ]; then
      sleep 1500
    fi
    

Nous pouvons maintenant demander de nouveaux certificats à Let’s Encrypt.

Avec OVH, la commande de génération du certificat valide pour les deux domaines « example.com » et « www.example.com » est la suivante :

/usr/bin/certbot certonly --manual \
                          --manual-public-ip-logging-ok \
                          --manual-auth-hook "/etc/letsencrypt/lexicon-ovh.sh create" \
                          --manual-cleanup-hook "/etc/letsencrypt/lexicon-ovh.sh delete" \
                          --preferred-challenges dns \
                          -d example.com \
                          -d www.example.com

Lorsque le script rend la main, un peu plus de 4 minutes plus tard (2 x 120 secondes), le certificat est disponible sur notre système :

  • /etc/letsencrypt/live/example.com/privkey.pem (clé privée)
  • /etc/letsencrypt/live/example.com/cert.pem (certificat)
  • /etc/letsencrypt/live/example.com/chain.pem (chaine de confiance intermédiaire)
  • /etc/letsencrypt/live/example.com/fullchain.pem (concaténation du certificat et de la chaine de confiance intermédiaire)

La commande équivalente pour Gandi est bien évidemment :

/usr/bin/certbot certonly --manual \
                          --manual-public-ip-logging-ok \
                          --manual-auth-hook "/etc/letsencrypt/lexicon-gandi.sh create" \
                          --manual-cleanup-hook "/etc/letsencrypt/lexicon-gandi.sh delete" \
                          --preferred-challenges dns \
                          -d example.com \
                          -d www.example.com

Ce script rend la main un peu plus de 50 minutes plus tard (2 x 1500 secondes).

Certbot enregistrant les paramètres à la création des certificats, la commande qui vérifie s’il est nécessaire de les renouveler et qui, le cas échéant, le fait, est des plus simples :

/usr/bin/certbot -q renew

Une dernière remarque, valable quelque soit le challenge choisi : pour que les nouveaux certificats soient pris en compte par les applications, les services correspondants doivent être redémarrés. Pour cela, on crée des « scripts de déploiement » dans le répertoire /etc/letsencrypt/renewal-hooks/deploy. Certbot les exécutera automatiquement dans leur ordre alphabétique à l’issue d’une création ou d’un renouvèlement effectif de certificat :

  • /etc/letsencrypt/renewal-hooks/deploy/10postfix :

    #!/bin/sh
    /bin/systemctl restart postfix
    
  • /etc/letsencrypt/renewal-hooks/deploy/20dovecot :

    #!/bin/sh
    /bin/systemctl restart dovecot
    
  • /etc/letsencrypt/renewal-hooks/deploy/30apache2 :

    #!/bin/sh
    /bin/systemctl restart apache2
    

Voilà, vous avez tous les éléments dont vous avez besoin pour gérer vos certificats TLS/SSL.

Conclusion

Quand on a connu la rapidité d’obtention des certificats avec le « challenge SNI », le recours au « challenge DNS » est un peu frustrant de par sa lenteur. Mais on ne peut pas en vouloir à Let’s Encrypt. Primo, il fallait écarter cette méthode de vérification qui pouvait être mise en défaut dans certains cas. Secundo, la lenteur constatée est imputable aux intermédiaires et au rythme d’actualisation de leurs serveurs DNS. Une personne administrant elle-même ses serveurs DNS et pouvant les mettre à jour dans l’instant ne verra pas la différence.