Description
Sous OpenBSD depuis 6.1, pour la génération de certificats avec l’autorité de certification LE , nous avons intégré au système de base le client acme, écrit en C, reconnu pour être sécurisé.
Je ne traiterais pas de la configuration, elle est très simple, et vraiment bien documentée dans le manpage ad hoc.
Ce mémo existe pour répertorier quoi faire dans le cas de sortie en erreur.
Dépannage
bad HTTP: 400
TL;DR : Le client acme n’arrive pas à se connecter à votre serveur web. Dans les faits, il faut en passer par une investigation et une explication un peu plus poussée…
Investigation :
Par exemple, voici la fin du message verbeux d’acme :
# acme-client -v mydomain.tld
(…)
acme-client: challenge, token: L8dcjbmFhbE6N2GtSKxEi9yzhR888oSdBdgzt7GnJbc, uri: https://acme-v02.api.letsencrypt.org/acme/chall-v3/6581640382/2HdrCw, status: -1
(…)
acme-client: https://acme-v02.api.letsencrypt.org/acme/chall-v3/6460247699/ixiI9A: bad HTTP: 400
acme-client: transfer buffer: [{ "type": "urn:ietf:params:acme:error:malformed", "detail": "Unable to update challenge :: authorization must be pending", "status": 400 }] (144 bytes)
acme-client: bad exit: netproc(70635): 1
Le réflexe à avoir est de basculer en mode doublement verbeux, ce qui nous donnera plus de détail :
# acme-client -vv mydomain.tld
(…)
acme-client: transfer buffer: [{ "identifier": { "type": "dns", "value": "www.mydomain.tld" }, "status": "invalid", "expires": "2020-08-24T22:24:39Z", "challenges": [ { "type": "http-01", "status": "invalid", "error": { "type": "urn:ietf:params:acme:error:connection", "detail": "Fetching http://mydomain.tld/.well-known/acme-challenge/xCefC0pD3W1VRLeWVYHmYmJj1Bf06G1hoskXdN1xIEg: Connection refused", "status": 400 }, "url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/6603509164/7d1MkA", "token": "xCefC0pD3W1VRLeWVYHmYmJj1Bf06G1hoskXdN1xIEg", "validationRecord": [ { "url": "http://www.mydomain.tld/.well-known/acme-challenge/xCefC0pD3W1VRLeWVYHmYmJj1Bf06G1hoskXdN1xIEg", "hostname": "www.mydomain.tld", "port": "80", "addressesResolved": [ "88.136.16.221", "2001:470:cc33::3" ], "addressUsed": "2001:470:cc33::3" }, { "url": "http://www.mydomain.tld/.well-known/acme-challenge/xCefC0pD3W1VRLeWVYHmYmJj1Bf06G1hoskXdN1xIEg", "hostname": "www.mydomain.tld", "port": "80", "addressesResolved": [ "88.136.16.221", "2001:470:cc33::3" ], "addressUsed": "88.136.16.221" }, { "url": "http://mydomain.tld/.well-known/acme-challenge/xCefC0pD3W1VRLeWVYHmYmJj1Bf06G1hoskXdN1xIEg", "hostname": "mydomain.tld", "port": "80", "addressesResolved": [ "88.136.16.221", "2001:470:cc33::3" ], "addressUsed": "2001:470:cc33::3" } ] } ] }] (1712 bytes)
acme-client: challenge, token: xCefC0pD3W1VRLeWVYHmYmJj1Bf06G1hoskXdN1xIEg, uri: https://acme-v02.api.letsencrypt.org/acme/chall-v3/6603509164/7d1MkA, status: -1
(…)
acme-client: https://acme-v02.api.letsencrypt.org/acme/chall-v3/6603509164/7d1MkA: bad HTTP: 400
acme-client: transfer buffer: [{ "type": "urn:ietf:params:acme:error:malformed", "detail": "Unable to update challenge :: authorization must be pending", "status": 400 }] (144 bytes)
acme-client: bad exit: netproc(37360): 1
Explications
Au dessus de la ligne terminant par status: -1
, on remarque une nouvelle
ligne. Cette ligne nous informe :
- que le status est
invalid
, - que la connexion a été refusée :
Connection refused
, - qu’elle a été fermée par le client :
status: 400
, - qu’elle a échoué sur la connexion de l’adresse IPv6 du serveur :
"addressUsed": "2001:470:cc33::3"
En fait, acme-client informe qu’il n’arrive pas à se connecter sur le serveur à l’adresse IPv6. La première chose à s’assurer est que le serveur web soit joignable sur votre adresse IPv6 !
Il peut y avoir diverses raisons pour lesquelles votre serveur web ne soit pas joignable sur IPv6 :
- assurez-vous que l’adresse IPv6 réponde à la commande
ping6
, puis… - l’oubli de configuration du serveur lui-même… vérifiez que vous avez bien
autorisé la connexion sur IPv6.
- pour httpd :
listen on votre-adresse-ipv6 port 80
- pour nginx :
listen [::]:80;
- pour httpd :
- le pare-feu local PF
qui n’autorise pas la
connexion sur les ports HTTP, voire HTTPS, sur l’adresse IPv6 du serveur :
pass in quick on egress inet6 proto tcp to votre-adresse-ipv6 port 80 flags S/SA modulate state
- Avez-vous en amont un pare-feu ? vérifiez que vos règles de parefeu redirigent bien le flux HTTP(S) vers votre serveur web…
Une manière de s’en assurer est d’utiliser un scanner de port sur IPv6, par exemple celui de ipv6scanner.com. Si le scanner vous dit qu’aucun port correspond n’est ouvert, cherchez profondément la raison…
Dans cet exemple, le serveur ne répondait pas lors de l’interrogation sur
son adresse IPv6. Le status 400
peut très bien être provoqué sur l’adresse
IPv4 du serveur. Appliquez le même raisonnement pour vous assurez que votre
serveur soit joignable sur son adresse IPv4.
Ceci est une des raisons pour lesquelles acme-client peut retourner un statut d’erreur 400. À chaque fois, il faut analyser profondément les informations retournées.
order.status -1
Le client acme restitue de manière sybilline ce message :
# acme-client -v mydomain.tld
(…)
acme-client: order.status -1
acme-client: bad exit: netproc(27643): 1
Là, il peut y avoir de multiples raisons qui empêche tout simplement le client acme de discuter avec votre serveur web.
Le propos est de vérifier qu’aucune écriture de la configuration du serveur n’empêche la lecture dans le répertoire de challenge acme.
Exemples pouvant générer :
- pour nginx, une simple écriture telle que la suivante peut empêcher l’accès
au répertoire de challenge acme :
location ~ /\. {
access_log off;
log_not_found off;
deny all;
}
- concernant relayd, des règles filtrantes les méthodes de connexion peuvent empêcher le bon fonctionnement…