Utilisation du challenge DNS de Let’s Encrypt
J’utilise beaucoup Let’s Encrypt mais j’ai rarement besoin d’un serveur web. Alors c’est parfois intéressant de regarder les autres manières de valider une demande de certificat. Ici j’ai décidé de jouer un peu avec DNS, pour fournir facilement un certificat TLS Let’s Encrypt à mon serveur IRC.
Configuration du serveur DNS
Knot est un logiciel de serveur DNS faisant autorité, c’est à dire un serveur DNS qui contient l’information demandée sans faire appel à un cache ou à un autre serveur DNS. Je gère mes propres zones DNS, par exemple opportunis.me, à l’aide de ce logiciel.
L’objectif est donc d’avoir une zone DNS qui pourra être modifiée automatiquement par mon client Let’s Encrypt pour y ajouter les enregistrements nécessaires à l’authentification. Par souci d’isolation, une zone spécifique à cet usage va être utilisée. En cas de compromission du serveur IRC, l’on ne pourra pas modifier d’autres informations.
Pour ce faire, sur mon serveur DNS je vais créer une nouvelle zone. Pour éviter de polluer le reste de la configuration, j’ai décidé de faire un dossier où chaque sous domaine pourra avoir son fichier de configuration :
mkdir -p /etc/knot/acme-conf.d/
echo "include: acme-conf.d/*.conf" >> /etc/knot/knot.conf
Il faut créer la clef nécessaire à l’authentification pour la modification de la zone par le serveur IRC :
keymgr tsig generate acme_irc_key > /etc/knot/acme-conf.d/irc.conf
Je modifie le fichier créé pour ajouter le reste de la configuration nécessaire au bon fonctionnement.
:~# cat /etc/knot/acme-conf.d/irc.conf
key:
- id: acme_irc_key
algorithm: hmac-sha256
secret: MaSuperClef
acl:
- id: acme_irc_acl
address: [10.100.4.19, 2a00:5881:4008:504::19]
action: update
key: acme_irc_key
zone:
- domain: _acme-challenge.irc.opportunis.me
file: "db.acme.irc.opportunis.me"
dnssec-signing: on
dnssec-policy: rsa
acl: acme_irc_acl
Ensuite, il faut créer un fichier de zone assez basique, par exemple ressemblant à :
:/etc/knot# cat /var/lib/knot/db.acme.irc.opportunis.me
;; Zone dump (Knot DNS 2.4.0)
_acme-challenge.irc.opportunis.me. 86400 SOA ns1.unicorntelecom.eu. root.unicorntelecom.eu. 2018021740 28800 7200 864000 86400
_acme-challenge.irc.opportunis.me. 3600 NS ns1.opportunis.me.
On demande à Knot de s’occuper de la signature de la zone, parce qu’il est gentil et sait faire ça tout seul, rendant la mise en place de DNSSEC presque triviale. Quand on se souvient jamais des commandes nécessaires pour ça, un restart du daemon fait l’affaire.
Il faut ajouter les enregistrements DS à la zone parente. On ne va pas rentrer dans le détail ici, mais les enregistrements DS, pour Delegation Signer, sont des condensats de la clé KSK, qui vont donc être signés par la zone parente. On pourrait s’amuser à les faire à la main. Mais knot vient avec un utilitaire de gestion de clefs pour DNSSEC : keymgr. Très pratique, on peut lui demander d’aller lire la configuration de knot, d’y chercher les informations nécessaires et de nous produire les enregistrements DS dont nous avons besoin.
Dans le cas présent, je modifie son retour pour deux choses : ajouter les points finaux à la fin des noms et ignorer le condensat SHA-1 critiqué en terme de sécurité :
:~# keymgr zone key ds _acme-challenge.irc.opportunis.me +active | tail -n 2 | sed 's/ .*/.&/'
_acme-challenge.irc.opportunis.me. DS 65020 8 2 8a0b120999530846e520a38f6b370001072eb8a843c351e635adf09d74d8c179
_acme-challenge.irc.opportunis.me. DS 65020 8 4 6625ada6353b78cf6274b9fbb2eac360f58265407260008b8069e669ffe0d85f9aa2da35a8bc085c071c2bb818ad718c
On ajoute donc ces enregistrements dans la zone parente, puis on recharge la zone. On peut maintenant passer à la configuration de notre client Let’s Encrypt.
Configuration du client Let’s Encrypt
On va commencer par créer le fichier qui contient la clef nécessaire à la modification de la zone. L’information est sous la forme « algorithm:ID:secret ». Par exemple dans mon cas, ça donnerait :
:~# cat /etc/letsencrypt/scripts/update-dns.key
hmac-sha256:acme_irc_key:MaSuperClef
Il faut ensuite scripter l’ajout et la suppression des enregistrements DNS nécessaires à la validation :
:~# cat /etc/letsencrypt/scripts/challenge-dns-add.bsh
#!/usr/bin/env bash
NSUPDATE="knsupdate -k /etc/letsencrypt/scripts/update-dns.key"
DNSSERVER="dns-ext.unicorntelecom.eu"
TTL=60
NAME="_acme-challenge.$CERTBOT_DOMAIN."
echo -e "server $DNSSERVER\nzone $NAME\norigin $NAME\nupdate add $NAME $TTL in TXT $CERTBOT_VALIDATION\nsend" | $NSUPDATE
exit 0
:~# cat /etc/letsencrypt/scripts/challenge-dns-clean.bsh
#!/usr/bin/env bash
NSUPDATE="knsupdate -k /etc/letsencrypt/scripts/update-dns.key"
DNSSERVER="dns-ext.unicorntelecom.eu"
TTL=60
NAME="_acme-challenge.$CERTBOT_DOMAIN."
echo -e "server $DNSSERVER\nzone $NAME\norigin $NAME\nupdate del $NAME $TTL in TXT $CERTBOT_VALIDATION\nsend" | $NSUPDATE
exit 0
On peut demander un certificat, nous sommes prêts ! On procède de la manière suivante pour avoir le moins d’interactions possibles :
certbot certonly --manual --manual-public-ip-logging-ok --agree-tos --preferred-challenges=dns --manual-auth-hook /etc/letsencrypt/scripts/challenge-dns-add.bsh --manual-cleanup-hook /etc/letsencrypt/scripts/challenge-dns-clean.bsh --renew-hook /etc/letsencrypt/scripts/reload-inspircd-cert.bsh -d irc.opportunis.me
Dans mon cas, le script renew-hook sert à recharger les informations liées à TLS d’un serveur inspircd, à l’aide d’un utilisateur opérateur sur le serveur IRC, créé pour l’occasion :
#!/usr/bin/env bash
set -uo
port=6669
user="MonSuperUSer"
password="MonSuperPassword"
cp /etc/letsencrypt/live/irc.opportunis.me/* /etc/inspircd/tls/
chown irc: /etc/inspircd/tls/
chmod 700 /etc/inspircd/tls/
exec 3<>/dev/tcp/localhost/$port
echo "NICK $user" >&3
echo "USER LetsEncrypt 0 0 :Lets Encrypt auto-update script" >&3
timeout 2s cat <&3
echo "OPER $user $password" >&3
timeout 2s cat <&3
echo "REHASH -ssl" >&3
echo QUIT >&3
timeout 2s cat <&3
Et voilà, un serveur IRC avec des jolis certificats Let’s Encrypt.