Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Mon livre « Cyberstructure »

Ève

Ce blog n'a d'autre prétention que de me permettre de mettre à la disposition de tous des petits textes que j'écris. On y parle surtout d'informatique mais d'autres sujets apparaissent parfois.


Cafouillage DNSSEC sur un de mes domaines personnels

Première rédaction de cet article le 6 novembre 2025


Ce matin, si vous avez testé, un de mes domaines personnels, bortzmeyer.fr, présentait des problèmes DNSSEC. Que s'est-il passé ? Était-ce de ma faute ? Va t-on tous mourir ?

Le signalement a été fait par un collègue (via Matrix puisqu'il était en télétravail et pas moi) : son résolveur DNS répondait SERVFAIL (SERver FAILure) pour l'enregistrement de type TXT de bortzmeyer.fr. C'est le type utilisé pour SPF donc c'est ennuyeux (le domaine principal, bortzmeyer.org, avait le même problème) :


% dig bortzmeyer.fr TXT    

; <<>> DiG 9.20.15-1~deb13u1-Debian <<>> bortzmeyer.fr TXT
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 28082
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
; EDE: 6 (DNSSEC Bogus)
;; QUESTION SECTION:
;bortzmeyer.fr.		IN TXT

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
;; WHEN: Thu Nov 06 08:17:48 UTC 2025
;; MSG SIZE  rcvd: 48

  

On voit que c'est un problème DNSSEC puisque l'EDE (Extended DNS Error, cf. RFC 8914) nous le dit (« 6 (DNSSEC Bogus) ») et aussi parce que, si on désactive la validation (cd = Checking Disabled), ça marche :


% dig +cd bortzmeyer.fr TXT

; <<>> DiG 9.20.15-1~deb13u1-Debian <<>> +cd bortzmeyer.fr TXT
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44332
;; flags: qr rd ra cd; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
; EDE: 6 (DNSSEC Bogus)
;; QUESTION SECTION:
;bortzmeyer.fr.		IN TXT

;; ANSWER SECTION:
bortzmeyer.fr.		45 IN TXT "DNS is innocent"
bortzmeyer.fr.		45 IN TXT "v=spf1 mx -all"
bortzmeyer.fr.		45 IN TXT "BTC" "1HtNJ6ZFUc9yu9u2qAwB4tGdGwPQasQGax"
bortzmeyer.fr.		45 IN RRSIG TXT 13 2 86400 (
				20251113173453 20251030142433 32088 bortzmeyer.fr.
				J1arZUBP+G5nc54zetyD45rqDyURJeZyFjRRkixQrPok
				BPxNkyt1RHzdH78a0rM93Zzu0q+N/zzxMQScl0KVHw== )
bortzmeyer.fr.		45 IN RRSIG TXT 13 2 86400 (
				20251113215344 20251030142433 32088 bortzmeyer.fr.
				bWAlKMSQPDALOeEmwwfh47qGjloBK3YRH3rGydVJBis4
				lFIsIsE09bkqviBiGyjNqpS/loFaiMS4FRIeh06jwg== )

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
;; WHEN: Thu Nov 06 08:17:51 UTC 2025
;; MSG SIZE  rcvd: 372

  

Le résultat de dig donne aussi une première explication : il y a deux signatures DNSSEC, faites avec la même clé, la 32088. Ce n'est pas normal. (Les enregistrements des autres types, comme SOA ou MX, n'avaient pas de problème.)

Le test avec Zonemaster montrait que les enregistrements NSEC3, qui servent à prouver la non-existence d'un nom ou d'un type d'enregistrement, étaient également cassés. Le test avec DNSviz donne une idée du problème : « RRSIG bortzmeyer.fr/TXT alg 13, id 32088: The cryptographic signature of the RRSIG RR does not properly validate. ».

Un test avec les sondes RIPE Atlas montre que le problème n'est pas local. Toutes les sondes utilisant un résolveur DNS validant voient le problème (les seules où ça marche sont celles qui ne valident pas) :

%  blaeu-resolve --displayvalidation --requested 200 --type TXT bortzmeyer.fr
[ERROR: SERVFAIL] : 147 occurrences 
["btc" "1htnj6zfuc9yu9u2qawb4tgdgwpqasqgax" "dns is innocent" "v=spf1 mx -all"] : 31 occurrences 
Test #136108974 done at 2025-11-06T08:24:58Z
  

L'explication du problème a été trouvée en examinant les fichiers de zone, avant et après la signature. Dans le fichier non signé, celui que j'édite, on trouve cette ligne :

; Type WALLET, enregistré à l'IANA, mais pas encore connu des logiciels
@ IN    TYPE262 \# 39 03425443 223148744E4A365A465563397975397532714177423474476447775051617351476178
  

Mais dans le fichier signé par OpenDNSSEC, on trouve :

bortzmeyer.fr.	86400	IN	TXT	"BTC" "1HtNJ6ZFUc9yu9u2qAwB4tGdGwPQasQGax"
  

Le type 262 (WALLET) est devenu un type 16 (TXT). Ce n'est pas du tout normal, c'est clairement une bogue du signeur d'OpenDNSSEC. Creusons un peu. Le type WALLET est un type d'enregistrement, et ce type est officiellement enregistré à l'IANA. Cela fait plus d'un an qu'il est annoncé dans mes domaines personnels. Comme, à l'époque, il n'était pas connu des logiciels, j'ai utilisé la syntaxe des types inconnus, syntaxe normalisée depuis plus de vingt ans, dans le RFC 3597. La traduction en TXT n'a pas de sens, car, si l'enregistrement de type WALLET a bien des données structurées comme le TXT, c'est un type différent. Le signeur a apparemment calculé une signature pour les TXT, une pour le WALLET, puis traduit le WALLET en TXT, cassant tout (les signatures s'appliquent à un ensemble d'enregistrements, pas à un enregistrement unique, donc deux TXT n'ont qu'une signature) :

bortzmeyer.fr.	86400	IN	TXT	"v=spf1 mx -all"
bortzmeyer.fr.	86400	IN	TXT	"DNS is innocent"
bortzmeyer.fr.	86400	IN	RRSIG	TXT 13 2 86400 20251113215344 20251030142433 32088 bortzmeyer.fr. bWAlKMSQPDALOeEmwwfh47qGjloBK3YRH
3rGydVJBis4lFIsIsE09bkqviBiGyjNqpS/loFaiMS4FRIeh06jwg==
bortzmeyer.fr.	86400	IN	TXT	"BTC" "1HtNJ6ZFUc9yu9u2qAwB4tGdGwPQasQGax"
bortzmeyer.fr.	86400	IN	RRSIG	TXT 13 2 86400 20251113173453 20251030142433 32088 bortzmeyer.fr. J1arZUBP+G5nc54zetyD45rqDyURJeZyF
jRRkixQrPokBPxNkyt1RHzdH78a0rM93Zzu0q+N/zzxMQScl0KVHw==

C'est ce fichier erroné (l'erreur cassait aussi les chaines NSEC3) qui a été produit et distribué aux serveurs faisant autorité, provoquant les problèmes détectés. La rupture des NSEC3 se voyait lorsqu'on demandait un nom non existant (ici avec les sondes Atlas) :

% blaeu-resolve --displayvalidation --requested 200 --type TXT doesnotexist.bortzmeyer.fr
[ERROR: SERVFAIL] : 144 occurrences 
[ERROR: NXDOMAIN] : 28 occurrences 
Test #136110101 done at 2025-11-06T08:35:32Z
  

Ce problème a été signalé sur la liste de diffusion d'OpenDNSSEC. Notez que ce logiciel est en fin de vie (son successeur, Cascade, est actuellement en version alpha) donc je ne sais pas encore si la bogue sera traitée.

Pour réparer le problème, j'ai retiré l'enregistrement WALLET et tout remarche. Ça va mieux (ici, certains résolveurs avaient encore les données erronnées dans leur mémoire) :

% blaeu-resolve --displayvalidation --requested 200 --type TXT
bortzmeyer.fr ["dns is innocent" "v=spf1 mx -all"] : 65 occurrences [
(Authentic Data flag) "dns is innocent" "v=spf1 mx -all"] : 128
occurrences [ERROR: SERVFAIL] : 4 occurrences [] : 1 occurrences Test
#136145680 done at 2025-11-06T14:29:36Z 

Le plus curieux est que cette bogue se soit déclenchée en l'absence de tout changement. Douze jours auparavant, il y avait le même fichier de zone (l'enregistrement WALLET datait de plus d'un an) et tout allait bien.

Quelle(s) leçon(s) à en tirer ? Que l'informatique, c'est compliqué, que les logiciels ont des bogues et que, si on est très intolérant aux problèmes, il ne faut pas chercher les cas exotiques. Et que le bon fonctionnement de l'Internet ne repose pas sur des règles bureaucratiques et des processus mais sur la réactivité, à la fois pour signaler les problèmes (merci à Marc van der Wal pour cela), et pour les traiter lorsqu'ils sont signalés.


L'article seul

Fiche de lecture : Les génies des mers

Auteur(s) du livre : Bill François
Éditeur : Champs
978-2-0804-9396-5
Publié en 2023
Première rédaction de cet article le 5 novembre 2025


Vous aimez les trucs qu'il y a dans l'eau, genre poissons, langoustes ou autres bestioles bien plus bizarres ? Pas pour les manger, non, mais pour s'instruire. Dans ce livre très vivant, vous apprendrez certainement plein de choses sur les habitant·es des mers.

L'auteur raconte les caractéristiques les plus étonnantes d'un bon nombre de « génies des mers » (connaitre un peu de physique peut aider, car l'auteur utilise souvent des concepts de cette science). Comment l'exocet arrive à « voler » (divulgâchage : il ne vole pas) ; pourquoi n'y a t-il pas de petits animaux à sang chaud dans les mers ; comment le cachalot gère sa respiration avant de plonger (autre divulgâchage : il ne remplit pas ses poumons d'air, cela l'empêcherait de plonger) ; pourquoi les torpilles qui vivent dans l'Amazone produisent du courant sous une tension plus élevée ; pourquoi le requin du Groenland vit si vieux… Et il y en a beaucoup d'autres, parfois connus, parfois inconnus (de moi).

C'est bien écrit, très dynamique, et, après l'avoir lu, vous aurez envie de plonger dans l'océan le plus proche pour aller voir de vous-même.


L'article seul

Noms de domaines internationalisés : affichage pendant un transfert

Première rédaction de cet article le 3 novembre 2025


Puisque je suis occupé à transférer beaucoup de noms de domaine vers un autre Bureau d'Enregistrement (BE), c'est l'occasion de se demander « et si c'est un nom de domaine internationalisé, que se passe-t-il ? ».

Sur les transferts d'un BE à un autre, j'ai déjà commis un article sur ce sujet. Mais j'ai dû, depuis, transférer un IDN, potamochère.fr. Vous savez que les IDN sont, pour des raisons complexes (et non, ce n'est pas parce que le DNS est limité à ASCII, essayez vous-même, vous verrez) encodés en Punycode (RFC 3492). potamochère.fr est donc encodé en xn--potamochre-66a.fr. Normalement, ce n'est qu'un détail technique, invisible aux utilisateurs mais, hélas, dix-neuf ans après la sortie du premier RFC sur les IDN (RFC 3490), certains logiciels continuent à présenter à l'utilisateur la forme en Punycode, en violation du RFC 2277.

Ainsi, le BE gagnant permet de commander le transfert en utilisant la forme en joli Unicode lisible mais, lors de l'affichage de la liste des serveurs actuels, le BE gagnant montre du Punycode : transfert-idn-serveurs-actuels.png

Même chose pour la configuration des serveurs : transfert-idn-config.png

Ce qui est amusant est que les courriers de ce même BE respectent tous le bon encodage et sont donc lisibles :


Subject: [potamochère.fr] Notification de transfert
From: support@netim.com
Date: Sun,  2 Nov 2025 12:19:26 +0100 (CET)

02/11/2025
Cher client,

Ceci est une notification au sujet du transfert de votre domaine potamochère.fr vers NETIM.

Alors qu'en général, le courrier est moins internationalisé que le Web.

D'un BE à l'autre, les choses sont différentes. Par exemple, chez le BE perdant :


Subject: [GANDI] IMPORTANT: Transfert sortant vers un autre prestataire du  domaine xn--potamochre-66a.fr
From: no-reply@gandi.net
Date: Sun, 02 Nov 2025 11:20:29 -0000

A l'attention de : Stéphane Bortzmeyer
Transfert du nom de domaine: xn--potamochre-66a.fr
Registrar demandeur: <NETIM SARL>

Et idem sur leur serveur Web : transfert-idn-autorisation.png

Corriger tous les endroits où l'IDN n'est pas traité proprement va prendre du temps…


L'article seul

Suite des tests du logiciel de gestion DNSSEC Cascade

Première rédaction de cet article le 2 novembre 2025


J'ai déjà parlé de Cascade, un logiciel actuellement en développement, qui automatise un certain nombre de tâches nécessaires pour DNSSEC, comme la re-signature ou le remplacement des clés. Le projet avance vite donc voyons quelques nouveaux essais.

D'abord, puisque Cascade tire son nom du traitement en plusieurs étapes d'une tâche, avec la possibilité d'insérer des validations à chaque étape, essayons la validation manuelle (j'ai déjà montré la validation automatique dans mon précédent article). Comme pour la validation automatique, vous pouvez valider la zone avant la signature et/ou après. Comme pour la validation automatique, vous devez indiquer, dans le fichier qui décrit une politique de gestion, que des examens (review) sont obligatoires :

% cat /etc/cascade/policies/default.toml
…
[loader.review]
required = true
…
[signer.review]
required = true
…

Par contre, et contrairement à ce qui se passe pour la validation automatique, vous n'indiquez pas de programme à exécuter (hook). Cela signifiera, pour Cascade, qu'il faut une validation manuelle. Vous rechargez alors les politiques (cascade policy reload) et (c'est une bogue de la version de développement), vous redémarrez le serveur. Lorsqu'une nouvelle version d'une zone est prête, et que vous l'indiquez au serveur (cascade zone reload example.org), vous voyez ceci :

% cascade zone status example.org                                                      
Status report for zone 'example.org' using policy 'default'
✔ Waited for a new version of the example.org zone
✔ Loaded version 2025102700
  Loaded at 2025-10-27T08:47:38+00:00 (13s ago)
  Loaded 243 B and 4 records from the filesystem in 0 seconds
• Waiting for approval to sign version 2025102700
! Zone will be held until manually approved
  Approve with: cascade zone approve --unsigned example.org 2025102700
  Reject with:  cascade zone reject --unsigned example.org 2025102700

La zone n'est pas encore signée, vous devez la valider. (Rappelez-vous qu'on peut imposer une validation de la zone non signée, de la zone signée, ou bien des deux.) Et Cascade vous indique comment faire, mais pas comment récupérer la zone à signer. Pour cela, le plus simple est un transfert de zones (RFC 5936) depuis le serveur indiqué par les directives .servers dans la configuration de Cascade.

% cat /etc/cascade/config.toml 
…    
[loader]
review.servers = ["127.0.0.1:4541", "[::1]:4541"]

[signer]
review.servers = ["127.0.0.1:4542", "[::1]:4542"]
…
% dig @127.0.0.1 -p 4541 example.org AXFR
…

On peut alors examiner la zone. Mettons qu'elle nous satisfasse. On va alors suivre les instructions qu'affichait cascade zone status :

% cascade zone approve --unsigned example.org 2025102700
Approved unsigned zone 'example.org' with serial number 2025102700

Personnellement, je ne vois pas trop l'intérêt d'examiner manuellement la zone non signée (on pourrait aussi examiner le fichier de zone qu'on a donné à Cascade) et je vais plutôt regarder la zone signée, qui est servie sur un autre port.

Autre activité, Cascade permet également d'automatiser une opération délicate : le remplacement des clés. Cette opération est nécessaire si on découvre qu'une clé privée a été compromise. Et on peut aussi estimer que le remplacement est utile, même en l'absence de compromission, pour pratiquer les procédures et vérifier que tout se passera bien lorsqu'on aura vraiment besoin d'un remplacement. Supposons une politique avec deux clés, la KSK (Key Signing Key, qui ne signe que les clés) et la ZSK (Zone Signing Key, qui signe le reste). D'abord, l'état initial :


% dig +multi +dnssec internautique.fr DNSKEY
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30159
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
…
;; ANSWER SECTION:
internautique.fr.	3600 IN	DNSKEY 257 3 15 (
				IDypc2j/kbE3iHtnbry715I6jwgVEPMLz1+QV9GNgMA=
				) ; KSK; alg = ED25519 ; key id = 33113
internautique.fr.	3600 IN	DNSKEY 256 3 15 (
				K+sBrJFESzsLwD6AFGW+zZrydRjk5QRtbshG4AKV/jI=
				) ; ZSK; alg = ED25519 ; key id = 56167
internautique.fr.	3600 IN	RRSIG DNSKEY 15 2 3600 (
				20251109195246 20251025195246 33113 internautique.fr.
				ARucHmrIB8ivtduHEzphj2SJEGRZ7BlP5c7686dD2DTy
				TDJpZc9Rcerrh5okki3rKKOsFMqOwXunlNl+cgGcBA== )
…
;; WHEN: Mon Oct 27 09:06:13 CET 2025

Nous avons dans le DNS la KSK, d'identification 33113, et la ZSK, d'identification 56167. C'est aussi ce que nous dit Cascade :

% cascade zone status --detailed internautique.fr           
…
✔ Published version 2025102612
  Published zone available on 127.0.0.1:8053
DNSSEC keys:
  ZSK tagged 56167:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+56167.key
    Actively used for signing
  KSK tagged 33113:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key
    Actively used for signing
  Details:
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+56167.key expires at 2025-11-02T14:51:53Z
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key expires at 2025-12-26T14:51:53Z
  

Et c'est aussi ce qu'on peut voir avec DNSviz. Maintenant, commençons le remplacement de la ZSK :

%  cascade keyset internautique.fr zsk start-roll 
Manual key roll for internautique.fr successful
  

Et voici le résultat, une nouvelle ZSK est apparue, la 30906 :

% cascade zone status --detailed internautique.fr 
Status report for zone 'internautique.fr' using policy 'default'
✔ Waited for a new version of the internautique.fr zone
✔ Loaded version 2025101721
  Loaded at 2025-10-26T14:51:53+00:00 (17h 16m 7s ago)
  Loaded 333 B and 7 records from the filesystem in 0 seconds
✔ Waited for approval to sign version 2025101721
✔ Approval received to sign version 2025101721, signing requested
✔ Signed version 2025101721 as version 2025102701
  Signing requested at 2025-10-27T08:07:36+00:00 (24s ago)
  Signing started at 2025-10-27T08:07:36+00:00 (24s ago)
  Signing finished at 2025-10-27T08:07:36+00:00 (24s ago)
  Collected 8 records in 0s, sorted in 0s
  Generated 6 NSEC(3) records in 0s
  Generated 8 signatures in 0s (8 sig/s)
  Inserted signatures in 0s (8 sig/s)
  Took 0s in total, using 1 threads
  Current action: Finished
✔ Waited for approval to publish version 2025102701
✔ Published version 2025102701
  Published zone available on 127.0.0.1:8053
DNSSEC keys:
  KSK tagged 33113:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key
    Actively used for signing
  ZSK tagged 30906:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+30906.key
  ZSK tagged 56167:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+56167.key
    Actively used for signing
  Details:
    ZskRoll: CacheExpire1(3600)
    Wait until 2025-10-27T09:07:36Z to let caches expire
    For the next step run:
    	cascade keyset internautique.fr zsk cache-expired1
    	automation is enabled for this step.
    
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key expires at 2025-12-26T14:51:53Z
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+30906.key expires at 2025-11-03T08:07:27Z
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+56167.key expires at 2025-11-02T14:51:53Z
  

À la fin, c'est bon, le remplacement s'est fait sans autre intervention, et en respectant les durées nécessaires pour que, à tout moment, tous les résolveurs DNS de la planète aient un chemin de validation complet, depuis la racine :

% cascade zone status --detailed internautique.fr
Status report for zone 'internautique.fr' using policy 'default'
✔ Waited for a new version of the internautique.fr zone
✔ Loaded version 2025101721
  Loaded at 2025-10-26T14:51:53+00:00 (23h 45m 37s ago)
  Loaded 333 B and 7 records from the filesystem in 0 seconds
✔ Waited for approval to sign version 2025101721
✔ Approval received to sign version 2025101721, signing requested
✔ Signed version 2025101721 as version 2025102705
  Signing requested at 2025-10-27T12:07:56+00:00 (2h 29m 34s ago)
  Signing started at 2025-10-27T12:07:56+00:00 (2h 29m 34s ago)
  Signing finished at 2025-10-27T12:07:56+00:00 (2h 29m 34s ago)
  Collected 8 records in 0s, sorted in 0s
  Generated 5 NSEC(3) records in 0s
  Generated 8 signatures in 0s (8 sig/s)
  Inserted signatures in 0s (8 sig/s)
  Took 0s in total, using 1 threads
  Current action: Finished
✔ Waited for approval to publish version 2025102705
✔ Published version 2025102705
  Published zone available on 127.0.0.1:8053
DNSSEC keys:
  KSK tagged 33113:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key
    Actively used for signing
  ZSK tagged 30906:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+30906.key
    Actively used for signing
  Details:
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+30906.key expires at 2025-11-03T08:07:27Z
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key expires at 2025-12-26T14:51:53Z

Vous pouvez admirer l'état final dans DNSviz.

Maintenant, remplaçons la KSK. C'est un peu plus délicat car il y a une opération manuelle à faire, auprès de la zone parente (.fr dans notre cas).

On voit l'état initial sur DNSviz. Démarrons l'opération :

%  cascade keyset internautique.fr ksk start-roll
Manual key roll for internautique.fr successful

% cascade zone status --detailed internautique.fr
Status report for zone 'internautique.fr' using policy 'default'
✔ Waited for a new version of the internautique.fr zone
✔ Loaded version 2025101721
  Loaded at 2025-10-26T14:51:53+00:00 (1day 19h 29m 50s ago)
  Loaded 333 B and 7 records from the filesystem in 0 seconds
✔ Waited for approval to sign version 2025101721
✔ Approval received to sign version 2025101721, signing requested
✔ Signed version 2025101721 as version 2025102801
  Signing requested at 2025-10-28T10:21:36+00:00 (7s ago)
  Signing started at 2025-10-28T10:21:36+00:00 (7s ago)
  Signing finished at 2025-10-28T10:21:36+00:00 (7s ago)
  Collected 8 records in 0s, sorted in 0s
  Generated 7 NSEC(3) records in 0s
  Generated 8 signatures in 0s (8 sig/s)
  Inserted signatures in 0s (8 sig/s)
  Took 0s in total, using 1 threads
  Current action: Finished
✔ Waited for approval to publish version 2025102801
✔ Published version 2025102801
  Published zone available on 127.0.0.1:8053
DNSSEC keys:
  KSK tagged 49915:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+49915.key
    Actively used for signing
  ZSK tagged 30906:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+30906.key
    Actively used for signing
  KSK tagged 33113:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key
    Actively used for signing
  Details:
    KskRoll: CacheExpire1(3600)
    Wait until 2025-10-28T11:21:36Z to let caches expire
    For the next step run:
    	cascade keyset internautique.fr ksk cache-expired1
    	automation is enabled for this step.
    
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+49915.key expires at 2025-12-28T10:21:27Z
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key expires at 2025-12-26T14:51:53Z
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+30906.key expires at 2025-11-03T08:07:27Z

La nouvelle KSK, la 49915, est publiée. Une fois le temps nécessaire écoulé (toujours cette histoire de la mémorisation, par les résolveurs, des données), Cascade nous dit qu'il faut maintenant changer la DS :

    
% cascade zone status --detailed internautique.fr
Status report for zone 'internautique.fr' using policy 'default'
✔ Waited for a new version of the internautique.fr zone
✔ Loaded version 2025101721
  Loaded at 2025-10-26T14:51:53+00:00 (1day 23h 59m 24s ago)
  Loaded 333 B and 7 records from the filesystem in 0 seconds
✔ Waited for approval to sign version 2025101721
✔ Approval received to sign version 2025101721, signing requested
✔ Signed version 2025101721 as version 2025102806
  Signing requested at 2025-10-28T14:22:01+00:00 (29m 15s ago)
  Signing started at 2025-10-28T14:22:01+00:00 (29m 15s ago)
  Signing finished at 2025-10-28T14:22:01+00:00 (29m 15s ago)
  Collected 8 records in 0s, sorted in 0s
  Generated 7 NSEC(3) records in 0s
  Generated 8 signatures in 0s (8 sig/s)
  Inserted signatures in 0s (8 sig/s)
  Took 0s in total, using 1 threads
  Current action: Finished
✔ Waited for approval to publish version 2025102806
✔ Published version 2025102806
  Published zone available on 127.0.0.1:8053
DNSSEC keys:
  KSK tagged 33113:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key
    Actively used for signing
  KSK tagged 49915:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+49915.key
    Actively used for signing
  ZSK tagged 30906:
    Reference: file:///var/db/cascade/keys/Kinternautique.fr.+015+30906.key
    Actively used for signing
  Details:
    KskRoll: Propagation2
    Check that all nameservers of the parent zone have the following RRset (or equivalent):
    internautique.fr. 3600 IN DS 49915 15 2 0ADF31249F82B92BC7A8068CB2D6117C35F78E5994F1AFF7BCFE6999A71F5091
    
    For the next step run:
    	cascade keyset internautique.fr ksk propagation2-complete <ttl>
    	automation is enabled for this step.
    
    Automatic key roll state:
    Roll KskRoll, state Propagation2:
    	Wait until the new DS RRset has propagated to all nameservers
    	of the parent zone. Try again after 2025-10-28T15:22:01Z
    
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+30906.key expires at 2025-11-03T08:07:27Z
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+33113.key expires at 2025-12-26T14:51:53Z
    key file:///var/db/cascade/keys/Kinternautique.fr.+015+49915.key expires at 2025-12-28T10:21:27Z
  

Vous avez vu, à la fin ? Il faut placer la DS internautique.fr. 3600 IN DS 49915 15 2 0ADF31249F82B92BC7A8068CB2D6117C35F78E5994F1AFF7BCFE6999A71F5091 dans la zone parente, ce qui se fait typiquement via l'interface (Web ou API) de son BE. Ce sera la seule opération manuelle qu'il faudra faire. cascade-rollover-add-ds.png

L'ancienne KSK sera supprimée automatiquement en temps utile. (Ici, la 33113 est encore présente, et les clés seront signées avec les deux KSK.)

Dans l'opération précédente, on avait deux clés, la KSK et la ZSK. C'est la configuration la plus courante. Mais elle n'est pas obligatoire. Elle avait été promue à une époque car elle résolvait un problème spécifique à l'algorithme de cryptographie RSA : ce dernier laissait le choix entre des clés courtes, compactes et rapides mais peu sûres, et des clés longues, dont les avantages et inconvénients étaient à l'opposé. Avec RSA, avoir une ZSK courte et changée souvent, ainsi qu'une KSK longue et changée plus rarement, était logique. Mais avec les algorithmes comme ceux fondés sur des courbes elliptiques, ce n'est plus utile. Autant n'avoir qu'une clé, qu'on nomme souvent CSK, pour Common Signing Key. Dans Cascade, cela se configure dans la politique (rappelez-vous que vous pouvez avoir plusieurs politiques, donc qu'une même instance de Cascade peut gérer des zones avec CSK et des zones avec KSK/ZSK) :

% cat  /etc/cascade/policies/one-key.toml
…
use-csk = true
  

Et voici ce qu'affiche Cascade :

    
% cascade zone status --detailed cours-dns.fr
Status report for zone 'cours-dns.fr' using policy 'one-key'
✔ Waited for a new version of the cours-dns.fr zone
✔ Loaded <serial number not yet known>
  Loaded at 2025-10-31T13:23:23+00:00 (1day 5h 36m 3s ago)
  Loaded 261 B and 5 records from the filesystem in 0 seconds
✔ Waited for approval to sign <serial number not yet known>
• Approval received to sign <serial number not yet known>, signing requested
DNSSEC keys:
  CSK tagged 50973:
    Reference: file:///var/db/cascade/keys/Kcours-dns.fr.+015+50973.key
    Actively used for signing
  Details:
    key file:///var/db/cascade/keys/Kcours-dns.fr.+015+50973.key expires at 2026-10-30T13:01:19Z

  

Et vous pouvez voir graphiquement la clé unique avec DNSviz.

Allez, un autre point, la technique utilisée pour indiquer les noms absents. DNSSEC en permet deux, les NSEC du RFC 4034 et les NSEC3 du RFC 5155. Cascade permet de configurer l'un ou l'autre dans la politique :

type = "nsec"
# type = "nsec3"
  

Ah, et une nouveauté relativement récente de Cascade, un moyen simple de tester si le démon tourne bien :

% cascade health
Ok
  

Ce qui est très pratique dans une supervision automatisée :

% sudo -u nagios /usr/local/lib/nagios/plugins/check_cascade                   
Cascade OK
  

L'article seul

Le résolveur DNS public de Freifunk München

Première rédaction de cet article le 16 octobre 2025


Allez, encore un résolveur DNS public européen, celui de Freifunk München. Rien d'extraordinaire mais rappelez-vous que, dans ce domaine, pluralisme et diversité sont cruciaux.

Des résolveurs DNS publics, il y en a beaucoup (j'en gère même un). Les utilisateurices et administrateurices système s'en servent pour des raisons variées, par exemple échapper à la censure. Mais ils ne sont pas tous équivalents, en terme de caractéristiques techniques, de fonctions (certains sont menteurs), de politique de gestion des données personnelles, etc. Le service de Freifunk München :

  • A DoT, DoH, DNSSEC et IPv6 (comme tous les résolveurs publics sérieux),
  • Promet d'être strict sur la vie privée et notamment de ne pas garder de trace des requêtes faites,
  • Est européen (certains résolveurs publics qui se présentent comme européens sont en fait des services étatsuniens un peu repeints, par exemple en s'appuyant sur la nationalité d'origine du fondateur), plus précisément allemand (par contre, le nom de domaine est dans un TLD étatsunien, ce qui est curieux, mais n'a pas trop d'importance car le nom ne sert pas beaucoup pour accéder à un résolveur DNS, sauf pour l'authentification TLS).

Faisons quelques essais techniques. Les adresses et noms à utiliser sont 185.150.99.255, 5.1.66.255, 2001:678:e68:f000::, 2001:678:ed0:f000::, dot.ffmuc.net, https://doh.ffmuc.net/dns-query et doq.ffmuc.net (je n'ai pas testé ce dernier). D'abord, avec dig :

    
% dig @2001:678:ed0:f000::  sci-hub.se
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24707
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
…
;; ANSWER SECTION:
sci-hub.se.		60 IN A	186.2.163.219

;; Query time: 34 msec
;; SERVER: 2001:678:ed0:f000::#53(2001:678:ed0:f000::) (UDP)
;; WHEN: Thu Oct 16 14:38:17 CEST 2025
;; MSG SIZE  rcvd: 55

  

OK, c'est bon, tout marche, et en un temps raisonnable depuis ma connexion Free à Paris. (Évidemment, un résolveur public ne sera jamais aussi rapide qu'un résolveur local et il n'est donc pas raisonnable d'utiliser un résolveur public « pour les performances ».)

Et avec DoT ?


% dig +tls @2001:678:ed0:f000:: liberation.fr
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31755
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
…
;; ANSWER SECTION:
liberation.fr.		300 IN A 3.165.113.53
liberation.fr.		300 IN A 3.165.113.101
liberation.fr.		300 IN A 3.165.113.127
liberation.fr.		300 IN A 3.165.113.118

;; Query time: 62 msec
;; SERVER: 2001:678:ed0:f000::#853(2001:678:ed0:f000::) (TLS)
;; WHEN: Thu Oct 16 14:40:02 CEST 2025
;; MSG SIZE  rcvd: 106

  

Et du DoH ?

    
% kdig +https=https://doh.ffmuc.net/dns-query  @185.150.99.255 bortzmeyer.fr NS

;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
;; HTTP session (HTTP/2-POST)-(doh.ffmuc.net/dns-query)-(status: 200)
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 0
;; Flags: qr rd ra ad; QUERY: 1; ANSWER: 7; AUTHORITY: 0; ADDITIONAL: 1

;; EDNS PSEUDOSECTION:
;; Version: 0; flags: ; UDP size: 512 B; ext-rcode: NOERROR
;; PADDING: 238 B
…
;; ANSWER SECTION:
bortzmeyer.fr.      	85495	IN	NS	ns2.1984hosting.com.
bortzmeyer.fr.      	85495	IN	NS	ns0.1984.is.
bortzmeyer.fr.      	85495	IN	NS	ns4.bortzmeyer.org.
bortzmeyer.fr.      	85495	IN	NS	ns-global.kjsl.com.
bortzmeyer.fr.      	85495	IN	NS	puck.nether.net.
bortzmeyer.fr.      	85495	IN	NS	ns2.bortzmeyer.org.
bortzmeyer.fr.      	85495	IN	NS	ns2.1984.is.

;; Received 468 B
;; Time 2025-10-16 14:42:39 CEST
;; From 185.150.99.255@443(HTTPS) in 91.2 ms

  

C'est parfait, tout marche. Pour la dernière requête, celle faite avec DoH, notez que le résolveur valide bien les domaines signés.

Configurons maintenant un résolveur local pour faire suivre à ffmuc, pour profiter de la mémorisation des réponses par celui-ci. On va utiliser Unbound et faire suivre en TLS :

forward-zone:
        name: "."
        # Freifunk München
        forward-addr: 2001:678:ed0:f000::@853#dot.ffmuc.net
        forward-tls-upstream: yes
  

Et tout marche, notre résolveur local fera suivre ce qu'il ne sait pas déjà à ffmuc.

ffmuc a-t-il plusieurs machines, réparties par anycast ? On peut regarder les identités de ces serveurs avec NSID (RFC 5001) :


%   dig +nsid @5.1.66.255 www.phy.cam.ac.uk     
…
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 512
; NSID: 64 6f 74 2e 66 66 6d 75 63 2e 6e 65 74 ("dot.ffmuc.net")
;; QUESTION SECTION:
;www.phy.cam.ac.uk.	IN A

;; ANSWER SECTION:
www.phy.cam.ac.uk.	3591 IN	CNAME tm-128-232-132-117.tm.uis.cam.ac.uk.
tm-128-232-132-117.tm.uis.cam.ac.uk. 3600 IN A 128.232.132.117
…

  

Le NSID est juste le nom du service. Un test avec les sondes RIPE Atlas montre qu'il n'y a en effet qu'un seul serveur.

Notez enfin que la configuration technique de ce service est publique ainsi que celle des autres services.

Est-ce que je vais utiliser ce service ? Non, il en existe plusieurs autres qui me conviennent (dont le mien, bien sûr, mais aussi celui de FDN) mais avoir le choix est une bonne chose.


L'article seul

Transférer un nom de domaine d'un Bureau d'Enregistrement à un autre

Première rédaction de cet article le 16 octobre 2025


Dans beaucoup de TLD, le ou la simple titulaire ne peut pas acheter directement un nom de domaine, il faut passer par un Bureau d'Enregistrement (BE). Le transfert, l'opération qui consiste à changer de bureau d'enregistrement, est parfois une opération frustrante. Je suis en train de transférer mes noms de domaine personnels (et ils sont tous signés avec DNSSEC) donc voici quelques notes, au cas où.

Dans le cas de .fr, l'obligation de passer par un BE est dans la loi : « L'attribution des noms de domaine est assurée par les offices d'enregistrement [sic ; il s'agit des registres], par l'intermédiaire des bureaux d'enregistrement. » (article L45-4 du Code des postes et des communications électroniques). Pour les TLD sous contrat avec l'ICANN, cette obligation est dans le contrat entre l'ICANN et le registre. Vous pouvez trouver le nom du BE via les différents outils d'accès à l'information, cette information est publique.

Le choix d'un BE est une opération complexe, déroutante pour la ou le titulaire d'un nom de domaine. Et on peut être amené à changer de BE, par exemple pour des raisons tarifaires. Ainsi, je transfère en ce moment mes domaines personnels, l'ancien BE, qui était une entreprise indépendante, ayant été racheté par un grand groupe qui a immédiatement considérablement augmenté les tarifs. (Problème courant quand une petite entreprise est rachetée par un grand groupe : celui-ci se dépêche de supprimer tout ce qui faisait l'intérêt de la petite entreprise pour les clients. On a vu cela avec Capitaine Train, par exemple.) Ces noms de domaine ont deux particularités :

  • L'hébergeur DNS, c'est moi. Je n'utilise pas, ni avant le transfert, ni après, les services d'hébergement DNS que fournissent la plupart des BE. Il n'y a donc pas de données DNS à transférer, les serveurs faisant autorité restent les mêmes.
  • Les domaines sont signés avec DNSSEC.

Outre les contraintes techniques, le transfert est une opération délicate puisque les deux BE, le perdant et le gagnant, sont a priori des concurrents et que certains BE perdants font preuve d'une extrême mauvaise volonté, essayant de retenir de force leur client par tous les moyens. Le transfert est donc piloté par le BE gagnant, puisqu'il est le plus motivé à ce que l'opération réussisse. Et, pour compliquer encore la question, il faut noter qu'il y a un problème de sécurité : un transfert peut être malveillant, par exemple parce qu'un méchant essaie de détourner un nom de domaine à son profit.

Les règles qui encadrent le transfert d'un nom de domaine varient selon les registres. Durant ma migration, j'ai transféré du .org, du .net et du .fr.

Le cheminement normal est le suivant :

  • Le ou la titulaire est prudent, teste qu'ielle a bien tous les éléments (mots de passe, etc) et ne commence pas le transfert la veille d'un jour où il faut absolument que le nom de domaine marche. N'attendez pas non plus le dernier moment avant l'expiration. Vouloir grapiller quelques euros peut vous coûter cher.
  • La ou le titulaire du nom va sur l'interface Web du futur BE, le gagnant (ou bien utilise l'API de ce BE) et demande le transfert d'un nom. On paie ce transfert.
  • Pour des raisons de sécurité évidentes, ce n'est pas fait immédiatement et automatiquement. Des mesures de sécurité sont appliquées.
  • Une fois le transfert validé, le registre met à jour sa base de données, indiquant le nouveau BE, et le titulaire est prévenu (typiquement par courrier).

Voici la demande faite auprès d'un BE gagnant (ici, Netim) : transfert-demande-aupres-be-gagnant.png

Ensuite, comme je l'ai dit, il faut s'assurer que la demande de transfert n'est pas en fait une tentative de détournement du nom de domaine. La principale mesure de sécurité est nommée, selon les BE, code d'autorisation, clé d'autorisation, code de transfert, auth code, authInfo… C'est un mot de passe qui a été généré par l'ancien BE et transmis au registre, et qui, indiqué par le titulaire au nouveau BE, permet de vérifier que la demande est légitime. (Si vous êtes fana de technique et de sécurité, le RFC 9154 propose une technique alternative.) Voici ici l'obtention de ce code sur l'ancien BE (ici, Gandi). Je vous rassure, le code affiché n'est plus le bon : transfert-cyberstructure-gandi.png

Il existe d'autres façons de transmettre ce mot de passe au titulaire, par exemple par courrier électronique, ce qui permet de valider l'adresse mais expose à pas mal de risques de sécurité. La sécurité, ce n'est de toute façon jamais simple et il faut rappeler que, s'il y a des BE qui tentent de gêner les transferts sortants, en invoquant la sécurité, il y a également des malveillants qui tentent de profiter d'une sécurité trop faible pour détourner un nom. Un compromis est donc nécessaire.

Typiquement, vous recevez une notification du BE perdant vous informant de la demande de transfert, ce qui peut permettre de s'y opposer, si elle n'était pas légitime. En effet, pour éviter qu'un BE de mauvaise volonté ne bloque le transfert indéfiniment en ne donnant pas accès au code d'autorisation, le transfert est typiquement effectué automatiquement au bout d'un certain temps. Faites donc bien attention aux messages de votre BE ! Et rappelez-vous qu'il y a autant de transferts légitimes retardés ou bloqués par des procédures kafkaïennes qu'il y a de domaines détournés parce que les procédures de sécurité étaient insuffisantes. J'en profite pour insister sur ce point : gérez vos noms de domaines. Que vous soyez particulier, profession libérale, association, petite entreprise, pensez à désigner plusieurs personnes responsables qui vont recevoir les messages du BE et les lire, et agir. Vous trouvez facilement en ligne d'innombrables témoignages de gens qui ont perdu leur nom de domaine par négligence.

Voici un exemple de message reçu de l'ancien BE (ici, Gandi) :


Subject: [GANDI] IMPORTANT: Transfert sortant vers un autre prestataire du  domaine sources.org
From: no-reply@gandi.net
To: [Moi]
Date: Mon, 01 Sep 2025 13:50:47 -0000
X-Mailer: Gandi Notification Mailer v1.2.12

Vous êtes contacté car vous êtes inscrit comme contact du nom de domaine suivant.

A l'attention de : Stéphane Bortzmeyer

Transfert du domaine: sources.org

Registrar demandeur: <NETIM SARL>

GANDI a été notifié d'une demande de transfert pour ce nom de domaine.

GANDI a reçu à la date 2025-09-01 13:44:18Z une notification selon
laquelle vous avez demandé le transfert de ce nom de domain vers un
autre bureau d'enregistrement.

Si vous souhaitez poursuivre le transfert, vous n'avez pas besoin de
répondre à cette notification. Faute de réponse de votre part avant le
2025-09-06 13:44:18, le transfert sera automatiquement validé.

Vous pouvez cependant accélérer ou refuser le transfert, en vous
rendant à cette adresse avant le 2025-09-06 13:44:18: [URL où il
faudra taper le code d'autorisation].

Vérifiez ensuite avec un RDDS que le domaine a bien été transféré. Ici, on va utiliser le classique client whois :

% whois cyberstructure.fr
…
domain:                        cyberstructure.fr
status:                        ACTIVE
…
registrar:                     NETIM

Parfait, le domaine est bien passé chez Netim.

Revenons à la technique : pensez à vérifier (avec des outils comme Zonemaster et DNSviz) le domaine après le transfert. J'ai vu par exemple un BE qui, lors d'un transfert entrant, limitait le nombre de serveurs faisant autorité à cinq et supprimait silencieusement les autres. Un exemple avec dig, où on interroge un des serveurs faisant autorité pour .fr :


% dig @d.nic.fr cyberstructure.fr NS
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18712
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 9, ADDITIONAL: 1
…
;; AUTHORITY SECTION:
cyberstructure.fr.	3600 IN	NS fns1.42.pl.
cyberstructure.fr.	3600 IN	NS ns4.bortzmeyer.org.
cyberstructure.fr.	3600 IN	NS fns2.42.pl.
cyberstructure.fr.	3600 IN	NS ns2.afraid.org.
cyberstructure.fr.	3600 IN	NS puck.nether.net.
cyberstructure.fr.	3600 IN	NS ns2.bortzmeyer.org.
cyberstructure.fr.	3600 IN	NS ns1.bortzmeyer.org.

Et vérifiez que la liste des serveurs faisant autorité pour votre domaine est la bonne. (Attention, la mise à jour des serveurs du domaine parent peut ne pas être immédiate ; pour .fr, à l'heure actuelle, c'est toutes les dix minutes.)

Et DNSSEC ? Vous avez peut-être remarqué, sur l'image où le BE perdant affichait le code d'autorisation, un avertissement sur DNSSEC. La sécurité supplémentaire qu'apporte DNSSEC se paie d'une complication supplémentaire pendant le transfert : si on utilise le BE comme hébergeur (ce n'est pas mon cas), il faut, soit assurer le passage d'une clé DNSSEC à une autre (ce qui, étant donné le fait que les résolveurs DNS mémorisent des informations, va nécessiter la publication simultanée de deux clés, et une collaboration étroite entre les deux BE, dont je rappelle qu'ils sont concurrents), soit supprimer DNSSEC temporairement, ce que conseille ici le BE. Dans mon cas, comme je suis mon propre hébergeur, il n'y a pas trop de problèmes, à part qu'entre Gandi et Netim, l'enregistrement DS (Delegation Signer) a été supprimé lors du transfert (chaque BE dit que c'est de la faute de l'autre) et qu'il a donc fallu le rétablir ensuite (voyez ci-dessus le point sur l'importance d'un test technique après transfert).

Voilà, dans mon cas, les choses se sont plutôt bien passées, il n'y a pas eu de blocage, et les transferts ont été bouclés en une heure ou deux. Il me reste à transférer le domaine de ce blog bortzmeyer.org, que j'ai gardé pour la fin ; la méthode de bon sens est en effet de commencer par les domaines les moins importants.


L'article seul

Premiers essais avec Cascade, le logiciel pour gérer ses zones DNSSEC

Première rédaction de cet article le 13 octobre 2025


Annoncé officiellement le 7 octobre, Cascade est le successeur d'OpenDNSSEC. Ce programme sert à gérer automatiquement les opérations répétitives liées à DNSSEC comme la re-signature ou le remplacement d'une clé. (Notez que Cascade évolue vite en ce moment et que cet article ne sera pas mis à jour. J'en écrirai d'autres.)

OpenDNSSEC est, depuis 15 ans, le logiciel utilisé par mes domaines personnels (comme bortzmeyer.org par lequel vous êtes passé pour voir cet article) mais aussi par plusieurs TLD comme .fr. Mais il est désormais en fin de vie (cela a été annoncé le 3 octobre), il n'y a plus que les mises à jour de sécurité. La même organisation, NLnet Labs, a développé un successeur, Cascade. Celui-ci est actuellement en alpha (ne l'utilisez pas pour la production !) et voici un premier essai.

DNSSEC, contrairement à ce qui se passe avec le DNS d'antan, nécessite des actions périodiques. La plus évidente est la re-signature, car les signatures DNSSEC ont une date d'expiration (pour éviter les attaques par rejeu). Ici, cette signature expire le 18 octobre 2025 (le « 20251018235702 ») :


% dig  +dnssec  cyberstructure.fr TXT 
…
;; ANSWER SECTION:
cyberstructure.fr.	86400 IN TXT "v=spf1 mx -all"
cyberstructure.fr.	86400 IN RRSIG TXT 13 2 86400 (
				20251018235702 20251004235301 34065 cyberstructure.fr.
				Hg/ModpW3pTLgUIcgp7yrVUxUyVAXdEIOtfONpN3pxG0
                                …

  

Mais il y a aussi la nécessité de pouvoir changer les clés cryptographiques utilisées, par exemple en cas de compromission, avérée ou suspectée. (La plupart des organisations changent les clés systématiquement, compromission ou pas, notamment pour être sûr de pouvoir le faire sans incident le jour où ça sera vraiment indispensable.) Cascade, comme OpenDNSSEC avant lui, automatise toutes ces tâches.

Cascade est développé en Rust, le langage à la mode, et qui pourrait remplacer une bonne partie du logiciel d'infrastructure de l'Internet, qui est quasi-uniquement en C (et C++). Il doit son nom au fait qu'une zone DNS qu'on va signer passe par plusieurs étapes successives, où on peut insérer différentes opérations, par exemple de validation.

Jouons donc un peu avec Cascade (j'ai bien dit que c'était en version alpha ; ne le faites pas en production). Notez que vous pouvez lire la documentation officielle au lieu de cet article. Mais j'ai bien dit que le logiciel était en version alpha, ne vous étonnez pas s'il manque beaucoup de choses dans la documentation (et dans le code, bien sûr). Il existe déjà des paquetages tout faits (mais évidemment pas encore distribués avec votre système d'exploitation) mais on va compiler, c'est plus amusant. Il faut donc une version de Rust qui marche. Rust est très pénible, avec ses changements permanents, qui font que certain·es conseillent même de récupérer et d'installer automatiquement une nouvelle version du compilateur chaque nuit. Mais, bon, la version qui est dans Debian stable (la version 13 de Debian) marche bien. Cascade est un logiciel libre, et il est hébergé sur GitHub :

% git clone https://github.com/NLnetLabs/cascade.git
…

# Installer les dépendances
% apt install cargo pkg-config libssl-dev
…

% rustc --version 
rustc 1.85.0 (4d91de4e4 2025-02-17) (built from a source tarball)

% cd cascade

% cargo build
…
  

Et vous n'avez plus qu'attendre que votre machine compile quelques centaines de crates Rust. Vous vous retrouvez alors avec deux exécutables, ./target/debug/cascade et ./target/debug/cascaded. Le second est le démon qui va tourner en permanence. (Si vous venez d'OpenDNSSEC, notez qu'il n'y a cette fois qu'un seul démon.) Notez que ce démon, bien qu'il inclue un serveur DNS minimal, ne doit pas être exposé sur l'Internet. La configuration recommandée est de l'utiliser comme serveur maitre caché, sur lequel s'alimenteront un ou plusieurs des serveurs faisant autorité pour vos domaines. (Si vous venez d'OpenDNSSEC, notez que Cascade ne sait actuellement pas écrire la zone sur le disque, il faut faire un transfert de zones, cf. RFC 5936.) Le premier exécutable, lui, est le programme que vous lancez pour accomplir telle ou telle tâche, et qui parle au démon, qui fera le boulot.

Sinon, une autre solution, pour compiler, est de suivre les instructions :

cargo install --locked --git https://github.com/nlnetlabs/cascade
cargo install --locked --branch keyset --git https://github.com/nlnetlabs/dnst

Et les exécutables atterriront dans votre ~/.cargo/bin.

Regardons les options des deux programmes :


% ./target/debug/cascade
Usage: cascade [OPTIONS] <COMMAND>

Commands:
  config    Manage Cascade's configuration
  zone      Manage zones
  policy    Manage policies
  keyset    Execute manual key roll or key removal commands
  hsm       Manage HSMs
  template  Print example config or policy files

Options:
  -s, --server <IP:PORT>   The cascade server instance to connect to [default: 127.0.0.1:4539]
      --log-level <LEVEL>  The minimum severity of messages to log [default: warning] [possible values: trace, debug, info,
                           warning, error, critical]
  -h, --help               Print help
  -V, --version            Print version



% ./target/debug/cascaded -h
Usage: cascaded [OPTIONS]

Options:
      --check-config
          Check the configuration and exit
      --state <PATH>
          The global state file to use
  -c, --config <PATH>
          The configuration file to load
      --log-level <LEVEL>
          The minimum severity of messages to log [possible values: trace, debug, info, warning, error, critical]
  -l, --log <TARGET>
          Where logs should be written to [possible values: stdout, stderr, file:<PATH>, syslog]
  -d, --daemonize
          Whether Cascade should fork on startup
  -h, --help
          Print help
  -V, --version
          Print version

Si vous avez procédé comme moi, un dernier truc à compiler, il nous faut aussi cascade-dnst, par la même organisation :

%   cargo install --locked --branch keyset --git https://github.com/nlnetlabs/dnst
 

Vérifiez bien que les exécutables sont dans votre PATH (par exemple, que ~/.cargo/bin y soit). Pour le démon, vous pouvez aussi utiliser la variable dnst-binary-path dans le config.toml.

Pour utiliser Cascade, vous devrez, comme avec OpenDNSSEC, définir une politique (quel algorithme de cryptographie, quelle durée de vie des signatures, quand remplacer les clés, etc), indiquer quelles zones Cascade doit gérer, et avoir un serveur faisant autorité qui va récupérer la zone sur Cascade.

Maintenant, configurons le logiciel, toujours en suivant la documentation. (Si vous oubliez cette étape, vous aurez un message « Cascade couldn't be configured: could not load the config file '/etc/cascade/config.toml': No such file or directory (os error 2) ».) Vous avez avec Cascade un fichier de configuration d'exemple, ./etc/config.template.toml, à la syntaxe TOML. Le fichier d'exemple peut être utilisé tel quel dans beaucoup de cas. J'ai juste modifié les paramètres de journalisation :

[daemon]
log-level = "debug"
log-target = { type = "file", path = "/dev/stdout"}
# Mais log-target = { type = "syslog" } est bien aussi.
 

Lançons maintenant le démon (voyez plus loin les mécanismes pour l'avoir en permanence). D'abord, on crée les répertoires configurés dans le config.toml :

% sudo mkdir /var/lib/cascade     
% sudo chown $USER /var/lib/cascade

% sudo mkdir /etc/cascade/policies 
  

Puis on y va :

% ./target/debug/cascaded         
[2025-10-09T15:56:07.480Z] INFO cascaded: State file not found; starting from scratch
…
[2025-10-09T15:56:09.182Z] DEBUG cascade::state: Saved the global state (to '/var/lib/cascade/state.db')
…

Maintenant que le démon tourne, testons le programme de contrôle :

% ./target/debug/cascade zone list
%
  

Pas de message d'erreur, mais pas de zones configurées, c'est normal. (Si le démon ne tournait pas, vous auriez eu «  [2025-10-09T15:59:37.183Z] ERROR cascade: Error: HTTP request failed: error sending request for url (http://127.0.0.1:4539/zone/) ».)

Maintenant, il faut créer une politique. La plus courte est :

version = "v1"

mais elle n'est pas très utile. Pour avoir un point de départ, on va demander à Cascade de nous générer un exemple :

%    ./target/debug/cascade template policy  > mypolicy.toml
  

(On peut aussi utiliser l'exemple en etc/policy.template.toml.) La politique contient l'algorithme de cryptographie utilisé (j'ai choisi Ed25519, cf. RFC 8080) :

algorithm = "ED25519"                                                    
  

Je n'ai rien changé d'autre. On copie le fichier en /etc/cascade/policies/default.toml (on peut évidemment avoir plusieurs politiques, je vous laisse choisir les noms). Et on indique à Cascade de la charger (ce n'est à faire qu'une fois, ce sera mémorisé) :


% ./target/debug/cascade policy reload      
Policies reloaded:
- default added

% ./target/debug/cascade  policy list  
default

% ./target/debug/cascade policy show default
default:
  zones: <none>
…
  signer:
    serial policy: date counter
    signature inception offset: 86400 seconds
    signature validity offset: 1209600 seconds
    denial: NSEC
…
    
  

Bon, on peut maintenant ajouter une zone à gérer. On crée un fichier de zone (je l'ai mis dans /etc/cascade/zones mais vous faites comme vous voulez). Et on dit à Cascade d'y aller, en indiquant la politique à suivre :

% ./target/debug/cascade zone add --source /etc/cascade/zones/rutaba.ga --policy default rutaba.ga
Added zone rutaba.ga
   

Et voilà :

% ./target/debug/cascade zone list
rutaba.ga

% ./target/debug/cascade zone status rutaba.ga
Status report for zone 'rutaba.ga' using policy 'default'
✔ Waited for a new version of the rutaba.ga zone
✔ Loaded version 2025100901
  Loaded at 2025-10-09T16:40:36+00:00 (17s ago)
  Loaded 246 B and 5 records from the filesystem in 0 seconds
✔ Auto approving signing of version 2025100901, no checks enabled in policy.
✔ Approval received to sign version 2025100901, signing requested
✔ Signed version 2025100901 as version 2025100902
  Signing requested at 2025-10-09T16:40:36+00:00 (17s ago)
  Signing started at 2025-10-09T16:40:36+00:00 (17s ago)
  Signing finished at 2025-10-09T16:40:36+00:00 (17s ago)
  Collected 5 records in 0s, sorted in 0s
  Generated 4 NSEC(3) records in 0s
  Generated 5 signatures in 0s (5 sig/s)
  Inserted signatures in 0s (5 sig/s)
  Took 0s in total, using 1 threads
  Current action: Finished
✔ Auto approving publication of version 2025100902, no checks enabled in policy.
✔ Published version 2025100902
  Published zone available on 127.0.0.1:4543
   

(L'option --detailed après status vous donnera… des détails.)

Vous pouvez maintenant récupérer la zone signée :

    
%  dig @localhost -p 4543 rutaba.ga AXFR
…
rutaba.ga.		600 IN SOA ns4.bortzmeyer.org. hostmaster.bortzmeyer.org. (
				2025100902 ; serial
…
rutaba.ga.		3600 IN	DNSKEY 257 3 15 (
				oQjxu7aDwhDyqZbqPfx0e3I4Y+UowV0eYlssuXgiMw0=
				) ; KSK; alg = ED25519 ; key id = 57782
…
rutaba.ga.		600 IN NS ns4.bortzmeyer.org.
rutaba.ga.		600 IN NS ns1.bortzmeyer.org.
…
rutaba.ga.		600 IN RRSIG SOA 15 2 600 (
				20251023164036 20251008164036 64849 rutaba.ga.
				xn3KC4qzitoUz5ABlJDTtMd9VGPU2l8dYpsd4bYjeCqo
				vpjBdWcBbwwk1HENj5ESCLEIobpjNS3/rA4qidUvCg== )
rutaba.ga.		600 IN RRSIG NS 15 2 600 (
				20251023164036 20251008164036 64849 rutaba.ga.
				gWrzM1jYO7GJ234bH8JDejrxMhwtHrGWjxRM30gz37Fb
				dxnSv2/DiAgOt5OhdTPKYc7+IX4gLR1DPAUlJgXqDA== )
rutaba.ga.		600 IN RRSIG DNSKEY 15 2 3600 (
				20251023163739 20251008163739 57782 rutaba.ga.
				t3wZ31rY7TgXY+QZXd/Yy7QtTmYdJy6v7Kuj6IN8G+m4
				GycTLLn9W3lXErMS7f93RfkvXfERrwzbHqrTp1/oDg== )
…
;; Query time: 1 msec
;; SERVER: ::1#4543(localhost) (TCP)
;; WHEN: Thu Oct 09 16:43:24 UTC 2025
;; XFR size: 15 records (messages 1, bytes 1201)

  

(On verra plus loin comment configurer le « vrai » serveur de noms pour utiliser cette zone.)

Comme OpenDNSSEC, Cascade regarde sa montre pour savoir si un délai suffisant s'est écoulé (rappelez-vous que les résolveurs DNS ont une mémoire, et que cela a une grande importance opérationnelle ; dans le DNS, rien n'est jamais instantané). Mais, contrairement à OpenDNSSEC, il demande au résolveur local des tests (à l'heure actuelle, la machine qui fait tourner Cascade doit donc avoir accès à un résolveur qui marche). En attendant, cascade zone status --detailed vous affichera des choses comme :

      Roll AlgorithmRoll, state Propagation1:
    	Wait until the new DNSKEY RRset has propagated to all nameservers.
    	Try again after 2025-10-09T16:47:42Z

Voyons plutôt une fonction essentielle de Cascade : les étapes de validation. Pour l'instant, je n'en ai configuré aucune. Mais, en production, on veut éviter à tout prix ces ennuyeuses erreurs, qui plantent DNSSEC et toute la zone. On va donc forcément valider la zone signée avant de la publier. Le principe est d'écrire un programme (typiquement un script shell) qui va lancer un programme de validation comme validns ou le dnssec-verify de BIND. On peut avoir cette étape de validation avant la signature (pour tester le fichier de zone original) ou après (ce que je fais ici). Essayons pour commencer avec un script de validation méchant qui refuse tout :

% cat /etc/cascade/reviews/always-reject.sh
#!/bin/sh

logger "Rejecting ${CASCADE_ZONE} of serial ${CASCADE_SERIAL} from ${CASCADE_SERVER}"

♯ Cascade va utiliser le code de retour pou savoir si la zone est
# validée ou pas.
exit 1

On configure la politique (pas la configuration générale : valider ou pas fait partie de la politique) :

% cat /etc/cascade/policies/default.toml
…
# How signed zones are reviewed.
[signer.review]

required = true

cmd-hook = "/etc/cascade/reviews/always-reject.sh"
  

On recharge la politique (cascade policy reload) et la validation est configurée (Cascade étant en version alpha, pour l'instant, il faut redémarrer le démon mais ce n'est pas normal). Le résultat est l'attendu :

% ./target/debug/cascade zone status rutaba.ga     
…
• Waiting for approval to publish version 2025101004
  Configured to invoke /etc/cascade/reviews/always-reject.sh
x An error occurred that prevents further processing of this zone version:
x Signed zone was rejected at the review stage.

Et dans le journal du démon :

[2025-10-10T06:32:49.335Z] INFO cascade::units::zone_server: [RS2]: Executed hook '/etc/cascade/reviews/always-reject.sh' for signed zone 'rutaba.ga' at serial 2025101004
[2025-10-10T06:32:49.339Z] DEBUG cascade::units::zone_server: [RS2]: Hook '/etc/cascade/reviews/always-reject.sh' exited with status exit status: 1
[2025-10-10T06:32:49.339Z] DEBUG cascade::targets::central_command: [CC]: Event received: ReviewZone { name: Name(rutaba.ga.), stage: Signed, serial: Serial(2025101004), decision: Reject }
…
[2025-10-10T06:32:49.339Z] DEBUG cascade::targets::central_command: [CC]: Event received: SignedZoneRejectedEvent { zone_name: Name(rutaba.ga.), zone_serial: Serial(2025101004) }

Écrivons maintenant un script plus sérieux. Il va obtenir la zone à valider via un transfert de zones. Notez que le port utilisé est obtenu via une variable d'environnement ; Cascade sert la zone sur des ports différents selon qu'elle a été validée ou pas (cf. les paramètres servers dans config.toml). Donc, le « vrai » serveur, présenté plus loin, n'utilisera pas le même port. Voici le script de validation utilisable en production :

% cat /etc/cascade/reviews/validate-signed.bash 
#!/bin/bash

# Yes, bash is required, to parse CASCADE_SERVER

set -e

logger -p daemon.notice -t cascade "Validating ${CASCADE_ZONE} of serial ${CASCADE_SERIAL} from ${CASCADE_SERVER}"

# Parsing magic by mozzieongit
SERVER=${CASCADE_SERVER%:*}
SERVER_IP="${SERVER//[\[\]]/}" # remove brackets from IPv6
SERVER_PORT=${CASCADE_SERVER##*:} # Using double '##' in case its an IPv6

tmp_zone=$(mktemp /tmp/.cascade_zone.XXXXXXXXXX)
# Clean when leaving
trap  "rm -f ${tmp_zone}; exit 1" 1 2 3 15
trap  "rm -f ${tmp_zone}" EXIT

# Too bad dig logs some errors on standard output :-(
dig @${SERVER_IP} -p ${SERVER_PORT} ${CASCADE_ZONE} AXFR > ${tmp_zone}

# validns does not handle Ed25519
# validns -z ${CASCADE_ZONE} -p all ${tmp_zone}
# or:
dnssec-verify -q -o ${CASCADE_ZONE} ${tmp_zone}
# Or:
# ldns-verify ${tmp_zone}

# Le code de retour du programme de vérification va être celui du script.
  

Et la zone est acceptée :

[2025-10-10T07:01:01.462Z] DEBUG cascade::units::zone_server: [RS2] Received command: SeekApprovalForSignedZone { zone_name: Name(rutaba.ga.), zone_serial: Serial(2025101001) }
[2025-10-10T07:01:01.462Z] INFO cascade::units::zone_server: [RS2]: Seeking approval for signed zone 'rutaba.ga' at serial 2025101001.
[2025-10-10T07:01:01.463Z] INFO cascade::units::zone_server: [RS2]: Executed hook '/etc/cascade/reviews/validns.sh' for signed zone 'rutaba.ga' at serial 2025101001
[2025-10-10T07:01:01.485Z] INFO domain::net::server::middleware::cookies: preprocess; request_ip=127.0.0.1
[2025-10-10T07:01:01.485Z] INFO domain::net::server::middleware::xfr::service: AXFR for rutaba.ga from 127.0.0.1:35159
[2025-10-10T07:01:01.498Z] DEBUG cascade::units::zone_server: [RS2]: Hook '/etc/cascade/reviews/validns.sh' exited with status exit status: 0
[2025-10-10T07:01:01.498Z] DEBUG cascade::targets::central_command: [CC]: Event received: ReviewZone { name: Name(rutaba.ga.), stage: Signed, serial: Serial(2025101001), decision: Approve }
[2025-10-10T07:01:01.498Z] INFO cascade::targets::central_command: [CC]: Passing back zone review

Bon, maintenant, configurons un « vrai » serveur de noms pour charger la zone et la servir à tous les résolveurs de l'Internet. J'utilise nsd. Dans le nsd.conf, je mets :

zone:
      name: "internautique.fr"
      zonefile: "/var/lib/nsd/%s.zone"
      request-xfr: AXFR 127.0.0.1@8053 NOKEY
      allow-notify: 127.0.0.1 NOKEY
      # Mettre ici les autres serveurs faisant autorité, qu'il faut notifier et autoriser. 

On va autoriser 127.0.0.1 (la machine locale, où tourne Cascade) à nous notifier les changements de la zone signée, et on demandera les transferts de zone à cette même machine, sur le port où écoute Cascade (ici 8053). nsd va alors charger la zone, éventuellement notifier les autres serveurs faisant autorité et tout roule.

Si vous connaissez DNSSEC, vous avez vu qu'il manque une étape : ajouter l'enregistrement DS (Delegation Signer) dans la zone parente. Cascade n'affiche pas ce DS tout de suite, il sait que les résolveurs ont une mémoire et il attend d'avoir vu la zone signée publiée et que la durée configurée soit écoulée. En version alpha, cette étape ne marche pas toujours donc on va le faire à la main (ne le faites pas en production !) :

    
% dig @127.0.0.1 -p 8053 AXFR internautique.fr > /tmp/internautique.fr

% dnssec-dsfromkey -f /tmp/internautique.fr internautique.fr
internautique.fr. IN DS 30790 15 2 BB8987B69144BFB6863D9F86983EE1FB465629CDB3EE8DC43EE6B7C11056598F

Et c'est ce DS qu'on va transmettre à la zone parente (peut-être via son BE). Les programmes de test disponibles en ligne montrent une configuration (presque) correcte : Zonemaster et DNSviz.

Avant-dernière chose à faire : faire tourner le démon en permanence sur la machine. Cascade vient avec un fichier de configuration pour systemd mais une bogue subtile l'empêche actuellement de fonctionner. J'ai donc utilisé un plus classique script rc.local :

% cat /etc/boot.d/cascade
#!/bin/sh

# You may change this one:
DEBUG=1

set -e 

# Don't touch
export PATH=${PATH}:/home/cascade/.cargo/bin

if [ ${DEBUG} -ge 1 ]; then
   DAEMON_OPTS="--log-level debug"
   export RUST_BACKTRACE=full
else
    DAEMON_OPTS="--log-level info"
fi
   
# Does not work with su (for unknown reasons): "Permission denied"
sudo -E -u cascade /home/cascade/.cargo/bin/cascaded --daemonize ${DAEMON_OPTS} --state=/var/lib/cascade/state.db --config=/etc/cascade/config.toml
# su -p cascade -c '/home/cascade/.cargo/bin/cascaded --state=/var/lib/cascade/state.db --config=/etc/cascade/config.toml'

Enfin, quand on sera en production, il faudra évidemment superviser Cascade. J'utilise un petit script qui suit l'API Nagios :

% cat /usr/local/lib/nagios/plugins/check_cascade
#!/bin/sh

# Simple Nagios/Icinga plugin for Cascade. 

USAGE="Usage: $0 -e string_to_expect"

TEMP=$(getopt -o "e:h" -- "$@")
if [ $? != 0 ]; then
    echo $USAGE
    exit 1
fi

eval set -- "$TEMP"

while true ; do
    case "$1" in
	-e) EXPECTED=$2; shift 2;;
	-h) echo $USAGE; exit 3;;
	--) shift ; break ;;
	*) echo "Internal error!"; exit 3 ;;
    esac
done

logger "Test Cascade, expect \"${EXPECTED}\""

E_OUT=$(/home/cascade/.cargo/bin/cascade zone list 2>/dev/null)

if [ $? -ne 0 ];
then
    echo "Cascade did not respond"
    exit 2
fi

if [ ! -z "${EXPECTED}" ]; then
    L_CNT=$(echo ${E_OUT} | grep "${EXPECTED}" 2>/dev/null | wc -l)
    if [ ${L_CNT} -eq 0 ]; then
	echo "Cascade zone list did not return a match on ${EXPECTED}"
	exit 2
    fi
fi

echo "Cascade OK ${S_OUT}"
exit 0

Et qui se configure comme cela dans Icinga :

object CheckCommand "cascade" {          
  command = [ PluginContribDir + "/check_cascade" ]       
  arguments = {                 
      "-e" = "$cascade_expect$", 
    } 
}
                                           
object Host "ayla-cascade" {  
  import "generic-host"   
  check_command = "remote_always_true"  
  vars.cascade = true   
  vars.cascade_expect="internautique\\.fr" 
}        

Et en conclusion, un peu d'opéra, avec l'air « Cascader la vertu » de La Belle Hélène, interprété par Gaëlle Arquez.


L'article seul

RFC 9861: KangarooTwelve and TurboSHAKE

Date de publication du RFC : Octobre 2025
Auteur(s) du RFC : B. Viguier (ABN AMRO Bank), D. Wong (zkSecurity), G. Van Assche (STMicroelectronics), Q. Dang (NIST), J. Daemen (Radboud University)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF cfrg
Première rédaction de cet article le 13 octobre 2025


Vous trouvez qu'il n'y a pas assez d'algorithmes en cryptographie ? En voici quatre de plus, ici pour condenser cryptographiquement des données, plus rapidement que les algorithmes existants.

Il s'agit de quatre fonctions, TurboSHAKE128 et TurboSHAKE256 (variantes de TurboSHAKE) ainsi que de KT128 et KT256 (variantes de Kangaroo Twelve qui est décrit dans cet article). Toutes sont des XOF (eXtendable Output Function), des fonctions dont le résultat peut être infiniment long (comme ce que produit un générateur pseudo-aléatoire), mais qu'on tronque à la valeur souhaitée (cf. section 2.1). Kangaroo Twelve (section 3) est bâti sur TurboSHAKE et apporte le parallélisme : il permet de répartir le travail sur plusieurs processeurs alors que SHA-3 et TurboSHAKE sont strictement séquentiels. TurboSHAKE (section 2), lui, est bâti sur Keccak (qui est aussi la base de SHA-3, la cryptographie, c'est compliqué). D'ailleurs, le logiciel qui met en œuvre les algorithmes de ce RFC est développé par l'équipe de Keccak.

Ne comptez pas sur moi pour des explications cryptographiques détaillées, je n'y connais rien, lisez les sections 2 et 3 du RFC. Mais Sakura, le système utilisé par Kangaroo Twelve pour répartir le travail semble intéressant (cf. l'article original).

En section 4 du RFC, vous trouverez comment utiliser Kangaroo Twelve pour faire un MAC (on condense le message puis on condense la concaténation de la clé et du résultat de la première condensation).

Et si vous voulez mettre en œuvre Kangaroo Twelve vous-même, la section 5 vous offre de nombreux vecteurs de test. Mais, avant, regardez l'annexe A qui vous offre du pseudo-code et rappelle qu'il y a une implémentation en C, XKCP (par l'équipe Keccak) et une en Python, décrite dans l'article «  KangarooTwelve: fast hashing based on Keccak-p » (les vecteurs de test du RFC viennent de là, normal, ce sont les mêmes auteurs). Enfin, une troisième est en Zig. Commençons en Python (j'ai fait une copie du code de l'article dans kangarootwelve.py et le programme de test est test-kangarootwelve.py). Si on prend ce vecteur de test dans le RFC :

  
      KT128(M=ptn(17 bytes), C=`00`^0, 32):
       `6B F7 5F A2 23 91 98 DB 47 72 E3 64 78 F8 E1 9B
        0F 37 12 05 F6 A9 A9 3A 27 3F 51 DF 37 12 28 88`

Le programme affiche :

% python test-kangarootwelve.py
6b f7 5f a2 23 91 98 db 47 72 e3 64 78 f8 e1 9b 0f 37 12 05 f6 a9 a9 3a 27 3f 51 df 37 12 28 88 

Ce qui est bien la bonne valeur.

Et avec XKCP, compilons la bibliothèque et le programme de test test-kangarootwelve.c :

git submodule update --init
make generic64/libXKCP.so
gcc -I ./bin/generic64/libXKCP.so.headers  -L ./bin/generic64   test-kangarootwelve.c -lXKCP
# ou
# gcc -I ./bin/generic64/libXKCP.so.headers test-kangarootwelve.c ./bin/generic64/libXKCP.so
LD_LIBRARY_PATH=./bin/generic64 ./a.out 
  

Et le programme affiche bien le vecteur de test indiqué dans le RFC.

Kangaroo Twelve a été ajouté au registre IANA, et les quatre algorithmes sont dans le registre de COSE (RFC 8152).


Téléchargez le RFC 9861


L'article seul

RFC 9859: Generalized DNS Notifications

Date de publication du RFC : Septembre 2025
Auteur(s) du RFC : J. Stenstam (The Swedish Internet Foundation), P. Thomassen (deSEC, Secure Systems Engineering), J. Levine (Standcore LLC)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 5 octobre 2025


Vous le savez, le protocole DNS permet plusieurs sortes de messages, identifiés par un code d'opération. Si le classique QUERY, pour demander à résoudre un nom en informations pratiques, est le plus connu, il y a aussi le UPDATE (modifier les données), le DSO (DNS Stateful Operations) et quelques autres. Et puis il y a le NOTIFY, qui sert à indiquer à un serveur DNS qu'il y a quelque chose de nouveau et d'intéressant. NOTIFY est surtout connu pour son utilisation afin de… notifier un serveur secondaire que le primaire a une nouvelle version de la zone (c'est dans le RFC 1996). Ce nouveau RFC généralise le concept et permet d'utiliser NOTIFY pour d'autres choses comme un changement dans la liste des serveurs faisant autorité ou un changement de clé DNSSEC.

Si vous avez oublié à quoi servait initialement NOTIFY, relisez le RFC 1996. Il permettait de donner une indication (uniquement une indication, le NOTIFY ne fait pas autorité) comme quoi il faudrait envisager un nouveau transfert de zone (RFC 5936) pour mettre à jour un serveur. Cela permettait des mises à jour plus rapides qu'avec le système traditionnel où chaque serveur esclave devait de temps en temps demander à son maître s'il y avait du nouveau. Mais il y a d'autres cas où un serveur DNS voudrait dire à un autre qu'il y a quelque chose à regarder, par exemple si une nouvelle clé doit être utilisée. D'où l'extension d'utilisation que permet notre RFC 9859. Elle ne change pas le protocole, elle se contente d'utiliser plus largement une fonction existante.

Bon, mais c'est bien joli de dire qu'on peut notifier pour bien des choses mais on notifie qui ? Dans le cas traditionnel d'une nouvelle version de la zone, le primaire savait qu'il devait notifier ses secondaires, qu'il connait (après tout, il doit leur autoriser le transfert de zone et, dans le pire des cas, il peut toujours regarder l'ensemble d'enregistrements NS de sa zone). Mais si on généralise le NOTIFY, on peut ne pas savoir qui notifier (les autres mécanismes de notification, comme une API ou comme la mise à jour dynamique du RFC 2136, ont d'ailleurs le même problème). La section 3 du RFC couvre ce problème. La méthode recommandée est de publier un enregistrement de type DSYNC, normalisé dans notre RFC, section 2. Il se place sous le nom _dsync de la zone :

sousdomaine._dsync  IN DSYNC  CDS   NOTIFY 5359 cds-scanner.example.net.
  

Notez qu'un joker est possible, par exemple :

*._dsync  IN DSYNC  CDS   NOTIFY 5359 cds-scanner.example.net.
*._dsync  IN DSYNC  CSYNC NOTIFY 5359 cds-scanner.example.net.
  

Ce nom _dsync a été ajouté dans le registre des noms précédés d'un trait bas (RFC 8552). Voici un autre exemple d'enregistrement DSYNC :

_dsync.example.net.   IN DSYNC  CDS   NOTIFY 5359 cds-scanner.example.net.
  

Que dit-il ? Qu'example.net, a priori un hébergeur DNS, voudrait que ses clients, lorsqu'ils ont un nouvel enregistrement CDS (indiquant l'activation d'une nouvelle clé DNSSEC, cf. RFC 8078), notifient cds-scanner.example.net sur le port 5359. Ce nouveau type DSYNC a été ajouté au registre des types, avec la valeur 66.

La section 4.1 détaille comment on trouve le serveur à qui envoyer la notification en utilisant _dsync. Imaginons qu'on gère extra.sub.exodus-privacy.eu.org et qu'on veuille notifier la zone parente. On insère le composant _dsync après le premier composant (cela donne extra._dsync.sub.exodus-privacy.eu.org) et on fait une requête pour ce nom et le type DSYNC. Si ça marche, c'est bon, sinon, on utilise le SOA dans la réponse pour trouver la zone parente, et on met le _dsync devant. Si ça ne donne toujours rien, on retire les composants avant la zone parente et on recommence. Donc, on interrogera successivement (jusqu'au premier succès), extra._dsync.sub.exodus-privacy.eu.org, extra.sub._dsync.exodus-privacy.eu.org et _dsync.exodus-privacy.eu.org.

Et le NOTIFY dans l'enregistrement DSYNC d'exemple plus haut ? C'est le plan (scheme, à ne pas confondre avec le code d'opération - opcode - NOTIFY) car DSYNC, dans le futur, pourra servir à autre chose que les notifications. Pour l'instant, dans le nouveau registre des plans, il n'y a que NOTIFY mais dans le futur, d'autres pourront être ajoutés (suivant la politique « Examen par un expert » du RFC 8126).

Comment utiliser ce mécanisme de notification généralisé pour traiter les enregistrements CDS et CDNSKEY des RFC 7344, RFC 8078 et RFC 9615 ? Ces enregistrements servent à signaler à la zone parente la disponibilité de nouvelles clés DNSSEC. La section 4 de notre RFC détaille comment le mécanisme de notification permet d'indiquer qu'un CDS ou un CDNSKEY a changé. Cela évite au gestionnaire de la zone parente de balayer toute la zone ce qui, pour un TLD de plusieurs millions de noms comme .fr serait long et pénible. La solution de ce RFC 9859 est donc de chercher les DSYNC puis d'envoyer les NOTIFY, au moins deux, un pour le type CDS et un pour le type CDNSKEY (on ne sait pas forcément à l'avance lesquels utilisent la zone parente). La zone parente doit effectuer les mêmes vérifications que lorsqu'elle a détecté un nouveau CDS (ou CDNSKEY) : RFC 9615 si on active une zone signée pour la première fois et RFC 7344 et RFC 8078 autrement.

Les messages utilisant le code d'opération NOTIFY sont censés produire une réponse (RFC 1996, section 4.7). Si aucune réponse n'arrive avant l'expiration du délai de garde, on réessaie. Si on a trop réessayé, on peut signaler le problème avec la technique du RFC 9567.

Il est important de se souvenir qu'une notification n'est pas sérieusement authentifiée, et que le récepteur doit donc, s'il choisit d'agir sur une notification, être prudent. Dans le RFC 1996, rien de grave ne pouvait arriver, le récepteur du NOTIFY demandait juste le SOA de la zone puis, si nécessaire, un transfert de zone. Mais avec les CDS et CDNSKEY, des attaques plus sérieuses sont possibles et le destinataire de la notification doit donc effectuer davantage de vérifications (par exemple, si la zone est déjà signée, faire une requête pour le CDS ou le CDNSKEY et vérifier que la réponse est valide et sécurisée, si elle ne l'est pas, faire tous les contrôles du RFC 9615). La notification elle-même n'est pas un problème de sécurité (elle dit juste « tu devrais regarder cela »), c'est l'action qui en résulte qui doit être bien réfléchie. Voilà pourquoi les notifications, même généralisées, ne sont pas plus sécurisées que cela. (Voir aussi la section 5 du RFC, qui insiste sur ce point ; la notification peut accélérer les choses mais ne doit pas à elle-même changer quelque chose.) Il est quand même préférable de limiter le nombre de notifications qu'on traite, au cas où un client malveillant vous bombarde de notifications.

Ces notifications généralisées pourront aussi s'utiliser pour les CSYNC du RFC 7477.

Qui met en œuvre ce RFC à l'heure actuelle ? Des opérateurs comme deSEC ont annoncé que c'était en cours, côté client. Côté serveur, les registres de .ch et .se ont annoncé que c'était en cours, ou bien prévu.

Voici un exemple d'un client en Python (utilisant dnspython) qui notifie pour un nouveau CDS :

import dns.message
import dns.query    
import dns.opcode

notify = dns.message.make_query("bortzmeyer.fr", "CDS")
notify.set_opcode(dns.opcode.NOTIFY)
response = dns.query.udp(notify, "192.0.2.211")
print(response)
  

Et en C ? Vous pouvez utiliser le programme d'exemple generalized-notify.c, qui utilise l'excellente bibliothèque ldns. Compiler et exécuter :

% gcc -o generalized-notify -Wall generalized-notify.c -lldns 

% ./generalized-notify bortzmeyer.fr 192.0.2.211
Reply code: NOERROR
  

Je note à ce sujet que certains serveurs faisant autorité, lorsqu'ils ne font pas autorité pour le domaine signalé, répondent REFUSED, ce qui est logique, mais on voit aussi des FORMERR ou NXDIOMAIN (!), sans doute parce que le type CDS ne leur plait pas.

L'annexe A du RFC prend de la hauteur et décrit plus en détail le problème du balayage DNS. Comment savoir qu'il y a du nouveau, sans tout examiner (dans le cas du SOA, au rythme indiqué par le champ Refresh ; mais il n'y a pas de telle solution pour les CDS et CDNSKEY). Le DNS traditionnel ne marchait que sur un modèle pull (et c'est pour cela qu'il est faux de parler de propagation DNS) mais les NOTIFY du RFC 1996 introduisaient un peu de push. Heureusement car balayer .com à la recherche de nouveaux enregistrements serait lent (et attirerait probablement l'attention des IDS). Pour les CDS et CDNSKEY, cela serait d'autant plus agaçant qu'ils seront a priori peu nombreux et que la plupart des requêtes ne donneront donc rien.


Téléchargez le RFC 9859


L'article seul

RFC 9874: Best Practices for Deletion of Domain and Host Objects in the Extensible Provisioning Protocol (EPP)

Date de publication du RFC : Septembre 2025
Auteur(s) du RFC : S. Hollenbeck (Verisign Labs), W. Carroll (Verisign), G. Akiwate (Stanford University)
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 5 octobre 2025


Dans un registre de noms de domaine, il existe une classe d'objets pour les domaines et parfois une pour les serveurs de noms (hosts). C'est en se basant sur les objets de ces classes que des informations sont ensuite publiées dans le DNS. Que se passe-t-il si on retire un objet de ces classes, alors que d'autres objets en dépendaient ? Va-t-on scier une branche sur laquelle quelqu'un est assis ? Ce RFC fait le point sur les solutions existantes, notant que certaines sont moins bonnes que d'autres, notamment pour la sécurité.

Prenons un exemple simple : le domaine monbeaudomaine.example est enregistré auprès du registre du TLD .example. Ses serveurs de noms sont ns1.domaine-d-un-copain.example et ns1.hébergeur.example et on va supposer que le registre de .example traite les serveurs de noms comme une classe distincte dans sa base de données. Maintenant, supposons que le domaine domaine-d-un-copain.example soit supprimé parce que son titulaire n'en voit plus l'utilité. Que va devenir le serveur de noms ns1.domaine-d-un-copain.example ? Il existe de nombreuses façons de traiter ce problème, et c'est le rôle de ce RFC de les analyser toutes.

Ainsi, la section 3.2.2 du RFC 5731 dit que ce n'est pas bien de détruire un domaine si des objets de type serveur de noms sont sous ce domaine. Dans l'exemple précédent, le registre de .example aurait refusé la suppression de domaine-d-un-copain.example. Mais cela laisse le problème entier : si le titulaire ne veut plus payer et donc veut supprimer le domaine, que faire ?

Un cas similaire se produit si on veut supprimer un serveur de noms. Si le client EPP demande au serveur EPP du registre de .example la suppression de l'objet ns1.domaine-d-un-copain.example, que doit faire le registre, sachant que ce serveur de noms est utilisé par monbeaudomaine.example ? La section 3.2.2 du RFC 5732 dit que le registre devrait refuser la suppression. C'est d'autant plus gênant que, dans le modèle RRR (Registrant-Registrar-Registry), domaine et serveur(s) peuvent être gérés par des BE différents, n'ayant pas le droit de toucher aux objets des autres BE.

Vous pouvez trouver des bonnes explications et des exemples réels dans les supports d'une présentation qui avait été faite à l'IETF.

Quelles sont donc les recommandations concrètes de ce RFC ? La section 6 les résume. Au choix :

  • Renommer les serveurs de noms vers le nom d'un serveur maintenu par le client EPP (le BE), comme décrit dans la section 5.1.3.4.
  • Demander au client EPP de supprimer les serveurs de noms mais avec possibilité de rétablissement (RFC 3915) comme décrit en section 5.2.2.3.
  • Renommer les serveurs de noms dans un nom de domaine spécial, dont il est garanti qu'il ne sera jamais utilisé, comme décrit en section 5.1.4.3. Pour éviter d'avoir à créer une nouvelle entrée dans le registre des noms de domaine spéciaux, le nom sacrificial.invalid, utilisant un TLD existant, est recommandé.

Le protocole EPP permet de changer le nom d'un serveur de noms (section 3.2.5 du RFC 5732). Une pratique déjà observée est donc de renommer les serveurs de noms. Dans l'exemple ci-dessus, recevant la demande de suppression de domaine-d-un-copain.example, le registre (ou le BE s'il le peut) renommerait ns1.domaine-d-un-copain.example en, mettons, ns1.domaine-d-un-copain.renamed.invalid. Ici, .invalid, TLD réservé par le RFC 6761, ne poserait pas vraiment de problème mais renommer dans un domaine ouvert à l'enregistrement pourrait créer un risque de sécurité, si le domaine de destination n'existe pas, un méchant pouvant alors l'enregistrer et être ainsi en mesure de répondre aux requêtes DNS.

La section 3 du RFC explique brièvement pourquoi les RFC 5731 et RFC 5732 déconseillent fortement de permettre la suppression d'un domaine tant que des serveurs de noms dans ce domaine existent. Il y a deux risques de sécurité si on détruisait le domaine en laissant les serveurs de noms tels quels dans la base de données du registre : un de déni de service (le nom ne se résout plus et le serveur va donc être inutile) et un de détournement (si un méchant peut ré-enregistrer le nom de domaine supprimé). Il y a aussi le problème de la colle orpheline, décrit dans le rapport du SSAC, « Comment on Orphan Glue Records in the Draft Applicant Guidebook ».

Si la clé qui identifie un serveur de noms est un numéro quelconque et pas son nom, on peut renommer le serveur sans changer les délégations, ce qui est particulièrement utile si le client EPP n'a pas le droit de changer des délégations qui appartiennent à un autre client du registre. Le nouveau nom ne va en général pas être associé à un serveur opérationnel : on sacrifie un serveur pour pouvoir supprimer le domaine parent. Mais cela entraine quelques risques (section 4 du RFC et l'article d' Akiwate, G., Savage, S., Voelker, G., et K. Claffy, « Risky BIZness: Risks Derived from Registrar Name Management »). Si on renomme vers un nom actuellement inexistant, le domaine peut être détourné si un malveillant enregistre ensuite ce domaine.

Compte tenu de tout cela, la section 5 du RFC étudie les différentes pratiques possibles, leurs avantages et leurs inconvénients. Pour les illustrer, je vais utiliser une base de données simple, décrite en SQL (les essais ont été faits avec PostgreSQL). Voici par exemple une création d'une telle base :

CREATE TABLE Domains (name TEXT UNIQUE);

-- Ici, la table Nameservers n'offre aucune valeur ajoutée par rapport
-- au fait de tout mettre dans Domains. Mais ce ne sera pas le cas par
-- la suite. 
CREATE TABLE Nameservers (name TEXT UNIQUE);

CREATE TABLE Delegation (domain TEXT REFERENCES Domains(name),
                         server TEXT REFERENCES Nameservers(name));

INSERT INTO Domains VALUES ('monbeaudomaine.example');
INSERT INTO Domains VALUES ('domaine-d-un-copain.example');

INSERT INTO Nameservers VALUES ('ns1.domaine-d-un-copain.example');
INSERT INTO Nameservers VALUES ('ns1.hébergeur.example');

INSERT INTO Delegation VALUES ('monbeaudomaine.example', 'ns1.domaine-d-un-copain.example');
INSERT INTO Delegation VALUES ('monbeaudomaine.example', 'ns1.hébergeur.example');
  

Dans ce premier cas très simple, la suppression du domaine domaine-d-un-copain.example est triviale :

registry=> DELETE FROM Domains WHERE name='domaine-d-un-copain.example';
DELETE 1
  

Mais elle laisse la possibilité de colle orpheline et surtout d'un détournement de monbeaudomaine.example si quelqu'un ré-enregistre domaine-d-un-copain.example. Ce premier essai n'est pas conforme aux exigences des RFC 5731 et RFC 5732. On va essayer de faire mieux.

Si on interdit (à juste titre) la suppression d'un domaine lorsque des serveurs de noms sont nommés dans ce domaine, on peut arriver à supprimer un domaine en supprimant d'abord les serveurs de noms qui sont nommés dans ce domaine (section 5.2) :

CREATE TABLE Domains (name TEXT UNIQUE);

-- Ajout d'une dépendance au domaine parent, pour éviter les suppressions. 
CREATE TABLE Nameservers (name TEXT UNIQUE, parent TEXT REFERENCES Domains(name));

CREATE TABLE Delegation (domain TEXT REFERENCES Domains(name),
                         server TEXT REFERENCES Nameservers(name));

INSERT INTO Domains VALUES ('monbeaudomaine.example');
INSERT INTO Domains VALUES ('domaine-d-un-copain.example');
INSERT INTO Domains VALUES ('hébergeur.example');

-- Pour une vraie base, on écrirait du code SQL qui extrait le parent
-- automatiquement.
INSERT INTO Nameservers VALUES ('ns1.domaine-d-un-copain.example', 'domaine-d-un-copain.example');
INSERT INTO Nameservers VALUES ('ns1.hébergeur.example', 'hébergeur.example');

INSERT INTO Delegation VALUES ('monbeaudomaine.example', 'ns1.domaine-d-un-copain.example');
INSERT INTO Delegation VALUES ('monbeaudomaine.example', 'ns1.hébergeur.example');

registry=> DELETE FROM Domains WHERE name='domaine-d-un-copain.example';
ERROR:  update or delete on table "domains" violates foreign key constraint "nameservers_parent_fkey" on table "nameservers"
DETAIL:  Key (name)=(domaine-d-un-copain.example) is still referenced from table "nameservers".
-- Ce comportement est ce que recommandent les RFC 5731 et 5732.

-- Cela oblige le client à supprimer les serveurs de noms d'abord, ce qui à
-- son tour nécessite potentiellement de changer les délégations :

registry=> DELETE FROM Delegation WHERE server = 'ns1.domaine-d-un-copain.example';
DELETE 1
registry=>  DELETE FROM Nameservers WHERE name='ns1.domaine-d-un-copain.example';
DELETE 1
registry=>  DELETE FROM Domains WHERE name='domaine-d-un-copain.example';
DELETE 1
  

Ici, il y a eu suppression explicite des serveurs de noms par le client (section 5.2.2.1). Cela peut poser des problèmes de permission, dans le cadre du système RRR, si tous les objets ne sont pas chez le même BE. Mais la suppression explicite est une des trois solutions recommandées, notamment si on ajoute la possibilité de rétablir l'état précédent (commande EPP <restore>, RFC 3915).

On peut aussi envisager une suppression implicite (section 5.2.1.1), le registre se chargeant du nettoyage (c'est le rôle de la directive SQL ON DELETE CASCADE) :

CREATE TABLE Domains (name TEXT UNIQUE);

CREATE TABLE Nameservers (name TEXT UNIQUE,
                          parent TEXT REFERENCES Domains(name) ON DELETE CASCADE);

CREATE TABLE Delegation (domain TEXT REFERENCES Domains(name) ON DELETE CASCADE,
                         server TEXT REFERENCES Nameservers(name) ON DELETE CASCADE);

INSERT INTO Domains VALUES ('monbeaudomaine.example');
INSERT INTO Domains VALUES ('domaine-d-un-copain.example');
INSERT INTO Domains VALUES ('hébergeur.example');

-- Pour une vraie base, on écrirait du code SQL qui extrait le parent
-- automatiquement.
INSERT INTO Nameservers VALUES ('ns1.domaine-d-un-copain.example', 'domaine-d-un-copain.example');
INSERT INTO Nameservers VALUES ('ns1.hébergeur.example', 'hébergeur.example');

INSERT INTO Delegation VALUES ('monbeaudomaine.example', 'ns1.domaine-d-un-copain.example');
INSERT INTO Delegation VALUES ('monbeaudomaine.example', 'ns1.hébergeur.example');

registry=> SELECT Domains.name,Nameservers.name FROM Domains,Delegation,Nameservers WHERE Delegation.domain=Domains.name AND Delegation.server=Nameservers.name;
          name          |              name               
------------------------+---------------------------------
monbeaudomaine.example | ns1.domaine-d-un-copain.example
monbeaudomaine.example | ns1.hébergeur.example
(2 rows)

registry=>  DELETE FROM Domains WHERE name='domaine-d-un-copain.example';
DELETE 1

-- Serveur de noms et délégation ont été détruits en cascade. Ce n'est
-- pas déraisonnable mais c'est quand même un peu effrayant.

registry=> SELECT Domains.name,Nameservers.name FROM Domains,Delegation,Nameservers WHERE Delegation.domain=Domains.name AND Delegation.server=Nameservers.name;
          name          |         name          
------------------------+-----------------------
monbeaudomaine.example | ns1.hébergeur.example
(1 row)
  

Cette solution est simple et efficace mais détruire implicitement des objets de la base de données peut inquiéter les responsables de cette base. Et cela peut laisser un domaine avec trop peu de serveurs de noms pour assurer sa continuité de service voire, dans le pire des cas, sans serveurs du tout. Et il serait bon de prévenir le client de cette suppression implicite, par exemple par le mécanisme de poll d'EPP (RFC 8590).

Si on interdit (à juste titre, le RFC le recommande) la suppression d'un domaine lorsque des serveurs de noms sont nommés dans ce domaine, une solution possible est de renommer les serveurs avant de supprimer le domaine (section 5.1). Le nouveau nom permet d'indiquer clairement la raison du renommage. Mais ce renommage laisse dans la base des « déchets » qu'il faudra nettoyer un jour. Cette catégorie contient de nombreuses variantes. Par exemple, on peut renommer dans un TLD spécial (RFC 6761), ici, .invalid :

CREATE TABLE Domains (name TEXT UNIQUE);

-- On introduit un identificateur du serveur de noms qui n'est *pas*
-- son nom, pour permettre le renommage.
CREATE TABLE Nameservers (id SERIAL UNIQUE, name TEXT UNIQUE,
                                 parent TEXT REFERENCES Domains(name));

CREATE TABLE Delegation (domain TEXT REFERENCES Domains(name),
                         server INTEGER REFERENCES Nameservers(id));

INSERT INTO Domains VALUES ('monbeaudomaine.example');
INSERT INTO Domains VALUES ('domaine-d-un-copain.example');
INSERT INTO Domains VALUES ('hébergeur.example');
-- Pour le renommage, un nom qui indique clairement le but :
INSERT INTO Domains VALUES ('renamed.invalid');

-- Pour une vraie base, on écrirait du code SQL qui extrait le parent
-- automatiquement.
INSERT INTO Nameservers (name, parent) VALUES ('ns1.domaine-d-un-copain.example', 'domaine-d-un-copain.example');
INSERT INTO Nameservers (name, parent) VALUES ('ns1.hébergeur.example', 'hébergeur.example');

INSERT INTO Delegation VALUES ('monbeaudomaine.example',
     (SELECT id FROM Nameservers WHERE name='ns1.domaine-d-un-copain.example'));
INSERT INTO Delegation VALUES ('monbeaudomaine.example',
     (SELECT id FROM Nameservers WHERE name='ns1.hébergeur.example'));

registry=> SELECT Domains.name,Nameservers.name FROM Domains,Delegation,Nameservers WHERE Delegation.domain=Domains.name AND Delegation.server=Nameservers.id;
          name          |              name               
------------------------+---------------------------------
monbeaudomaine.example | ns1.domaine-d-un-copain.example
monbeaudomaine.example | ns1.hébergeur.example
(2 rows)

registry=> UPDATE Nameservers SET name='ns1.domaine-d-un-copain.example.renamed.invalid', parent='renamed.invalid' WHERE id=1;
UPDATE 1
registry=> DELETE FROM Domains WHERE name='domaine-d-un-copain.example';
DELETE 1

registry=> SELECT Domains.name,Nameservers.name FROM Domains,Delegation,Nameservers WHERE Delegation.domain=Domains.name AND Delegation.server=Nameservers.id;
          name          |                      name                       
------------------------+-------------------------------------------------
monbeaudomaine.example | ns1.domaine-d-un-copain.example.renamed.invalid
monbeaudomaine.example | ns1.hébergeur.example
(2 rows)

Par contre, il ne faut pas utiliser .alt, qui est explicitement réservé aux protocoles non-DNS (RFC 9476). Notez que certains serveurs EPP peuvent tester le nom des serveurs de noms et refuser des TLD « inconnus ».

Dans la nature, on a pu observer d'autres pratiques, comme de renommer dans un sous-domaine de as112.arpa, nom garanti ne pas exister (RFC 7535), mais qui n'est pas censé servir à cela (RFC 6305). On a vu aussi des renommages vers des résolveurs DNS publics, ce qui est également une horreur, la délégation doit être faite vers des serveurs faisant autorité, pas des résolveurs.

Certains clients EPP maintiennent des serveurs de noms actifs qui servent pour le renommage. Cela fait davantage de travail mais cela protège contre le détournement. Le DNS va continuer à fonctionner normalement. On pourrait aussi imaginer des serveurs de noms actifs, répondant NXDOMAIN (« ce domaine n'existe pas »), qui soient gérés collectivement (section 5.1.4.4) ; un tel service serait certainement utile. On pourrait même créer un nom de domaine pour ce service (sacrificial.arpa ?) mais personne ne l'a encore fait. Pour l'instant, la solution des serveurs maintenus par le client EPP (section 5.1.3.4) fait partie des trois solutions recommandées. Le client prudent doit bien verrouiller le domaine dans lequel ces serveurs sont nommés (enregistrement multi-années, verrouillage par le registre, etc).

On peut aussi renommer les serveurs de noms vers un nom non-existant dans un TLD existant. Ça s'est déjà vu mais il ne faut surtout pas faire cela : un attaquant pourrait enregistrer le nom et capter ainsi le trafic (.invalid n'a pas ce problème). Idem si le nom n'est pas sous votre contrôle. Un exemple est donné par le domaine lenvol.re : ses serveurs de noms étaient ns.hostin.io, ns.hostin.mu et ns.hostin.re. Lors de la suppression de hostin.re en octobre 2024, le dernier serveur de noms a été renommé host1.renamedbyregistry.com (et, en dépit du nom, pas par le registre). Ce domaine renamedbyregistry.com étant enregistré, et par un autre BE, on voit le risque.

%  whois lenvol.re
domain:                        lenvol.re
status:                        ACTIVE
…
nserver:                       host1.renamedbyregistry.com
nserver:                       ns.hostin.io
nserver:                       ns.hostin.mu
  

D'autres noms qui utilisaient ce même serveur ont également le problème :

% dig @d.nic.fr savanna.re. NS       
…
;; AUTHORITY SECTION:
savanna.re.		3600	IN	NS	host1.renamedbyregistry.com.
savanna.re.		3600	IN	NS	ns.hostin.mu.
savanna.re.		3600	IN	NS	ns.hostin.io.

;; Query time: 8 msec
;; SERVER: 2001:678:c::1#53(d.nic.fr) (UDP)
;; WHEN: Tue Jun 24 14:33:32 CEST 2025

En lecture supplémentaire, notre RFC recommande le rapport « SSAC 125 "Report on Registrar Nameserver Management" », ainsi que l'article « Risky BIZness: Risks Derived from Registrar Name Management ».


Téléchargez le RFC 9874


L'article seul

monlycée.net marche désormais avec l'accent

Première rédaction de cet article le 4 octobre 2025


Le site Web « MonLycée.net » a été souvent mentionné dans la presse française il y a deux semaines, suite à l'annonce que la région Île-de-France allait remplacer certains logiciels Microsoft par des logiciels français. Mais la communication était mal faite, seul http://monlycee.net/ marchait, pas http://monlycée.net/ (avec l'accent sur le E). C'est désormais réparé.

L'essentiel de la communication en français utilisait en effet le nom de domaine monlycée.net, ce qui est logique, le lycée prend en effet un accent. Mais, depuis très longtemps, le nom de domaine à utiliser était monlycee.net, le nom correct n'étant pas configuré.

Cette copie d'écran du site Web montre bien que l'orthographe correcte est utilisée depuis longtemps : monlycee.png

Mais, en septembre 2025, au moment où ce site était beaucoup mentionné dans les médias, le domaine monlycée.net existait bien mais n'était pas correctement configuré dans le DNS, aucun des deux serveurs de noms ne répondait. Voici ce qu'on voyait avec dig le 29 septembre (si vous utilisez un dig compilé sans IDN, vous devrez utiliser xn--monlyce-gya.net au lieu de monlycée.net) :


% dig @l.gtld-servers.net. monlycée.net NS
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14466
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
…
;; AUTHORITY SECTION:
monlycée.net.		172800 IN NS dns1.namebay.com.
monlycée.net.		172800 IN NS dns2.namebay.com.

;; Query time: 41 msec
;; SERVER: 2001:500:d937::30#53(l.gtld-servers.net.) (UDP)
;; WHEN: Mon Sep 29 09:14:57 CEST 2025
;; MSG SIZE  rcvd: 244


% dig @dns1.namebay.com. monlycée.net. NS
…
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 32749
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
…
; COOKIE: e359c164d9f8ced10100000068da31fe1b539adeb5eee54e (good)
; EDE: 18 (Prohibited)
; EDE: 20 (Not Authoritative): (recursion disabled)
…
;; Query time: 20 msec
;; SERVER: 2a01:c8:ff00:201::133#53(dns1.namebay.com.) (UDP)
;; WHEN: Mon Sep 29 09:15:09 CEST 2025
;; MSG SIZE  rcvd: 106

  

Le serveur faisant autorité pour .net annonce que les deux serveurs faisant autorité pour monlycée.net sont dns1.namebay.com et dns2.namebay.com mais aucun des deux ne veut répondre. (L'EDE - Extended DNS Error, cf. RFC 8914 indique que le serveur ne fait pas autorité pour ce nom.) Le domaine ne marche donc pas du tout.

De telles erreurs de communication et/ou de configuration avec les noms de domaine internationalisés sont hélas fréquentes.

Voulant signaler le problème, j'ai utilisé whois :

%  whois monlycée.net 
…
   Creation Date: 2016-06-27T11:07:12Z
…
Tech Name: Communication Direction
Tech Organization: CONSEIL REGIONAL ILE DE FRANCE
Tech Street: 2, rue Simone Veil Bon de commande 81236 Numero d'engagement Chorus 792650
Tech City: SAINT-OUEN
…
Tech Email: https://www.validname.com/whoiscontact/?id=4377E185-08DF-4351-91A9-D02B507D3806&c=T
  

Zut, on n'a pas l'adresse du contact technique, juste un formulaire Web pénible. Bon, j'ai écrit. Le message de confirmation disait « E-Mail routed to the contact. Thank you for using our services. Best regards. A-Bot, in charge of routing anonymous [sic] e-mails. ». Je n'ai évidemment jamais eu de réponse mais peut-être que quelqu'un a fait quelque chose puisque, le 1 octobre, un certificat Let's Encrypt a été obtenu pour ce nom, prouvant que le domaine remarchait. Bienvenue à https://monlycée.net.

Dans son effort louable de progression vers l'autonomie numérique, le Conseil régional a aussi installé un PeerTube, ce qui est une excellente idée. Mais il annonce « PeerTube de Monlycée.net » alors que seul https://peertube.monlycee.net/ fonctionne, pas https://peertube.monlycée.net/ (nom non existant). On retombe dans le problème de communication cité plus haut.

Et quelques petits détails techniques pour finir. D'abord, X.509 ne permettant pas apparemment l'Unicode (ou bien c'est l'AC qui ne le fait pas ?), le certificat utilise la forme Punycode, xn--monlyce-gya.net :

% gnutls-cli monlycée.net
…
Resolving 'xn--monlyce-gya.net:443'...
Connecting to '185.65.56.149:443'...
- Certificate type: X.509
…
 - subject `CN=xn--monlyce-gya.net', issuer `CN=E7,O=Let's Encrypt,C=US', serial 0x06f5adb213041273640f32f866068a400090, EC/ECDSA key 256 bits, signed using ECDSA-SHA384, activated `2025-10-01 09:39:17 UTC', expires `2025-12-30 09:39:16 UTC', pin-sha256="iDBvNYX0tCBtaAEMSv5m2AknA+bFcLX3WCWtCA33S0U="

J'ai vu que ce certificat du 1 octobre est le plus ancien, par une recherche sur crt.sh. Ce service, par ailleurs très utile, ne permet pas de recherche sur le nom en Unicode, mais je crois que c'est une limite de X.509.

Enfin, monlycée.net est signé avec DNSSEC mais n'a pas (encore ?) publié de lien dans le domaine parent.


L'article seul

Utiliser des paquetages de Debian unstable via Ansible

Première rédaction de cet article le 4 octobre 2025


Bon, c'est un article d'ultra-niche. Mais, comme j'ai eu du mal à faire fonctionner cette configuration, je me dis que je peux la décrire, et que peut-être quelqu'un·e trouvera cet article un jour via un moteur de recherche, et que cela lui servira. Donc, le problème était : soit un ensemble de machines Debian configurées via Ansible. Un paquetage dont j'avais besoin n'était que dans la version Debian unstable. Comment l'installer via le module Ansible apt ?

D'abord, je détaille le problème. Pour une formation DNSSEC, je gère N machines virtuelles tournant sous Debian. Le paquetage d'OpenDNSSEC, qui était dans les précédentes versions de Debian, a été retiré de l'actuelle version stable, la version 13, alias « trixie ». La configuration que j'utilisais d'habitude avec Ansible et son module apt n'est donc pas utilisable telle quelle pour installer OpenDNSSEC.

Il y a bien sûr plusieurs solutions à ce problème (mais pas celle des backports, OpenDNSSEC n'y est pas) :

  • Ne pas utiliser de paquetage du tout, compiler OpenDNSSEC moi-même et l'installer sur les machines via d'autres modules Ansible qui permettent de copier exécutables et fichiers de configuration. Pas pratique ; cela fait perdre un des gros avantages de l'utilisation de Debian, son excellent système de paquetages.
  • Utiliser le successeur d'OpenDNSSEC (qui est officiellement retiré), Cascade. Pas de paquetage (donc on revient au point précédent) et, en plus, Cascade est loin d'être utilisable.
  • Utiliser la version unstable de Debian au lieu de la stable. Pour des travaux pratiques dans une formation, le risque d'utiliser unstable est moins grave qu'en production mais, quand même, je ne voudrais pas qu'un problème surgisse pendant les exercices.
  • Créer un paquetage Debian sur une machine stable (en utilisant le source du paquetage de unstable), le copier vers les machines virtuelles et l'installer avec dpkg -i. À la réflexion, c'est sans doute ce que j'aurais dû faire, mais il n'est pas garanti que cela aurait bien marché.

J'ai finalement choisi une autre voie : utiliser stable mais permettre l'installation de paquetages qui existent dans unstable. Bien que déconseillée par Debian, cette méthode est assez banale et on trouve en ligne plein d'instructions sur comment la réaliser. Une configuration simple est, dans /etc/apt/sources.list.d/unstable.sources :

Types: deb deb-src
URIs: mirror+file:///etc/apt/mirrors/debian.list
Suites: sid
Components: main
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
  

(sid est le petit nom de la version unstable. Si vous avez vu Toy Story, vous savez pourquoi.) Avec cela, vous pouvez maintenant installer des paquetages d'unstable mais attention, vous ne voulez pas que ces paquetages remplacent vos paquetages stable existants ! Il faut donc aussi donner une basse priorité à unstable, avec un /etc/apt/preferences.d/99unstable :

Package: *
Pin: release a=unstable
Pin-Priority: 1
  

Désormais, après le prochain apt update, un apt install quelquechose installera bien la version stable du paquetage « quelquechose », qui a une priorité supérieure. Et apt install opendnssec installera la version d'unstable puisqu'il n'y en a pas dans stable.

Tout cela est classique et bien connu, et ça marche. Mais depuis Ansible, patatras. Le module apt d'Ansible prétend qu'il n'existe pas d'OpenDNSSEC, alors que ça marche en ligne de commande avec apt. Voici la configuration du module :

  tasks:
    …
    - name: install packages
      apt:   # https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html
        name: [truc, machin, opendnssec]
        state: present
  

C'est apparemment parce que Ansible ajoute à apt une option qui lui dit de n'utiliser que stable et je n'ai pas trouvé le moyen de couper cette option.

La solution est donc de faire deux tâches Ansible, une pour stable et une pour unstable, en utilisant le paramètre default_release. Voici ma configuration complète et qui marche :

  tasks:
    …
    - name: "Copy apt sources"
      copy:
        src: apt-unstable.sources
        dest: /etc/apt/sources.list.d/unstable.sources
    - name: "Copy apt preferences"
      copy:
        src: apt-preferences.txt
        dest: /etc/apt/preferences.d/99unstable
    - name: "Update Repository cache"
      apt: # https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html
        update_cache: true
        upgrade: dist
        cache_valid_time: 3600
        force_apt_get: true
    - name: install packages
      apt:
        name: [truc, chose, machin]
        state: present
    - name: install unstable packages
      apt:
        name: [opendnssec, softhsm2]
        default_release: sid
        state: latest

Notez le state: latest. Si on laisse à la valeur par défaut, present, Ansible produit un message d'erreur incohérent prétendant que sid n'est pas dans les sources.


L'article seul

Version 17 d'Unicode

Première rédaction de cet article le 9 septembre 2025


Le 9 septembre 2025 est sortie la version 17 d'Unicode. Une description officielle des principaux changements est disponible mais voici ceux qui m'ont intéressé particulièrement. (Il n'y a pas de changement radical.)

Pour explorer plus facilement la grande base Unicode, j'utilise un programme qui la convertit en SQL et permet ensuite de faire des analyses variées. Faisons quelques requêtes SQL :

ucd=> SELECT count(*) AS Total FROM Characters;
 total  
--------
 159866

Combien de caractères sont arrivés avec la version 17 ?

ucd=> SELECT version,count(version) FROM Characters GROUP BY version ORDER BY version::float;
...
 14.0    |   838
 15.0    |  4489
 15.1    |   627
 16.0    |  5185
 17.0    |  4803

4 803 nouveaux caractères, c'est pas mal, mais moins que la précédente version. Quels sont ces nouveaux caractères ?

ucd=> SELECT To_U(codepoint) AS Code_point, name FROM Characters WHERE version='17.0' ORDER BY Codepoint;
 code_point |                                    name                                    
-----------+----------------------------------------------------------------------------
 U+FDCC     | ARABIC LIGATURE SALLALLAHU ALAYHI WA-ALAA AALIHEE WA-SALLAM
 …
 U+10940    | SIDETIC LETTER N01
 U+10941    | SIDETIC LETTER N02
 U+10942    | SIDETIC LETTER N03
 …
 U+11DB0    | TOLONG SIKI LETTER I
 U+11DB1    | TOLONG SIKI LETTER E
 U+11DB2    | TOLONG SIKI LETTER U
 U+11DB3    | TOLONG SIKI LETTER O
 …
 U+16EA0    | BERIA ERFE CAPITAL LETTER ARKAB
 U+16EA1    | BERIA ERFE CAPITAL LETTER BASIGNA
 U+16EA2    | BERIA ERFE CAPITAL LETTER DARBAI
 …
 U+1CEC8    | EUNOMIA
 U+1CEC9    | PSYCHE
 U+1CECA    | THETIS
 U+1CECB    | MELPOMENE
 …
 U+1F6D8    | LANDSLIDE
 …
 U+1FAC8    | HAIRY CREATURE
 …

Cette version amène en effet quatre nouvelles écritures. Si l'écriture sidétique n'est connue que par une douzaine d'inscriptions, le zaghawa (Beria Erfe), lui, est toujours en usage. On voit aussi arriver le Tolong Siki et le Lai Tay (Tai Yo). Vous noterez aussi les symboles astronomiques pour les plus gros astéroïdes, comme Victoria.

Il y a d'autres nouveautés que l'addition de caractères. Par exemple les propriétés pour la coupure de mots de la cédille, malgré l'ancienneté de ce caractère, ont été changées pour se conformer aux règles du saanich. Ou bien une nouvelle classe pour les tirets a été créée, Unambiguous_Hyphen, pour des caractères comme le ‐ (regardez bien, c'est le tiret Unicode, U+2010, pas le tiret ASCII, U+002D, qui peut être un tiret ou bien le signe moins).

En plus futile, il y a l'habituelle arrivée d'emojis comme le trombone ou le coffre au trésor. Par contre, le trognon de pomme a finalement été retiré et n'apparaitra pas avant Unicode 18.

Si vous avez les bonnes polices de caractères, vous allez pouvoir voir quelques exemples (sinon, le lien mène vers Uniview). Voici par exemple, la troisième lettre de l'alphabet sidétique 𐥂, le caractère Zaghawa Kobo 𖺩, l'astéroïde Psyche 𜻉, le yéti 🫈 et le glissement de terrain 🛘.

Vous voulez davantage de détails ? Vous en trouverez plein dans l'article d'Ysabeau.


L'article seul

Fiche de lecture : Geopolitics at the Internet’s Core

Auteur(s) du livre : Fiona M. Alexander, Laura DeNardis, Nanette S. Levinson, Francesca Musiani
Éditeur : Palgrave Macmillan Cham
978-3-031-89477-0
Publié en 2025
Première rédaction de cet article le 29 août 2025


Ce livre parle de géopolitique de l'Internet. Banal, vous allez me dire, plein de livres ont déjà été écrits sur ce sujet. Mais celui-ci parle bien de l'Internet, pas des services qui tournent dessus, comme le font la très grande majorité des livres qui prétendent parler de l'Internet, mais ne connaissent que de ce qu'on voit sur l'écran. Ici, au contraire, c'est bien l'infrastructure, le cœur de l'Internet, qui est étudiée par les auteures.

Car l'infrastructure n'est pas neutre et les questions politiques liées à l'Internet ne concernent pas que les trois ou quatre réseaux sociaux centralisés étatsuniens que mentionnent la plupart des auteurs. Ce livre va donner de nombreux exemples, du choix et du déploiement d'IPv6 à la gestion des serveurs racine du DNS, en passant par la censure ou les demandes de blocage des réseaux russes suite à l'invasion de l'Ukraine, ainsi que des sujets très peu traités quand on parle de « gouvernance de l'Internet » comme la distribution d'adresses IP, avec l'exemple des attaques contre Afrinic. Mais pas d'Instagram (cité uniquement à propos d'un problème BGP) ou de Twitch.

De nombreux autres sujets d'infrastructure sont traités, montrant à chaque fois les conséquences politiques de choix que l'on pourrait croire plutôt techniques. Et ce sont aussi bien des sujets déjà largement décrits (la « guerre des protocoles » entre TCP/IP et OSI), que des sujets un peu plus rares comme la normalisation technique ou dont les conséquences politiques sont vraiment très peu discutées, comme la sécurisation de BGP ou les certificats (mais DANE n'est pas mentionné dans cette section, ce qui est dommage).

En prime, le livre est correct techniquement, ce qui n'est pas le cas de tous les textes parlant des aspects sociaux ou politiques de l'Internet, par exemple il ne reprend pas le cliché « les adresses IPv4 sont composées de quatre groupes de chiffres », expliquant même leur format binaire.

Par contre, je regrette que le livre ait un biais très étatsunien. Cela se voit dans des clichés comme parler de «  constitutional protections of free expression » (alors que le fameux premier amendement ne s'applique qu'à l'État, et qu'il laisse les entreprises privées s'attaquer à la liberté d'expression tant qu'elles veulent), dans une présentation peu critique de l'ICANN (création du gouvernement étatsunien et qui ne représente pas du tout la mythique « communauté Internet »), ou lorsque des termes juridiques étatsuniens sont utilisés sans être expliqués (sans être lecteur de Grisham, savez-vous ce qu'est un amicus curiae ?). De même, l'Internet Society est présentée en termes vraiment acritiques. Un autre exemple est la place excessive donnée à New IP, projet qui a été beaucoup plus mis en avant par les Étatsuniens, en termes chauvins (« Mon Dieu, les Chinois veulent faire un nouveau protocole »), que par les Chinois eux-mêmes (qui se sont contentés de quelques PowerPoint).

La préface de Fiona Alexander pose un autre problème : elle a été un des acteurs principaux du feuilleton de la « privatisation » de l'ICANN. D'un côté, c'est bien d'avoir comme auteure quelqu'un qui connait le dossier de l'intérieur. De l'autre, elle se donne le beau rôle et fait une présentation très unilatérale (le politicien de droite extrême Ted Cruz étant bien pratique comme repoussoir).

Bref, sur un sujet aussi complexe, il y a plusieurs points de vue, ce qui est logique en politique, donc n'hésitez pas à demander à votre institution de se procurer ce livre (très cher à l'achat, malheureusement ; on peut avoir toutes les conneries qu'on veut en ligne gratuitement mais l'information sérieuse est souvent payante).

(Au passage : j'ai reçu un exemplaire gratuit de l'ouvrage.)


L'article seul

Pas Sage En Steïr (et mon blabla sur l'IA)

Première rédaction de cet article le 25 août 2025
Dernière mise à jour le 31 août 2025


Du 21 au 23 août 2025, c'était Pas Sage En Steïr. C'était très intéressant, très sympa, et je me suis permis d'y parler d'IA.

Pas Sage En Steïr est l'héritier des réunions Entrée Libre (qui se tenait à Quimper) et Pas Sage En Seine (qui se tenait à Choisy-le-Roi). Pas Sage En Seine a dû être annulé en raison d'une campagne de harcèlement contre les organisateur·rices, d'où Pas Sage En Steïr (car le Steïr coule à Quimper, où se tenait la conférence, au Centre des Abeilles).

Comme dit plus haut, j'ai parlé d'IA, sous le titre un peu sensationnaliste « L'IA n'existe pas ». Vous pouvez récupérer le support (et son source).

Mais il y avait plein d'autres choses intéressantes ! (Le programme est en ligne.) Tout a été enregistré et les vidéos sont disponibles (cf. la mienne). Et il y a aussi une transcription de mon intervention.

Par exemple, « Le testament numérique : que faire de mes données après moi », par Natouille, un « sujet d'après-repas » (non, en fait, c'est un sujet triste mais qu'il faut regarder en face). Il y a aussi bien le problème de données qui disparaissent involontairement que celui de données qui restent en ligne involontairement. Les gros machins (GAFA) ont tous des fonctions (« contact légataire » ou équivalent). Il faut juste leur faire confiance. Il existe des boites privées qui permettent de gérer ses données post-mortem. Faut juste leur faire confiance (honnêteté et pérennité). Dans les conférences, il y avait aussi la place des femmes dans l'informatique, par Isabella Vanni. Ou bien le concept de « Jardinage numérique », par Mindiell, qui présente son jardin, contre les « 5M » : Mondialisation / Monopolisation / Minitelisation / Monétisation / Merdification. Ou le classique mais toujours indispensable « Quelles alternatives pour se libérer des GAFAM », par Bookynette, qui note que la partie n'est pas égale « Les GAFAM peuvent avoir vingt rendez-vous par jour avec des ministres, et l'April zéro ».

Mais il y avait aussi des ateliers passionnants. Les Petits Débrouillards pratiquaient la science participative en guidant les participant·es dans le projet [Plantes] Sauvages de ma rue (on trouve désormais de nombreuses photos des plantes de Quimper). Une sortie bien agréable sous le beau soleil breton, et une découverte. Autre excellent atelier, celui sur l'installation et l'utilisation de Thunderbird (pour échapper aux gros webmails dominants). À noter qu'un des problèmes rencontrés était dû à Orange : IMAP n'est pas activé par défaut sur les comptes de courrier Orange, il faut le faire via son espace client sur le Web mais c'est refusé si on n'est pas sur sa connexion à la maison (cf. la documentation). Mais, bon, Proton Mail fait payer pour la même opération.

Et, comme d'habitude au Centre des Abeilles, mille mercis pour Brigitte et son rôle crucial.

Autres articles sur cet événement :


L'article seul

IETF 123 hackathon: experimenting RPP

First publication of this article on 25 August 2025


July 2025? Among many other events in the world, this was the IETF meeting, the 123rd, in Madrid. Before the meeting, there was the now traditional hackathon. I worked on the future protocol RPP (RESTful Provisioning Protocol), which may be the successor of EPP.

EPP (RFC 5730) is the current standard for provisioning, specially domain names. When you register a domain name, the registrar you use probably talks to the registry with EPP. EPP has some limits and the IETF is working (in the rpp working group) on another protocol, RPP (for RESTful Provisioning Protocol). Such a REST API is already done, for instance at Afnic (the domain name registry of .fr).

At the time of the IETF hackathon, RPP was far from done, there was not even a consensus on some basic aspects. So, unlike many hackathon projects, there was no attempt to test interoperability of different implementations of a protocol. The idea was instead to explore various things related to a REST provisioning protocol.

OK, so my specific work (the code is on GitHub):

  • Develop a (very small, and very minimum don't be afraid) domain name registry, with a pseudo-RPP interface,
  • Develop a client test suite.

Let's do a demo first:

% createdb registry
% psql -f ./create.sql registry
% ./test-server.py
  

Then let's get information about a domain with curl (one of the goals of a RESTful protocol like RPP is to be able to use regular HTTP clients):

% curl --header @headers.txt http://localhost:8080/domains/nic.example
{"result": "Domain nic.example exists", "holder": 1, "tech_contact": 1, "admin_contact": 1, "registrar": 1, "created": "2025-08-25T14:04:18.271586", "status_code": 200, "status_message": "OK"}
  

And create a domain (note we have to authentify this time):

  
% curl --header @headers.txt --request PUT --user 2:qwerty --data '{"holder": 2, "tech": 2, "admin": 2}'  http://localhost:8080/domains/durand.example
{"result": "durand.example created", "status_code": 201, "status_message": "Created"}

OK, you get the idea, the README.md file will give you some examples.

The registry is a PostgreSQL database. The server is written in Python, uses the WSGI framework, depends on several modules from the Python standard library and on psycopg2 and jsonschema. We use JSONschema for the validation of the incoming JSON (remember that RPP is far from being standardized and no schema language have been chosen yet). The test client, as you saw, is regular curl.

I added a (very small) test suite written in Zig:

% cd tests
% zig build test  
  

(I took the opportunity of the hackathon to post a sample HTTP client written in Zig to the Ziggit forum.)

The work done at the RPP table of the hackathon is presented here. All the live presentations are on YouTube and ours is at 1:19:08. Now, the rpp working group continues its work… (See the development on Github.)


L'article seul

Fiche de lecture : Robin des Bois - de Sherwood à Hollywood

Auteur(s) du livre : William Blanc, Justine Breton, Jonathan Fruoco
Éditeur : Libertalia
9-782377-293513
Publié en 2024
Première rédaction de cet article le 10 juillet 2025


Dans ce livre, William Blanc, avec Justine Breton et Jonathan Fruoco, poursuit son exploration des mythes nés de héros de fiction. Car, si Robin des Bois n'a probablement jamais existé, le mythe, lui, est bien réel, et a été repris par d'innombrables créateurs, bien différents.

Ce livre commence par creuser les racines du mythe. Au Moyen Âge, il était suffisamment connu pour que les premières mentions écrites du héros, au détour d'autres ouvrages, ne soient pas expliquées : tous les lecteurs connaissaient la référence, grâce à la tradition orale. Puis Robin des Bois a eu droit à ses propres livres et pièces de théâtre, après le Moyen Âge, et a commencé une carrière médiatique que le cinéma, au XXe siècle, a puissamment relancé. Je rappelle que ce livre ne porte pas sur le personnage historique (qui est sans doute imaginaire) mais sur sa reprise par les créateurs. C'est l'histoire du mythe, pas l'histoire de Robin.

Comme avec tous les mythes, Robin des Bois a été utilisé par de nombreux courants artistiques et politiques. Si le roi Arthur, sujet d'un précédent livre du même auteur, est plutôt « de droite » (après tout, c'est un roi), Robin des Bois, proscrit caché dans la forêt, qui affronte les autorités, serait plutôt « de gauche ». Mais attention, c'est bien plus compliqué que cela et les auteurices du livre montrent que Robin des Bois a été présenté de nombreuses façons différentes, et parfois en allié du roi, ou, au XXe siècle, en libertarien partisan des esclavagistes. De même, Robin a souvent changé de classe sociale, parfois yeoman (paysan propriétaire), parfois noble. Bien des bandits et des rebelles ont été qualifiés de « Robin des Bois moderne » (y compris les plus contestables).

Le livre analyse aussi un certain nombre de points spécifiques : le rôle des femmes dans la légende, Robin des Bois en France, les questions que se posent les médias pour enfants (présenter un voleur comme personnage positif…), l'importance du déguisement dans la légende (et, de manière plus générale, de l'inversion), bref, un livre d'une grande richesse où vous découvrirez plein de choses.


L'article seul

Messages de menaces de Cloud Innovation

Première rédaction de cet article le 8 juillet 2025
Dernière mise à jour le 15 juillet 2025


J'ai reçu trois messages de menaces d'une société nommée Cloud Innovation, car j'avais relayé un article qui dénonçait, à juste titre, leur rôle dans les attaques contre Afrinic. Je crois utile de rendre publiques ces menaces.

Un peu de contexte : Afrinic est le RIR pour l'Afrique. Le plus petit et le moins riche des RIR, le moins doté en ressources humaines et en appuis politiques, il est aussi le seul à avoir encore beaucoup d'adresses IPv4, ce qui aiguise des convoitises. Une entreprise chinoise, Cloud Innovation (elle utilise d'autres noms comme Larus ou Number Resource Society) a ainsi obtenu des adresses IP via une société-écran aux Seychelles, adresses ensuite utilisées pour des activités sans lien avec l'Afrique. Afrinic a tenté de récupérer au moins une partie de ces adresses, ce à quoi Cloud Innovation a réagi par une série de procès. Afrinic, victime de décisions mal réfléchies de la justice du pays de son siège social, a été privé des moyens de se défendre et est entré, depuis plusieurs années, dans une crise dont on ne voit pas le bout. (Le registre n'a, actuellement, ni directeur, ni conseil d'administration, les dernières élections, en juin 2025, ont été annulées.)

Tout ceci est bien décrit dans un excellent article d'Emmanuel Vitus (en anglais, mais il y a aussi la version en français). Et c'est la cause immédiate des messages que j'ai reçus de Cloud Innovation. J'ai tweeté sur cet article. Je reçois donc, sur une adresse personnelle et une professionnelle, un message en anglais signé de Cloud Innovation, m'enjoignant de « IMMEDIATELY remove and cease and desist from further sharing, disseminating, or promoting the Article through your X (formerly known as Twitter) profile, which disparage Cloud Innovation Limited and its officers, and further invite and open the floodgate for defamatory commentary regarding the same ». (Vous avez le mesage complet ici ; notez qu'il mentionne mon employeur - qu'on trouve facilement en ligne - mais que celui-ci n'y est pou rien.) Et le message me donne 24 heures et se termine par d'autres menaces, citant par exemple un procès en cours au Ghana (pourquoi le Ghana ???). Quelques jours après, un autre message de menace me relance puis un troisième.

J'ai hésité à partager cette information car je craignais d'aider Cloud Innovation dans sa politique d'intimidation (certaines personnes ont cédé mais en relançant publiquement la diffusion de l'information). Mais il me semble qu'il serait pire de ne pas faire connaitre les méthodes de cette entreprise. Comme vous le voyez, je n'ai pas supprimé mon tweet, que j'assume totalement. (Dans des cas comme ceci, il est important d'archiver les articles cités, car il y a toujours un risque d'une action d'effacement. Voici les archives, faites par Seb Sauvage.) D'autres personnes ont fait le même choix de publication, comme Joe Hall (voir aussi son tweet).

(Notez que j'ai cité Wikipédia, comme je le fais souvent sur ce blog, mais ne vous fiez pas à la page Wikipédia sur Afrinic : sans doute modifié par Cloud Innovation, elle - aussi bien en version francophone qu'anglophone - reprend des accusations mensongères contre Afrinic. Le compte à l'origine de ces modifications a d'ailleurs été bloqué.)

Sur ce même sujet, vous pouvez lire :


L'article seul

RFC 9799: Automated Certificate Management Environment (ACME) Extensions for ".onion" Special-Use Domain Names

Date de publication du RFC : Juin 2025
Auteur(s) du RFC : Q. Misell (AS207960)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF acme
Première rédaction de cet article le 27 juin 2025


Le protocole ACME, normalisé dans le RFC 8555, permet d'automatiser le processus de création et de renouvellement de certificats utilisables, par exemple, pour TLS. L'extension normalisée dans ce nouveau RFC permet d'obtenir et de renouveler des certificats pour un service utilisant le .onion de Tor. Si vous voulez passer à la télévision en disant « j'ai obtenu un certificat pour le Dark Web », ce RFC est la bonne lecture.

Ces services en .onion sont décrits dans la spécification de Tor. Le nom .onion a été enregistré comme « nom spécial » (RFC 6761) par le RFC 7686. Ces noms en .onion ne sont pas résolvables via le DNS et on ne peut donc pas utiliser les défis ACME (RFC 8555, section 8) classiques avec une AC habituelle.

La norme ACME, le RFC 8555, définit dans sa section 9.7.7 des types d'identificateurs. Pour les .onion, ce sera le type dns, même si Tor n'utilise pas le DNS. J'aurais préféré que ce type se nomme domainname, ça aurait été plus cohérent (voir l'annexe A du RFC, qui discute ce choix, qui prend place dans un cadre très embrouillé, avec beaucoup de gens qui confondent noms de domaine et DNS). L'identificateur est le nom dans .onion, éventuellement avec des sous-domaines. Par exemple, le JSON envoyé par ACME, pour la version en .onion de ce blog, serait :

{
  "type": "dns",
  "value": "sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion"
}
  

Reste à permettre au serveur ACME (l'AC) de le valider. Le forum AC/Navigateur permet les .onion (annexe B.2 de ses « Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates »). ACME utilise le concept de défi pour ces validations. Le client ACME demande un certificat pour un certain identificateur, le serveur lui renvoie un défi, une tâche à accomplir, du genre « prouve-moi que tu peux mettre ces données que je t'envoie dans un endroit accessible en HTTP via le nom pour lequel tu veux un certificat ». Il existe plusieurs défis utilisables avec les .onion :

  • http-01 (section 8.3 du RFC 8555), notre RFC dit qu'il faut suivre les redirections HTTP, même si elles mènent à un serveur HTTP qui n'est pas en .onion ; c'est ce défi que j'ai utilisé pour mon blog.
  • tls-alpn-01 (RFC 8737).
  • onion-csr-01, qui, contrairement aux deux précédents, est spécifique aux .onion et normalisé dans ce RFC 9799. C'est aussi le seul des trois défis utilisables qui permettre des jokers dans l'identificateur (*.bbcweb3hytmzhn5d532owbu6oqadra5z3ar726vq5kgwwn6aucdccrad.onion). Je le décris en détail plus loin. Cette méthode de validation figure désormais dans le registre des types de défis.
  • dns-01 (section 8.4 du RFC 8555), par contre, ne peut pas être utilisé pour les .onion, ceux-ci ne se servant pas du DNS.

Le défi onion-csr-01 consiste à demander au client de générer une CSR (RFC 2986) signée avec la clé privée du nom en .onion (je rappelle qu'un nom en .onion est une clé publique, plus exactement est dérivé d'une clé publique). Pour cela, le serveur ACME envoie un numnique et sa clé publique Ed25519, qu'il utilise pour Tor. Le client doit répondre avec le CSR, signé avec sa clé privée, CSR contenant parmi ses attributs le numnique choisi par le serveur, et un autre choisi par le client. Le serveur vérifie alors ce CSR (le nom en .onion, et bien sûr la signature).

Notez qu'on peut imaginer une AC qui ne soit elle-même accessible que via un nom en .onion, ce qui serait cohérent (section 5 du RFC). Par contre, en sortie, une AC doit utiliser Tor pour se connecter aux .onion (je veux dire l'utiliser directement, pas en passant par une passerelle, cf. section 8.8) mais une AC ne doit pas utiliser Tor pour se connecter à des domaines non-onion (section 8.4) car il y a des nœuds de sortie méchants.

Et les enregistrements de type CAA, normalisés dans le RFC 8659, et qui augmentent la sécurité du système en indiquant à quelles AC le client ACME a recours ? On ne peut pas utiliser le CAA traditionnel puisqu'il dépend du DNS. La solution, décrite dans la section 6 du RFC, est de mettre cette information dans le descripteur de service Tor (celui que le serveur en .onion envoie aux serveurs d'annuaire Tor).

Passons à la pratique. Il existe une AC expérimentale (et qui ne prétend pas offrir les garanties de sécurité d'une « vraie »), par l'auteure du RFC, documentée sur le site Web du projet. Je m'en suis servi pour obtenir un certificat pour mon blog. Vous pouvez donc désormais vous connecter en HTTPS, à https://sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion/ (votre navigateur va sans doute râler car il ne connait pas cette AC). Attention, l'AC a enregistré ce certificat via Certificate Transparency (RFC 9162) et est donc publiquement visible. Ne demandez pas de certificat à une AC qui utilise Certificate Transparency si ça vous dérange (section 8.9 du RFC).

Quelle est la marche à suivre pour obtenir un tel certificat ? D'abord, configurer Tor pour accepter le port de HTTPS :

  
  HiddenServiceDir /var/lib/tor/blogv2/
  HiddenServicePort 80 127.0.0.1:80
  HiddenServicePort 443 127.0.0.1:443

Ensuite, configurer son serveur HTTP, Apache dans mon cas :

  
# Pour le client ACME (qui écoutera sur le port 8080) :
ProxyPass /.well-known/acme-challenge http://localhost:8080/.well-known/acme-challenge
ProxyPassReverse /.well-known/acme-challenge http://localhost:8080/.well-known/acme-challenge
ProxyPreserveHost on
# Ne pas oublier d'activer mod_proxy *et* mod_proxy_http.

# HTTPS :
<VirtualHost _default_:443>
   ServerName sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion

   # Liens symboliques vers les certificats obtenus par ACME, dans mon
   # cas /etc/letsencrypt/live/sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion/…
   SSLCertificateFile	/etc/apache2/server.pem
   SSLCertificateKeyFile /etc/apache2/server.key

On lance ensuite le client ACME. J'ai utilisé certbot :

% sudo certbot certonly -v --server https://acme.api.acmeforonions.org/directory \
             --standalone --http-01-port 8080 --http-01-address 127.0.0.1 \
             -d sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion
…
Performing the following challenges:
http-01 challenge for sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion
Waiting for verification...
Cleaning up challenges
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion/privkey.pem
This certificate expires on 2025-05-28.
…

(Et pensez à configurer le renouvellement automatique du certificat, normalement, certbot l'a fait tout seul, mais vérifiez.)

Un tcpdump montre le trafic échangé entre Apache (qui a reçu le défi de l'AC, via Tor) et certbot (qui avait envoyé la demande et stocké le défi du serveur ACME), sur ma machine :

    
127.0.0.1.60978 > 127.0.0.1.8080: Flags [P.] … HTTP, length: 376
	GET /.well-known/acme-challenge/G4JwaWsRDG42H_FAESGCQrY7ZYk3D3mY9Ob_j458z6M HTTP/1.1
	Host: sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion
	X-Forwarded-For: 127.0.0.1
	X-Forwarded-Host: sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion
	X-Forwarded-Server: sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion
	
127.0.0.1.8080 > 127.0.0.1.60978: Flags [P.] … HTTP, length: 92
	HTTP/1.0 200 OK
	Server: BaseHTTP/0.6 Python/3.11.2
	Date: Thu, 27 Feb 2025 17:04:53 GMT

  

Si vous voulez ajouter cette AC (mais rappelez-vous qu'elle est expérimentale et sans garanties de sécurité) au Tor Browser, vous devrez sans doute, dans about:config, configurer, avant d'importer l'AC :

security.nocertdb false
browser.privatebrowsing.autostart false
security.ssl.enable_ocsp_stapling false
  

Mais je répète mon avertissement : cela peut affecter votre vie privée. Ne le faites pas si vous ne comprenez pas en détail les conséquences.

J'ai utilisé le défi http-01, avec un certbot ordinaire. Le défi onion-01 nécessite un certbot modifié (du code est disponible).

Petite anecdote : l'auteure du RFC travaille pour l'AS 207960, dont l'objet RIPE annonce :

aut-num:        AS207960
as-name:        AS-TRANSRIGHTS
descr:          Trans Rights! Hell Yeah!

Et le site Web montre d'ailleurs le drapeau correspondant. Il est assez rare de voir des prises de position politiques dans la base des RIR.


Téléchargez le RFC 9799


L'article seul

RFC 9773: ACME Renewal Information (ARI) Extension

Date de publication du RFC : Juin 2025
Auteur(s) du RFC : A. Gable (Internet Security Research Group)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF acme
Première rédaction de cet article le 18 juin 2025


Si vous utilisez Let's Encrypt (et tout le monde l'utilise, de nos jours), vous avez sans doute reçu les messages « Let's Encrypt Expiration Emails Update » qui vous préviennent que cette AC n'enverra plus de rappels que vos certificats vont bientôt expirer. C'est parce qu'un meilleur système est maintenant disponible, ARI (ACME Renewal Information). ARI permet à une AC utilisant le protocole ACME d'indiquer à ses clients des suggestions sur le renouvellement des certificats. Il est décrit dans ce RFC.

ACME est normalisé dans le RFC 8555 et est surtout connu via le succès de Let's Encrypt. Les certificats sont de courte durée (aujourd'hui trois mois mais ça va diminuer) et il faut donc les renouveler souvent. On peut le faire automatiquement via cron, ou bien analyser le certificat et renouveler quand sa date d'expiration approche. L'un des problèmes est que cela peut mener à ce que plusieurs demandes de renouvellement arrivent en même temps sur l'AC. Le pic d'activité qui en résulterait pourrait charger l'AC inutilement. L'idée est donc que ce soit le serveur ACME, l'AC, qui planifie les renouvellements, et pas le client ACME. Cela permettrait aussi des changements de planning, comme une réduction des durées de validité, ou bien une révocation.

Donc, ARI ajoute un nouvel URL à la description d'une AC, renewalInfo. Voici celui de Let's Encrypt (qui met en œuvre ARI depuis deux ans) :

% curl https://acme-v02.api.letsencrypt.org/directory
{
  "keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change",
  "meta": {
    "caaIdentities": [
      "letsencrypt.org"
    ],
    "profiles": {
          …
    },
    "termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.5-February-24-2025.pdf",
    …
  },
  "newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct",
  "newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce",
  "newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order",
  "renewalInfo": "https://acme-v02.api.letsencrypt.org/draft-ietf-acme-ari-03/renewalInfo",
  "revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert"
  

Ce nouveau membre a été ajouté au registre IANA.

Et comment on obtient quelque chose à cet URL https://acme-v02.api.letsencrypt.org/draft-ietf-acme-ari-03/renewalInfo ? On doit passer un identificateur du certificat. Il est formé par la concaténation de l'identifiant de l'AC et du numéro de série du certificat (pour garantir qu'il soit unique). Je passe sur les détails de construction (lisez la section 4 du RFC pour cela) mais ce petit script Python ari-make-path.py vous fait le calcul. Utilisons-le sur l'actuel certificat de ce blog :

% openssl x509 -text -in cert.pem
[Notez les valeurs]

% python ari-make-path.py 93:27:46:98:03:A9:51:68:8E:98:D6:C4:42:48:DB:23:BF:58:94:D2 00:06:7d:03:91:c6:49:b8:83:ba:e5:c6:da:8a:dc:be:4d:33:78
kydGmAOpUWiOmNbEQkjbI79YlNI.AAZ9A5HGSbiDuuXG2orcvk0zeA

% curl -i https://acme-v02.api.letsencrypt.org/draft-ietf-acme-ari-03/renewalInfo/kydGmAOpUWiOmNbEQkjbI79YlNI.AAZ9A5HGSbiDuuXG2orcvk0zeA
…
retry-after: 21600
…
{
  "suggestedWindow": {
    "start": "2025-06-23T03:50:00Z",
    "end": "2025-06-24T23:00:50Z"
  }
}
  

Voilà, on a fait un URL en concaténant la valeur de renewalInfo avec celle obtenue à partir du certificat et on sait désormais quand est-ce que Let's Encrypt suggère de renouveler ce certificat. Le format de sortie est clair mais vous avez les détails dans la section 4.2. Les dates sont évidemment au format du RFC 3339. Au passage, la date d'expiration est le 24 juillet 2025 donc Let's Encrypt n'attend pas le dernier moment. (Comme le dit un commentaire dans le code source du serveur, « calculate a point 2/3rds of the way through the validity period (or halfway through, for short-lived certs) ».)

Le RFC précise qu'un membre explanationURL peut être ajouté mais Let's Encrypt ne le fait pas. Les membres possibles figurent dans un nouveau registre IANA, auquel on pourra ajouter de nouveaux membres, en fournissant une spécification (« Spécification nécessaire » du RFC 8126).

Et on utilise cet intervalle entre deux dates comment ? Le RFC recommande de choisir une date au hasard dans l'intervalle. Le client ACME ne doit pas dormir jusqu'à la date sélectionnée, il doit réessayer de temps en temps car l'AC a pu changer la date (c'est tout le principe d'ARI). Mais attention à respecter le Retry-After: (six heures, ici), cf. RFC 9110, section 10.2.3.

Notre RFC ajoute également un membre à la description d'une commande de certificat : replaces indique quel certificat on est censé remplacer. (En utilisant le même identificateur de certificat qu'indiqué plus haut.) Il a été ajouté au registre IANA.

ARI est mis en œuvre dans le serveur ACME de Let's Encrypt, Boulder. Regardez core/objects.go et notamment la fonction RenewalInfoSimple. Côté client, Lego, acmez et le CertMagic de Caddy ont ARI mais certbot ou dehydrated ne gèrent pas ARI. Si vous voulez vous y mettre, Let's Encrypt a écrit un guide d'intégration d'ARI.


Téléchargez le RFC 9773


L'article seul

Articles des différentes années : 2025  2024  2023  2022  2021  2020  2019  Précédentes années

Syndication : Flux Atom avec seulement les résumés et Flux Atom avec tout le contenu.

Un article de ce blog au hasard.