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.


RFC 9920: RFC Editor Model (Version 3)

Date de publication du RFC : Février 2026
Auteur(s) du RFC : P. Hoffman (ICANN), A. Rossi (RFC Series Consulting Editor)
Pour information
Réalisé dans le cadre de l'activité d'édition des RFC rswg
Première rédaction de cet article le 12 février 2026


Vous avez toujours voulu savoir qui s'occupait de publier les RFC et qu'est-ce que ce mystérieux « RFC Editor » ? Ce nouveau RFC décrit ce rôle et la façon dont il doit accomplir sa tâche. Il s'agit d'une légère mise à jour du RFC 9280, sans changement de version. (Parmi les nouveautés, la définition de la notion d'utilisateurice des RFC, avant, on ne parlait que des auteurices.)

Les RFC sont un ensemble de documents techniques au sujet de l'Internet, qui comprend notamment, mais pas uniquement, les normes TCP/IP. Les RFC sont librement accessibles en ligne et sont publiés depuis 1969 (cf. RFC 8700).

Il y a très longtemps, quand les dinosaures étaient petits, le rôle de RFC Editor s'incarnait bien dans une personne unique, Jon Postel. Aujourd'hui, c'est plus complexe et il n'y a plus, depuis la version 2 du modèle (qui était dans le RFC 8728), de RFC Editor unique. Ce rôle est désormais réparti en deux tâches principales, définir la politique, et la mettre en œuvre. La première tâche est du ressort du RSWG (RFC Series Working Group), pour discuter et proposer (tout le monde, et son chat, peut y participer), puis du RSAB (RFC Series Approval Board), pour décider (ce comité est plus fermé). La deuxième tâche, la mise en œuvre de la politique, est du ressort du RFC Production Center, typiquement une organisation privée sous contrat avec l'IETF LLC, l'organisation administrative derrière l'IETF (RFC 8711). Ce sont donc tous ces groupes ensemble qui composent le « RFC Editor ». Tout ceci, et quelques autres points, étaient déjà dans le RFC 9280, notre nouveau RFC 9920 n'apporte que des changements de détail.

Les RFC sont publiés par cinq voies (streams) différentes, dont une, la voie IETF, sert pour les normes (mais tous les RFC ne sont pas des normes). Chaque voie a une organisation ou une personne responsable du contenu des RFC (pour la voie IETF, le responsable est… l'IETF), la fonction RFC Editor ne gère pas le contenu (ou seulement la forme, mais pas le fond des documents) mais la publication. Le RFC 8729, dans sa section 5.1, décrit les quatre voies classiques (IETF, IRTF, IAB et indépendante). En plus, le RFC 9280 avait introduit la voie éditoriale, dédiée aux évolutions des RFC (les RFC parlant des RFC…). C'est via cette voie que seront publiés les propositions issues du RSWG et approuvées par le RSAB. Un exemple est le récent RFC 9896, dont vous pouvez suivre l'historique.

Dans les deux tâches décrites plus haut (la définition de la politique, et sa mise en œuvre), je n'ai pas mentionné un acteur supplémentaire (comme s'il n'y en avait pas déjà assez !), le RFC Series Consulting Editor, qui est un·e expert·e en édition, membre du RSAB.

Et donc, si vous avez des idées géniales pour améliorer les RFC, il faut faire comment pour qu'elles triomphent ? Lisez la section 3 et notamment la 3.2.2 :

  • Les propositions sont écrites (un Internet draft) et diffusées au RSWG (rappelez-vous : il est ouvert, tout le monde peut y participer),
  • Après un dernier appel dans le RSWG, puis plus large, s'il y a un consensus approximatif (RFC 2418),
  • Les propositions atterrissent au RSAB, qui décidera (avec des votes YES ou CONCERN, le second n'étant pas un NO mais une demande de discussion).

Chaque acteur est décrit ensuite plus en détail. Le RSWG est le groupe le plus ouvert du lot, vous vous abonnez à sa liste de diffusion et c'est parti, vous êtes membre. Et ceci que vous soyez déjà participant à l'IETF ou pas. Les développeur·ses qui mettent en œuvre les RFC, les auteurs de RFC, les auteurs de logiciels qui traitent les RFC, les gens qui citent les RFC dans leurs appels d'offres, tous ceux et toutes celles là sont les bienvenu·es. Les discussions sont publiques. Le RSWG peut aussi utiliser des outils en ligne comme MicrosoftHub (RFC 8874).

Le RSAB, lui, est relativement fermé, aussi bien dans sa composition (les membres avec droit de vote sont un représentant de chaque voie plus le RFC Series Consulting Editor, cf. section 5) que dans son travail (mais les minutes des réunions sont publiques). Par contre, il n'est pas censé court-circuiter le RSWG : toutes les propositions doivent d'abord être discutées au RSWG.

Les boilerplates, ces textes rigides présents au début de chaque RFC et expliquant le statut de celui-ci, décrits dans le RFC 7841, sont décidés par chaque voie puis approuvés par le RSAB, le RFC Production Center et l'IETF Trust.

Le RPC (RFC Production Center) est décrit en section 4. C'est actuellement AMS. C'est ce RPC qui va écrire et maintenir le guide stylistique à respecter (RFC 7322 et documents en ligne), décider des formats acceptés (par exemple du profil restreint de SVG, cf. RFC 9896), et préciser les points que le RFC 7991 laissait libres. C'est aussi le RPC qui doit faire le travail de publication effectif :

  • Attribuer les numéros des RFC (qui sera le numéro 10 000 ?),
  • Relire et corriger les textes soumis, en respectant le sens technique,
  • Garder trace de toutes les modifications faites (des « corrections » peuvent accidentellement changer le fond du texte),
  • Assurer la communication avec les auteurs et avec des organisations comme l'IANA, quand le futur RFC leur donne un rôle, par exemple de créer un nouveau registre de paramètres, et archiver tous ces dialogues,
  • Demander le cas échéant au RSCE et/ou au RSAB, et leur faire des suggestions, fondées sur son expérience de publication des RFC,
  • Participer au RSWG,
  • Rendre compte à la communauté des auteurs et lecteurs de RFC, d'une part, et à l'IETF LLC d'autre part,
  • Mettre les RFC en ligne, veiller au serveur www.rfc-editor.org qui les distribue, archiver les RFC (RFC 8153), annoncer les publications (le RPC est sur le fédivers, voyez par exemple cette annonce, et il y a bien sûr un flux de syndication sur le site Web),
  • Maintenir le système d'errata (« Submit an issue »),
  • Et répondre aux demandes judiciaires car il y a parfois des procès impliquant un RFC.

Le RPC est choisi suite à un appel d'offres de l'IETF LLC, qui est chargée d'écrire le cahier des charges détaillé et de sélectionner le vainqueur. L'argent vient du budget de l'IETF LLC, qui paie le RPC (AMS, actuellement, comme indiqué plus haut).

Et le RSCE (RFC Series Consulting Editor) ? La section 5 le décrit plus en détail. C'est une personne expérimentée dans les questions d'édition. Il ou elle doit guider le RPC sur les questions techniques liées à la publication (la section 4 du RFC 8729 donne une liste complète de ces questions). La RSCE actuelle, depuis 2022, est Alexis Rossi, une des auteures de ce RFC.

Une des nouveautés du RFC 9280 était la voie éditoriale (Editorial Stream), la cinquième voie de création de RFC, consacrée aux RFC qui parlent des RFC. La section 6 de notre RFC la détaille. Cette voie est empruntée par les RFC discutés par le RSWG puis approuvés par le RSAB, comme ce RFC 9920 dont vous êtes en train de lire le résumé. Le statut de ces RFC est forcément « Pour information » (informational, cf. RFC 8729), mais ce sont bien des règles impératives pour les RFC qu'ils spécifient.

Et, pour finir, la section 7 de notre RFC liste les propriétés « historiques » des RFC qui, si elles n'ont pas forcément été mises par écrit, surtout au début, ont toujours été respectées et ne doivent pas être prises à la légère lors des propositions d'évolution :

  • Libre disponibilité des documents, que tout le monde peut télécharger, lire, redistribuer. (Ce n'est pas évident, puisque des organisations fermées comme l'AFNOR ou l'ISO ne fournissent toujours pas d'accès libre à leurs documents et, même si vous avez payé pour en avoir un, vous ne pouvez pas le redistribuer).
  • Accessibilité (format texte d'abord, puis XML avec plusieurs formats de sortie, sans dépendance vis-à-vis de logiciels privateurs comme le font les organisations fermées,
  • En anglais (notez que la licence permet de faire une traduction sans demander d'autorisation). Opinion personnelle : ce n'est évidemment pas génial mais il n'y pas d'alternative réaliste : traduire des documents normatifs en N autres langues est très coûteux (chaque virgule compte et, par exemple, un texte en français tend facilement à être plus directif que le texte en anglais).
  • Diversité : tous les RFC ne sont pas des normes, il y a des informations, des discussions, de l'humour
  • Qualité des documents.
  • Stabilité et longévité : le contenu sémantique des RFC n'est jamais modifié, et les formats sont choisis pour garantir qu'ils seront toujours lisibles dans des dizaines d'années (formats standards, acceptés par de nombreux logiciels). À part le cas particulier du RFC 20, le RFC le plus ancien et qui est toujours applicable est le RFC 768, qui a presque un demi-siècle. L'archivage à long terme est un point crucial des RFC, à la fois pour les RFC anciens mais toujours d'actualité, et aussi pour les historien·nes de l'informatique.

La section 1.1 de notre RFC résume les changements depuis le RFC 9280, qui avait spécifié la version 3 du modèle « RFC Editor ». On reste en version 3 et les changements sont peu importants :

  • Précisions des responsabilités de développement des outils informatiques nécessaires. C'est au RFC Production Center de s'en occuper. Cela implique des choses comme les choix de formats ou bien les grammaires XML.
  • Définition de la notion d'utilisateur des RFC (consumer of RFCs, section 3.3), en se référant au RFC 3935, qui décrit la mission de l'IETF mais qui n'expliquait pas clairement cette notion. Les utilisateurs des RFC sont un ensemble plus large que celui des participants à l'IETF, et un RFC doit être utilisable par des gens qui ne connaissent rien à l'IETF.
  • En cohérence avec le RFC 9720, on précise désormais qu'un RFC, une fois publié, peut être modifié (sa forme, mais pas son fond).

La section 9, quant à elle, raconte les changements qu'il y avait eu depuis la version 2 du modèle (la version 1 du modèle était dans le RFC 5620 en 2009 et la version 2 dans le RFC 6635 en 2012) :

  • Éclatement de la fonction de RFC Editor en plusieurs acteurs (RSWG, RSAB, etc).
  • Fusion des fonctions RFC Production Center et RFC Publisher.
  • Suppression du RSOC (RFC Series Oversight Commitee).
  • Création de la voie éditoriale pour les RFC parlant des RFC.

Téléchargez le RFC 9920


L'article seul

RFC 9915: Dynamic Host Configuration Protocol for IPv6 (DHCPv6)

Date de publication du RFC : Janvier 2026
Auteur(s) du RFC : T. Mrugalski (ISC), B. Volz (Individual Contributor), M. Richardson (SSW), S. Jiang (BUPT), T. Winters (QA Cafe)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dhc
Première rédaction de cet article le 11 février 2026


IPv6 dispose de trois mécanismes principaux pour l'allocation d'une adresse IP à une machine. L'allocation statique, « à la main », le système d'« autoconfiguration » SLAAC du RFC 4862 et DHCP. DHCP pour IPv6 était normalisé dans le RFC 8415, que notre RFC met à jour. Le protocole n'a guère changé, le principal changement est la suppression de certaines fonctions peu utilisées.

DHCP permet à une machine (qui n'est pas forcément un ordinateur, ou plus exactement qui n'est pas perçue comme telle) d'obtenir une adresse IP (ainsi que plusieurs autres informations de configuration) à partir d'un serveur DHCP du réseau local. C'est donc une configuration « avec état », du moins dans son mode d'utilisation le plus connu. (Notre RFC documente également un mode sans état.) DHCP nécessite un serveur, par opposition à l'autoconfiguration du RFC 4862 qui ne dépend pas d'un serveur (cette autoconfiguration sans état peut être utilisée à la place de ou bien en plus de DHCP). Deux utilisations typiques de DHCP sont le SoHo où le routeur est également serveur DHCP pour les trois PC connectés et le réseau local d'entreprise où deux ou trois machines Unix distribuent adresses IP et informations de configuration à des centaines de machines.

Le principe de base de DHCP (IPv4 ou IPv6) est simple : la nouvelle machine annonce à la cantonade qu'elle cherche une adresse IP, le serveur lui répond, l'adresse est allouée pour une certaine durée, le bail, la machine cliente devra renouveler le bail de temps en temps.

L'administrateur d'un réseau IPv6 se pose souvent la question « DHCP ou SLAAC » ? Notez que les deux peuvent coexister, ne serait-ce que parce que certaines possibilités n'existent que pour un seul des deux protocoles. Ainsi, DHCP seul ne peut indiquer l'adresse du routeur par défaut. Pour le reste, c'est une question de goût.

Le DHCP spécifié par notre RFC ne fonctionne que pour IPv6, les RFC 2131 et RFC 2132 traitant d'IPv4. Les deux protocoles restent donc complètement séparés, le RFC 4477 donnant quelques idées sur leur coexistence. Il a parfois été question de produire une description unique de DHCPv4 et DHCPv6, ajoutant ensuite les spécificités de chacun, mais le projet n'a pas eu de suite (section 1.2 de ce RFC), les deux protocoles étant trop différents.

DHCP fonctionne par diffusion restreinte. Un client DHCP, c'est-à-dire une machine qui veut obtenir une adresse, diffuse (DHCP fonctionne au-dessus d'UDP, RFC 768, le port source est 546, le port de destination, où le serveur écoute, est 547) sa demande à l'adresse multicast locale au lien ff02::1:2. Le serveur se reconnait et lui répond. S'il n'y a pas de réponse, c'est, comme dans le DNS, c'est au client de réémettre (section 15). L'adresse IP source du client est également une adresse locale au lien.

(Notez qu'une autre adresse de diffusion restreinte est réservée, ff05::1:3 ; elle inclut également tous les serveurs DHCP mais, contrairement à la précédente, elle exclut les relais, qui transmettent les requêtes DHCP d'un réseau local à un autre.)

Le serveur choisit sur quels critères il alloue les adresses IP. Il peut les distribuer de manière statique (une même machine a toujours la même adresse IP) ou bien les prendre dans un pool d'adresses et chaque client aura donc une adresse « dynamique ». Le fichier de configuration du serveur DHCP Kea ci-dessous montre un mélange des deux approches.

Il faut bien noter (et notre RFC le fait dans sa section 22) que DHCP n'offre aucune sécurité. Comme il est conçu pour servir des machines non configurées, sur lesquelles on ne souhaite pas intervenir, authentifier la communication est difficile. Un serveur DHCP pirate, ou, tout simplement, un serveur DHCP accidentellement activé, peuvent donc être très gênants.

Outre l'adresse IP, DHCP peut indiquer des options comme les adresses des serveurs DNS à utiliser (RFC 3646).

Notre version IPv6 de DHCP est assez différente de la version IPv4 (et le RFC est plus de trois fois plus long). Par exemple, l'échange « normal » entre client et serveur prend quatre paquets IP (section 5) et non pas deux. (Mais il y a aussi un échange simplifié à deux paquets, cf. section 5.1.) L'encodage des messages est très différent, et il y a des différences internes comme l'IA (Identity Association) de la section 12. Il y a aussi des différences visibles à l'utilisateur comme le concept de DUID (DHCP Unique IDentifier), section 11, qui remplace les anciens client identifier et server identifier de DHCP v4. Les différences sont telles que le RFC précise que leur intégration avec DHCP pour IPv4 n'est pas envisagée.

À l'heure actuelle, il existe plusieurs mises en œuvre de DHCPv6, comme Kea (serveur seulement) et dhcpcd (client seulement). (Notez qu'une liste complète figurait dans le brouillon du RFC.) Pour celles et ceux qui utilisent une Freebox comme serveur DHCP, il semble qu'elle ait DHCPv6 depuis 2018 (je n'ai pas testé). Il parait que la Livebox le fait également. Je n'ai pas non plus essayé pour la Turris Omnia mais cela devrait marcher puisqu'elle utilise le serveur odhcpd, qui sait faire du DHCPv6 (ceci dit, je ne vois pas comment l'activer depuis les menus de Luci). Et il y a bien sûr des implémentations non-libres dans des équipements comme les Cisco. Notez que ces mises en œuvre de DHCPv6 n'ont pas forcément déjà intégré les modifications de notre RFC 9915.

Il existe aussi des programmes qui ne sont plus maintenus comme Dibbler (client et serveur), l'ancien programme de l'ISC (le nouveau est Kea), etc.

Voici un exemple d'utilisation de Dibbler, face à Kea, qui nous affiche les quatre messages (Solicit - Advertise - Request - Reply) :


% sudo dibbler-client run 
...
2026.02.11 08:28:04 Client Notice    DUID creation: Generating 14-bytes long link-local+time (duid-llt) DUID.
2026.02.11 08:28:04 Client Notice    DUID creation: generated using wlan0/4 interface.
2026.02.11 08:28:04 Client Info      My DUID is 00:01:00:01:31:1e:fa:14:f6:fc:69:10:65:09.
...
2026.02.11 08:28:04 Client Info      Creating SOLICIT message with 1 IA(s), no TA and 0 PD(s) on eth1/3 interface.
2026.02.11 08:28:04 Client Debug     Sending SOLICIT(opts:1 3 39 8 6 ) on eth1/3 to multicast.
2026.02.11 08:28:04 Client Info      Received ADVERTISE on eth1/3,trans-id=0x5ded1b, 5 opts: 1 2 3 23 39
2026.02.11 08:28:05 Client Info      Processing msg (SOLICIT,transID=0x5ded1b,opts: 1 3 39 8 6)
2026.02.11 08:28:05 Client Info      Creating REQUEST. Backup server list contains 1 server(s).
2026.02.11 08:28:05 Client Debug     Advertise from Server ID=00:01:00:01:31:19:db:68:38:f7:cd:ce:22:c6, preference=0.[using this]
2026.02.11 08:28:05 Client Debug     Sending REQUEST(opts:1 3 39 6 2 8 ) on eth1/3 to multicast.
2026.02.11 08:28:05 Client Info      Received REPLY on eth1/3,trans-id=0x6eb008, 5 opts: 1 2 3 23 39
2026.02.11 08:28:05 Client Notice    Address fc00:cafe:1234:4321:5678::2/128 added to eth1/3 interface.
2026.02.11 08:28:05 Client Debug     RENEW(IA_NA) will be sent (T1) after 1000, REBIND (T2) after 2000 seconds.
2026.02.11 08:28:08 Client Notice    FQDN: About to perform DNS Update: DNS server=2001:db8:2::dead:beef, IP=fc00:cafe:1234:4321:5678::2 and FQDN=grace
...

   

Le serveur en face était un Kea ainsi configuré :

"subnet6": [
        {
           "interface": "eth0",  // Ce n'est pas bien documenté mais
	   // cette option est cruciale, autrement le client reçoit
	   // des « Server could not select subnet for this client ».
           "subnet": "fc00:cafe:1234:4321::/64",
           "pools": [ { "pool": "fc00:cafe:1234:4321:5678::/80" } ],
...

Si vous voulez, le pcap de l'échange est disponible (capture faite avec tcpdump -w /tmp/dhcpv6-bis.pcap udp and \(port 546 or port 547\)). tcpdump voit le trafic ainsi :

09:28:04.624687 IP6 fe80::606d:ad11:58ca:6cab.546 > ff02::1:2.547: dhcp6 solicit
09:28:04.639479 IP6 fe80::6ee4:5672:da95:1018.547 > fe80::606d:ad11:58ca:6cab.546: dhcp6 advertise
09:28:05.652900 IP6 fe80::606d:ad11:58ca:6cab.546 > ff02::1:2.547: dhcp6 request
09:28:05.667843 IP6 fe80::6ee4:5672:da95:1018.547 > fe80::606d:ad11:58ca:6cab.546: dhcp6 reply
   

La requête est émise depuis une adresse lien-local (ici fe80::606d:ad11:58ca:6cab) pas depuis une adresse « tout zéro » comme en IPv4 (section 17 du RFC). On voit bien les quatre messages (Solicit - Advertise - Request - Reply), décrits section 5.2 (et la liste des types possibles est en section 7.3). Le serveur n'a pas répondu directement avec un Reply, parce que le client n'a pas inclut l'option Rapid Commit (section 21.14). Dans l'échange à quatre messages, le client demande à tous (Solicit), un(s) serveur(s) DHCP répond(ent) (Advertise), le client envoie alors sa requête au serveur choisi (Request), le serveur donne (ou pas) son accord (Reply). Avec l'option -vvv, tcpdump est plus bavard et montre qu'il analyse bien DHCPv6 :

09:28:04.624687 IP6 (flowlabel 0x51d28, hlim 1, next-header UDP (17) payload length: 99) fe80::606d:ad11:58ca:6cab.546 > ff02::1:2.547: [udp sum ok] dhcp6 solicit (xid=5ded1b (client-ID hwaddr/time type 1 time 824113684 f6fc69106509) (IA_NA IAID:1 T1:4294967295 T2:4294967295 (IA_ADDR :: pltime:4294967295 vltime:4294967295)) (Client-FQDN) (elapsed-time 0) (option-request DNS-server Client-FQDN))
09:28:04.639479 IP6 (flowlabel 0xf6846, hlim 64, next-header UDP (17) payload length: 140) fe80::6ee4:5672:da95:1018.547 > fe80::606d:ad11:58ca:6cab.546: [udp sum ok] dhcp6 advertise (xid=5ded1b (client-ID hwaddr/time type 1 time 824113684 f6fc69106509) (server-ID hwaddr/time type 1 time 823778152 38f7cdce22c6) (IA_NA IAID:1 T1:1000 T2:2000 (IA_ADDR fc00:cafe:1234:4321:5678::2 pltime:3000 vltime:4000)) (DNS-server 2001:db8:2::dead:beef 2001:db8:2::cafe:babe) (Client-FQDN))
09:28:05.652900 IP6 (flowlabel 0x51d28, hlim 1, next-header UDP (17) payload length: 117) fe80::606d:ad11:58ca:6cab.546 > ff02::1:2.547: [udp sum ok] dhcp6 request (xid=6eb008 (client-ID hwaddr/time type 1 time 824113684 f6fc69106509) (IA_NA IAID:1 T1:4294967295 T2:4294967295 (IA_ADDR fc00:cafe:1234:4321:5678::2 pltime:3000 vltime:4000)) (Client-FQDN) (option-request DNS-server Client-FQDN) (server-ID hwaddr/time type 1 time 823778152 38f7cdce22c6) (elapsed-time 0))
09:28:05.667843 IP6 (flowlabel 0xf6846, hlim 64, next-header UDP (17) payload length: 140) fe80::6ee4:5672:da95:1018.547 > fe80::606d:ad11:58ca:6cab.546: [udp sum ok] dhcp6 reply (xid=6eb008 (client-ID hwaddr/time type 1 time 824113684 f6fc69106509) (server-ID hwaddr/time type 1 time 823778152 38f7cdce22c6) (IA_NA IAID:1 T1:1000 T2:2000 (IA_ADDR fc00:cafe:1234:4321:5678::2 pltime:3000 vltime:4000)) (DNS-server 2001:db8:2::dead:beef 2001:db8:2::cafe:babe) (Client-FQDN))
   

Mais si vous préférez tshark, l'analyse de cet échange est également disponible.

Notez que certains clients DHCP dépendent de la présence d'un routeur qui envoie des RA (RFC 4861) avec le bit M - Managed - à 1 (RFC 4861, section 4.2). En l'absence de ces annonces, le client se contente de demander des informations diverses au serveur DHCP, mais pas d'adresse IP.

Et si vous voulez compiler dhcpcd vous-même, c'est simple :

wget https://github.com/NetworkConfiguration/dhcpcd/releases/download/v10.3.0/dhcpcd-10.3.0.tar.xz
dhcpcd-10.3.0.tar.xz 
tar xvf dhcpcd-10.3.0.tar 
dhcpcd-10.3.0
./configure
make
sudo make install
 

L'échange à deux messages (Solicit - Reply) est, lui, spécifié dans la section 5.1. Il s'utilise si le client n'a pas besoin d'une adresse IP, juste d'autres informations de configuration comme l'adresse du serveur NTP, comme décrit dans le RFC 4075. Même si le client demande une adresse IP, il est possible d'utiliser l'échange à deux messages, via la procédure rapide avec l'option Rapid Commit.

Tout client ou serveur DHCP v6 a un DUID (DHCP Unique Identifier, décrit en section 11). Le DUID est opaque et ne devrait pas être analysé par la machine qui le reçoit. La seule opération admise est de tester si deux DUID sont égaux (indiquant qu'en face, c'est la même machine). Il existe plusieurs façons de générer un DUID (dans l'exemple plus haut, Dibbler avait choisi la méthode duid-llt, adresse locale et heure) et de nouvelles pourront apparaitre dans le futur. Par exemple, un DUID peut être fabriqué à partir d'un UUID (RFC 6355).

Mais l'utilisation exclusive du DUID, au détriment de l'adresse MAC, n'est pas une obligation du RFC (le RFC, section 11, dit juste « DHCP servers use DUIDs to identify clients for the selection of configuration parameters », ce qui n'interdit pas d'autres méthodes). On peut utiliser l'adresse Ethernet. En combinaison avec des commutateurs qui filtrent sur l'adresse MAC, cela peut améliorer la sécurité.

Puisqu'on peut aussi attribuer des adresses statiquement à une machine, en la reconnaissant, par exemple, à son adresse MAC ou à son DUID, voici comment on peut configurer Kea pour donner une adresse IP fixe au client d'un certain DUID :

"reservations": [
  {
    "duid": "00:01:00:01:31:1e:fa:14:f6:fc:69:10:65:09",
    "ip-addresses": [ "fc00:cafe:1234:4321:b0f:1111::1" ]
  }
   

La section 6 de notre RFC décrit les différentes façons d'utiliser DHCPv6. On peut se servir de DHCPv6 en mode sans état (section 6.1), lorsqu'on veut juste des informations de configuration, ou avec état (section 6.2, qui décrit la façon historique d'utiliser DHCP), lorsqu'on veut réserver une ressource (typiquement l'adresse IP) et qu'il faut alors que le serveur enregistre (et pas juste dans sa mémoire, car il peut redémarrer) ce qui a été réservé. On peut aussi faire quelque chose qui n'a pas d'équivalent en IPv4, se faire déléguer un préfixe d'adresses IP entier (section 6.3). Un client DHCP qui reçoit un préfixe, mettons, /60, peut ensuite redéléguer des bouts, par exemple ici des /64. (Le RFC 7084 est une utile lecture au sujet des routeurs installés chez M. Toutlemonde.)

Le format détaillé des messages est dans la section 8. Le début des messages est toujours le même, un type d'un octet (la liste des types est en section 7.3) suivi d'un identificateur de transaction de trois octets. Le reste est variable, dépendant du type de message.

On a déjà parlé du concept de DUID plus haut, donc sautons la section 11 du RFC, qui parle du DUID, et allons directement à la section 12, qui parle d'IA (Identity Association). Une IA est composée d'un identifiant numérique, l'IAID (IA IDentifier) et d'un ensemble d'adresses et de préfixes. Le but du concept d'IA est de permettre de gérer collectivement un groupe de ressources (adresses et préfixes). Pour beaucoup de clients, le concept n'est pas nécessaire, on n'a qu'une IA, d'identificateur égal à zéro. Pour les clients plus compliqués, on a plusieurs IA, et les messages DHCP (par exemple d'abandon d'un bail) indiquent l'IA concernée.

Comme pour DHCPv4, une bonne partie des informations est transportée dans des options, décrites dans la section 21. Certaines options sont dans ce RFC, d'autres pourront apparaitre dans des RFC ultérieurs. Toutes les options commencent par deux champs communs, le code identifiant l'option (deux octets), et la longueur de l'option. Ces champs sont suivis par des données, spécifiques à l'option. Ainsi, l'option Client Identifier a le code 1, et les données sont un DUID (cf. section 11). Autre exemple, l'option Vendor Class (code 16) permet d'indiquer le fournisseur du logiciel client (notez qu'elle pose des problèmes de sécurité, cf. RFC 7824, et section 23 de notre RFC). Notez qu'il peut y avoir des options dans les options, ainsi, l'adresse IP (code 5) est toujours dans les données d'une option IA (les IA sont décrites en section 12).

Puisqu'on a parlé de sécurité, la section 22 du RFC détaille les questions de sécurité liées à DHCP. Le fond du problème est qu'il y a une profonde incompatibilité entre le désir d'une autoconfiguration simple des clients (le but principal de DHCP) et la sécurité. DHCP n'a pas de chiffrement et tout le monde peut donc écouter les échanges de messages, voire les modifier. Et, de toute façon, le serveur n'est pas authentifié, donc le client ne sait jamais s'il parle au serveur légitime. Il est trivial pour un méchant de configurer un serveur DHCP « pirate » et de répondre à la place du vrai, indiquant par exemple un serveur DNS que le pirate contrôle. Les RFC 7610 et RFC 7513 décrivent des solutions possibles à ce problème.

Des attaques par déni de service sont également possibles, par exemple via un client méchant qui demande des ressources (comme des adresses IP) en quantité. Un serveur prudent peut donc limiter la quantité de ressources accessible à un client.

Maintenant, les questions de vie privée. La section 23 rappelle que DHCP est très indiscret. Le RFC 7824 décrit les risques que DHCP fait courir à la vie privée du client (et le RFC 7844 des solutions possibles).

Les registres IANA ne changent pas par rapport à l'ancien RFC. Les différents paramètres sont en ligne.

L'annexe A de notre RFC décrit les changements depuis l'ancien RFC 8415. Rien d'essentiel n'a été changé. On notera :

  • Suppression de certains mécanismes optionnels (comme ils étaient de toute façon optionnels, cela n'affecte pas l'interopérabilité) qui étaient complexes et peu mis en œuvre : les adresses temporaires (IA_TA), il faut désormais relâcher explicitement celles qu'on veut temporaires, la possibilité de faire de l'unicast, et donc, logiquement, l'option qui forçait le multicast.
  • Correction de plusieurs erreurs signalées (certaines n'ont pas été corrigées puisqu'elles s'appliquaient à des options supprimées).
  • Texte plus précis sur les ports UDP utilisés.
  • Progression du RFC au statut de norme Internet complète (alors que le RFC 8415 était officiellement une proposition de norme).

Téléchargez le RFC 9915


L'article seul

RFC 9896: SVG in RFCs

Date de publication du RFC : Janvier 2026
Auteur(s) du RFC : A. Rossi (RFC Series Consulting Editor), N. Brownlee, J. Mahoney (RFC Production Center), M. Thomson
Pour information
Réalisé dans le cadre de l'activité d'édition des RFC rswg
Première rédaction de cet article le 5 février 2026


Le RFC 7996 avait introduit la possibilité de mettre des images dans les RFC. Ce nouveau document le remplace, il décrit les règles pour inclure du SVG. Il est très court, se limitant aux principes, il n'a plus de mention d'un profil SVG particulier.

On peut donc mettre des images vectorielles car, a priori, dans un RFC, il y aura beaucoup plus de schémas que de photos. Donc, pas de bitmap.

Au fait, c'est quoi, SVG ? Il s'agit d'une norme d'un format d'images vectoriel, gérée par le W3C, et fondée sur XML. Voici un exemple d'un simple dessin en SVG :


<svg xmlns="http://www.w3.org/2000/svg" version="1.2">
  <rect x="25" y="25" width="200" height="200" fill="white" stroke-width="4" stroke="black" />
  <circle cx="125" cy="125" r="75" fill="black" />
  <polyline points="50,150 50,200 200,200 200,100" stroke="black" stroke-width="4" fill="none" />
  <line x1="50" y1="50" x2="200" y2="200" stroke="white" stroke-width="4" />
</svg>

  

Et voici son rendu : simple-rfc-picture

Ce RFC décrit des objectifs, une politique. Contrairement à son prédécesseur, le RFC 7996, il ne décrit pas de profil de SVG, il fixe des buts, pas les moyens de les atteindre. Ces buts sont :

  • Le schéma en SVG ne doit jamais être la seule représentation. Le RFC doit être compréhensible sans les schémas.
  • Le SVG ne doit pas inclure d'animations et ne doit pas être trop réactif (pas question de changer quand on tourne le téléphone).
  • Le but étant d'être vu et compris par le public le plus large possible, il ne faut pas utiliser les fonctions de SVG qui ne sont pas largement mises en œuvre. Pas de pointeurs vers des ressources externes, pas de script exécutable (pas de JavaScript actif dans un RFC…), attention portée à l'accessibilité, en suivant WAI. Pour la même raison, il ne faut pas de dépendance vis-à-vis des couleurs (RFC 6949, section 3.2) et pas de choix arbitraire de police.

Il est crucial que les RFC soient accessibles à tou·te·s, non seulement quel que soit le matériel utilisé, mais également quels que soient les handicaps dont souffre leur propriétaire. C'est bien joli de vouloir ajouter des tas de choses dans les RFC mais encore faut-il ne pas creuser ainsi davantage le fossé entre les utilisateurs. Ainsi, accepter de la couleur (le RFC 6949 limite les futurs RFC au noir et blanc) fait courir le risque que les daltoniens ne puissent pas comprendre un RFC. De même, les graphiques, qui ont l'air comme ça d'être une bonne idée, peuvent aggraver la situation des malvoyants. Le texte seul peut toujours être lu à voix haute par un synthétiseur de parole mais pas le graphique.

La traduction de ces principes en conseils techniques concrets, et l'application de ces règles seront du ressort du RFC Production Center. Il suivra peut-être les nombreux détails pratiques décrits dans le RFC 7996 mais il n'y est pas obligé.

Comment on écrit du SVG ? S'il est évidemment possible de le faire entièrement à la main avec un éditeur ordinaire, gageons que peu de gens le tenteront. Le RFC 7996 citait des éditeurs graphiques, produisant du SVG, comme les logiciels libres Inkscape et Dia. (Et, si on aime programmer en Python, il y a svgwrite, que je présente plus en détail à la fin.) Attention, Inkscape et Dia produisent du SVG généraliste, qui peut inclure des fonctions de SVG qui ne suivent pas les principes de notre RFC.

Autre solution, utiliser la bibliothèque Fletcher de Typst :

% typst compile --format svg essai-fletcher.typ
  

Le texte « Tips for Creating Accessible SVG » donne des bons conseils pour faire du SVG accessible. Et il y a bien sûr la norme ARIA, dont il existe une introduction et de bons exemples. (Désolé, je n'ai pas suivi ces excellents principes dans les exemples ci-dessous, mais j'accepte les patches.)

Avec Inkscape, il faut veiller à sauver le fichier en Plain SVG (autrement, on a des ennuis avec les éléments spécifiques d'Inkscape, ex-Sodipodi). Mais il reste malgré cela deux ou trois trucs à corriger manuellement, avant que le document produit par Inkscape soit accepté. Pour Dia, il faut utiliser l'action Export (par défaut, Dia n'enregistre pas en SVG), mais Dia produit alors un document avec une DTD. Si on la retire (manuellement, ou bien avec xmllint --dropdtd), tout se passe bien.

Si vous voulez voir des exemples concrets de RFC utilisant SVG, vous avez entre beaucoup d'autres, le RFC 8899, le RFC 9869 ou encore le RFC 9750. Ainsi, le RFC 8899 inclut un schéma qui a deux formes alternatives, en art ASCII :


<artwork type="ascii-art" name="" alt="" align="left" pn="section-4.4-2.1.2">
Any additional
  headers         .--- MPS -----.
         |        |             |
         v        v             v
  +------------------------------+
  | IP | ** | PL | protocol data |
  +------------------------------+

             &lt;----- PLPMTU -----&gt;
  &lt;---------- PMTU --------------&gt;
</artwork>

  

Et en SVG, que vous pouvez admirer dans le rendu HTML du RFC et son rendu en PDF.

Une alternative, pour tester les SVG est svgcheck. Ici avec le vrai source du RFC 9750 :

% svgcheck rfc9750.xml  
INFO: File conforms to SVG requirements.
  

Et si je tente de mettre des couleurs vives en modifiant le XML :

% svgcheck rfc9750.xml      
rfc9750.xml:438: The attribute 'stroke' does not allow the value 'red', replaced with 'black'
ERROR: File does not conform to SVG requirements
  

Autre solution que j'aime bien pour faire du SVG, dès qu'on a des éléments répétitifs et qu'on veut donc automatiser (en Python), svgwrite. Ce schéma en art ASCII :

 +--------------+				     +----------------+
 |  Alice       |------------------------------------|      Bob	      |
 | 2001:db8::1  |                                    |   2001:db8::2  |
 +--------------+				     +----------------+

aurait pu être créé avec svgwrite avec network-schema-svgwrite.py, qui donne network-schema-svgwrite

Bien sûr, pour un schéma aussi simple, le gain n'est pas évident, mais il le devient pour les schémas comportant beaucoup d'éléments récurrents. Mais notez que svgwrite, par défaut, peut donc produire des SVG non conformes aux règles du RFC (par exemple avec de la couleur). Le programmeur doit donc faire attention.


Téléchargez le RFC 9896


L'article seul

Le mini-PC Shuttle Nano NE10N

Première rédaction de cet article le 29 janvier 2026


J'ai acheté un mini-PC Shuttle Nano NE10N et cet article est là pour documenter cet appareil et parler du problème d'installation avec sa carte Ethernet.

Je voulais ce mini-PC pour un serveur à la maison, restant allumé en permanence. Mon cahier des charges était :

  • Peut faire tourner Linux, de préférence Debian,
  • Très silencieux (puisque allumé en permanence),
  • Faible consommation électrique (puisque allumé en permanence),
  • Au moins 100 Go de disque et 2 Go de RAM (le processeur n'est pas important, un ARM aurait parfaitement convenu),
  • Ethernet (pas besoin de Wifi). Et cela a été le plus gros problème lors de l'installation.

En outre, je souhaitais :

  • Pas sur Amazon ou AliExpress,
  • Peu encombrant (bon, c'est le principe du mini-PC),
  • Pas de ventilateur,
  • Vendu sans Windows pour ne pas payer de licence,
  • Dans un seul boitier (pas de bricolage matériel nécessaire).

J'indiquerai plus tard quelques pistes que j'ai suivies. Mais d'abord, la machine.

Voici l'engin de face, de dos et l'intérieur : nano-ne10-front.jpg nano-ne10-back.jpg nano-ne10-internal.jpg

Voici le matériel sur le bus PCI :

% lspci
00:00.0 Host bridge: Intel Corporation Alder Lake-N Processor Host Bridge/DRAM Registers
00:02.0 VGA compatible controller: Intel Corporation Alder Lake-N [UHD Graphics]
00:08.0 System peripheral: Intel Corporation GNA Scoring Accelerator
00:0a.0 Signal processing controller: Intel Corporation Platform Monitoring Technology (rev 01)
00:0d.0 USB controller: Intel Corporation Alder Lake-N Thunderbolt 4 USB Controller
00:14.0 USB controller: Intel Corporation Alder Lake-N PCH USB 3.2 xHCI Host Controller
00:14.2 RAM memory: Intel Corporation Alder Lake-N PCH Shared SRAM
00:16.0 Communication controller: Intel Corporation Alder Lake-N PCH HECI Controller
00:17.0 SATA controller: Intel Corporation Alder Lake-N SATA AHCI Controller
00:1c.0 PCI bridge: Intel Corporation Alder Lake-N PCI Express Root Port #4
00:1f.0 ISA bridge: Intel Corporation Alder Lake-N PCH eSPI Controller
00:1f.3 Audio device: Intel Corporation Alder Lake-N PCH High Definition Audio Controller
00:1f.4 SMBus: Intel Corporation Alder Lake-N SMBus
00:1f.5 Serial bus controller: Intel Corporation Alder Lake-N SPI (flash) Controller
01:00.0 Ethernet controller: Motorcomm Microelectronics. YT6801 Gigabit Ethernet Controller (rev 01)
  

Les caractéristiques du processeur sont en nano-ne10-cpu.txt et les messages de démarrage en nano-ne10-boot.txt.

La consommation électrique, mesurée par un wattmètre Chacon est de 5 W au calme et de 15 W quand la machine travaille à fond.

Où trouve-t-on cette machine ? Shuttle ne vend pas en direct. On peut trouver des machines chez des vendeurs en ligne mais je n'avais pas envie d'acheter chez des boites peu sympathiques et puis je voulais davantage de RAM et de disque que dans le cas de l'offre par défaut, et je n'avais pas envie de monter cela moi-même (ce qui aurait été assez facile, le boitier s'ouvre facilement et tout est accessible). Heureusement, des revendeurs de Shuttle rendent ce service. Si MisterOops n'a jamais répondu à mes demandes de devis, M2N a été très réactif et m'a rapidement livré un PC comme je voulais (« Shuttle XPC nano NE1000N Configurable (Sans Système d'exploitation, 8 Go DDR4, 500 Go SSD M.2 (SATA), Aucun) 1,00 Unité(s) »), après passage par le configurateur de Shuttle. (La bonne RAM serait de la DDR5, mais actuellement elle est hors de prix et peu disponible.)

J'avais demandé une machine sans système d'exploitation en me disant qu'il n'y avait pas de raison que l'installation pose des problèmes. J'ai téléchargé une image « netinst » (Network Installation) Debian, copié sur une clé USB avec cp debian.iso /dev/sdX quand soudain, le drame : « A driver for your hardware is not available. You may need to load drivers from removable media. Load missing drivers from removable media? ». L'installateur de l'actuelle version de Debian n'a en effet pas de pilote pour la carte Ethernet « YT6801 Gigabit Ethernet ». (Ah, comment on trouve le type de la carte et donc le pilote nécessaire ? Alt-F2 pour changer de console puis lspci.)

Je vous donne tout de suite la solution :

  • Installer Debian sans le réseau en utilisant l'image dite « DVD » (pas sur un DVD, bien sûr, mais sur une clé USB de bonne capacité car l'image fait 3,7Go),
  • Comme il va falloir installer des paquetages pour compiler un pilote, mettre dans /etc/apt/sources.list la ligne deb file:///mnt trixie main (où /mnt est l'endroit où vous montez la clé USB), puis apt update,
  • Installer avec apt install build-essential kmod dpkg-dev build-essential patch linux-headers-6.12.48+deb13-amd64,
  • Récupérer le pilote chez Tuxedo, wget https://deb.tuxedocomputers.com/debian/pool/main/t/tuxedo-yt6801/tuxedo-yt6801_1.0.30tux4_all.deb, et le mettre sur une clé USB,
  • Récupérer le paquetage dkms, qui existe chez Debian, et est indispensable pour compiler proprement le pilote, mais n'est pas dans l'image d'installation, wget http://ftp.fr.debian.org/debian/pool/main/d/dkms/dkms_3.2.2-1~deb13u1_all.deb, et le mettre sur une clé USB,
  • Installer les deux paquetages téléchargés (mettons que la clé USB ait été montée en /mnt2) avec apt install /mnt2/tuxedo-yt6801_1.0.30tux4_all.deb /mnt2/dkms_3.2.2-1~deb13u1_all.deb.

Le pilote va alors être compilé et installé, tout est bien qui finit bien, Ethernet marche.


% dpkg -l | grep yt6801
ii  tuxedo-yt6801                           1.0.29tux0                           all          Driver for Motorcomm YT6801

% lsmod | grep yt6801
yt6801                163840  0

% ip link show
…
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 38:f7:cd:ce:22:c6 brd ff:ff:ff:ff:ff:ff
    altname enx38f7cdce22c6

Cette approche est également documentée sur le forum de Linux Mint ou bien sur Reddit. Notez que ce pilote réseau fait débat, ce qui peut expliquer son absence de Debian.

Attention, il faudra recommencer à chaque mise à jour du noyau, puisque le module n'est compilé que pour une version particulière du noyau. Si apt dist-upgrade vous dit qu'il n'a pas compilé les modules parce qu'il n'avait pas les fichiers d'en-tête du noyau, danger ! Il faut installer ces en-têtes, puis utiliser dkms build et dkms install. Voici par exemple comment voir les versions compilées et installées du module Ethernet, ici pour deux versions du noyau :

% dkms status
tuxedo-yt6801/1.0.29tux0, 6.12.63+deb13-amd64, x86_64: installed
tuxedo-yt6801/1.0.29tux0, 6.12.69+deb13-amd64, x86_64: installed
  

Les autres machines que j'avais considérées :

  • Le Trigkey Key-N150 (je ne me souviens même plus pourquoi je l'avais écarté),
  • Les Raspberry Pi sont cools mais je préférais une solution toute intégrée, pas un SBC,
  • Les Tuxedo étaient bien trop chers, trop haut de gamme (j'ai un portable de cette marque),
  • Même chose pour le Slimbook,
  • Le Geekom aussi est cher,
  • Le BMax B5A Pro (16Go de RAM, 512Go de SSD, AMD Ryzen7 5825U) sur AliExpress (un utilisateur m'a raconté qu'il avait dû changer l'alimentation, défaillante),
  • Le MiniPC Soyo M4 Plus Intel N150 (je crois que je ne l'avais trouvé que sur AliExpress),
  • Le Beelink ME Pro, plutôt pour faire NAS que serveur mais, de toute façon, il ne semble pas encore sorti.

Vous avez aussi un test de la machine que j'ai achetée sur l'excellent site MiniMachines. Notez qu'ils sont sur le fédivers, en @PierreLecourt@oisaur.com.


L'article seul

RFC 9816: Usage and Applicability of BGP Link-State Shortest Path Routing (BGP-SPF) in Data Centers

Date de publication du RFC : Juillet 2025
Auteur(s) du RFC : K. Patel (Arrcus), A. Lindem (LabN Consulting), S. Zandi, G. Dawra (Linkedin), J. Dong (Huawei Technologies)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lsvr
Première rédaction de cet article le 27 janvier 2026


Le RFC 9815 normalise l'utilisation de l'algorithme de routage SPF avec BGP. Dans quels cas est-ce que ça peut s'appliquer à l'intérieur d'un centre de données ? Ce RFC 9816 donne des éléments de réponse.

Il s'agit donc d'un court complément au RFC 9815 pour un cas courant, le centre de données qui suit la topologie décrite par Charles Clos dans son article de 1952, « A study of non-blocking switching networks ». (On trouve aussi cet article sur Sci-Hub ou à divers endroits sur le Web.) Pour que le trafic circule de n'importe quel nœud d'entrée vers n'importe quel nœud de sortie, on peut connecter tous les nœuds d'entrée à tous les nœuds de sortie mais cela fait beaucoup de connexions, qui coûtent cher. Ou bien on peut connecter tous les nœuds d'entrée à un dispositif de commutation qui ira vers tous les nœuds de sortie. Mais le trafic risque d'être bloqué si ce dispositif est surchargé. Dans un réseau Clos, on met des nœuds intermédiaires, avec une connectivité suffisante pour qu'on ne soit pas bloqué dans la plupart des cas. Il y a donc plusieurs chemins possibles d'un bout à l'autre du tissu ainsi formé (ce qui fait qu'un algorithme de routage comme le spanning tree n'est pas optimal puisqu'il ne trouve qu'un seul chemin). Dans un centre de données moderne, il y a en général une épine dorsale formée des commutateurs rapides et, dans chaque baie, un commutateur ToR (Top of Rack, rien à voir avec Tor). Tous les commutateurs ToR sont connectés à l'épine dorsale (liaison dite Nord-Sud, l'épine dorsale étant représentée en haut, le Nord) alors qu'il n'y a pas forcément de liaison entre les commutateurs ToR (liaison dite Est-Ouest).

Dans un centre de données non public (où toutes les machines appartiennent à la même entité), quel protocole de routage utiliser ? A priori, un IGP, non, puisqu'il s'agit de routage interne ? Mais pour diverses raisons, entre autres pour se simplifier la vie avec un seul protocole pour tout, certains utilisent BGP (RFC 7938) et même EBGP (External BGP), où les routeurs sont dans des AS différents (regardez la section 5 du RFC 7938 pour comprendre ce choix). Mais avec EBGP, les sessions BGP correspondent au chemin des données, ce qui empêche d'utiliser des réflecteurs de route. Et puis l'algorithme de routage classique de BGP ne converge pas assez vite en cas de changement, ce qui n'est pas grave sur l'Internet public mais est plus gênant à l'intérieur du centre de données. C'est là que le BGP-SPF du RFC 9815 devient intéressant, en remplaçant l'algorithme de routage traditionnel par SPF.

Utiliser BGP permet aussi de simplifier l'authentification, en se reposant sur les mécanismes existants comme celui du RFC 5925.

Autre avantage, les équipements réseau de ce centre de données aiment bien avoir de l'information détaillée sur la topologie et c'est justement ce que fournit l'extension BGP Link State, normalisée dans le RFC 9552, et dont BGP-SPF dépend. Il n'y a plus qu'à écouter le trafic BGP pour tout apprendre du réseau et bâtir ainsi divers services.

Plusieurs topologies d'appairage sont possibles entre les routeurs, collant à la topologie physique ou bien s'en écartant. Les routeurs peuvent utiliser BFD (RFC 5880) pour vérifier en permanence qu'ils arrivent bien à se joindre.

Même si le vieux protocole IPv4 est présent, on peut ne s'appairer qu'en IPv6 (cf. le RFC 8950) voire qu'avec des adresses locales (RFC 7404).

Et si un routeur veut jouer à BGP avec les autres routeurs mais sans être utilisé pour transmettre des paquets ? (Par exemple parce qu'il héberge des services applicatifs qui doivent être joignables.) Le TLV SPF status (RFC 9815, section 5.2.1.1) sert à cela : s'il est présent, avec une valeur de 2, le nœud ne sera pas utilisé pour le transit des paquets.


Téléchargez le RFC 9816


L'article seul

RFC 9815: BGP Link-State Shortest Path First (SPF) Routing

Date de publication du RFC : Juillet 2025
Auteur(s) du RFC : K. Patel (Arrcus), A. Lindem (LabN Consulting), S. Zandi (LinkedIn), W. Henderickx (Nokia)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lsvr
Première rédaction de cet article le 27 janvier 2026


Vous le savez (peut-être), le protocole de routage BGP est du type « vecteur de chemin ». Mais il peut aussi transporter des états des liens, si on souhaite faire des choses plus proches des protocoles à état des liens. Ce RFC décrit comment, avec ces informations sur l'état des liens, décider du routage par l'algorithme SPF (Shortest Path First) plutôt que par la méthode traditionnelle de BGP.

Pour simplifier, un protocole à état des liens (comme OSPF ou IS-IS) permet à chaque routeur d'avoir l'état complet du réseau, et donc de faire tourner des algorithmes comme SPF, qui nécessite justement cette connaissance totale. Par contre, un protocole à vecteur de distance comme RIP ou à vecteur de chemin comme BGP n'a pas besoin de cette information et consomme donc moins de mémoire (pour BGP, stocker l'état de tous les liens de l'Internet serait évidemment impossible). Mais le protocole doit développer des mécanismes pour éviter, par exemple, les boucles de routage, qui pourraient arriver puisque chaque routeur décide sur la base d'une information incomplète.

Les deux types de protocole ont des avantages et des inconvénients et il est donc tentant de les combiner. Le RFC 9552 normalise justement un moyen de transporter l'état des liens avec BGP. Cela permet des prises de décision plus « intelligentes », comme dans le cas du RFC 9107 pour un réflecteur ou du RFC 7971 pour le mécanisme de décision ALTO. Pour transporter cet état, le RFC 9552 normalise un AFI (Address Family Identifier, RFC 4760, section 3) et un SAFI (Sub-address Family Identifier, même référence). Ce sont l'AFI 16388 et le SAFI 71. Ce BGP-LS (Border Gateway Protocol - Link State) sert de base à ce nouveau RFC, qui décrit une des manières d'utiliser cette information sur l'état des liens. (Cette technique a plusieurs années mais le développement du RFC a été long.)

Beaucoup de gros centres de données utilisent en interne BGP (RFC 4271) pour distribuer l'information de routage, car la densité des équipements créerait trop de trafic, avec les protocoles plus bavards comme OSPF (c'est documenté dans le RFC 7938 et RFC 9816). (Ces gros centres sont parfois appelés MSDC pour Massively Scaled Data Centers.) En outre, BGP repose sur TCP, ce qui élimine les problèmes de gestion des paquets perdus qu'ont les IGP traditionnels. Et puis cela permet de n'utiliser qu'un seul protocole comme IGP et EGP. Il ne restait qu'à étendre BGP pour pouvoir utiliser l'algorithme SPF de certains IGP, ce que fait notre RFC. Le principal avantage (section 1.2 du RFC) de cet ajout est que tous les routeurs auront désormais une vue complète de tout le réseau, sans qu'il y ait besoin de multiplier les sessions BGP. C'est utile pour des services comme ECMP, le calcul à l'avance de routes de secours (RFC 5286), etc.

Un peu de terminologie s'impose pour suivre ce RFC (section 1.1) :

  • Domaine de routage BGP SPF : un ensemble de routeurs sous la même administration (cf. section 10.1) qui échangent de l'information sur l'état des liens via BGP, et calculent la table de routage avec SPF.
  • NLRI (Network Layer Reachability Information) BGP-LS-SPF (LS = Link State, état des liens) : les NLRI (RFC 4271, section 3.1) sont les données envoyées dans les messages BGP. Ce type particulier de NLRI sert à transmettre les informations sur l'état des liens. Il a le numéro 80 et est encodé exactement comme le NLRI BGP-LS (tout court) du RFC 9552.
  • Algorithme de Dijkstra : un autre nom de SPF (Shortest Path First), la grande nouveauté de notre RFC.

Bon, maintenant, le protocole lui-même. C'est bien sûr le bon vieux BGP du RFC 4271, avec l'extension LS du RFC 9552 et « juste » en plus l'utilisation de SPF pour le mécanisme de décision (« quelle route choisir ? »). Autrement, le RFC insiste, c'est du BGP normal, avec son automate, son format de paquets, ses signalements d'erreurs (RFC 7606), etc. Du fait du nouveau mécanisme de décision, les attributs optionnels des chemins annoncés n'ont pas à être transmis (les attributs obligatoires, comme leur nom l'indique, sont toujours transmis, même si SPF ne les utilise pas). Comme le calcul des routes se fait sur la base de l'information sur l'état des liens, un routeur BGP-LS-SPF n'attend pas d'avoir fait son calcul local avant de transmettre une annonce, il envoie tout (section 2). Le traditionnel mécanisme de décision de BGP (celui de la section 9.1 du RFC 4271) disparait et est remplacé par celui décrit en section 6. Cela implique, entre autres, que le chemin d'AS n'est plus utilisé pour empêcher les boucles.

On l'a dit, BGP-LS-SPF s'appuie sur le BGP-LS du RFC 9552 mais, avec un nouveau SAFI (Subsequent Address Family Identifier), le 80, puisque le SAFI du RFC 9552 supposait le processus de décision traditionnel de BGP (SPF ne doit être utilisé qu'avec les informations obtenues via les NLRI BGP-LS-SPF, cf. section 5.1). Par contre, les autres paramètres de BGP-LS sont utilisés tels quels (section 5.1.1). Il y a aussi des ajouts, par exemple pour indiquer qu'un lien ou un préfixe doit être considéré comme inutilisable par BGP-LS-SPF.

Les messages peuvent être gros, vu qu'on doit transporter l'information au sujet de tout le domaine de routage. Il est donc recommandé de mettre en œuvre le RFC 8654, qui permet d'avoir des messages BGP de plus de 4 096 octets.

La section 4 du RFC explique comment s'appairer pour échanger des informations sur les états des liens. En gros, rien de spécial, appairage direct ou passage par un réflecteur (RFC 4456) sont possibles comme avant.


Téléchargez le RFC 9815


L'article seul

RFC 9910: Registration Data Access Protocol (RDAP) Regional Internet Registry (RIR) Search

Date de publication du RFC : Janvier 2026
Auteur(s) du RFC : T. Harrison (APNIC), J. Singh (ARIN)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 8 janvier 2026


Le protocole RDAP, successeur de whois, n'est pas utilisé que par les registres de noms de domaine. Il sert aussi chez les RIR, pour obtenir des informations sur les entités qui sont derrière une adresse IP (ou un AS). RDAP dispose dès le début de fonctions de recherche (regardez le RFC 9082) mais ce nouveau RFC ajoute des choses en plus.

Traditionnellement, les serveurs whois des RIR disposaient de fonction de recherche « avancées » comme la possibilité de chercher par valeur (« quels sont tous les préfixes IP de tel titulaire ? »). L'idée de ce RFC est de permettre la même chose en RDAP. RDAP de base permet les recherches des informations associées à une adresse IP :

% curl -s https://rdap.db.ripe.net/ip/2001:41d0:302:2200::180 | jq . 
{
  "handle": "2001:41d0::/32",
  "name": "FR-OVH-20041115",
  "country": "FR",
  "parentHandle": "2001:4000::/23",
  …
  "status": [
    "active"
  ],
  "entities": [
    {
      "handle": "OK217-RIPE",
      …
            "text",
            "Octave Klaba"
      …
  

En plus de ip, ce RFC ajoute ips (section 2.1) qui permet une recherche sur tous les préfixes dont le nom correspond à un certain motif (ici, tous ceux d'OVH) :

% curl -s https://rdap.db.ripe.net/ips\?name="FR-OVH-*" | jq '.ipSearchResults.[].handle'
"109.190.0.0 - 109.190.255.255"
"135.125.0.0 - 135.125.255.255"
"137.74.0.0 - 137.74.255.255"
"141.94.0.0 - 141.95.255.255"
"145.239.0.0 - 145.239.255.255"
"147.135.128.0 - 147.135.255.255"
"149.202.0.0 - 149.202.255.255"
"152.228.128.0 - 152.228.255.255"
"159.173.0.0 - 159.173.255.255"
"162.19.0.0 - 162.19.255.255"
…
  

J'ai utilisé ici curl et jq mais, évidemment, l'avantage de RDAP est que vous pouvez utiliser un client dédié ou bien n'importe quel logiciel qui sait faire du HTTP et du JSON (voyez plus loin un exemple en Python).

Et chez un autre RIR :

% curl -s https://rdap.arin.net/registry/ips\?name='CLOUDFLARE*' | \
       jq '.ipSearchResults.[].handle'
"NET-104-16-0-0-1"
"NET-108-162-192-0-1"
"NET-156-146-101-152-1"
"NET-162-158-0-0-1"
"NET-172-64-0-0-1"
"NET-173-245-48-0-1"
"NET-198-41-128-0-1"
"NET-199-27-128-0-1"
"NET6-2606-4700-1"
  

De la même façon, vous pouvez chercher par numéro d'AS :

% curl -s https://rdap.db.ripe.net/autnums\?name="*NIC-FR*" | \
          jq '.autnumSearchResults.[].handle'
"AS2483"
"AS2484"
"AS2485"
"AS2486"
  

La section 3 décrit ensuite des moyens de trouver les objets parents et enfants (puisque l'allocation des adresses IP est hiérarchique, toute adresse est dans un préfixe plus général et contient des préfixes plus spécifiques, cf. la section 3.2.1 du RFC) :

% curl -s https://rdap.db.ripe.net/ips/rirSearch1/rdap-up/2001:41d0:302:2200::180 | \
       jq '.handle'                   
"2001:41d0::/32"
  
% curl -s https://rdap.db.ripe.net/ips/rirSearch1/rdap-down/2001:4000::/23 | \
          jq '.ipSearchResults.[].handle'
"2001:4000::/32"
"2001:4010::/32"
"2001:4018::/29"
"2001:4020::/32"
…
  

Notez la relation (rdap-up et rdap-down). Notez aussi que rdap-up renvoie au maximum un objet alors que rdap-down peut en renvoyer plusieurs (cf. section 4.2), et c'est pour cela qu'il a fallu itérer en jq (le .[] parcourt le tableau). Quant au rirSearch1, le 1 indique la version de cette extension de recherche chez les RIR (désormais enregistrée à l'IANA).

Et en Python, ça donnerait :

#!/usr/bin/python

# Example of using search extensions to RDAP (RFC 9910)

# https://requests.readthedocs.io
import requests

# Standard library
import json
import sys

# Yes, we should use the registry documented in RFC 7484…
BASE_URL = "https://rdap.db.ripe.net/ips/rirSearch1/rdap-down"

if len(sys.argv) != 2:
    raise Exception("Usage: %s ip-prefix-at-ripe" % sys.argv[0])
arg = sys.argv[1]

response = requests.get("%s/%s" % (BASE_URL, arg))
if response.status_code != 200:
    raise Exception("Wrong HTTP return code from %s: %s" % (BASE_URL, response.status_code))
data = json.loads(response.text)
for prefix in data["ipSearchResults"]:
    print(prefix["handle"])
  

Et les recherches inverses, telles que décrites dans le RFC 9536 ? La section 5 du RFC les présente et voici un exemple qui marche (trouver tous les préfixes de Webflow) :

% curl -s 'https://rdap.arin.net/registry/ips/reverse_search/entity?handle=WEBFL' | \
            jq '.ipSearchResults.[].handle'
"NET-198-202-211-0-1"
"NET6-2620-CB-2000-1"
  

Un serveur RDAP qui gère les extensions de ce RFC doit le signaler dans ses réponses (section 6) :

…    
"rdapConformance" : [ "geofeed1", "rirSearch1", "ips", "cidr0",
       "rdap_level_0", "nro_rdap_profile_0", "redacted" ],
…
  

Mais aussi dans les liens donnés dans les réponses (ici, en réponse à une requête traditionnelle ip) :

  "links": [
    {
      "value": "https://rdap.db.ripe.net/ip/2001:41d0:302:2200::180",
      "rel": "rdap-up",
      "href": "https://rdap.db.ripe.net/ips/rirSearch1/rdap-up/2001:41d0::/32",
      "type": "application/rdap+json"
    },
  

Et bien sûr dans les réponses d'aide :

% curl -s https://rdap.arin.net/registry/help 
{
  "rdapConformance" : [ "nro_rdap_profile_0", "rdap_level_0", "cidr0", "nro_rdap_profile_asn_flat_0", "arin_originas0", "rirSearch1", "ips", "ipSearchResults", "autnums", "autnumSearchResults", "reverse_search" ],
  "notices" : [ {
    "title" : "Terms of Service",
    "description" : [ "By using the ARIN RDAP/Whois service, you are agreeing to the RDAP/Whois Terms of Use" ],
    "links" : [ {
      "value" : "https://rdap.arin.net/registry/help",
      "rel" : "terms-of-service",
      "type" : "text/html",
      "href" : "https://www.arin.net/resources/registry/whois/tou/"
    } ]
…
  

Les fonctions de recherche, c'est très bien mais c'est, par construction, indiscret. La section 8 de notre RFC détaille les risques de ces extensions de recherche pour la vie privée (relire le RFC 7481 est une bonne idée).

Les extensions de ce RFC, rirSearch1, ips, autnums, ipSearchResults et autnumSearchResults ont été enregistrées à l'IANA (section 10 du RFC). Les relations rdap-up, rdap-down, rdap-top et rdap-bottom sont dans le registre des liens (RFC 8288). Et le registre des recherches inverses inclut désormais fn, handle, email et role, avec leur correspondance en JSONPath.

Et question mise en œuvre et déploiement, on a quoi ? ARIN et RIPE ont annoncé avoir programmé les extensions de ce RFC mais elles ne sont pas forcément accessibles via le serveur RDAP public, entre autre pour les raisons de vie privée discutées dans la section 8. Aujourd'hui, comme vous le voyez dans les exemples ci-dessus, au moins ARIN et RIPE rendent une partie de ces extensions utilisables.


Téléchargez le RFC 9910


L'article seul

Quel risque que des satellites se tamponnent ?

Première rédaction de cet article le 12 décembre 2025


Il y a beaucoup, mais vraiment beaucoup, de satellites au-dessus de nos têtes, notamment sur les orbites basses. Risquent-ils de se tamponner ? Oui, montre cette étude : en l'absence de réaction de leurs opérateurs, il y aurait une collision tous les 2,8 jours.

Le risque de collision entre satellites est connu depuis longtemps. Le cas le plus fameux est bien sûr la collision du 10 février 2009 (cf. le bilan récent « Subsequent Assessment of the Collision between Iridium 33 and COSMOS 2251 »). Les conséquences d'un choc direct sont évidemment la destruction des deux satellites mais le principal problème est surtout la nuée de débris ainsi créée, et les nombreuses collisions secondaires qu'elle peut entrainer. En cas d'éparpillement des satellites impliqués, l'orbite peut devenir inutilisable.

Mais l'espace est grand et un satellite est petit, non ? Les collisions devraient être rares. Ce serait le cas s'il y avait peu de satellites. Mais de nos jours, avec les méga-constellations, le nombre de satellites est tel que la probabilité de collision a sérieusement augmenté. Et puis un satellite en orbite basse fait beaucoup de tours de la Terre, augmentant le risque. C'est comme jouer à la roulette russe en essayant souvent.

Peut-on quantifier ce risque ? C'est ce que tente l'article « An Orbital House of Cards: Frequent Megaconstellation Close Conjunctions ». Les auteurices ont développé un indice, spirituellement appelé « CRASH clock » (pour Collision Realization And Significant Harm), exprimé sous forme d'une durée, et qui indique l'intervalle moyen entre collisions. Je vous laisse lire l'article pour les détails du calcul (comme toutes les modélisations, c'est compliqué et certains facteurs ne sont pas connus avec précision). Mais ce qui est inquiétant est le résultat : le CRASH clock qui était de 121 jours avant l'arrivée des méga-constellations est descendu à 2,8 jours, en grande partie grâce à Starlink.

Bon mais, en pratique, on ne voit pas de collision tous les 2,8 jours, non ? C'est parce que les opérateurs des satellites surveillent les environs et actionnent de temps en temps les moteurs pour s'éloigner d'un risque de collision. On ne voit donc pas souvent d'accident. Le risque est qu'un événement survienne qui empêche ces réactions pendant quelques jours. Les auteurices imaginent un problème logiciel important chez un des opérateurs, voire sur plusieurs, ou bien une crise socio-politique importante sur Terre, ou tout simplement une tempête solaire, qui pourrait perturber les communications pendant une durée significative par rapport au CRASH clock. (Le risque des tempêtes solaires n'est pas purement théorique.) Le CRASH clock indique donc ce qui se passerait lors d'un de ces événements. Les opérateurs de satellites ne peuvent pas se permettre une interruption de leur travail, même de seulement quelques heures.

En français, vous serez peut-être intéressé par cet article du CNES.


L'article seul

RFC 9824: Compact Denial of Existence in DNSSEC

Date de publication du RFC : Septembre 2025
Auteur(s) du RFC : S. Huque (Salesforce), C. Elmerot (Cloudflare), O. Gudmundsson
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 12 décembre 2025


Plus fort que le chat de Schrödinger qui était à la fois mort et vivant, ce RFC permet à un nom de domaine d'être à la fois existant et non-existant. Plus précisément, il permet de fournir une preuve cryptographique avec DNSSEC, prouvant que le nom existe (alors qu'il n'existe pas) mais n'a pas les données demandées. Cette technique (autrefois connue sous le nom de « black lies », et largement déployée) est particulièrement adaptée au cas des signatures générées dynamiquement, mais a l'inconvénient de « mentir » sur l'existence du nom.

Pour comprendre, il faut revenir à un problème agaçant pour DNSSEC : le déni d'existence. Ce terme désigne le fait de prouver qu'un nom, ou qu'un certain type de données pour un nom, n'existe pas. Car DNSSEC fonctionne en signant les données. Mais en cas de non-existence, il n'y a rien à signer. DNSSEC a donc ajouté un type d'enregistrement nommé NSEC qui encadre le nom manquant. Et ces enregistrements, eux, sont signés. Un enregistrement NSEC affirme « ce nom n'existe pas » en donnant le nom précédent et le nom suivant (ici, la réponse d'un serveur racine à qui on a demandé www.trump alors que le TLD .trump n'existe pas) :


;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 65362
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1
…
travelersinsurance.	86400	IN	NSEC	trust. NS DS RRSIG NSEC

On obtient un NXDOMAIN (ce nom n'existe pas), zéro réponse (la section Answer est vide) et l'enregistrement NSEC nous dit qu'il n'y a rien entre travelersinsurance et trust. Comme il est signé (je n'ai pas montré les signatures), nous avons une preuve de non-existence.

Et si le nom existe, mais n'a pas de données du type demandé ? J'interroge mon résolveur sur la localisation associée au nom www.iis.se :


% dig +dnssec www.iis.se LOC
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39872
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
…
www.iis.se.		480	IN	NSEC	xmpp01.iis.se. A AAAA RRSIG NSEC
…

  

Le NOERROR est normal (le nom existe) mais la section Answer est vide (puisqu'il n'y a pas d'enregistrement de type LOC) et le NSEC nous dit que le nom existe bien (le nom suivant étant xmpp01.iis.se.) mais n'a comme type d'enregistrement que A, AAAA, RRSIG et NSEC. On a donc prouvé que le LOC n'existe pas.

Bon, tout ça, c'est du DNSSEC classique, tel que normalisé dans les RFC 4034 et RFC 4035. Ça marche, mais cela a des inconvénients, notamment pour les signatures générées dynamiquement. Cela nécessite plusieurs NSEC (il faut aussi montrer qu'il n'y a pas de joker), avec leurs signatures.

CDE (Compact Denial of Existence, alias black lies) fonctionne (section 3 du RFC) en calculant un intervalle minimal entre les noms, comme le faisaient les white lies du RFC 4470. Mais, contrairement à eux, CDE fait partir cet intervalle du nom demandé. Par exemple, si on demande foobar.example et que ce nom n'existe pas, les white lies fabriqueront un NSEC allant de ~.foobaq~.example à foobar!.example alors que les black lies de notre RFC feront un NSEC allant de foobar.example à \000.foobar.example. Cet intervalle démarrant au nom de domaine demandé, il ne faut plus jamais renvoyer de NXDOMAIN, uniquement des NODATA (NOERROR mais avec une section Answer vide).

Voici un exemple de black lie chez Cloudflare (qui a déployé CDE il y a longtemps) :


% dig +dnssec doesnotexistcertainly.cloudflare.com                           
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43853
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
…
;; AUTHORITY SECTION:
doesnotexistcertainly.cloudflare.com. 300 IN NSEC \000.doesnotexistcertainly.cloudflare.com. RRSIG NSEC TYPE128
…
;; Query time: 14 msec
;; SERVER: 192.168.2.254#53(192.168.2.254) (UDP)
;; WHEN: Fri Dec 12 14:10:39 CET 2025
;; MSG SIZE  rcvd: 400

  

Mais puisqu'on ne renvoie jamais de NXDOMAIN, comment distinguer un nom qui n'existe pas d'un nom qui n'a simplement pas le type demandé ? La section 2 du RFC présente le type NXNAME (enregistré avec le numéro 128, d'où le TYPE128 ci-dessus). Sa présence dans le NSEC indique que le nom n'existe pas. Comparez la réponse ci-dessus avec celle-ci, où le nom existe, mais pas le type (hex.pm est chez Cloudflare et n'a pas d'adresse IPv6 associée à son nom) :


% dig +dnssec hex.pm AAAA
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5431
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1
…
hex.pm.			1800	IN	NSEC	\000.hex.pm. A NS SOA HINFO MX TXT LOC SRV NAPTR CERT SSHFP RRSIG NSEC DNSKEY TLSA SMIMEA HIP CDS CDNSKEY OPENPGPKEY SVCB HTTPS URI CAA

  

La longue liste de types dans le NSEC (mais sans le NXNAME/TYPE128) est due au fait que le serveur, qui génère des NSEC (et des signatures) dynamiquement ne connait pas la vraie liste donc il met tout ce qu'il connait.

Un résolveur qui connait les NXNAME peut donc refabriquer un code de retour NXDOMAIN et l'envoyer à ses clients (section 5 du RFC). Un nouveau booléen dans la question envoyée aux serveurs faisant autorité, le CO (Compact Answers OK) peut être utilisé pour dire au serveur faisant autorité qu'il peut répondre avec un NXDOMAIN, le client DNS qui met le CO indique qu'il saura lire et interpréter le NXNAME. (Le CO a été enregistré à l'IANA, le premier booléen d'une requête EDNS à être enregistré depuis DO il y a vingt-quatre ans.) Cette possibilité de rétablissement du NXDOMAIN ne semble pas très souvent mise en œuvre actuellement.

Notez (section 4) qu'on peut utiliser CDE avec NSEC3 mais que cela n'a aucun intérêt, vu que la compacité de l'intervalle des noms empêche déjà l'énumération des noms.

Enfin, si vous aimez les détails, l'annexe A du RFC discute des raisons des choix faits, et des alternatives étudiées (et même testées dans la nature), mais non retenues.

CDE est largement déployé aujourd'hui, notamment chez Cloudflare, déjà cité, mais aussi chez d'autres hébergeurs utilisant la signature dynamique, comme NS1. En logiciel libre, Knot permet le CDE. Il y a aussi une implémentation (mais qui n'a pas été maintenue synchrone avec le RFC) dans mon serveur Drink. Elle fut développée lors d'un hackathon IETF. Elle n'est pas activée par défaut, il faut changer un paramètre dans le source (lib/drink/dnssec.ex).


Téléchargez le RFC 9824


L'article seul

RFC 9868: Transport Options for UDP

Date de publication du RFC : Octobre 2025
Auteur(s) du RFC : J. Touch, C. Heard
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tsvwg
Première rédaction de cet article le 5 décembre 2025


Des protocoles de transport, comme TCP, ont le concept d'options, ajoutées à l'en-tête et permettant de régler divers paramètres. Ce RFC ajoute ce concept à UDP et standardise quelques options. Désormais, il y a des moyens standards de faire du « ping » en UDP.

Mais comment ajouter des options à un vénérable protocole (UDP a été normalisé en 1980, dans le RFC 768), qui n'a pas de place pour cela dans son en-tête très minimal ? L'astuce est que UDP indique une taille de paquet qui peut être différente de celle indiquée par IP. Si elle est supérieure, les octets ainsi disponibles peuvent stocker des options, à la fin du paquet. Ce sera donc un pied (trailer) et pas un en-tête.

Voici un paquet UDP simplifié (j'ai aussi mis une partie de l'en-tête IP) analysé par tshark :

Internet Protocol Version 6, Src: 2a04:cec0:10fc:5bd8:efd1:79bb:7356:4583, Dst: 2001:db8::1
    0110 .... = Version: 6
…
    Payload Length: 56
    Next Header: UDP (17)
    Hop Limit: 64
    Source Address: 2a04:cec0:10fc:5bd8:efd1:79bb:7356:4583
    Destination Address: 2001:db8::1
User Datagram Protocol, Src Port: 57560 (57560), Dst Port: domain (53)
    Source Port: 57560 (57560)
    Destination Port: domain (53)
    Length: 56
…
    UDP payload (48 bytes)
  

Vous avez vu ? Il y a deux champs qui indiquent la longueur du paquet UDP, un dans l'en-tête IP (Payload length) et un dans l'en-tête UDP (Length). Ici, les deux sont identiques, ce qui est le cas « normal ». Mais s'ils sont différents ? C'est le point de départ de ce RFC. Les options se nicheront dans la différence entre la longueur du paquet UDP et celle du paquet IP qui l'encapsule, dans une zone connue sous le nom de surplus (surplus area). Normalement, elles seront ainsi ignorées de tous les vieux programmes (mais il y a parfois eu des bogues).

Comme TCP (RFC 9293), les protocoles de transport SCTP (RFC 9260) et DCCP (RFC 4340) ont dans leur en-tête un espace réservé pour d'éventuelles options. Pas UDP. C'est ce manque que comble notre RFC. Comme UDP est un protocole sans connexion, ces options ne serviront que pour un seul paquet (alors que TCP peut les garder pour toute la connexion). Des options, dans les autres protocoles de transport, il y en a plein. Pour TCP, le RFC cite par exemple, la dilatation de fenêtres (RFC 7323), l'authentification (RFC 5925) et d'autres. On en trouve pour des protocoles avec état (comme TCP, où elles vont s'appliquer à toute la connexion) ou sans état (comme IP, où elles ne s'appliquent qu'au paquet qui les porte). Comme vu plus haut, seul UDP (RFC 768) n'avait pas le moyen d'ajouter des options.

(Notez que TeredoRFC 4380 - utilisait déjà un truc analogue.)

À quoi vont servir ces options (section 5 du RFC) ? À court terme, à permettre des mécanismes de fragmentation, de test (« ping UDP »), etc. À plus long terme (mais ce n'est pas encore normalisé), elles rendront possible d'ajouter de l'authentification ou du chiffrement à UDP (DTLS, RFC 9147 chiffre mais ne protège pas l'en-tête de la couche Transport). On pourra aussi faire de la découverte de la MTU du chemin, comme spécifié dans le RFC 9869. Et enfin, cela pourra permettre d'augmenter la taille des paquets DNS comme proposé dans draft-heard-dnsop-udp-opt-large-dns-responses.

La section 6 du RFC spécifie le cahier des charges de ces options UDP :

  • UDP est sans état (sans connexion) et doit le rester.
  • UDP est unidirectionnel et doit le rester. Les protocoles requête/réponse (comme le DNS) sont bâtis sur UDP mais doivent s'occuper de faire correspondre requêtes et réponses sans l'aide d'UDP.
  • Les options UDP n'ont pas de taille maximale (à part celle du paquet UDP lui-même, 65 536 octets).
  • Les options UDP ne visent pas à remplacer des protocoles existants. Le « ping UDP » ne cherche pas à être équivalent au ping utilisant ICMP. Après tout, UDP est fait pour être minimal, et il doit le rester.
  • Les options UDP ne constituent pas un protocole à elles seules : un protocole situé au-dessus d'UDP va devoir spécifier bien des détails (par exemple le RFC 9869 qui décrit le protocole PLPMTUD).
  • Tout le mécanisme est conçu pour qu'un logiciel ancien, qui ne connaisse pas ces options, fonctionne comme aujourd'hui.

Un peu de terminologie avant d'attaquer les détails, notamment (section 3) :

  • Option sûre (SAFE option) : option qui pourra être ignorée par un receveur qui ne la comprend pas. Elle ne modifie pas la compréhension du paquet.
  • Option non sûre (UNSAFE option) : option qui ne doit pas être ignorée, car elle modifie la compréhension du paquet (par exemple car elle indique qu'il est chiffré).

La description technique de l'emplacement des options figure en section 7 du RFC. Le champ Longueur de l'en-tête UDP, qui a toujours été redondant avec celui d'IP, peut désormais prendre une valeur inférieure à celle du champ dans IP (mais supérieure à huit, la taille de l'en-tête UDP). La différence entre les deux longueurs est la taille du surplus, la zone à la fin du paquet où se logent les options (qui ne sont donc pas dans l'en-être mais dans le pied). Notez qu'il n'y a pas d'obligation que le surplus soit aligné sur des multiples (2 ou 4) d'octets. Si les longueurs IP et UDP sont identiques, c'est qu'il n'y a pas d'options.

La structuration du surplus en options est dans la section 8. Le surplus commence avec un champ de deux octets qui est une somme de contrôle. Cet OCS (Option CheckSum) est une somme de contrôle Internet traditionnelle (RFC 791, section 3.1, et RFC 1071). Pourquoi cette somme de contrôle alors qu'UDP en a déjà une ? Pour détecter les cas où un autre mécanisme jouerait avec la différence des champs Longueur d'IP et d'UDP et aurait son propre pied, distinct de celui normalisé dans notre RFC. Simple somme de contrôle, l'OCS ne joue pas un rôle en sécurité, il ne sert qu'à vérifier que le surplus est bien utilisé conformément à la norme sur les options UDP. (Les options sont ignorées si l'OCS n'est pas correcte, voir la section 14.) La section 18 insiste sur ce point : comme cette distinction entre deux champs Longueur est ancienne, il est possible que certains s'en servaient et il faut donc faire face à la possibilité que des paquets UDP aient des champs Longueur différents sans pour autant avoir des options telles que normalisées ici. L'OCS est donc aussi une sorte de nombre magique mais en plus fort, puisqu'il n'est pas statique.

Comme toujours, il faut prévoir le cas de middleboxes boguées qui calculeraient la somme de contrôle UDP sur tout le paquet IP, au lieu de s'arrêter à la longueur indiquée dans l'en-tête UDP. L'OCS est conçu pour annuler l'effet d'une telle bogue (merveille des sommes de contrôle, qui n'ont pas de dispersion des résultats). La somme de contrôle UDP étant optionnelle (mais c'est évidemment très déconseillé), l'OCS l'est aussi (et peut donc valoir zéro). Rappelez-vous que ce qui marchait avant en UDP doit encore marcher avec les options donc, par défaut, même si l'OCS est incorrect, le paquet doit être transmis aux applications réceptrices.

À propos de middleboxes, le RFC précise aussi (section 16) que les options sont de bout en bout et ne doivent pas être modifiées par les équipements intermédiaires (mais cela risque de rester un vœu pieux). Des tests effectués ont montré que des systèmes d'exploitation courants (Linux, macOS, Windows) n'avaient pas de problème en recevant des paquets UDP contenant des options (section 18). Ils n'envoient à l'application que la partie du paquet indiquée par le champ Longueur d'UDP, et ignorent donc les options, qu'on peut donc envoyer sans craindre de perturber les systèmes pré-existants. Toutefois, au moins un objet connecté (non nommé par le RFC) passe tout le datagramme (y compris, donc, le surplus contenant les options) à l'application. Et au moins un IDS, l'Alcatel-Lucent Brick considère les paquets UDP ayant des options comme des attaques (les IDS sont un des facteurs importants de l'ossification de l'Internet car ils interprètent tout ce qui ne colle pas à leur vision étroite comme une attaque).

Les options suivent la somme de contrôle et la plupart sont encodées selon un classique schéma TLV, comme pour la plupart des options TCP (RFC 9293, section 3.1). Les deux exceptions à cet encodage TLV sont les options NOP (un seul octet, qui vaut un, cette option, comme son nom l'indique, ne sert à rien, à part éventuellement à aligner les données) et EOL (End Of Options, un seul octet, qui vaut zéro, elle indique la fin de la liste d'options). Autrement, les options sont en TLV, le premier octet indiquant le type, la longueur faisant un octet (un mécanisme existe pour des options faisant plus de 255 octets). Les options sûres ont un type de 0 à 191, les autres sont non sûres. Parmi les options sûres enregistrées par notre RFC, outre EOL et NOP, on trouve entre autres (section 11) :

  • APC (Additional Payload Checksum), type 2, qui ajoute une somme de contrôle plus forte (CRC32c, la même que celle utilisée dans iSCSI, RFC 3385 et SCTP).
  • FRAG (Fragmentation), type 3, qui permet une fragmentation au niveau UDP. Peut se combiner avec MDS (Maximum Datagram Size, type 4), qui indique la taille maximale qu'un récepteur peut accepter sans que le datagramme soit fragmenté.
  • REQ (Echo Request, type 6) et RES (Echo Response, type 7) permettent de faire un « ping UDP ». (Plus propre que les trucs abusivement baptisés « ping UDP » comme cet exemple ou celui-ci.)
  • TIME, type 8, permet d'envoyer et de recevoir l'heure, sous forme d'une estampille temporelle de quatre octets. C'est l'analogue de l'option TCP Timestamp (RFC 7323, section 3). UDP ne garantit pas que ces estampilles correspondent à l'heure officielle, juste qu'elles seront monotones (toujours croissantes).
  • AUTH (Authentication), n'est pas réellement spécifiée. Son type, 9, est réservé mais les autres points sont repoussés à un futur RFC, peut-être à partir de l'Internet-Draft draft-touch-tsvwg-udp-auth-opt.

Les options EOL, NOP et quelques autres doivent normalement être reconnues par toutes les mises en œuvres des options UDP (cf. section 14).

Et les options non sûres ? Rappelons que ce sont celles qui modifient l'interprétation du contenu du paquet. On y trouve (section 12 du RFC) entre autres :

  • UCMP (Unsafe Compression), type 192, pour des contenus comprimés.
  • UENC (Unsafe Encryption), type 193, pour des contenus chiffrés.

Ces deux options ne sont pas autrement spécifiées, seul un type est réservé. Le reste devra être précisé dans un RFC ultérieur.

De nouvelles options seront peut-être rajoutées dans le futur au registre des options (section 13 du RFC). La politique IANA (section 26) est « action de normalisation » ou « approbation par l'IESG », donc des procédures assez lourdes (RFC 8126) : il ne sera pas facile d'ajouter des options.

Question mise en œuvre, ce n'est pas forcément trivial à faire soi-même. Pour pouvoir lire et à plus forte raison écrire des options UDP, il faut actuellement court-circuiter UDP et écrire directement au-dessus d'IP (raw sockets) ou pire, dans certains cas, directement sur le réseau. Notre RFC propose des extensions à l'API socket pour lire et écrire les options (avec aussi des variables sysctl) mais je ne connais pas encore de système d'exploitation qui les intègre. Notez que ces API ne permettent pas d'influencer l'ordre des options, et c'est exprès.

Une difficulté supplémentaire vient du fait qu'UDP est sans état, sans connexion. D'une part, les options ne peuvent pas être négociées lors de l'établissement de la connexion (puisqu'il n'y a pas de connexion), d'autre part, elles doivent être répétées dans chaque paquet (puisqu'il n'y a pas d'état). Le RFC précise donc (section 19) qu'il faut ignorer les options qu'on ne connait pas (du moins celles marquées comme sûres). D'autre part il impose qu'on passe les données à l'application même si on ne comprend pas les options. Ah, et aussi, les options UDP sont conçues pour l'unicast essentiellement.

Et leurs conséquences pour la sécurité (section 25 du RFC) ? Fondamentalement, les options UDP ne posent pas plus (ou pas moins…) de problèmes de sécurité que les options TCP, qui sont normalisées depuis longtemps. Comme pour TCP, elles ne sont pas sécurisées par TLS (ici, DTLS, RFC 9147), TLS qui protège uniquement les données applicatives. Si vous voulez les protéger, il faudra attendre la spécification des futures options AUTH (qui fournira à peu près le même service que le AO de TCP, normalisé dans le RFC 5925) et UENC. Ou alors, protégez toute la couche transport avec IPsec (RFC 4301).

Mais il y a un autre danger, dans la façon dont vont être traitées les options UDP par le récepteur. Si celui-ci boucle imprudemment, en attendant juste l'option EOL, le risque de débordement est élevé. Ou si le récepteur alloue de la mémoire en utilisant le champ Longueur d'UDP puis y copie tout le paquet, les options faisant alors déborder son tampon.

Voilà, je n'ai pas tout détaillé, la section 25 est longue, lisez-la.

Un petit point d'histoire (section 4) : pourquoi diable UDP a-t-il ce champ Longueur, alors que celui d'IP suffisait, d'autant plus que l'en-tête UDP est de taille fixe ? Aucun autre protocole de transport ne fait cela (à part peut-être Teredo, RFC 6081). Le RFC note que des historiens de l'Internet ont été consultés mais sans résultat. Les raisons de ce choix (qu'on apprécie aujourd'hui mais qui est resté sans utilité pratique pendant 45 ans) restent mystérieuses. Permettre de placer plusieurs paquets UDP dans un seul datagramme IP ? Pour remplir les paquets de manière à ce qu'ils soient alignés sur des multiples d'octets ? Aucune explication ne semble satisfaisante et les archives sont muettes.

Notez aussi que d'autres propositions avaient été faites pour ajouter des options à UDP, comme celle décrite dans l'Internet Draft draft-hildebrand-spud-prototype.

Autre question que vous vous posez peut-être : et UDP-Lite (RFC 3828) ? Lui aussi jouait avec la différence des champs Longueur d'IP et d'UDP, pour indiquer la partie du paquet couverte par la somme de contrôle. De ce fait, le mécanisme d'options ne peut pas être utilisé pour UDP-Lite (section 17).

Et les mises en œuvre concrètes ? Il n'existe pas, à ma connaissance, de serveurs de test publics sur l'Internet. Mais vous avez une liste des programmes existants (je n'en ai pas vu en Python utilisant Scapy, ce qui serait pourtant une expérience intéressante).

D'autres lectures sur ces options UDP ? Vous avez l'article de Raffaele Zullo et les supports d'un de ses exposés, qui expliquent bien la question. Et, sinon ChatGPT, consulté s'en ne s'en est pas trop mal tiré, inventant un mécanisme qui ressemble à celui de TCP.


Téléchargez le RFC 9868


L'article seul

Fletcher, l'outil de dessin de schémas de Typst

Première rédaction de cet article le 1 décembre 2025


Vous le savez, j'ai expérimenté récemment l'outil de formatage de textes Typst. Un de ses intérêts est de disposer d'un langage pour réaliser des schémas, nommé Fletcher. Voici quelques points intéressants de Fletcher.

Fletcher est sur le même créneau que des systèmes comme Asymptote (que j'utilise jusqu'à présent), Graphviz ou TikZ : un langage pour décrire les schémas techniques, qui évite de passer du temps avec la souris en essayant de placer ses éléments de schémas au bon endroit. Fletcher est écrit avec le langage de programmation de Typst, et s'appuie sur le système CeTZ.

Sans plus tarder, un exemple de code en Fletcher :

#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge

#diagram(
  {
    node([Un], stroke: 1pt)
    node((rel: (1, 1)), [Deux], stroke: 1pt)
    node((rel: (-1, 1)), [Trois], stroke: 1pt)
    edge(auto, auto, [Par ici], "->")
  }
)
  

Compilé avec typst compile --format png simple-fletcher-1.typ, il donne : simple-fletcher-1.png

Voyons les détails :

  • Il faut importer le paquetage Fletcher (il sera téléchargé la première fois qu'on compile).
  • On dessine trois boites ; par défaut, la bordure est de taille nulle donc on demande une bordure d'un point.
  • La première boite est, par défaut, placée à l'origine des coordonnées, les deux suivantes sont placées relativement à la boite précédente (l'axe des X va vers la droite, celui des Y vers le bas, ce qui est un peu déconcertant mais c'est documenté).
  • On termine avec une flèche, sans s'embêter à indiquer où elle s'accroche donc elle est mise sur le dernier élément dessiné. Notez la façon d'indiquer la forme de la flèche, avec un exemple sous forme d'une chaine de caractères.

Maintenant, si on veut davantage de contrôle sur le placement, on va donner des noms aux objets (et puis on met des valeurs par défaut pour les boites) :


#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge

#diagram(
 // Valeurs par défaut pour les nœuds
 node-stroke: 2pt,
 node-shape: fletcher.shapes.house,
 {
    node([Un], name: <un>)
    node((rel: (1, 1)), [Deux], name: <deux>)
    node((rel: (-1, 1), to: <un>), [Trois], name: <trois>)
    node((rel: (3, 4), to: <deux>), [Quatre], name: <quatre>) 
    edge(<un>, <deux>, [Par ici], "->")
  }
  )

  

Et cela donne : simple-fletcher-2.png

Vous noterez, dans le deuxième essai :

  • La boite Trois est désormais relative à la boite Un, pas à la dernière boite placée.
  • L'image s'agrandit automatiquement (regardez la boite Quatre, qui est placée assez loin).
  • La flèche est explicitement accrochée à deux des boites, et placée automatiquement au bon endroit.

Pour un exemple plus réaliste, je vous ai mis un schéma de la résolution DNS et un autre de l'arborescence des noms de domaine, avec le PDF (ce que produit Typst par défaut) et le source : fletcher-resolution-dns.pdf, fletcher-resolution-dns.typ, fletcher-arbre-domaines.pdf et fletcher-arbre-domaines.typ. La combinaison de coordonnées relatives, et de flèches qu'on accroche à des boites, sans se préoccuper de donner des coordonnées, est très agréable.

Voilà, bien sûr, Fletcher est beaucoup plus riche que cela et vous pouvez faire bien d'autres choses, n'hésitez pas à lire la très complète documentation.


L'article seul

Fiche de lecture : Un pacte avec le diable

Auteur(s) du livre : Michel Tedoldi
Éditeur : Espaces Libres
978-2-226-49957-8
Publié en 2023
Première rédaction de cet article le 1 décembre 2025


Après la Seconde Guerre mondiale, la France a récupéré un certain nombre de scientifiques et d'ingénieurs nazis, notamment pour travailler dans l'aéronautique et le spatial. Un phénomène bien moins connu que la récupération analogue faite par les Étatsuniens.

Car tout le monde sait que von Braun et ses complices ont échappé à toute poursuite après la guerre car les États-Unis avaient besoin d'eux pour construire des missiles, domaine où l'Allemagne était très en avance. L'Union Soviétique a fait pareil, quoique plus discrètement. Et la France, qui, ayant été occupée des années par les nazis, aurait pu hésiter à les embaucher ? Elle l'a fait aussi. Ce petit livre résume ce qu'on sait sur ce recrutement de nazis après 1945. Qui étaient-ils ? Combien ? Que sont-ils devenus ? (Ils sont restés à travailler en France et y sont morts tranquillement.)

Un petit mot sur ma famille, à ce sujet : mon père avait travaillé à Vernon, haut lieu de l'aérospatial français, à l'époque où il y avait encore pas mal d'ex-nazis qui y étaient présents. Ils ne se cachaient pas spécialement, et tout le monde savait donc que la France avait recruté des Allemands. Mais l'affaire a été plutôt refoulée de la mémoire collective. L'un des ingénieurs a même eu une rue à son nom, débaptisée il y a seulement un an.

Même le PCF avait bruyamment approuvé cette embauche, le stalinien Jacques Duclos déclarant dans un discours public à Decize « Ceux qui critiquent l'installation des Allemands à Decize ne pourraient être que des collaborateurs. »

Bref, ce livre est un ajout utile à l'histoire de France.


L'article seul

Typst, un compilateur de texte

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


Au Capitole du Libre de novembre 2025, j'ai suivi l'exposé de Patrick Massot sur Typst, un concurrent possible de LaTeX. Je ne suis pas un expert de Typst, je n'ai pas encore écrit de vrai texte avec donc ce qui suit est un résumé de l'exposé, agrémenté de quelques essais.

Typst est un compilateur de texte, un système où vous tapez du texte et du formatage avec un éditeur quelconque, texte qui sera ensuite traduit dans des formats de distribution comme PDF. Les avantages de ces compilateurs (ou formatteurs), comme noté par Patrick Massot, sont la reproductibilité, la possibilité de choisir librement son éditeur, la flexibilité, et la possibilité d'automatiser la production de documents. Typst est donc une alternative à LaTeX ou à Pandoc+Markdown. C'est évidemment un logiciel libre, sinon on n'en parlerait pas à Capitole du Libre. Il se veut plus perfectionné que Pandoc+Markdown (par exemple pour des documents complexes avec de la mathématique) et plus simple à utiliser que LaTeX, avec une syntaxe moins tordue (pas difficile) et des messages d'erreur plus clairs (pas difficile non plus).

Voici un très simple document (tant que ça reste aussi simple, la syntaxe ressemble vaguement à Markdown) :

= J'essaie Typst

Ça marche. Je peux *insister* ou faire des listes :

+ liberté
+ égalité
+ fraternité

== Des maths

$E = m c^2$

== Du texte plus long

#text(red)[
On peut colorier.
]

J'ai pas envie de tout taper. #lorem(100)
  

On le compile :

% typst compile essai.typ
  

Et on obtient un PDF.

Comme promis, les messages d'erreur sont clairs et précis, grosse différence avec LaTeX :

% typst compile essai.typ
error: unclosed delimiter
   ┌─ essai.typ:15:9
   │
15 │ #set text(
   │          ^
  

Si on veut de l'HTML (option actuellement présentée comme expérimentale et, comme vous le verrez dans l'exemple suivant, la mathématique n'est pas traitée) :

% typst compile --features html --format html essai.typ
warning: html export is under active development and incomplete
 = hint: its behaviour may change at any time
 = hint: do not rely on this feature for production use cases
 = hint: see https://github.com/typst/typst/issues/5512 for more information

warning: equation was ignored during HTML export
   ┌─ essai.typ:11:0
   │
11 │ $e = m c^2$
   │ ^^^^^^^^^^^
  

Aucun CSS n'est produit, le texte en rouge n'est donc pas colorié. Bref, la production de HTML est à l'heure actuelle très limitée. Pour cela, Pandoc est une meilleure solution, car il sait lire le Typst, même les formules mathématiques :

% pandoc -i essai.typ -o essai.html
  

Typst dispose d'innombrables autres possibilités, lisez la documentation pour en avoir une idée. On peut créer ses propres fonctions (Typst inclut un langage de programmation complet), définir des gabarits, etc. Il existe déjà plein de paquetages rigolos pour Typst sur le dépôt officiel, notamment pour l'écriture d'articles techniques et scientifiques. Voyons un exemple avec le paquetage atomic :

// (Ceci est un commentaire.) https://typst.app/universe/package/atomic
#import "@preview/atomic:1.0.0"

= Un essai d'un paquetage Typst

*atomic* permet de dessiner des atomes, avec leurs électrons. Ici, le plutonium.

// Cf. Article Wikipédia « Électrons par niveau d’énergie 	2, 8, 18, 32, 24, 8, 2 »
#atomic.atom(94,239, "Pu", (2, 8, 18, 32, 24, 8, 2))
  

(Attention, à la première utilisation, il faut être connecté à l'Internet, le paquetage étant téléchargé.) Compilé, cela donnera ce joli PDF.

De la même façon, vous pouvez faire des supports de présentation, avec touying qui est donc un concurrent de Beamer :

#import "@preview/touying:0.6.1": *
#import themes.simple: *
#show: simple-theme

= Mon exposé

Le sujet de l'exposé

== Première diapo

Je mets du texte.

== Deuxième diapo

Je continue.

Et encore.
  

Ah, et comment ai-je installé Typst ? C'est bien documenté. Il n'y a apparemment pas de paquetage de Typst existant, pour aucun système, mais Typst fournit des binaires (déjà compilés). Comme je préfère vérifier que les sources compilent, je l'ai compilé moi-même :

% cargo install --locked typst-cli

Typst est écrit en Rust et compiler du Rust est souvent une expérience frustrante. Sur un Ubuntu stable :

error: cannot install package `typst-cli 0.14.0`, it requires rustc 1.88 or newer, while the currently active rustc version is 1.75.0
`typst-cli 0.11.1` supports rustc 1.74

Eh oui, comme trop souvent avec Rust, il faut la toute, vraiment toute dernière version du compilateur, le langage changeant tout le temps. (J'ai donc fait mes tests sur une Debian unstable.)

Est-ce que je vais utiliser Typst ? Je ne sais pas encore. Le logiciel est déjà très utilisable, en tout cas sur un système d'exploitation très récent. Je vais faire les supports de ma présentation au prochain SplinterCon en Typst, ce sera une occasion de tester. L'une de mes inquiétudes est la gestion du projet. Typst est à 100 % du logiciel libre (licence Apache) mais son développement est apparemment étroitement contrôlé par une seule entreprise, qui commercialise des extensions privatrices à Typst et dont il n'est pas certain qu'ils acceptent d'ouvrir le projet.

Quelques autres ressources :

  • Le tutoriel officiel est très bien.
  • Typst est bien documenté.
  • Les exemples utilisés dans la présentation de Patrick Massot : git clone https://codeberg.org/pmassot/typst_cdl2025.

L'article seul

Capitole du Libre 2025 (et un exposé sur les types de média)

Première rédaction de cet article le 19 novembre 2025
Dernière mise à jour le 19 décembre 2025


Les 15 et 16 novembre s'est tenue à Toulouse Capitole du Libre, conférence consacrée aux logiciels libres mais pas que. Quelques notes suivent, entre autres sur l'exposé que j'ai fait sur les types de média (comme text/html pour ce blog).

Le poète toulousain Louis-Catherine Vestrepain, dont la statue n'est pas trop éloignée du lieu où se tient Capitole du Libre : louis-catherine-vestrepain.jpg

Capitole du Libre, c'est gros, avec beaucoup de monde, et un programme chargé (93 exposés, 1 400 participants, 10 kg de café, 500 repas aux camions de nourriture, 10 mètres carrés de pizzas pour les bénévoles). Je ne vais donc pas parler de tout. Déjà, j'ai suivi un exposé et un atelier qui font l'objet d'articles séparés :

  • L'exposé sur le compilateur de texte Typst, un concurrent de LaTeX,
  • L'atelier (donc, avec de la pratique) sur le mécanisme eBPF, qui permet de développer plus facilement des programmes dans le noyau Linux, ici un programme qui intercepte et autorise (ou pas) les requêtes DNS.

Autres moments passionnants :

  • L'exposé de Natouille sur « C’est quoi l’UX design et pourquoi le libre aurait à y gagner en intégrant ces profils ? », où elle rappelait que la conception d'UX, ce n'est pas juste pour les cliquodromes et que tout logiciel peut avoir intérêt à avoir une démarche UX, et, entre autres, que les développeureuses devraient parler avec leurs utilisateurices et les observer. « Le design, c'est faire des produits utilisables, limiter frictions, risques et déceptions » et « Il y a utile, utilisable et utilisé. Instagram est utilisé, le site des impôts est utile, un site / logiciel bien conçu est utilisable ».
  • La présentation par Bookynette de l'escape game (en cartes) de l'April pour faire connaitre le logiciel libre.
  • L'exposé sur Ceph m'a plutôt convaincu que cette solution de stockage réparti n'était pas pour moi, pas parce que le logiciel n'est pas bon mais parce qu'il traite un problème bien précis qui n'est pas forcément le mien : avoir des données qui soient toujours disponibles, en les stockant dans des endroits bien distincts. Chaque fichier confié à Ceph est divisé en objets et chaque objet se retrouve sur plusieurs disques de plusieurs serveurs. L'interface de Ceph peut être du S3, un système de fichiers accessible comme un partage réseau ou même un block device. Ceph en lui-même ne remplace pas les sauvegardes. On peut toutefois utiliser des snapshots qui, à condition qu'ils ne soient pas accessibles aux clients, protègent contre erreurs humaines et rançongiciels.
  • La présentation de Skeptikon « Le logiciel libre est un projet politique » insistait sur la lutte contre les mensonges et le discours anti-scientifique. Dans le contexte de la montée de l'extrême-droite, le logiciel libre est une des armes pour préserver sa liberté.
  • Et puis, il n'a pas été présenté à Capitole du Libre mais saviez-vous qu'il existe un langage de programmation conçu et maintenu à Toulouse, Smala ?

De mon côté, j'ai fait un exposé sur les types de médias (également appelés types MIME, vous savez, les text/plain, image/png et autres haptics/ivs). Voici les supports et leur source (en LaTeX, pas en Typst) et la vidéo.

Et j'ai participé à la table ronde « Souveraineté numérique : pourquoi le logiciel libre est stratégique », avec Ludovic Dubost et Nicolas Vivant, animée par Aline Paponaud. L'idée (assez consensuelle parmi les participants) était qu'il n'y a pas de vraie souveraineté sans logiciel libre, entre autres parce que remplacer un GAFA étatsunien par un GAFA français qui ferait exactement la même chose n'a pas beaucoup d'intérêt. Le logiciel libre permet la souveraineté de l'utilisateurice, pas seulement celle de l'État.

Photos et vidéos sont disponibles, les photos sur https://photos.capitoledulibre.org/ et les vidéos (via PeerTube, bien sûr) sur https://videos.capitoledulibre.org/.

Ah, et le quotidien la Dépêche a fait un article très louangeur.

Bref, soutenez Capitole du Libre : capitole-du-libre-2025.jpg.


L'article seul

Atelier sur eBPF au Capitole du Libre

Première rédaction de cet article le 19 novembre 2025
Dernière mise à jour le 20 novembre 2025


Au Capitole du Libre de novembre 2025, j'ai participé à un passionnant atelier de Maxime Chevallier et Alexis Lothoré sur la programmation réseau en eBPF. Le but était de mettre en œuvre un bloqueur de publicité (ou autre contenu qu'on ne voulait pas voir), agissant via les requêtes DNS, que le programme va intercepter et analyser.

eBPF désigne à la fois un jeu d'instructions machine, normalisé dans le RFC 9669, et un système permettant de produire des programmes utilisant ce jeu d'instructions, et des les installer dans le noyau Linux. eBPF permet ainsi d'ajouter des fonctions au noyau, sans modifier et recompiler celui-ci (ce qui est vraiment une tâche difficile) et sans redémarrer la machine. Je vous renvoie à mon article sur le RFC 9669 pour plus de détail. Un exemple d'utilisation de eBPF dans le contexte du DNS est dans le serveur NSD.

Le cahier des charges de l'atelier était d'écrire (en C) et de faire tourner un programme eBPF qui va regarder toutes les requêtes DNS de la machine, les comparer à une liste de blocage, et stopper ces requêtes si le nom de domaine est dans cette liste. Si vous avez un peu de temps, je recommande de faire l'exercice vous-même, téléchargez le support de l'atelier (git clone https://github.com/bootlin/ebpf-workshop-cdl), lisez le fichier Readme.md (il est en anglais) et essayez. Cet article que vous lisez est plutôt destiné aux gens qui veulent la solution.

Comme le système eBPF sur Linux n'est pas d'une clarté parfaite, il vaut mieux commencer par un programme trivial. Attention, comme indiqué dans le Readme.md, ça marchera… ou pas, selon la version exacte du système d'exploitation que vous utilisez (j'ai tout fait sur une Ubuntu stable, la version 24.04). Quelques paquetages à installer :

% sudo apt install linux-tools-common build-essential clang libbpf-dev
  

Après cela, si, quand vous essayez de lancer bpftool, vous avez :

% bpftool 
WARNING: bpftool not found for kernel 6.14.0-112033

  You may need to install the following packages for this specific kernel:
    linux-tools-6.14.0-112033-tuxedo
    linux-cloud-tools-6.14.0-112033-tuxedo

  You may also want to install one of the following packages to keep up to date:
    linux-tools-tuxedo
    linux-cloud-tools-tuxedo
  

N'essayez pas d'installer les paquetages cités, ça ne servira sans doute pas. À la place, installez à la main :

% git clone --recurse-submodules https://github.com/libbpf/bpftool.git
% cd src
% make
% sudo make install   
  

Maintenant, écrivons le programme trivial qui ne fait pas grand'chose :


% cat hello.bpf.c

#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include "bpf/bpf_helpers.h"
#include "bpf/bpf_endian.h"

SEC("tc")
int hello(struct __sk_buff *skb)
{
  bpf_printk("Hello, world !"); 
  return TC_ACT_OK;
}

char __license[] SEC("license") = "GPL";

  

Ce programme crée une fonction (qui sera donc exécutée par le noyau), qui sera appelée à chaque paquet réseau et affichera le classique « Bonjour, tout le monde », avant de laisser passer le paquet. Compilons-le en eBPF :

% clang -Wall  -g -O1 -target bpf -c hello.bpf.c -o hello.bpf.o

Pour des raisons que je ne comprends pas bien, les options -g et surtout -On avec n > 0 semblent indispensables. Apparemment (c'est peu documenté), l'optimisation est nécessaire pour retirer du code qui, autrement, serait refusé par le vérificateur. Regardez la documentation « Clang implementation notes » et l'article « Building BPF applications with libbpf-bootstrap » (« -g is mandatory to make Clang emit BTF information. -O2 is also necessary for BPF compilation).

La compilation va produire du code eBPF :

% file hello.bpf.o
hello.bpf.o: ELF 64-bit LSB relocatable, eBPF, version 1 (SYSV), with debug_info, not stripped
  

Le désassembleur par défaut ne connait pas eBPF :

% objdump -d hello.bpf.o 
hello.bpf.o:     file format elf64-little
objdump: can't disassemble for architecture UNKNOWN!
  

Mais il y en a un autre qui marche (suivez le RFC 9669) mais je le trouve moins joli :


% llvm-objdump -d hello.bpf.o     
hello.bpf.o:	file format elf64-bpf

Disassembly of section tc:

0000000000000000 <hello>:
       0:	18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00	r1 = 0x0 ll
       2:	b7 02 00 00 0f 00 00 00	r2 = 0xf
       3:	85 00 00 00 06 00 00 00	call 0x6
       4:	b7 00 00 00 00 00 00 00	r0 = 0x0
       5:	95 00 00 00 00 00 00 00	exit

  

Bon, ce n'est pas tout de produire un binaire eBPF, il faut le charger dans le noyau, et dire au noyau de l'exécuter pour chaque paquet sortant. Il existe plusieurs méthodes pour cela (on peut aussi écrire son propre programme qui fait le chargement), on va utiliser tc, et attacher le code eBPF à l'interface réseau active, ici wlp1s0 :

% sudo tc qdisc add dev wlp1s0 clsact
% sudo tc filter add dev wlp1s0 egress bpf direct-action object-file  hello.bpf.o sec tc
  

(La syntaxe de tc est merveilleuse. Sur les noyaux Linux plus récents bpftool prog load fichier.o chemin suivi de bpftool net attach tcx_egress pinned chemin dev interface devrait pouvoir charger puis attacher des programmes, sans utiliser tc.) Une fois que tout cela est fait sans erreur, on peut afficher les messages :

%  sudo bpftool prog tracelog
 irq/80-iwlwifi:-928     [011] b.s3. 200018.139564: bpf_trace_printk: Hello, world !
 irq/81-iwlwifi:-929     [008] b.s3. 200018.139564: bpf_trace_printk: Hello, world !
   Socket Thread-576739  [000] b..1. 200018.139832: bpf_trace_printk: Hello, world !
   Socket Thread-576739  [000] b..1. 200018.139976: bpf_trace_printk: Hello, world !
…
  

Et voilà, à chaque paquet sortant (la directive egress dans l'appel à tc), on a un message affiché.

Comme eBPF inclut un vérificateur qui examine le code avant de l'exécuter (pour éviter qu'un programme eBPF bogué ne plante tout le noyau), des erreurs apparemment innocentes (comme d'aller visiter de la mémoire en dehors de nos variables) va se traduire par des erreurs au chargement, assez difficiles à déboguer :

libbpf: prog 'dns_filter': BPF program load failed: Permission denied
libbpf: prog 'dns_filter': -- BEGIN PROG LOAD LOG --
0: R1=ctx() R10=fp0
; int             dns_filter(struct __sk_buff *skb) @ dns-filter.bpf.c:107
0: (bf) r7 = r1                       ; R1=ctx() R7_w=ctx()
1: (b7) r6 = 0                        ; R6_w=0
...
124: (bf) r3 = r1                     ; R1_w=scalar(id=253) R3_w=scalar(id=253)
125: (0f) r3 += r2                    ; R2_w=0 R3_w=scalar(id=253+0)
126: (71) r3 = *(u8 *)(r3 +0)
R3 invalid mem access 'scalar'
processed 9460 insns (limit 1000000) max_states_per_insn 4 total_states 129 peak_states 129 mark_read 128
-- END PROG LOAD LOG --
libbpf: prog 'dns_filter': failed to load: -13
libbpf: failed to load object 'dns-filter.bpf.o'
Unable to load program
  

Si vous voyez cela, dites-vous que votre programme doit être trop laxiste et, pour reprendre une expression utilisée par les animateurs de l'atelier, qu'il jardine en dehors de ses plate-bandes.

Maintenant, revenons au vrai projet. L'atelier était prévu pour une découverte progressive, et c'est ce que vous ferez si vous suivez le support de l'atelier mentionné plus tôt (git clone https://github.com/bootlin/ebpf-workshop-cdl). Ici, je vais simplement présenter le résultat final. Mon code complet (légèrement différent de celui qui figure en correction dans les documents de l'atelier) est dns-filter.bpf.c. Je ne vais mentionner ici que le plus important, des commentaires dans le source éclairent d'autres points.

La liste des domaines bloqués est un dictionnaire, structure de données fournie par eBPF pour communiquer entre le programme dans le noyau, et l'extérieur :

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 2);
    __type(key, int);
                    __type(value, char[253]);
} blocklist     SEC(".maps");
  

On va utiliser beaucoup de structures de données TCP/IP dont la description est déjà fournie par Linux (comme struct udphdr pour un en-tête UDP). Il n'y en a apparemment pas pour le DNS, donc on la crée :

struct dnshdr {
    __be16          trans_id;
    __be16          flags;
    __be16          nr_quest;
    __be16          nr_answ;
    __be16          nr_auth_rr;
    __be16          add_rr;
    };
  

Pour l'analyse des paquets, on va charger les octets dans nos structures de données, à partir d'un index (la variable offset) avec la fonction BPF bpf_skb_load_bytes. Voici les étapes :


/* Ce paquet est-il un paquet IP ? Chargeons-le dans ethdhr avant de
regarder son champ Protocol. */	     
ret = bpf_skb_load_bytes(skb, offset, &ethhdr, sizeof(ethhdr));
if (ret) {
    return TC_ACT_OK;
}
if ((ethhdr.h_proto != __bpf_constant_htons(ETH_P_IPV6)) &&
        (ethhdr.h_proto != __bpf_constant_htons(ETH_P_IP))) {
        return TC_ACT_OK;
} 

  

Si une fonction échoue (j'ai omis ce test par la suite mais dans le vrai code, il faut le mettre, rappelez-vous que le vérificateur vous surveille), ou bien si le paquet n'est pas de l'IP, on le laisse passer (on renvoie OK à tc). Ensuite, après avoir sauté l'en-tête IP (attention, il n'a pas la même taille en IPv4 et IPv6), on regarde si c'est bien de l'UDP, et s'il utilise bien le port 53 du DNS :


if (l4_proto != IPPROTO_UDP) {
        return TC_ACT_OK;
}
ret = bpf_skb_load_bytes(skb, offset, &udph, sizeof(udph));
if (__bpf_constant_ntohs(udph.dest) != 53) {
        return TC_ACT_OK;
}

  

On va ensuite récupérer le nom de domaine demandé (attention à l'analyse d'un paquet DNS, lisez bien le RFC 9267) :


offset += sizeof(dnsh);
ret = parse_query(skb, offset, query, 253);
ctx.query = query;

  

On va ensuite tester ce nom query pour voir s'il est dans la liste de blocage, avec bpf_for_each_map_elem. Notez le code de retour TC_ACT_SHOT, qui dit à tc de jeter le paquet sans autre forme de procès.

    
bpf_for_each_map_elem(&blocklist, dns_check, &ctx, 0);
if (ctx.match) {
        bpf_printk("*REJECTED* DNS query of %s", query);
        return TC_ACT_SHOT;
}
bpf_printk("Accepted DNS query of %s", query);
return TC_ACT_OK;

  

Reste un petit détail : il faut peupler cette liste de blocage (la variable blocklist, de type dictionnaire). Une méthode possible est d'utiliser la commande bpftool mais, évidemment, en vrai, on écrirait un programme avec une interface utilisateur plus agréable :

% sudo bpftool map update name blocklist key 0 0 0 0 value $(printf '%-253s' x.com | tr ' ' '\0' | xxd -i -c 253| tr -d ,)
  

Armé de tout cela, on peut utiliser cette suite de commandes pour compiler le programme, attacher l'eBPF via tc, remplir la liste de blocage et regarder le résultat :

#!/bin/sh

sudo tc qdisc del dev wlp1s0 clsact
sudo tc qdisc add dev wlp1s0 clsact
clang -Wall -g -O1 -target bpf -c dns-filter.bpf.c -o dns-filter.bpf.o
sudo tc filter add dev wlp1s0 egress bpf direct-action object-file dns-filter.bpf.o sec tc
sudo bpftool map update name blocklist key 0 0 0 0 value $(printf '%-253s' x.com | tr ' ' '\0' | xxd -i -c 253| tr -d ,)
sudo bpftool map update name blocklist key 1 0 0 0 value $(printf '%-253s' facebook.com | tr ' ' '\0' | xxd -i -c 253| tr -d ,)
sudo bpftool prog tracelog 
  

Testons-le :

% ping facebook.com
ping: facebook.com: Temporary failure in name resolution

% ping bootlin.com  
PING bootlin.com (87.98.181.233) 56(84) bytes of data.
64 bytes from bootlin.com (87.98.181.233): icmp_seq=1 ttl=49 time=15.8 ms
  

Et, affiché par bpftool :

systemd-resolve-1112    [000] b..1. 208052.900167: bpf_trace_printk: *REJECTED* DNS query of facebook.com
systemd-resolve-1112    [000] b..1. 208052.900183: bpf_trace_printk: *REJECTED* DNS query of facebook.com
systemd-resolve-1112    [005] b..1. 208264.967434: bpf_trace_printk: Accepted DNS query of bootlin.com
  

Objectif atteint. On peut aller boire une bière. Notez que vous pouvez afficher la liste de blocage :

% sudo bpftool map dump name blocklist 
[{
        "key": 0,
        "value": "x.com"
    },{
        "key": 1,
        "value": "facebook.com"
    }
]
  

Quelques détails toutefois. D'abord, le programme de filtrage étant sommaire, il ne bloque que le nom exact dans la liste, pas ses sous-domaines :


 % dig facebook.com
;; communications error to 127.0.0.53#53: timed out
…
;; no servers could be reached

% dig www.facebook.com
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54041
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
…
;; ANSWER SECTION:
www.facebook.com.	2724 IN	CNAME star-mini.c10r.facebook.com.
star-mini.c10r.facebook.com. 29	IN A 157.240.253.35

;; Query time: 22 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Mon Nov 17 16:08:00 CET 2025
;; MSG SIZE  rcvd: 90

  

Corriger cette limite est laissé au lecteur ou à la lectrice (notez toutefois que dans l'atelier, il y avait environ vingt participants et pas de participante).

Ensuite, puisque tc va purement et simplement jeter le paquet, le client DNS n'aura aucune information, réessaiera et finira par laisser tomber mais au bout d'un délai qui est certainement pénible pour l'utilisateur. Il vaudrait mieux fabriquer une réponse mensongère, genre NXDOMAIN. Je ne suis pas sûr que cela soit facilement faisable avec tc mais il existe d'autres façons d'exécuter de l'eBPF donc le problème est certainement soluble.

Enfin, le DNS ne marche pas que sur UDP, il fonctionne aussi sur TCP (RFC 7766). Le programme ci-dessus peut donc être facilement contourné :


% dig @9.9.9.9 facebook.com
;; communications error to 9.9.9.9#53: timed out
…;; no servers could be reached

% dig +tcp @9.9.9.9 facebook.com
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24690
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
…
;; ANSWER SECTION:
facebook.com.		30 IN A	57.144.222.1

;; Query time: 7 msec
;; SERVER: 9.9.9.9#53(9.9.9.9) (TCP)
;; WHEN: Mon Nov 17 16:16:59 CET 2025
;; MSG SIZE  rcvd: 63
    
  

Là encore, si vous voulez perfectionner ce programme, n'hésitez pas (mais c'est plus difficile, une requête DNS peut se retrouver répartie dans deux paquets IP différents).

Merci aux organisateurs de cet excellent atelier, Alexis Lothoré et Maxime Chevallier, et à Quentin Monnet pour un déboguage de cet article.


L'article seul

Cafouillage DNSSEC sur un de mes domaines personnels

Première rédaction de cet article le 6 novembre 2025
Dernière mise à jour le 8 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 (la bibliothèque ldns, voir plus loin). 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 erroné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. Une mise à jour de ldns a apoporté la bogue.

Celle-ci a été signalée, bogue ldns #285 et corrigée mais pas encore forcément déployée partout.

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

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

Articles des différentes années : 2026  2025  2024  2023  2022  2021  2020  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.