Dans l’article précédent, je vous ai expliqué comment installer la bonne version de certbot pour générer des certificats wildcard letsencrypt et je vous ai montré comment en générer un avec une édition manuelle de la zone DNS.

Dans ce second article de cette série sur la génération de certificats wildcard letsencrypt, je vais vous montrer comment générer automatiquement un certificat wildcard.

Nous allons commencer par un domaine dont le DNS est géré sur un fournisseur DNS pour lequel il exist un plugin pour certbot. Nous utiliserons AWS route53 dans cet exemple

Ensuite, nous verrons comment utiliser des scripts maison pour faire l’ajout de l’entrée DNS nécessaire au challenge ACME DNS-01 (le protocole de validation pour l’émission du certificat wildcard). Nous utiliserons cette technique pour générer des certificats lorsque la zone DNS est hébergée sur un bind local ou lorsqu’elle est hébergée sur les DNS de gandi (LiveDNS) ou d'OVH.

Génération de certificat wildcard avec DNS route53

Pour pouvoir créer les entrées DNS nécessaires à la validation du domaine, il vous faudra une clé d’accès et une clé secrète pour l’API AWS. Vous devez donc commencer par créer un utilisateur dans la console IAM de la façon suivante :

  1. Choisissez “Ajouter un utilisateur”
  2. Appelez-le “letsencrypt_validation” par exemple et choisissez un accès par programmation (pour obtenir une clé d’API) et cliquez sur “Suivant” img1

  3. Choisissez d’attacher directement les stratégies existantes et cliquez sur “Créer une stratégie” pour créer les droits de votre nouvel utilisateur img2

  4. Créez une nouvelle stratégie pour le service Route53 qui autorise les actions “GetChange”, “ListHostedZones” et “ChangeRessourceRecordSets” sur tous les objets (vous pouvez sinon restreindre l’action “ChangeRessourceRecordSets” sur les seuls domaines pour lesquels vous souhaitez pouvoir effectuer une validation DNS-01) img3

  5. Nommez votre stratégie (par exemple letsencrypt_validation) et validez img4

  6. Revenez à votre utilisateur en cours de création, cliquez sur “Actualiser” pour recharger la liste des stratégies puis recherchez celle que vous venez de créer et cochez la case en début de ligne pour l’associer à votre utilisateur, puis cliquez sur “Suivant” img5

  7. Vérifiez le résumé et cliquez sur “Créer un utilisateur” pour procéder à la création img6

  8. Dans le résumé de création de l’utilisateur, notez l’ID de clé d’accès ainsi que la clé d’accès secrète qui vous serviront à vous authentifier auprès de l’API route53 img7

Le plugin route53 de certbot utilise ces identifiants. Nous allons donc installer le paquet awscli et configurer les identifiants :

$ apt-get install awscli
$ aws configure
AWS Access Key ID [None]: AKIAXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: 74Bo!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Default region name [None]: eu-central-1
Default output format [None]:

Nous allons ensuite installer le plugin route53 de certbot :

$ /opt/eff.org/certbot/venv/bin/pip install certbot-route53
Collecting certbot-route53
  Downloading certbot-route53-0.2.0.tar.gz
[...]
Successfully built certbot-route53
Installing collected packages: jmespath, python-dateutil, docutils, botocore, futures, s3transfer, boto3, certbot-dns-route53, certbot-route53
Successfully installed boto3-1.6.11 botocore-1.9.11 certbot-dns-route53-0.22.0 certbot-route53-0.2.0 docutils-0.14 futures-3.2.0 jmespath-0.9.3 python-dateutil-2.6.1 s3transfer-0.1.13

Et nous avons désormais tout ce qu’il nous faut pour générer un certificat wildcard letsencrypt avec un domaine géré sur route53. Nous utiliserons les options suivantes de certbot :

  • --server https://acme-v02.api.letsencrypt.org/directory On utilise l’API ACME v2
  • --dns-route53 L’entrée DNS pour le challenge sera créée via l’api route53
  • -d '*.domain.tld' le sujet du certificat qu’on souhaite émettre
$ cd /opt/eff.org/certbot/venv/bin
$ ./certbot certonly \
    --server https://acme-staging-v02.api.letsencrypt.org/directory \
    --dns-route53 \
    -d '*.domain.tld'
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Found credentials in shared credentials file: ~/.aws/credentials
Plugins selected: Authenticator dns-route53, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for domain.tld
Starting new HTTPS connection (1): route53.amazonaws.com
Waiting 10 seconds for DNS changes to propagate
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/domain.tld/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/domain.tld/privkey.pem
   Your cert will expire on 2018-06-15. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Et voilà ! nous avons généré automatiquement notre certificat wildcard letsencrypt pour un domaine géré sur route53. Et comme la validation s’est faite automatiquement, sans intervention manuelle, la renouvèlement se passera de la même façon.

Génération de certificat wildcard avec scripts maison d’édition de zone

Fonctionnement

Lors d’une génération de certificat avec validation manuelle, certbot dispose des options --manual-auth-hook et --manual-cleanup-hook qui permettent de définir des scripts qui doivent être appelés pour créer l’entrée dns avant la validation puis pour la supprimer après. Voir à ce sujet la documentation officielle.

Ces scripts sont appelés avec les variables d’environnements suivantes définies (pour une validation DNS-01) :

  • CERTBOT_DOMAIN : le domaine à valider
  • CERTBOT_VALIDATION : le contenu de l’entrée TXT à créer

Le script de nettoyage post-validation reçoit également la variable d’environnement CERBOT_AUTH_OUTPUT qui contient la sortie standard du script de validation. Ceci peut servir par exemple à passer entre les 2 scripts l’ID de l’enregistrement créé.

Je vous ai mis dans github tous les scripts utilisés ci-dessous. Ces scripts sont des exemples pour vous aider à développer les vôtres. Commencez par cloner ces scripts dans /opt/le-scripts :

    $ git clone https://github.com/sblaisot/certbot-dns-01-authenticators.git /opt/le-scripts

4 ensembles de scripts sont disponibles, le premier pour tester le fonctionnement de certbot avec des scripts de validation personnels, le second si vous avez votre serveur DNS bind sur la même machine que celle avec laquelle pour générez les certificats et les deux derniers pour générer des certificats wildcard letsencrypt lorsque vos zones DNS sont gérées sur le service LiveDNS de Gandi ou chez OVH.

Chaque ensemble est composé de 2 scripts : un script auth qui permet de créer l’entrée DNS pour la validation et un script cleanup qui permet de la retirer une fois la validation effectuée.

Un script pour tester

Les scripts du répertoire test se contentent d’afficher le contenu des variables d’environnements passées par certbot, pour en étudier le fonctionnement. De ce fait, ils ne créent ni ne suppriment aucune entrée DNS et la génération de certificat échoue forcément.

Néanmoins, ils nous permettent de voir comment cela fonctionne. Appelons certbot en lui demandant d’utiliser ces scripts :

$ cd /opt/eff.org/certbot/venv/bin
$ ./certbot certonly \
    --server https://acme-staging-v02.api.letsencrypt.org/directory \
    --manual-public-ip-logging-ok \
    --manual \
    --manual-auth-hook /opt/le-scripts/test/auth.sh \
    --manual-cleanup-hook /opt/le-scripts/test/cleanup.sh \
    -d '*.domain.tld'
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for domain.tld
Output from auth.sh:
------ AUTH ------
CERTBOT_VALIDATION=39sbL18pMHYSusn0zZoenN3kUtJyq7DU-XnpzkFQdBA
CERTBOT_DOMAIN=domain.tld
------ AUTH ------

Waiting for verification...
Cleaning up challenges
Output from cleanup.sh:
------ CLEANUP ------
CERTBOT_VALIDATION=39sbL18pMHYSusn0zZoenN3kUtJyq7DU-XnpzkFQdBA
CERTBOT_DOMAIN=domain.tld
CERTBOT_AUTH_OUTPUT=------ AUTH ------
CERTBOT_VALIDATION=39sbL18pMHYSusn0zZoenN3kUtJyq7DU-XnpzkFQdBA
CERTBOT_DOMAIN=domain.tld
------ AUTH ------
------ CLEANUP ------

Failed authorization procedure. domain.tld (dns-01): urn:ietf:params:acme:error:unauthorized :: The client lacks sufficient authorization :: No TXT record found at _acme-challenge.domain.tld

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: domain.tld
   Type:   unauthorized
   Detail: No TXT record found at _acme-challenge.domain.tld

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A/AAAA record(s) for that domain
   contain(s) the right IP address.

Nous voyons ici que les scripts sont appelés avant la validation pour créer l’entrée DNS et après la validation pour la supprimer. Ces scripts reçoivent bien les variables d’environnement de certbot.

Génération de certificat wildcard avec DNS bind local

Le répertoire local-bind contient des scripts qui permettent d’ajouter l’entrée de validation dans des fichiers de zone bind accessible en local sur la machine qui émet le certificat.

Ces scripts freezent la zone dns (pour éviter une rotation de clé dnssec lors de l’ajout de l’entrée), mettent à jour le numéro de série de la zone et ajoutent l’entrée (ou la suppriment pour le script de cleanup)

Pour les utiliser, copiez le fichier config.sh.example en config.sh et adaptez le pour y indiquer l’emplacement de vos fichiers de zone, le format de leur nom (qui doit contenir le nom de domaine) ainsi que la chaine à rechercher pour trouver le serial de la zone.

Vous pourrez ensuite générer votre certificat wildcard de la façon suivante :

$ cd /opt/eff.org/certbot/venv/bin
$ ./certbot certonly \
    --server https://acme-v02.api.letsencrypt.org/directory \
    --manual-public-ip-logging-ok \
    --manual \
    --manual-auth-hook /opt/le-scripts/local-bind/auth.sh \
    --manual-cleanup-hook /opt/le-scripts/local-bind/cleanup.sh \
    -d '*.domain.tld'
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for domain.tld
Output from auth.sh:
Freeze zone
Update serial
Add challenge to zone file
Release zone
A zone reload and thaw was started.
Check the logs to see the result.
Wait 5 seconds for repplication to masters
Done

Waiting for verification...
Cleaning up challenges
Output from cleanup.sh:
Freeze zone
Update serial
Remove challenge from zone file
Release zone
A zone reload and thaw was started.
Check the logs to see the result.
Done


IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/domain.tld/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/domain.tld/privkey.pem
   Your cert will expire on 2018-06-16. To obtain a new or tweaked
   version of this certificate in the future, simply run
   letsencrypt-auto again. To non-interactively renew *all* of your
   certificates, run "letsencrypt-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Il serait également possible de configurer bind pour accepter les mises à jour dynamiques (rfc2136) et utiliser le plugin dns-rfc2136 de certbot. Je vous le laisse en exercice ;)

Génération de certificat wildcard avec DNS Gandi (LiveDNS)

Maintenant qu’on a bien joué avec nos scripts de test, voici un exemple plus près de la vie réelle pour la génération d’un certificat wildcard lorsque la zone DNS est hébergée chez Gandi.

Il est indispensable que votre zone DNS soit hébergée sur la solution LiveDNS de gandi (v5) qui permet une mise à jour des enregistrement DNS instantanée contrairement à l’ancienne solution (v4). Dans l’ancienne version, il fallait plusieurs minutes pour que vos enregistrements DNS soient créés et ceci n’est pas compatible avec la validation de domaine.

Ensuite, il vous suffit dans le répertoire /opt/le-scripts/gandi-livedns de copier le fichier config.py.example en config.py et d’ajuster les paramètres :

  • livedns_api = "https://dns.api.gandi.net/api/v5/" c’est le endpoint de l’API LiveDNS de Gandi. Il n’est habituellement pas nécessaire de modifier ce paramètre
  • livedns_apikey = "YOUR-KEY-HERE" Votre clé d’API LiveDNS que vous pourrez générer dans vos paramètres de compte à la rubrique “Modification de mot de passe et configuration des restrictions d’accès
  • livedns_sharing_id = None Si vos domaines ne sont pas rattachés directement à votre compte mais gérés dans une organisation, il faudra indiquer ici votre sharing_id qui permet de gérer l’organisation à partir de votre compte. Vous le retrouverez dans l’URL du manager lorsque vous listez votre domaine sous la forme d'un GUID.

Une fois la configuration effectuée, il ne vous reste plus qu’à générer votre certificat :

$ cd /opt/eff.org/certbot/venv/bin
$ ./certbot certonly \
    --server https://acme-v02.api.letsencrypt.org/directory \
    --manual-public-ip-logging-ok \
    --manual \
    --manual-auth-hook /opt/le-scripts/gandi-livedns/auth.py \
    --manual-cleanup-hook /opt/le-scripts/gandi-livedns/cleanup.py \
    -d '*.domain.tld'
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for domain.tld
Output from auth.py:
all good, entry created

Waiting for verification...
Cleaning up challenges
Output from cleanup.py:
all good, entry deleted


IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/domain.tld/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/domain.tld/privkey.pem
   Your cert will expire on 2018-06-15. To obtain a new or tweaked
   version of this certificate in the future, simply run
   letsencrypt-auto again. To non-interactively renew *all* of your
   certificates, run "letsencrypt-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Génération de certificat wildcard avec DNS OVH

Et si vos domaines sont hébergés chez OVH, c'est la même chose.

On installe d'abord les prérequis :

$ sudo apt-get install python3-dnspython
$ sudo pip3 install ovh

Vous devez ensuite copier le fichier /opt/le-scripts/ovh/ovh.conf.example en /opt/le-scripts/ovh/ovh.conf et l'ajuster avec vos clés d'api.

Et vous pouvez ensuite génèrer le certificat :

$ cd /opt/eff.org/certbot/venv/bin/
$ ./certbot certonly \
    --server https://acme-v02.api.letsencrypt.org/directory \
    --manual-public-ip-logging-ok \
    --manual \
    --manual-auth-hook /opt/le-scripts/ovh/auth.py \
    --manual-cleanup-hook /opt/le-scripts/ovh/cleanup.py \
    -d '*.domain.tld'

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for domain.tld
Output from auth.py:
All good, DNS record created
Waiting for record to be available on DNS servers
DNS record available on DNS server

Waiting for verification...
Cleaning up challenges
Output from cleanup.py:
All good, DNS record deleted


IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/domain.tld/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/domain.tld/privkey.pem
   Your cert will expire on 2018-06-17. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Points complémentaires

Emplacement des scripts et renouvèlement

Si vous devez déplacer les scripts sur votre système, le renouvèlement de certificat risque de ne pas les trouver, et donc d’échouer. Il vous faut alors mettre à jour le fichier de configuration du renouvèlement qui se trouve dans /etc/letsencrypt/renewal/<domain>.conf

Délai de création de l’entrée DNS

Il est indispensable pour la validation automatique (et donc pour le renouvèlement) que votre système DNS permette de créer immédiatement une entrée DNS. S’il vous faut plusieurs minutes/heures/jours pour que l’entrée DNS soit créée, vous ne pourrez pas générer de certificat wildcard avec cette méthode. Bien que le protocole ACME semble semble permettre la validation asynchrone, je ne sais pas quelle est la durée de vie maximale du jeton d’authentification et le client certbot ne le permet pas, il vous faudra donc trouver ou coder un autre client ACME.

Mais Hey, on est en 2018, il y a plein de super services DNS qui permettent de mettre en ligne immédiatement une entrée, avec un pilotage par api. Il est peut-être temps de changer de fournisseur DNS dans ce cas.

Et la sécurité dans tout ça ?

Dans les exemples donnés, le serveur qui génère le certificat letsencrypt a un contrôle total sur la zone DNS. Il n’est pas souhaitable, en réalité, que le serveur web qui a besoin du certificat ait la possibilité de modifier tout le contenu de la zone DNS. Dans ce cas, vous pouvez imaginer générer le certificat à partir d’une machine d’administration puis le pousser sur le serveur web qui en a besoin, ou développer une api interne accessible par le serveur web qui permette uniquement de gérer l’entrée _acme-challenge et qui fera le relai vers le fournisseur DNS.

Conclusion

Vous avez maintenant tous les éléments en main pour générer des certificats letsencrypt. Si vous vous posez encore des questions, n’hésitez pas à les laisser en commentaire, j’essaierai d’y répondre.


Comments

comments powered by Disqus