Première rédaction de cet article le 4 juillet 2021
Dernière mise à jour le 7 octobre 2022
C'est dimanche, il ne fait pas beau, pas question de pique-nique, on va donc faire un peu de JSON. Un des problèmes les plus courants avec les noms de domaine est l'expiration accidentelle du nom. « Nous avons oublié de le renouveler » et paf plus rien ne marche. Pourtant, la date à laquelle le domaine expire est publiquement annoncée, via des protocoles comme whois ou RDAP. Comment utiliser RDAP pour récupérer cette date dans un programme, par exemple un programme d'alerte qui préviendra si l'expiration approche ?
Un peu de contexte d'abord ; un certain nombre (mais pas tous) de
registres de noms de domaine exigent un renouvellement
périodique du nom, avec paiement et parfois acceptation de
nouvelles conditions. L'oubli de ce renouvellement est un gag très
fréquent. On ne paie pas, et, à la date d'expiration, le domaine
disparait ou bien est mis en attente, plus publié dans le DNS, ce qui fait que tous les
services associés cessent de fonctionner. Dans le premier cas, le
pire, le domaine a été supprimé, et un autre peut l'enregistrer
rapidement. Il est donc crucial de ne pas laisser le domaine
expirer. Un des outils indispensables quand on gère un domaine
important est donc une supervision
automatique de l'approche de l'expiration. L'information est
diffusée par le registre, via plusieurs protocoles. Le traditionnel
whois, normalisé dans le RFC 3912 a plusieurs inconvénients notamment le fait que le
format de sortie n'est pas normalisé. Il est donc difficile d'écrire
un programme qui analyse l'information retournée (il existe quand
même des solutions comme, en Perl, Net::DRI). Au
contraire, le plus moderne RDAP a un format
de sortie normalisé (dans le RFC 9083). Utilisons-le, en prenant comme exemple le domaine
de ce blog, bortzmeyer.org
.
RDAP utilise HTTPS et produit du JSON (RFC 8259). D'autre part, pour interroger le bon serveur RDAP
(celui du registre), il faut connaitre son nom, ce qui se trouve
dans un registre
IANA qui associe le TLD à son serveur RDAP. On apprend ainsi que les
informations concernant
.org
sont à demander à
https://rdap.publicinterestregistry.org/rdap/
. Utilisant
le RFC 9082 pour former une requête RDAP
correcte, on va utiliser curl
pour poser la question sur bortzmeyer.org
:
% curl -s https://rdap.publicinterestregistry.org/rdap/domain/bortzmeyer.org
On récupère un gros JSON compliqué, que je ne vous montre pas ici. On veut juste la date d'expiration du domaine. On va se servir pour cela de l'excellent outil jq :
% curl -s https://rdap.publicinterestregistry.org/rdap/domain/bortzmeyer.org | \ jq '.events | map(select(.eventAction == "expiration"))[0] | .eventDate' "2021-08-29T09:32:04.304Z"
OK, j'ai triché, ça m'a pris plus de quelques secondes pour écrire le programme jq qui extrait cette information, et j'ai dû vérifier dans le RFC 9083. Expliquons donc.
Partons du haut de l'objet JSON retourné. Il contient un membre
nommé events
(RFC 9083,
section 4.5) qui indique les évènements passés ou futurs pertinents,
comme la création du domaine ou comme sa future expiration. On doit
donc commencer par demander au programme jq de filtrer sur ce
membre, d'où le .events
au début. Ensuite, on
veut ne garder, parmi les évènements, que l'expiration. On applique
donc (map
) un test (.eventAction ==
"expiration"
) à chaque élement du tableau
events
et on ne garde
(select
) que celui qui nous intéresse. Oui,
« celui » et pas « ceux » car il n'y a normalement qu'un évènement
d'expiration, donc on ne garde que le premier élement
([0]
) du tableau produit. Ce premier élément,
comme tous ceux qui composent le tableau events
est un évènement (RFC 9083, section 4.5), un
objet qui a ici deux membres, eventAction
, son
type (celui sur lequel on filtre, avec .eventAction ==
"expiration"
et eventDate
,
l'information qu'on recherche. On termine donc par un filtre du seul
eventDate
, date qui est au format du RFC 3339. Et voilà. On peut ensuite traiter cette
date comme on veut. Par exemple, le programme GNU date peut vous
traduire cette date en secondes écoulées depuis l'epoch :
% date --date=$(curl -s https://rdap.publicinterestregistry.org/rdap/domain/bortzmeyer.org | \ jq -r '.events | map(select(.eventAction== "expiration"))[0] | .eventDate') \ +%s 1630229524
Abandonnons maintenant curl et jq pour un programme écrit en Python. On utilise la bibliothèque Requests pour faire du HTTPS et les bibliothèques standard incluses dans Python pour faire du JSON, du calcul sur les dates, etc. Récupérer le JSON est simple :
response = requests.get("%s/%s" % (SERVER, domain)) if response.status_code != 200: raise Exception("Invalid RDAP return code: %s" % response.status_code)
Le transformer en un dictionnaire Python également :
rdap = json.loads(response.content)
On va ensuite y chercher la date d'expiration, qu'on transforme en une belle date-et-heure Python sur laquelle il sera facile de faire des calculs :
for event in rdap["events"]: if event["eventAction"] == "expiration": expiration = datetime.datetime.strptime(event["eventDate"], RFC3339) now = datetime.datetime.utcnow() rest = expiration-now
Et on peut ensuite mettre les traitements qu'on veut, ici prévenir si l'expiration approche :
if rest < CRITICAL: print("CRITICAL: domain %s expires in %s, HURRY UP!!!" % (domain, rest)) sys.exit(2) elif rest < WARNING: print("WARNING: domain %s expires in %s, renew now" % (domain, rest)) sys.exit(1) else: print("OK: domain %s expires in %s" % (domain, rest)) sys.exit(0)
Le code complet est disponible (il faut aussi ce module). Notez qu'il n'est pas
tellement robuste, il faudrait prévoir davantage de traitement
d'erreur. Python fait toutefois en sorte que la plupart des erreurs
soient détectées automatiquement (par exemple s'il n'existe pas de
membre events
dans le résultat) donc, au moins,
vous n'aurez pas de résultats erronés. Et puis, par rapport au
programme curl+jq plus haut, un grand avantage de ce programme
Python est qu'il utilise le registre IANA des serveurs (décrit dans le RFC 9224) et qu'il trouve donc tout seul le serveur
RDAP :
% ./expiration-rdap.py bortzmeyer.org OK: domain bortzmeyer.org expires in 55 days, 18:54:01.281920 % ./expiration-rdap.py quimper.bzh WARNING: domain quimper.bzh expires in 6 days, 18:17:25.919080, renew now
Une version plus élaborée de ce programme Python, utilisable depuis des logiciels de supervision automatique comme Nagios, Icinga ou Zabbix est disponible.
Notez qu'un tel outil n'est qu'un élément parmi tout ce qu'il faut faire pour bien gérer l'éventuelle expiration de votre domaine. Il faut aussi mettre les dates de renouvellement dans votre agenda, il est recommandé d'activer l'auto-renouvellement ou, mieux, chez les registres qui le permettent, de louer pour plusieurs années. Je vous recommande également la lecture du « Guide du Titulaire Afnic » ou des « Bonnes pratiques pour l'acquisition et l'exploitation de noms de domaine de l'ANSSI ».
Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)
Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)