Les RFC (Request For Comments) sont les documents de référence de l'Internet. Produits par l'IETF pour la plupart, ils spécifient des normes, documentent des expériences, exposent des projets...
Leur gratuité et leur libre distribution ont joué un grand rôle dans le succès de l'Internet, notamment par rapport aux protocoles OSI de l'ISO organisation très fermée et dont les normes coûtent cher.
Je ne tente pas ici de traduire les RFC en français (un projet pour cela existe mais je n'y participe pas, considérant que c'est une mauvaise idée), mais simplement, grâce à une courte introduction en français, de donner envie de lire ces excellents documents. (Au passage, si vous les voulez présentés en italien...)
Le public visé n'est pas le gourou mais l'honnête ingénieur ou l'étudiant.
Date de publication du RFC : Janvier 2025
Auteur(s) du RFC : P. Hoffman (ICANN), H. Flanagan
(Spherical Cow Consulting)
Pour information
Première rédaction de cet article le 18 janvier 2025
Vous le savez, les RFC ne sont plus en texte brut depuis des années, leur format officiel est du XML. Le système était décrit dans le RFC 7990, que ce nouveau RFC remplace. Il y a beaucoup de changements sur la forme et aussi sur le fond, notamment la possibilité de modifier un fichier déjà publié (sans changer sa sémantique, bien sûr).
La section 2 de notre RFC décrit ce qu'est la « version définitive » d'un RFC. Écrite dans le format RFCXML spécifié dans le RFC 7991, elle peut donc comporter des images en SVG (voir le RFC 7996) et des caractères non-ASCII (cf. RFC 7997). Cette version définitive contient tout ce qui est nécessaire pour produire le RFC publié. (Les versions de développement, avant la publication, peuvent utiliser des références externes, qui devront être résolues dans la version définitive, qui sera auto-suffisante.)
Mais le changement le plus spectaculaire de notre RFC 9720 est que cette version « définitive » peut désormais changer. Si on découvre un problème dans le fichier XML, il pourra être modifié, si cela ne change pas le sens, bien sûr. La section 2.1 décrit précisément les règles de changement. En cas de modification, les versions antérieures devront être gardées, avec leurs métadonnées (comme la date) et accessibles publiquement, comme les RFC l'ont toujours été (section 4), ce qui a beaucoup contribué au succès de l'Internet.
De nouvelles versions de publication, produites à partir de la version définitive, seront produites, soit parce que la version définitive a changé, soit parce que les outils ont évolué et permettent un meilleur rendu (section 3). (La re-publication pour corriger des erreurs de rendu existait déjà et la liste des cas est en ligne.)
Notez que ce RFC ne s'applique qu'au produit final. L'élaboration des RFC, via les Internet drafts, n'est pas concernée, même si elle va sans doute s'aligner. (Si vous en écrivez et que vous voulez utiliser le format XML, ce que je recommande, une page Web dédiée est là pour vous aider.)
La section 1.1 résume les changements depuis le RFC 7990. On garde le format XML normalisé dans le RFC 7991 mais on change le concept de « format canonique », qui mélangeait plusieurs choses. Il y a désormais :
Notre RFC spécifie dans quelles conditions on peut désormais changer la version finale (contrairement à la règle du RFC 9280). En gros, il faut que la sémantique reste inchangée. Cela permet d'effectuer les changements suite à des erreurs de format XML ou bien lorsqu'une évolution des logiciels ne permet plus de traiter l'ancien fichier. Mais cela pose peut-être des problèmes de sécurité et la section 6 appelle à la vigilance face au risque d'utiliser une version non-officielle.
Et, sinon, le RFC Editor » devient le « RFC Production Center » (mais je parie que l'ancien terme, chargé d'histoire, va rester encore longtemps).
On sait que le premier RFC publié sous le nouveau système (format de référence en XML) était le RFC 8651. Depuis, tous les autres RFC ont suivi ce système.
Date de publication du RFC : Janvier 2025
Auteur(s) du RFC : J. Abley (Cloudflare), J. Schlyter
(Kirei AB), G. Bailey
(Independent), P. Hoffman (ICANN)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 25 janvier 2025
Le mécanisme d'authentification des informations DNS nommé DNSSEC repose sur la même structure arborescente que le DNS : une zone publie un lien sécurisé vers les clés de ses sous-zones. Un résolveur DNS validant n'a donc besoin, dans la plupart des cas, que d'une seule clé publique, celle de la racine. Elle lui servira à vérifier les clés des TLD, qui serviront à valider les clés des domaines de deuxième niveau et ainsi de suite. Reste donc à configurer la clé de la racine dans le résolveur : c'est évidemment crucial, puisque toute la sécurité du système en dépend. Si un résolveur est configuré avec une clé fausse pour la racine, toute la validation DNSSEC est menacée. Comment est-ce que l'ICANN, qui gère la clé principale de la racine, publie cette clé cruciale ? C'est documenté dans ce RFC qui remplace le RFC 7958, avec plusieurs changements, comme l'abandon des signatures PGP.
Notez que ce nouveau RFC documente l'existant, déjà mis en œuvre, et ne prétend pas décrire la meilleure méthode. Notez aussi que ce format et cette méthode de distribution pourraient encore changer à l'avenir, comme ils l'ont fait depuis la sortie du RFC 7958.
Si vous voulez réviser DNSSEC d'abord, outre les RFC de base sur ce système (RFC 4033, RFC 4034, RFC 4035...), notez surtout le RFC 6781, qui décrit les questions opérationnelles liées au bon fonctionnement de DNSSEC.
Les clés publiques configurées dans les résolveurs qui valident avec DNSSEC, sont appelées « points de départ de la confiance » (trust anchors). Un point de départ de la confiance est une clé dont l'authenticité est admise, et non pas dérivée d'une autre clé, via une chaîne de signatures. Il en faut au moins un, celui de la racine, bien que certains résolveurs en ajoutent parfois deux ou trois pour des zones qu'ils veulent vérifier indépendamment. Lorsque le résolveur recevra une réponse de la racine, signée, il l'authentifiera avec la clé publique de la racine (le point de départ de la confiance). S'il veut vérifier une réponse d'un TLD, il l'authentifiera avec la clé publique du TLD, elle-même signée (et donc authentifiée) par la clé de la racine. Et ainsi de suite même pour les zones les plus profondes.
(Notez qu'il existe deux clés pour la plupart des zones, la KSK - Key Signing Key, et la ZSK - Zone Signing Key, mais on ne s'intéresse ici qu'aux KSK, c'est elles qui sont signées par la zone parente, et configurées comme points de départ de la confiance.)
La gestion de la clé de la racine par l'ICANN (via sa filiale PTI) est décrite dans leur DPS (DNSSEC Practice Statement).
Le RFC rappelle aussi qu'il y a d'autres possibilités d'installation d'un point de départ de la confiance. Par exemple, si un tel point a été configuré une fois, ses remplacements éventuels peuvent être faits via le RFC 5011.
La section 2 du RFC décrit le format des clés publiées par l'IANA. Il s'agit d'un fichier XML contenant les condensats des clés, utilisant le format de présentation du RFC 4034. Leur syntaxe formelle est exprimée en Relax NG, le schéma est en section 2.1 du RFC (copie locale). Une vérification du fichier avec rnv fonctionne :
% rnv dnssec-root-trust-anchors.rnc root-anchors.xml root-anchors.xml
Voici un exemple du fichier XML (à ne pas prendre comme s'il faisait autorité, évidemment) :
<TrustAnchor id="0C05FDD6-422C-4910-8ED6-430ED15E11C2" source="http://data.iana.org/root-anchors/root-anchors.xml"> <Zone>.</Zone> <KeyDigest id="Klajeyz" validFrom="2017-02-02T00:00:00+00:00"> <KeyTag>20326</KeyTag> <Algorithm>8</Algorithm> <DigestType>2</DigestType> <Digest>E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D</Digest> <PublicKey>AwEAAaz/…</PublicKey> <Flags>257</Flags> </KeyDigest> </TrustAnchor>
L'élément <KeyTag>
indique
l'identifiant de la clé, actuellement 20326, comme on peut le
voir avec dig :
% dig +multi +nodnssec . DNSKEY ... ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ... ;; ANSWER SECTION: . 86254 IN DNSKEY 257 3 8 ( AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTO … ) ; KSK; alg = RSASHA256 ; key id = 20326 ...
L'attribut id
de l'élément
<TrustAnchor>
est un UUID. L'attribut
id
de l'élément
<KeyDigest>
sert à identifier un
condensat particulier. Le fichier peut également comporter des
commentaires XML (mais ce n'est pas le cas actuellement). Le fichier
indique aujourd'hui le condensat de la clé et la clé mais cette
dernière est optionnelle (on ne s'en sert pas par la suite).
Pour produire un enregistrement DS à partir de
ce fichier XML, il suffit de mettre
<KeyTag>
,
<Algorithm>
,
<DigestType>
et
<Digest>
bout à bout. Par exemple, avec
le fichier XML ci-dessus, cela donnerait :
. IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D
(Des résolveurs comme Unbound acceptent ce format, pour le point de confiance de départ.)
Ici, je n'ai montré qu'une clé mais il peut y en avoir plusieurs,
donc plusieurs <KeyDigest>
. En novembre
2024, le fichier comportait trois clés, l'ancienne 19036, abandonnée
il y a des années, l'actuelle 20326 et la future 38696 (qui n'était pas encore
publiée dans le DNS mais qui l'a été en janvier 2025).
Comment récupérer le fichier XML de
manière à être sûr de son authenticité ? C'est ce que spécifie la
section 3 du RFC : on utilise HTTPS (ou HTTP tout court mais ce n'est pas
forcément une bonne idée et, de toute façon, aujourd'hui, une
politique HSTS le rend difficile). L'URL est
https://data.iana.org/root-anchors/root-anchors.xml
. Si
on fait confiance à l'AC utilisée par
l'ICANN, on peut être sûr de l'authenticité du fichier. (Mais notez,
comme le fait remarquer gregR, que
data.iana.org
et iana.org
n'ont pas d'enregistrement CAA, RFC 8659.)
Une
autre solution est de le récupérer en HTTP et de le vérifier avec la
signature fournie, en CMS (RFC 5652) - son URL est
https://data.iana.org/root-anchors/root-anchors.p7s
. (Non,
je ne sais pas comment la vérifier.)
Bien sûr, un résolveur
validant n'est pas obligé d'utiliser ces points de départ de la
confiance. Cela reste une décision locale. La très grande majorité
des résolveurs utiliseront sans doute plutôt un
paquetage fourni par leur système
d'exploitation, comme dns-root-data
sur Debian.
Pour les amateurs d'histoire, l'annexe B rappelle que la clé 19036 (également appelée KSK-2010) avait été générée au cours d'une cérémonie à Culpeper, le 16 juin 2010. Elle avait été publiée dans le DNS pour la première fois le 15 juillet 2010. La clé actuelle, la 20326 (également nommée KSK-2017), a été également générée à Culpeper le 27 octobre 2016 et publiée le 11 novembre 2018. La liste des cérémonies de création de clés est en ligne.
L'annexe A de notre RFC contient les changements depuis le RFC 7958. Notamment :
Date de publication du RFC : Janvier 2025
Auteur(s) du RFC : T. Li (Juniper Networks)
Pour information
Première rédaction de cet article le 29 janvier 2025
Tout le monde connait aujourd'hui l'utilisation de constellations de satellites en orbite basse pour les télécommunications et, par exemple, pour l'accès à l'Internet. Contrairement à l'ancienne technique de communication via des satellites en orbite géostationnaire, la communication directe entre satellites y joue un rôle important. Les satellites se déplacent les uns par rapport aux autres, changeant en permanence la topologie de ce réseau. Alors, comment se fait le routage ? Ce RFC décrit une méthode possible, reposant largement sur le protocole IS-IS.
Ce n'est pas forcément la technique réellement utilisée. Les constellations existantes, dont la plus connue est Starlink, sont des entreprises privées, très secrètes, et qui ne décrivent pas leur configuration interne. Ce RFC est donc un travail de réflexion, mené à partir des rares informations publiques. Il ne peut pas spécifier une solution complète. Si vous envisagez de créer une constellation, il vous restera du travail pour avoir une solution de routage.
D'abord, il faut voir les particularités de ce réseau de satellites. Il est bien moins fiable qu'un réseau terrestre (l'espace est agité, un satellite en panne n'est pas facile à réparer) et il est tout le temps en mouvement. Ce rythme important de changement est un défi pour les protocoles de routage. Sauf si les satellites sont exactement sur la même orbite, leur mouvement relatif fait qu'on a une possibilité de communiquer à certains moments seulement, voire quasiment jamais. Si deux satellites se déplacent en direction opposée, leur vitesse relative, de l'ordre de dizaines de milliers de km/h, ne permettra guère de communication entre eux.
Par contre, un point qui va faciliter les choses est que ces mouvements, et donc l'établissement et la coupure du lien, sont prévisibles. On peut compter sur Kepler et Newton !
La section 1.2 du RFC rappelle le vocabulaire important. Notons le sigle ISL, pour Inter-Satellite Link, la liaison directe entre satellites, typiquement via un faisceau laser. (Le RFC cite ce très vieil article de Bell, « On the Production and Reproduction of Sound by Light ».) Si vous ne le connaissez pas, il faut aussi apprendre le sigle LEO, pour Low Earth Orbit, désignant les satellites qui orbitent à moins de 2 000 km de la surface de la Terre. On utilisera aussi SR (Segment Routing), la technique du routage par segments, décrite dans le RFC 8402 et SID (Segment IDentifier), qui vient de cette technique. Et IS-IS (Intermediate System to Intermediate System ) ? Ce protocole de routage est normalisé par l'ISO (et donc la spécification n'est normalement pas librement disponible) mais il est aussi décrit dans le RFC 1142 et le RFC 1195 explique comment s'en servir pour IP. Attention : c'est un vieux RFC, datant d'une époque où on pensait encore que les protocoles OSI avaient peut-être un avenir (aujourd'hui, ils n'ont même pas de page Wikipédia). Le RFC 1195 se concentre donc sur la possibilité d'utiliser IS-IS pour router à la fois OSI et TCP/IP. Aujourd'hui, il ne sert plus qu'aux protocoles Internet.
La section 2 de notre RFC rappelle les points essentiels des satellites LEO, qui vont impacter les choix techniques. Quand deux satellites se parlent via un ISL (Inter-Satellite Link), la liaison ne peut avoir qu'une durée limitée, les deux satellites ne restant pas visibles l'un par l'autre éternellement, s'ils sont sur des orbites différentes. Par contre, les périodes de visibilité et de non-visibilité peuvent être calculées à l'avance, on peut compter sur les lois de la physique.
Les constellations peuvent être de grande taille. En 2021, déjà, celle du milliardaire d'extrême-droite représentait un tiers du total des satellites en orbite. Aujourd'hui, des constellations de dizaines de milliers de satellites sont prévues (occupant l'espace public et bloquant la visibilité sans vergogne). Les protocoles de routage traditionnels peuvent gérer des réseaux de grande taille mais à condition qu'ils soient relativement statiques, avec juste une panne ou une addition d'un lien de temps en temps, ce qui n'est pas le cas pour des réseaux de satellites en mouvement. L'IETF a sous la main deux protocoles de routage normalisés pour les réseaux mono-organisation, OSPF (RFC 2328 et RFC 5340) et IS-IS (RFC 1195). (Il existe d'autres protocoles pour ces réseaux mais convenant plutôt à des réseaux plus petits.) Tous les deux sont à état des liaisons et, pour passer à l'échelle, tous les deux permettent de partitionner le réseau en zones (areas) à l'intérieur desquelles l'information de routage est complète, alors qu'entre zones, on ne passe qu'une partie de l'information. Dans OSPF, il y a forcément une zone spéciale, l'épine dorsale (backbone, la zone de numéro 0). Rien que pour cela, le RFC estime qu'OSPF ne serait pas un bon choix : contrairement aux réseaux terrestres, les constellations n'ont rien qui pourrait servir d'épine dorsale. IS-IS repose sur un système différent, où des zones dites « L1 » (de niveau 1) regroupent des parties du réseau qui ne servent pas au transit entre zones, qui est assuré par des zones « L2 ». La machine individuelle peut aussi bien être dans L1 que dans L2 ou même dans les deux. Si on adoptait le système simple de mettre chaque satellite dans une zone L2 (puisque tout satellite peut servir au transit), on perdrait tous les avantages du partitionnement. L'utilisation de IS-IS va donc nécessiter des ajustements.
La section 3 du RFC expose le mécanisme de transmission des paquets choisi. Elle propose d'utiliser MPLS (RFC 3031), en raison de sa souplesse (puisque la topologie change tout le temps), avec le SR (Segment Routing) du RFC 8402 (et RFC 8660 pour son adaptation à MPLS). Dans cette vision, il n'y a pas de transmission au niveau IP et donc un traceroute ne montrerait pas le trajet entre les satellites.
Reste le choix de l'IGP. La section 4 décrit la préférence de l'auteur pour IS-IS. Dans cette hypothèse, tous les satellites seraient des nœuds L1L2 (appartement aux deux types de zones). Le passage à l'échelle serait assuré grâce au système des relais, normalisé dans le RFC 9666. Un réseau de 1 000 zones L1, chacune comportant 1 000 satellites, n'aurait besoin que de mille entrées (et pas un million) dans ses tables de routage, aussi bien L1 que L2. Idem pour la table des étiquettes MPLS.
La section 5 introduit le concept de bande (stripe). La bande est un ensemble de satellites dont les orbites sont suffisamment proches pour que la connectivité entre eux change peu.
On l'a dit,
une particularité importante des réseaux de satellites est la
prévisibilité des coupures. Un calcul de mécanique
céleste permet de dire quand deux satellites
ne pourront plus communiquer. On peut bien sûr router comme s'il
s'agissait de pannes imprévues, mais on améliorera certainement la
qualité du routage (et on diminuera le nombre de pertes de paquets
lorsque la topologie change) si on prévient les satellites à
l'avance. Par exemple, une fois informés d'un changement proche, les
routeurs peuvent annoncer une métrique très élevée pour les ISL qui
vont bientôt être coupés, comme on le fait sur les réseaux
terrestres quand une maintenance est prévue. Il y a une proposition
concrète d'un mécanisme d'information dans le brouillon
draft-ietf-tvr-schedule-yang
.
Quelques lectures recommandées par notre RFC :
Date de publication du RFC : Janvier 2025
Auteur(s) du RFC : K. Fujiwara (JPRS), P. Vixie (AWS
Security)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 28 janvier 2025
Les requêtes et réponses DNS peuvent voyager sur divers transports, mais le plus fréquent est UDP. Une réponse de grande taille sur UDP peut dépasser la MTU et donc devoir être fragmentée. La fragmentation crée divers problèmes et ce RFC décrit les problèmes, et comment éviter la fragmentation.
Un peu de rappel : avec UDP (RFC 768), il n'y a pas de négociation de la taille des paquets et un émetteur UDP peut donc, en théorie, envoyer ce qu'il veut, jusqu'à la limite maximum de 65 536 octets. Bon, en pratique, on voit rarement un paquet aussi grand. Déjà, la norme originelle du DNS mettait une limite à 512 octets. Cette limite a disparu depuis longtemps, grâce à EDNS (RFC 6891) et, désormais, le logiciel DNS peut indiquer la taille maximale qu'il est prêt à recevoir. Pendant longtemps, on voyait souvent une taille de 4 096 octets, ce qui excède la MTU de la plupart des liens Internet. Un paquet aussi grand devait donc être fragmenté (que cela soit en IPv4 ou en IPv6). Voici un exemple de réponse plus grande que la MTU typique :
% dig oracle.com TXT … ;; MSG SIZE rcvd: 3242
Si cette réponse était transmise dans un seul datagramme UDP, elle serait fragmentée, ce qui ne marcherait pas toujours, comme l'explique le RFC 8900 et créerait des risques de sécurité.
TCP (RFC 9293) évite le problème grâce à la négociation de la MSS (Maximum Segment Size, section 3.7.1 du RFC 9293) et au découpage en paquets plus petits que la MTU. La requête DNS ci-dessus ne poserait aucun problèmes avec TCP :
% dig +tcp oracle.com TXT … ;; SERVER: 192.168.2.254#53(192.168.2.254) (TCP) ;; MSG SIZE rcvd: 3242
Mais UDP, plus rigide, n'a pas un tel mécanisme. On pourrait tout faire en TCP (le RFC 7766 rappelle opportunément qu'il n'est pas en option : tout serveur DNS doit l'accepter) mais cela a d'autres inconvénients donc restons avec UDP pour l'instant.
Donc, il est recommandé d'éviter la fragmentation. La section 3
traduit cela en recommandations concrètes : ne pas fragmenter au
départ (IPv6) et mettre le bit DF (Don't fragment,
pour IPv4, cf. RFC 791) car, sinon, un paquet
IPv4 pourra être fragmenté en route (cf. section 7.1). Malheureusement, tous
les systèmes
d'exploitation ne donnent pas un accès facile à ce
bit. Sur Linux, par
exemple, il faut passer (ce qui n'est pas très intuitif) par
l'option IP_MTU_DISCOVER
, ce qui mène à
d'autres problèmes (cf. annexe C du RFC).
Le logiciel qui répond à une requête DNS doit la maintenir sous une taille qui est le miminum de ces quatre tailles :
Si on ne peut pas fabriquer une réponse de taille inférieure à ce minimum, il faut mettre le bit TC (TrunCated) dans la réponse DNS.
Ça, c'était pour les répondeurs (les serveurs DNS). Et pour les
demandeurs (les clients) ? Ils ne devraient pas indiquer en EDNS une
taille supérieure au minimum des trois dernières valeurs citées plus
haut. Ici, vue par tshark,
la requête faite par dig avec l'option
+dnssec
mais sans autre mention, notamment de
taille. dig indique une taille maximale de 1 232 octets, conforme
aux recommandations du RFC :
Domain Name System (query) Flags: 0x0120 Standard query 0... .... .... .... = Response: Message is a query … Queries re: type NS, class IN Name: re [Name Length: 2] [Label Count: 1] Type: NS (2) (authoritative Name Server) Class: IN (0x0001) Additional records <Root>: type OPT Name: <Root> Type: OPT (41) UDP payload size: 1232 Higher bits in extended RCODE: 0x00 EDNS0 version: 0 Z: 0x8000 1... .... .... .... = DO bit: Accepts DNSSEC security RRs .000 0000 0000 0000 = Reserved: 0x0000 Data length: 12 Option: COOKIE Option Code: COOKIE (10) Option Length: 8 Option Data: 83d7d3e78b76b45d Client Cookie: 83d7d3e78b76b45d Server Cookie: <MISSING>
Le RFC demande également aux clients DNS de refuser les réponses fragmentées, compte tenu de certains risques de sécurité (risque de ne pas pouvoir valider les réponses), et de ne pas hésiter à essayer avec d'autres protocoles de transport, comme TCP.
La section 4, elle, rassemble les recommandations pour les hébergeurs DNS. Comme ils contrôlent les zones publiées, ils peuvent faire en sorte que les réponses ne soient pas trop longues, rendant la fragmentation inutile. Par exemple, ils devraient faire attention à ne pas lister « trop » de serveurs de noms, ne pas mettre « trop » d'enregistrements d'adresses à ces serveurs, etc. Un conseil DNSSEC : certains algorithmes cryptographiques ont des clés et des signatures plus courtes que d'autres. Utilisez-les. (Et, donc, ECDSA ou EdDSA, plutôt que RSA.)
Le RFC note aussi que certains logiciels ne sont pas conformes à la norme. Par exemple, la taille maximale indiquée dans la requête est ignorée et une réponse plus grande que cette taille renvoyée. Et, bien sûr, on trouve encore des serveurs qui n'acceptent pas TCP.
Mais quel était le problème de sécurité avec la fragmentation, au juste ? Comme détaillé en section 7.3, qui cite le papier « Fragmentation Considered Poisonous », de Amir Herzberg et Haya Shulman, l'empoisonnement de la mémoire d'un résolveur est plus facile lorsqu'il y a fragmentation, puisque certains des éléments dans le paquet qui peuvent être utilisés pour juger de la légitimité d'une réponse peuvent se retrouver dans un autre fragment. (Voir aussi cet exposé au RIPE.) Ces attaques permettent ensuite plein d'autres attaques, par exemple contre les autorités de certification.
DNSSEC résout évidemment ces problèmes mais attention, les délégations ne sont pas signées donc DNSSEC ne protège que si les victimes ont signé leur zone et, en prime, DNSSEC permet de détecter la tentative d'empoisonnement et de la refuser, mais pas d'obtenir la bonne réponse.
L'annexe A du RFC détaille les observations quantitatives sur la taille des réponses et les raisons qui peuvent guider le choix des tailles maximum. La section 5 du RFC 8200 garantit que 1 280 octets passeront partout en IPv6, c'est la MTU minimale (mais elle est bien plus basse dans l'ancienne version d'IP). Le RFC 4035 (section 3) impose 1 220 octets minimums pour DNSSEC. Le DNS flag day 2020 proposait 1 232 octets comme taille maximale acceptée (MTU de 1 280 octets, moins 48 octets d'en-têtes). La MTU typique dans l'Internet est de 1 500 octets et, au moins dans le cœur, il n'y a pratiquement pas de liens avec une MTU plus basse. Une taille de 1 452 octets (la MTU moins les en-têtes) est donc réaliste mais, pour tenir compte de certaines techniques qui abaissent la MTU effective, comme les tunnels, notre RFC arrive finalement aux 1 400 octets cités plus haut (section 3).
L'annexe B, elle, décrit le concept de « réponses minimisées ». Certaines mises en œuvre du DNS ont une option qui permet de dire « essaie de réduire la taille de la réponse, en n'incluant pas certaines informations non indispensables ». Attention, des informations comme la colle (les adresses IP des serveurs nommés dans la zone qu'ils servent) sont indispensables (RFC 9471).
Par exemple, pour un serveur faisant autorité qui fait tourner BIND, qui envoie des réponses minimales par défaut, si on lui demande de ne pas être si économe :
options { minimal-responses no; };
Il va alors renvoyer des informations qu'on n'avait pas demandé et dont l'utilité se discute (ici, les deux enregistrements NS) :
% dig @::1 -p 8053 under.michu.example … ;; ANSWER SECTION: under.michu.example. 600 IN A 192.0.2.56 ;; AUTHORITY SECTION: michu.example. 600 IN NS ns2.nic.fr. michu.example. 600 IN NS ns1.nic.fr. …
Par défaut, BIND, conformément aux recommandations de notre RFC, n'enverra pas ces deux NS. Ici, il s'agissait d'un serveur faisant autorité mais cette option s'applique aussi aux résolveurs. (Notez que, pour d'autres logiciels, le fait d'envoyer des réponses minimales est par défaut, et pas modifiable.)
En parlant de logiciels, l'annexe C liste un certain nombre de serveurs DNS libres (BIND, Knot, Knot resolver, Unbound, PowerDNS, PowerDNS recursor et dnsdist) en regardant quelles recommandations de ce RFC sont appliquées.
Date de publication du RFC : Janvier 2025
Auteur(s) du RFC : J. Daley, S. Turner (IETF
Administration LLC)
Première rédaction de cet article le 27 janvier 2025
L'IETF utilise, pour le cœur de son travail de normalisation, l'Internet : listes de diffusion, forge de documents, outils divers de travail en groupe. Mais il y a quand même des réunions physiques, trois par an, rien ne remplaçant à 100 % le contact direct. Les discussions sur les lieux de ces réunions sont un des sujets favoris à l'IETF et ce RFC résume les règles actuelles de sélection d'un lieu. Pour savoir pourquoi il n'y a jamais eu de réunion en Inde, une seule en Chine et une seule en Amérique du Sud, lisez-le.
La politique officielle est documentée dans les RFC 8718 et RFC 8719. Ce nouveau RFC 9712 vise à détailler ces RFC et à expliquer certains points, d'autant plus que des choses ont pu changer depuis ces RFC (écrits avant la pandémie de Covid-19…). Pas de changements radicaux de la politique, surtout des clarifications (la section 2 de notre RFC les résume).
Ainsi, le RFC explique que ce sera toujours l'IASA (IETF Administration Support, RFC 8711), qui décidera, pas l'IETF dans son ensemble. Il y a déjà eu des cas d'enthousiasme de l'IETF pour un lieu de réunion… qui ne se traduisaient pas par un grand nombre de participants, bien au contraire (je me souviens que ce fut le cas de la réunion de Buenos Aires).
Le RFC 8718 énonçait que les hôtels devaient être « proches » du lieu de la réunion. Certains avaient interprété cela comme imposant un seul lieu (la one-roof policy). Notre nouveau RFC clarifie donc que « proche » veut dire « moins de dix minutes à pied », pas forcément sous le même toit.
L'IASA réservait traditionnellement un grand nombre de chambres dans les hôtels proches mais cela a toujours fait courir un risque financier en cas d'annulation, comme cela s'est produit pendant la pandémie de Covid-19. Depuis cette pandémie, les hôtels sont d'ailleurs bien plus prudents, demandant des paiements en avance. Bref, notre RFC n'impose plus de chiffre minimal de réservation, contrairement à ce que disait la section 3.2.4 du RFC 8718.
Une ancienne tradition disparait avec ce RFC : dans les réunions professionnelles d'informaticiens·nes, il y avait toujours une terminal room, ainsi appelée car elle était autrefois équipée de terminaux pour se connecter à ses ordinateurs distants. L'évolution de l'informatique fait que cette salle n'a plus de terminaux, mais juste tables, chaises et prises de courant. Désormais, elle n'est même plus obligatoire dans les réunions IETF (où on voyait de toute façon souvent des gens ouvrir leur ordinateur portable assis par terre dans le couloir, ou au bistrot d'à côté).
Date de publication du RFC : Novembre 2024
Auteur(s) du RFC : J. Snijders
(Fastly), B. Cartwright-Cox (Port
179), Y. Qu (Futurewei)
Chemin des normes
Première rédaction de cet article le 8 novembre 2024
Que doit faire un routeur BGP lorsque le pair en face ne traite manifestement plus ses messages ? Ce n'était pas précisé avant mais la réponse est évidente : raccrocher (mettre fin à la communication).
Un problème classique lors d'une connexion réseau, par exemple sur TCP, est de détecter si la machine en face est toujours là. Par défaut, TCP ne fournit pas ce service : s'il n'y a aucun trafic, vous ne pouvez pas savoir si votre partenaire est mort ou simplement s'il n'a rien à dire. Une coupure de réseau, par exemple, ne sera pas détectée tant que vous n'avez pas de trafic à transmettre (avec attente d'une réponse). Et BGP ne transmet que les changements donc l'absence de trafic ne signale pas forcément un problème. Il existe des solutions, comme d'envoyer périodiquement des messages même quand on n'a rien à dire (RFC 4271, section 4.4), mais aucune n'est parfaite : un programme qui utilise TCP ne sait typiquement pas immédiatement si ses messages sont vraiment partis (et l'alarme actuelle ne couvre que la réception des messages, pas leur envoi). Et BGP n'a pas de fonction « ping », qui exigerait une réponse.
Quand la coupure est franche et détectée, aucun problème, la session BGP (RFC 4271) s'arrête et les routes correspondantes sont retirées de la table de routage. Mais ce RFC traite le cas de où le routeur BGP d'en face a un problème mais qu'on ne détecte pas. Un exemple : si ce routeur en face a complètement fermé sa fenêtre TCP de réception (RFC 9293, notamment la section 3.8.6), on ne pourra pas lui envoyer de messages, mais la session BGP ne sera pas coupée et les paquets continueront à être transmis selon des annonces de routage dépassées, alors qu'ils finiront peut-être dans un trou noir (le problème des « zombies BGP »).
La solution (section 3 de notre RFC) est de modifier
l'automate de BGP (RFC 4271, section 8), en ajoutant une alarme (RFC 4271, section 10),
SendHoldTimer
. Quand elle expire, on coupe la
connexion TCP et on retire les routes qu'avait annoncé le pair dont
on n'a plus de nouvelles. Le RFC recommande une configuration par
défaut de huit minutes de patience avant de déclencher l'alarme.
L'erreur « Send Hold Timer Expired » est désormais dans le registre IANA des erreurs BGP et tcpdump sait l'afficher. Il existe plusieurs mises en œuvre de ce RFC :
Si les processus IETF vous passionnent, il y a une documentation des discussions autour de ce RFC.
Date de publication du RFC : Novembre 2024
Auteur(s) du RFC : C. Bormann (Universität Bremen
TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cbor
Première rédaction de cet article le 19 novembre 2024
Voici une légère mise à jour de la grammaire du langage de description de schéma CDDL (Concise Data Definition Language), originellement normalisé dans le RFC 8610. Pas de gros changement.
Il y avait juste quelques errata à traiter, et des ambiguités concernant notamment les chaines de caractères. Il était devenu nécessaire de modifier l'ABNF. CDDL a été décrit dans le RFC 8610, puis étendu par le RFC 9165. Il vise à décrire un schéma pour des fichiers CBOR ou JSON. Depuis quatre ans qu'il est normalisé, plusieurs erreurs ont été relevées dans la norme. (Dont une, c'est amusant, causée par le traitement du source du RFC, traitement qui avait fait disparaitre accidentellement des barres obliques inversées.)
Donc, notre RFC 9682 modifie l'ABNF qui décrit les littéraux pour les chaines de caractères, ABNF qui était trop laxiste (section 2 du RFC). Il change également la grammaire générale (section 3) pour autoriser (au niveau syntaxique) des schémas vides (ils restent interdits au niveau sémantique). Ainsi, l'ancienne règle :
cddl = S 1*(rule S)
devient :
cddl = S *(rule S)
Date de publication du RFC : Octobre 2024
Auteur(s) du RFC : J. M. Halpern (Ericsson), J. Daley
(IETF Administration LLC)
Pour information
Première rédaction de cet article le 31 octobre 2024
Les normes publiées par l'IETF ne sont pas que des documents techniques à seule destination des techniciens. L'Internet et, de manière plus générale, les protocoles TCP/IP sont aussi une grosse industrie qui brasse beaucoup d'argent. Il y a donc un risque que des acteurs de cette industrie essaient d'influencer les normes à leur profit, par exemple en formant des alliances qui, dans certains pays, seraient illégales au regard des lois antitrust. Ce court RFC administratif explique aux participant·es IETF ce que sont ces lois et comment éviter de les violer.
En effet, les organisations, notamment les entreprises à but lucratif, qui participent à l'IETF peuvent être concurrentes sur leurs marchés. Or, le développement de normes nécessite de la collaboration entre ces concurrents. Pour maintenir une concurrence et pour éviter les ententes, plusieurs pays ont des lois, dites « antitrust », lois que des participant·es à l'IETF ne connaissent pas forcément bien. La justification idéologique de ces lois est rappelée par le RFC, dans le cas étatsunien mais d'autres pays capitalistes ont des principes similaires : « Competition in a free market benefits consumers through lower prices, better quality and greater choice. Competition provides businesses the opportunity to compete on price and quality, in an open market and on a level playing field, unhampered by anticompetitive restraints. » Que ce soit vrai ou pas, peu importe, les lois antitrust doivent être respectées. Ce RFC n'édicte pas de règles pour l'IETF (« respectez la loi » est de toute façon déjà obligatoire), mais il explique des subtilités juridiques aux participant·es à l'IETF.
Il y a en effet deux risques pour l'IETF, qu'un·e représentant officiel de l'IETF soit accusé de comportement anti-concurrentiel, engageant la responsabilité de l'organisation, ou que des participant·es soient accusés de comportement anti-concurrentiel, ce qui n'engagerait pas la responsabilité de l'IETF mais pourrait affecter sa réputation.
Les participant·es à l'IETF sont censés suivre des règles dont certaines limitent déjà le risque de comportement anti-concurrentiel, entre autres :
Maintenant, s'y ajoutent les questions spécifiques au respect des lois sur la concurrence. La section 4, le cœur de ce RFC, attire l'attention sur :
legal@ietf.org
) ou via
le service
spécifique pour les lanceurs d'alerte.Date de publication du RFC : Octobre 2024
Auteur(s) du RFC : R. Hinden (Check Point
Software), G. Fairhurst (University of
Aberdeen)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 31 octobre 2024
Parmi les différentes options qui peuvent être placées dans un en-tête d'un paquet IPv6, certaines sont à traiter par chaque routeur situé sur le trajet. On les appelle les options « par saut » (hop-by-hop). Elles sont très peu utilisées en pratique, entre autres parce que leur traitement, tel que spécifié dans le RFC 8200, est trop contraignant pour les routeurs. Ce nouveau RFC change donc les règles, dans le sens d'un plus grand pragmatisme.
À part l'en-tête « Hop-by-hop Options » (RFC 8200, section 4.3), tous les en-têtes IPv6 ne concernent que les machines terminales. « Hop-by-hop Options », lui, concerne les routeurs et, avant le RFC 8200, tous les routeurs sur le trajet avaient l'obligation de le lire et d'agir en fonction des options qu'il contenait (la liste complète des options possibles est dans un registre IANA). Bien trop coûteuse pour les routeurs, cette obligation a été supprimée par le RFC 8200. Ce nouveau RFC 9673 modifie le traitement des options de cet en-tête (et donc le RFC 8200) dans l'espoir qu'il voit enfin un vrai déploiement dans l'Internet (actuellement, cet en-tête par saut - hop-by-hop - est quasiment inutilisé). Si vous concevez des routeurs, et êtes pressé·e, sautez directement à la section 5 du RFC, qui décrit les nouvelles règles, mais ce serait dommage.
Petite révision sur l'architecture des routeurs (section 3 du RFC). Les routeurs de haut de gamme ont une voie rapide (fast path) pour le traitement des paquets, lorsque ceux-ci n'ont pas de demande particulière. Mise en œuvre en dehors du processeur principal du routeur, cette voie rapide est traitée par des circuits spécialisés, typiquement des ASIC. Si le paquet nécessite des opérations plus complexes, on passe par une voie plus lente, utilisant des méthodes et du matériel plus proches de ceux d'un ordinateur classique. (Les RFC 6398 et RFC 6192 sont des lectures recommandées ici.) D'autre part, on distingue souvent, dans le routeur, la transmission (forwarding plane) et le contrôle (control plane). La transmission est le travail de base du routeur (transmettre les paquets reçus sur une interface via une autre interface, et le plus vite possible), le contrôle regroupe notamment les opérations de manipulation de la table de routage, par exemple lors de mises à jour reçues via des protocoles comme OSPF ou BGP. Contrairement à la transmission, le contrôle n'est pas en « temps réel ».
Aujourd'hui, un paquet IPv6 utilisant des options par saut risque fort de ne même pas arriver à destination, sacrifié par des routeurs qui ne veulent pas le traiter. (Voir le RFC 7872, l'exposé « Internet Measurements: IPv6 Extension Header Edition » ou l'article « Is it possible to extend IPv6? ».)
Que disent donc les nouvelles procédures (section 5) ?
Pour faciliter la tâche des routeurs, et toujours dans l'espoir que les options par saut deviennent enfin une possibilité réaliste, la section 6 du RFC encadre la définition de nouveaux en-têtes (le RFC 8200 recommandait carrément de ne plus en définir, tant qu'on n'avait pas mieux défini leur utilisation). Les éventuelles futures options doivent être simples à traiter et conçues en pensant au travail qu'elles imposeront au routeur. Le protocole qui les utilise doit intégrer le fait que le routeur est autorisé à ignorer cette option, voire qu'il puisse jeter le paquet.
Ah, et un mot sur la sécurité (section 8). Plusieurs RFC ont déjà documenté les problèmes de sécurité que peuvent poser les options par saut, notamment le risque qu'elles facilitent une attaque par déni de service sur le routeur : RFC 6398, RFC 6192, RFC 7045 et RFC 9098.
Est-ce que le déploiement de ce RFC va améliorer les choses pour l'en-tête par saut, qui est jusqu'à présent un exemple d'échec ? Je suis assez pessimiste, étant donné la difficulté à changer des comportements bien établis.
Un peu d'histoire pour terminer (section 4 du RFC) : les
premières normes IPv6 (RFC 1883, puis RFC 2460) imposaient à tous les routeurs d'examiner
et de traiter les options par saut. Le RFC 7045 avait été le premier à constater que cette règle
n'était pas respectée et ne pouvait pas l'être vu l'architecture des
routeurs modernes. Le problème de performance dans le traitement des
options était d'autant plus grave que les options n'avaient pas
toutes le même format (cela a été résolu par le RFC 6564, qui imposait un format unique). Le résultat, comme
vu plus haut, était que les routeurs ignoraient les options par saut
ou, pire, jetaient le paquet qui les contenait. (Le RFC 9288 et l'article « Threats
and Surprises behind IPv6 Extension Headers »
expliquent pourquoi c'est une mauvaise
idée. L'Internet Draft
draft-ietf-v6ops-hbh
discute également cette
question.)
Date de publication du RFC : Octobre 2024
Auteur(s) du RFC : D. Thaler
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF bpf
Première rédaction de cet article le 1 novembre 2024
On a souvent envie de faire tourner des programmes à soi dans le noyau du système d'exploitation, par exemple à des fins de débogage ou d'observation du système. Cela soulève plein de problèmes (programmer dans le noyau est délicat) et la technique eBPF permet, depuis de nombreuses années, de le faire avec moins de risques. Ce RFC spécifie le jeu d'instructions eBPF. Programmeureuses en langage d'assemblage, ce RFC est pour vous.
eBPF désigne ici un jeu d'instructions (comme ARM ou RISC-V). Programmer en eBPF, c'est donc programmer en langage d'assemblage et, en général, on ne le fait pas soi-même, on écrit dans un langage de plus haut niveau (non spécifié ici mais c'est souvent un sous-ensemble de C) et on confie à un compilateur le soin de générer les instructions. Ce jeu d'instructions a plusieurs particularités. Notamment, il est délibérément limité, puisque toute bogue dans le noyau est particulièrement sérieuse, pouvant planter la machine ou pire, permettre son piratage. Vous ne pouvez pas faire de boucles générales, par exemple. eBPF est surtout répandu dans le monde Linux (et c'est là où vous trouverez beaucoup de ressources) où il est une alternative aux modules chargés dans le noyau. Pas mal du code réseau d'Android est ainsi en eBPF. Normalisé ici, eBPF peut être mis en œuvre sur d'autres noyaux (il tourne sur Windows, par exemple). Le monde eBPF est très riche, il y a plein de logiciels (pas toujours faciles à utiliser), plein de tutoriels (pas toujours à jour et qui ne correspondent pas toujours à votre système d'exploitation) mais cet article se focalise sur le sujet du RFC : le jeu d'instructions.
On trouve de nombreux exemples d'utilisation en production par exemple le répartiteur de charge Katran chez Facebook, via lequel vous êtes certainement passé, si vous utilisez Facebook. En plus expérimental, j'ai trouvé amusant qu'on puisse modifier les réponses DNS en eBPF.
Passons tout de suite à la description de ce jeu
d'instructions (ISA = Instruction Set
Architecture). D'abord, les types (section 2.1) :
u32
est un entier non
signé sur 32 bits, s16
, un signé
sur 16 bits, etc. eBPF fournit des fonctions de conversions utiles
(section 2.2) comme be16
qui convertit en
gros boutien (le RFC cite IEN137…). Au
passage, une mise en œuvre d'eBPF n'est pas obligée de tout fournir
(section 2.4). La norme décrit des groupes de conformité et une
implémentation d'eBPF doit lister quels groupes elle met en
œuvre. Le groupe base32
(qui n'a rien à voir
avec le Base32 du RFC 4648) est le minimum requis dans tous les cas. Par
exemple, divmul32
ajoute multiplication et
division. Tous ces groupes figurent dans un registre IANA.
Les instructions eBPF sont encodées en 64 ou 128 bits (section
3). On y trouve les instructions classiques de tout jeu, les
opérations arithmétiques (comme ADD
), logiques
(comme AND
), les sauts
(JA
, JEQ
et autrs), qui se
font toujours vers l'avant, pour, je suppose, ne pas permettre de boucles
(souvenez-vous du problème de l'arrêt, qui n'a
pas de solution avec un jeu
d'instructions plus étendu), l'appel de fonction, etc.
En parlant de fonctions, eBPF ne peut pas
appeler n'importe quelle fonction. Il y a deux sortes de fonctions
utilisables, les fonctions d'aide (section 4.3.1), pré-définies par
la plateforme utilisée, et non normalisées (pour celles de Linux,
voir la
documentation, qui est sous
Documentation/bpf
si vous avez les sources du
noyau). Il y a aussi les fonctions locales (section 4.3.2), définies
par le programme eBPF.
Il y a enfin des instructions pour lire et écrire dans la mémoire
(LD
, ST
, etc). Pour
mémoriser plus facilement, eBPF utilise
des dictionnaires (maps,
cf. section 5.4.1).
La section 6 concerne la sécurité, un point évidemment crucial puisque les programmes eBPF tournent dans le noyau, où les erreurs ne pardonnent pas. Un programme eBPF malveillant peut provoquer de nombreux dégâts. C'est pour cela que, sur Linux, seul root peut charger un tel programme dans le noyau. Le RFC recommande de faire tourner ces programmes dans un environnement limité (bac à sable), de limiter les ressources dont ils disposent et de faire tourner des vérifications sur le programme avant son exécution (par exemple, sur Linux, regardez cette documentation ou bien l'article « Simple and Precise Static Analysis of Untrusted Linux Kernel Extensions »).
Enfin, section 7, les registres (pas les registres du processeur, ceux où on enregistre les codes utilisés). Deux registres IANA sont créés, celui des groupes de conformité et celui du jeu d'instructions. L'annexe A du RFC donne les valeurs actuelles. Les registres sont extensibles et la politique d'enregistrement est « Spécification nécessaire » et « Examen par un expert », cf. RFC 8126. (J'avoue ne pas savoir pourquoi, si les opcodes sont enregistrés, les mnémoniques ne le sont pas, cela rend les registres difficiles à lire.)
Un peu d'histoire, au passage. eBPF est dérivé de BPF, ce qui voulait dire Berkeley Packet Filter, et était spécifique au filtrage des paquets réseau. Cet usage a été notamment popularisé par tcpdump. D'ailleurs, ce programme a une option pour afficher le code BPF produit :
% sudo tcpdump -d port 53 (000) ldh [12] (001) jeq #0x86dd jt 2 jf 10 (002) ldb [20] (003) jeq #0x84 jt 6 jf 4 (004) jeq #0x6 jt 6 jf 5 (005) jeq #0x11 jt 6 jf 23 (006) ldh [54] … (021) jeq #0x35 jt 22 jf 23 (022) ret #262144 (023) ret #0
Si vous voulez vous mettre à eBPF (attention, la courbe
d'apprentissage va être raide), man 4 bpf
est
utile. Typiquement, vous écrirez vos programmes dans un
sous-ensemble de C et vous
compilerez en eBPF, par exemple avec clang,
après avoir installé tous les outils et bibliothèques nécessaires
(il faut souvent des versions assez récentes) :
% cat count.c … int count_packets(struct __sk_buff *skb) { __u32 key = 0; __u64 *counter; counter = bpf_map_lookup_elem(&pkt_counter, &key); if (counter) { (*counter)++; } return 0; } … % clang -target bpf -c count.c % file count.o count.o: ELF 64-bit LSB relocatable, eBPF, version 1 (SYSV), not stripped % objdump -d count.o … 0000000000000000 <count_packets>: 0: 7b 1a f8 ff 00 00 00 00 stxdw [%r10-8],%r1 8: b7 01 00 00 00 00 00 00 mov %r1,0 10: 63 1a f4 ff 00 00 00 00 stxw [%r10-12],%r1 18: 18 01 00 00 00 00 00 00 lddw %r1,0 20: 00 00 00 00 00 00 00 00 28: bf a2 00 00 00 00 00 00 mov %r2,%r10 30: 07 02 00 00 f4 ff ff ff add %r2,-12
(Notez l'utilisation du désassembleur objdump.) Vous pouvez alors charger le code eBPF dans votre noyau, par exemple avec bpftool (et souvent admirer de beaux messages d'erreur comme « libbpf: elf: legacy map definitions in 'maps' section are not supported by libbpf v1.0+ »). Si tout fonctionne, votre code eBPF sera appelé par le noyau lors d'événements particuliers que vous avez indiqués (par exemple la création d'un processus, ou bien l'arrivée d'un paquet par le réseau) et fera alors ce que vous avez programmé. Comme me le fait remarquer Pierre Lebeaupin, il y a une bogue dans le source ci-dessus : l'incrémentation du compteur n'est pas atomique et donc, si on a plusieurs CPU, on risque de perdre certaines incrémentations. La solution de ce problème est laissé à la lectrice.
Un exemple d'utilisation d'eBPF pour observer ce que fait le noyau (ici avec un outil qui fait partie de bcc), on regarde les exec :
% sudo /usr/sbin/execsnoop-bpfcc PCOMM PID PPID RET ARGS check_disk 389622 1628 0 /usr/lib/nagios/plugins/check_disk -c 10% -w 20% -X none -X tmpfs -X sysfs -X proc -X configfs -X devtmpfs -X devfs -X check_disk 389623 1628 0 /usr/lib/nagios/plugins/check_disk -c 10% -w 20% -X none -X tmpfs -X sysfs -X proc -X configfs -X devtmpfs -X devfs -X check_swap 389624 1628 0 /usr/lib/nagios/plugins/check_swap -c 25% -w 50% check_procs 389625 1628 0 /usr/lib/nagios/plugins/check_procs -c 400 -w 250 ps 389627 389625 0 /bin/ps axwwo stat uid pid ppid vsz rss pcpu etime comm args sh 389632 389631 0 /bin/sh -c [ -x /usr/lib/php/sessionclean ] && if [ ! -d /run/systemd/system ]; then /usr/lib/php/sessionclean; fi sessionclean 389633 1 0 /usr/lib/php/sessionclean sort 389635 389633 0 /usr/bin/sort -rn -t: -k2,2 phpquery 389638 389634 0 /usr/sbin/phpquery -V expr 389639 389638 0 /usr/bin/expr 2 - 1 sort 389642 389638 0 /usr/bin/sort -rn
Le code eBPF est interprété par une machine virtuelle ou bien traduit à la volée en code natif.
Même ChatGPT peut écrire de l'eBPF (les tours de Hanoi et un serveur DNS).
De nombreux exemples se trouvent dans le répertoire
samples/bpf
des sources du noyau Linux. (Le
fichier README.rst
explique comment compiler
mais seulement dans le cadre de la compilation d'un noyau. En gros,
c'est make menuconfig
, cd
samples/bpf
puis make -i
.) Un bon
exemple, relativement simple, pour commencer avec le réseau est
tcp_clamp_kern.c
.
Si vous préférez travailler en Go (là aussi, avec un Go récent…), il existe un bon projet. Si vous suivez bien la documentation, vous pourrez compiler des programmes et les charger :
% go mod init ebpf-test % go mod tidy % go get github.com/cilium/ebpf/cmd/bpf2go % go generate % go build % sudo ./ebpf-test 2024/08/20 15:21:43 Counting incoming packets on veth0.. … 2024/08/20 15:22:03 Received 25 packets 2024/08/20 15:22:04 Received 26 packets 2024/08/20 15:22:05 Received 27 packets 2024/08/20 15:22:06 Received 502 packets <- ping -f 2024/08/20 15:22:07 Received 57683 packets 2024/08/20 15:22:08 Received 75237 packets ^C2024/08/20 15:22:09 Received signal, exiting..
Vous trouverez beaucoup de ressources eBPF sur
. Et si vous voulez plonger dans
les détails précis des choix de conception d'eBPF, je recommande
ce
document.https://ebpf.io/
Ce RFC avait fait l'objet de pas mal de débats à l'IETF car, normalement, l'IETF ne normalise pas de langages de programmation ou de jeux d'instructions. (La première réunion était à l'IETF 116 en mars 2023 donc c'est quand même allé assez vite.)
Date de publication du RFC : Octobre 2024
Auteur(s) du RFC : H. Salgado (NIC Chile), M. Vergara (DigitalOcean), D. Wessels (Verisign)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 13 octobre 2024
Cette nouvelle option du DNS permet au client d'obtenir du serveur le numéro de version de la zone servie. (Et, non, le numéro de série dans l'enregistrement SOA ne suffit pas, lisez pour en savoir plus.)
Cela permettra de détecter les problèmes de mise à jour des serveurs faisant autorité si, par exemple, un des secondaires ne se met plus à jour. C'est surtout important pour l'anycast, qui complique le déboguage. En combinaison avec le NSID du RFC 5001, vous trouverez facilement le serveur qui a un problème. Cette nouvelle option ressemble d'ailleurs à NSID et s'utilise de la même façon.
Vous le savez, le DNS ne garantit pas que tous les serveurs faisant
autorité d'une zone servent la même version de la zone au
même moment. Si je regarde la zone
.com
à cet instant (14
juillet 2024, 09:48 UTC) avec check-soa, je
vois :
% check-soa com a.gtld-servers.net. 2001:503:a83e::2:30: OK: 1720950475 192.5.6.30: OK: 1720950475 b.gtld-servers.net. 192.33.14.30: OK: 1720950475 2001:503:231d::2:30: OK: 1720950475 c.gtld-servers.net. 2001:503:83eb::30: OK: 1720950475 192.26.92.30: OK: 1720950475 d.gtld-servers.net. 2001:500:856e::30: OK: 1720950475 192.31.80.30: OK: 1720950475 e.gtld-servers.net. 2001:502:1ca1::30: OK: 1720950475 192.12.94.30: OK: 1720950475 f.gtld-servers.net. 2001:503:d414::30: OK: 1720950475 192.35.51.30: OK: 1720950475 g.gtld-servers.net. 192.42.93.30: OK: 1720950475 2001:503:eea3::30: OK: 1720950475 h.gtld-servers.net. 192.54.112.30: OK: 1720950475 2001:502:8cc::30: OK: 1720950475 i.gtld-servers.net. 2001:503:39c1::30: OK: 1720950460 192.43.172.30: OK: 1720950475 j.gtld-servers.net. 2001:502:7094::30: OK: 1720950475 192.48.79.30: OK: 1720950475 k.gtld-servers.net. 192.52.178.30: OK: 1720950460 2001:503:d2d::30: OK: 1720950475 l.gtld-servers.net. 192.41.162.30: OK: 1720950475 2001:500:d937::30: OK: 1720950475 m.gtld-servers.net. 2001:501:b1f9::30: OK: 1720950475 192.55.83.30: OK: 1720950475
On observe que, bien que le numéro de série dans l'enregistrement SOA soit 1720950475, certains serveurs sont restés à 1720950460. Le DNS est « modérement cohérent » (RFC 3254, sur ce concept).
Dans l'exemple ci-dessus, check-soa a simplement fait une requête pour le type de données SOA (section 4.3.5 du RFC 1034). Une limite de cette méthode est que, si on observe des données d'autres types qui ne semblent pas à jour et que, pour vérifier, on fait une requête de type SOA, on n'a pas de garantie de tomber sur le même serveur, notamment en cas d'utilisation d'anycast (RFC 4786) ou bien s'il y a un répartiteur de charge avec plusieurs serveurs derrière. (D'ailleurs, dans l'exemple ci-dessus, vous avez remarqué que vous n'avez pas la même réponse en IPv4 et IPv6, probablement parce que vous arriviez sur deux instances différentes du nuage anycast.) Il faut donc un mécanisme à l'intérieur même de la requête qu'on utilise, comme pour le NSID. C'est le cas du ZONEVERSION de ce RFC, qui permet d'exprimer la version sous forme d'un numéro de série (comme avec le SOA) ou par d'autres moyens dans le futur, puisque toutes les zones n'utilisent pas le mécanisme de synchronisation habituel du DNS. On peut par exemple avoir des zones entièrement dynamiques et tirées d'une base de données, ou bien d'un calcul.
Notez aussi que certaines zones, comme
.com
, changent très
vite, et que donc, même si on tombe sur le même serveur, le numéro
de série aura pu changer entre une requête ordinaire et celle pour
le SOA. C'est une raison supplémentaire pour avoir le mécanisme
ZONEVERSION.
Bref, le nouveau mécanisme (section 2 du RFC), utilise EDNS (section 6.1.2 du RFC 6891). La nouvelle option EDNS porte le numéro 19. Encodée en TLV comme toutes les options EDNS, elle comprend une longueur et une chaine d'octets qui est la version de la zone. La longueur vaut zéro dans une requête (le client indique juste qu'il souhaite connaitre la version de la zone) et, dans la réponse, une valeur non nulle. La version est elle-même composée de trois champs :
foo.bar.example.org
et que le serveur renvoie
la version de la zone example.org
, ce champ
vaudra deux.
Si vous voulez voir dans un pcap, regardez
.zoneversion.pcap
L'option ZONEVERSION n'est renvoyée au client qui si celui-ci l'avait demandé, ce qu'il fait en mettant une option ZONEVERSION vide dans sa requête (section 3 du RFC). Si le serveur fait autorité pour la zone concernée (ou une zone ancêtre), et qu'il gère cette nouvelle option, il répond avec une valeur. Même si le nom demandé n'existe pas (réponse NXDOMAIN), l'option est renvoyée dans la réponse.
Comme les autres options EDNS, elle n'est pas signée par DNSSEC (section 8). Il n'y a donc pas de moyen de vérifier son authenticité et elle ne doit donc être utilisée qu'à titre informatif, par exemple pour le déboguage. (En outre, elle peut être modifiée en route, sauf si on utilise un mécanisme comme DoT - RFC 7858.)
Question mises en œuvre de cette option, c'est surtout du code expérimental pour l'instant, voir cette liste (pas très à jour ?). Personnellement, j'ai ajouté ZONEVERSION à Drink, et écrit un article sur l'implémentation d'options EDNS (mais notez que l'option a changé de nom et de format depuis). Notez que, contrairement à presque toutes les options EDNS, ZONEVERSION est par zone, pas par serveur, ce qui est une contrainte pour le ou la programmeureuse, qui ne peut pas choisir la valeur avant de connaitre le nom demandé. Du côté des autres logiciels, NSD a vu un patch (mais apparemment abandonné). Voici ce que voit dig actuellement (en attendant une intégration officielle de l'option) :
% dig +ednsopt=19 @ns1-dyn.bortzmeyer.fr dyn.bortzmeyer.fr SOA … ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64655 ;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 … ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 1440 ; OPT=19: 03 00 78 a5 08 cc ("..x...") … ;; ANSWER SECTION: dyn.bortzmeyer.fr. 0 IN SOA ns1-dyn.bortzmeyer.fr. stephane.bortzmeyer.org. ( 2024081612 ; serial …
Vous noterez que "03" indique trois composants
(dyn.bortzmeyer.fr
), "00" le SOA-SERIAL, et "78
a5 08 cc" égal 2024081612. Vous pouvez aussi tester ZONEVERSION avec
le serveur de test 200.1.122.30
(un NSD
modifié), avec le domaine example.com
.
Date de publication du RFC : Septembre 2024
Auteur(s) du RFC : M. Nottingham
(Cloudflare), P-H. Kamp (The Varnish Cache
Project)
Chemin des normes
Première rédaction de cet article le 6 octobre 2024
Plusieurs en-têtes HTTP sont structurés,
c'est-à-dire que le contenu n'est pas juste une suite de caractères
mais est composé d'éléments qu'un logiciel peut analyser. C'est par
exemple le cas de Accept-Language:
ou de
Content-Disposition:
. Mais chaque en-tête ainsi
structuré a sa propre syntaxe, sans rien en commun avec les autres
en-têtes structurés, ce qui en rend l'analyse pénible. Ce nouveau
RFC (qui
remplace le RFC 8941) propose donc
des types de données et des algorithmes que les futurs en-têtes qui
seront définis pourront utiliser pour standardiser un peu l'analyse
d'en-têtes HTTP. Les en-têtes structurés anciens ne sont pas
changés, pour préserver la compatibilité. De nombreux RFC utilisent
déjà cette syntaxe (RFC 9209, RFC 9211, etc).
Imaginez : vous êtes un concepteur ou une conceptrice d'une extension au protocole HTTP qui va nécessiter la définition d'un nouvel en-tête. La norme HTTP, le RFC 9110, section 16.3.2, vous guide en expliquant ce à quoi il faut penser quand on conçoit un en-tête HTTP. Mais même avec ce guide, les pièges sont nombreux. Et, une fois votre en-tête spécifié, il vous faudra attendre que tous les logiciels, serveurs, clients, et autres (comme Varnish, pour lequel travaille un des auteurs du RFC) soient mis à jour, ce qui sera d'autant plus long que le nouvel en-tête aura sa syntaxe spécifique, avec ses amusantes particularités. Amusantes pour tout le monde, sauf pour le programmeur ou la programmeuse qui devra écrire l'analyse.
La solution est donc que les futurs en-têtes structurés réutilisent les éléments fournis par notre RFC, ainsi que son modèle abstrait, et la sérialisation proposée pour envoyer le résultat sur le réseau. Le RFC recommande d'appliquer strictement ces règles, le but étant de favoriser l'interopérabilité, au contraire du classique principe de robustesse, qui mène trop souvent à du code compliqué (et donc dangereux) car voulant traiter tous les cas et toutes les déviations. L'idée est que s'il y a la moindre erreur dans un en-tête structuré, celui-ci doit être ignoré complètement.
La syntaxe est malheureusement spécifiée sous forme d'algorithmes d'analyse. L'annexe C fournit toutefois aussi une grammaire en ABNF (RFC 5234).
Voici un exemple fictif d'en-tête structuré très simple
(tellement simple que, si tous étaient comme lui, on n'aurait sans
doute pas eu besoin de ce RFC) :
Foo-Example:
est défini comme ne prenant qu'un
élément comme valeur, un entier compris entre 0 et 10. Voici à quoi
il ressemblera en HTTP :
Foo-Example: 3
Il accepte des paramètres après un
point-virgule, un seul paramètre est défini,
foourl
dont la valeur est un URI. Cela pourrait
donner :
Foo-Example: 2; foourl="https://foo.example.com/"
Donc, ces solutions pour les en-têtes structurés ne serviront que pour les futurs en-têtes, pas encore définis, et qui seront ajoutés au registre IANA. Imaginons donc que vous soyez en train de mettre au point une norme qui inclut, entre autres, un en-tête HTTP structuré. Que devez-vous mettre dans le texte de votre norme ? La section 2 de notre RFC vous le dit :
Foo-Example:
, la valeur
était un élément, de type entier.Foo-Example:
imposait une valeur entière
entre 0 et 10.Une spécification d'un en-tête structuré ne peut que rajouter des contraintes à ce que prévoit ce RFC 9651. S'il en retirait, on ne pourrait plus utiliser du code générique pour analyser tous les en-têtes structurés.
Rappelez-vous que notre RFC est strict : si une erreur est présente
dans l'en-tête, il est ignoré. Ainsi, s'il était spécifié que la
valeur est un élément de type entier et qu'on trouve une chaîne de
caractères, on ignore l'en-tête. Idem dans l'exemple ci-dessus si on
reçoit Foo-Example: 42
, la valeur excessive
mène au rejet de l'en-tête.
Les valeurs peuvent inclure des paramètres (comme le
foourl
donné en exemple plus haut), et le RFC
recommande d'ignorer les paramètres inconnus, afin de permettre
d'étendre leur nombre sans tout casser.
On a vu qu'une des plaies du Web était le laxisme trop grand dans l'analyse des données reçues (c'est particulièrement net pour HTML). Mais on rencontre aussi des cas contraires, des systèmes (par exemple les pare-feux applicatifs) qui, trop fragiles, chouinent lorsqu'ils rencontrent des cas imprévus, parce que leurs auteurs avaient mal lu le RFC. Cela peut mener à l'ossification, l'impossibilité de faire évoluer l'Internet parce que des nouveautés, pourtant prévues dès l'origine, sont refusées. Une solution récente est le graissage, la variation délibérée des messages pour utiliser toutes les possibilités du protocole. (Un exemple pour TLS est décrit dans le RFC 8701.) Cette technique est recommandée par notre RFC.
La section 3 du RFC décrit ensuite les types qui sont les briques de base avec lesquelles on va pouvoir définir les en-têtes structurés. La valeur d'un en-tête peut être une liste, un dictionnaire ou un élément. Les listes et les dictionnaires peuvent à leur tour contenir des listes. Une liste est une suite de termes qui, dans le cas de HTTP, sont séparés par des virgules :
ListOfStrings-Example: "foo", "bar", "It was the best of times."
Et si les éléments d'une liste sont eux-mêmes des listes, on met ces listes internes entre parenthèses (et notez la liste vide à la fin, et l'espace comme séparateur) :
ListOfListsOfStrings-Example: ("foo" "bar"), ("baz"), ("bat" "one"), ()
Un en-tête structuré dont la valeur est une liste a toujours une valeur, la liste vide, même si l'en-tête est absent.
Un dictionnaire est une suite de termes nom=valeur. En HTTP, cela donnera :
Dictionary-Example: en="Applepie", fr="Tarte aux pommes"
Et nous avons déjà vu les éléments simples dans le premier
exemple. Les éléments peuvent être de type
entier,
chaîne de
caractères, booléen,
identificateur, valeur binaire, date, et un dernier type plus exotique
(lisez le RFC pour en savoir plus). L'exemple avec
Foo-Example:
utilisait un entier. Les exemples
avec listes et dictionnaires se servaient de chaînes de
caractères. Ces chaînes sont encadrées de
guillemets (pas
d'apostrophes).
Compte-tenu des analyseurs HTTP
existants, les chaînes de caractères ordinaires
doivent être en ASCII (RFC 20). Si on veut envoyer de l'Unicode, il faut utiliser
un autre type, Display String. Quant aux booléens, notez qu'il faut
écrire 1 et 0 (pas true
et
false
), et préfixé d'un point
d'interrogation.
Toutes ces valeurs peuvent prendre des paramètres, qui sont eux-mêmes une suite de couples clé=valeur, après un point-virgule qui les sépare de la valeur principale (ici, on a deux paramètres) :
ListOfParameters: abc;a=1;b=2
J'ai dit au début que ce RFC définit un modèle abstrait, et une sérialisation concrète pour HTTP. La section 4 du RFC spécifie cette sérialisation, et son inverse, l'analyse des en-têtes (pour les programmeur·ses seulement).
Ah, et si vous êtes fana de formats structurés, ne manquez pas l'annexe A du RFC, qui répond à la question que vous vous posez certainement depuis plusieurs paragraphes : pourquoi ne pas avoir tout simplement décidé que les en-têtes structurés auraient des valeurs en JSON (RFC 8259), ce qui évitait d'écrire un nouveau RFC, et permettait de profiter du code JSON existant ? Le RFC estime que le fait que les chaînes de caractères JSON soient de l'Unicode est trop risqué, par exemple pour l'interopérabilité. Autre problème de JSON, les structures de données peuvent être emboîtées indéfiniment, ce qui nécessiterait des analyseurs dont la consommation mémoire ne peut pas être connue et limitée. (Notre RFC permet des listes dans les listes mais cela s'arrête là : on ne peut pas poursuivre l'emboîtement.)
Une partie des problèmes avec JSON pourrait se résoudre en se limitant à un profil restreint de JSON, par exemple en utilisant le RFC 7493 comme point de départ. Mais, dans ce cas, l'argument « on a déjà un format normalisé, et mis en œuvre partout » tomberait.
Enfin, l'annexe A note également qu'il y avait des considérations d'ordre plutôt esthétiques contre JSON dans les en-têtes HTTP.
Toujours pour les programmeur·ses, l'annexe B du RFC donne quelques conseils pour les auteur·es de bibliothèques mettant en œuvre l'analyse d'en-têtes structurés. Pour les aider à suivre ces conseils, une suite de tests est disponible. Quant à une liste de mises en œuvre du RFC, vous pouvez regarder celle-ci.
Actuellement, il y a dans le registre
des en-têtes plusieurs en-têtes structurés, comme le
Accept-CH:
du RFC 8942 (sa valeur est une liste d'identificateurs), le
Cache-Status:
du RFC 9211
ou le Client-Cert:
du RFC 9440.
Si vous voulez un exposé synthétique sur ces en-têtes structurés, je vous recommande cet article par un des auteurs du RFC.
Voyons maintenant un peu de pratique avec une des mises en œuvre citées plus haut, http_sfv, une bibliothèque Python. Une fois installée :
% python3 >>> import http_sfv >>> my_item=http_sfv.Item() >>> my_item.parse(b"2") >>> print(my_item) 2
On a analysé la valeur « 2 » en déclarant que cette valeur devait être un élément et, pas de surprise, ça marche. Avec une valeur syntaxiquement incorrecte :
>>> my_item.parse(b"2, 3") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/stephane/.local/lib/python3.12/site-packages/http_sfv/util.py", line 61, in parse raise ValueError("Trailing text after parsed value") ValueError: Trailing text after parsed value
Et avec un paramètre (il sera accessible après l'analyse, via le
dictionnaire Python params
) :
>>> my_item.parse(b"2; foourl=\"https://foo.example.com/\"") >>> print(my_item.params['foourl']) https://foo.example.com/
Avec une liste :
>>> my_list=http_sfv.List() >>> my_list.parse(b"\"foo\", \"bar\", \"It was the best of times.\"") >>> print(my_list) "foo", "bar", "It was the best of times."
Et avec un dictionnaire :
>>> my_dict=http_sfv.Dictionary() >>> my_dict.parse(b"en=\"Applepie\", fr=\"Tarte aux pommes\"") >>> print(my_dict) en="Applepie", fr="Tarte aux pommes" >>> print(my_dict["fr"]) "Tarte aux pommes"
L'annexe D résume les principaux changements depuis le RFC 8941. Rien de trop crucial, à part le fait que l'ABNF est reléguée à une annexe. Sinon, il y a deux nouveaux types de base, pour les dates et les chaines de caractères en Unicode (Display Strings).
Date de publication du RFC : Novembre 2024
Auteur(s) du RFC : J. Zern, P. Massimino, J. Alakuijala
(Google)
Pour information
Première rédaction de cet article le 19 novembre 2024
Un RFC sur un
format d'image : il décrit le format WebP et
enregistre officiellement le type image/webp
.
Bon, des images au format WebP, vous en avez forcément vu un peu partout sur le Web. Mais le format n'était pas encore documenté par un organisme de normalisation. C'est désormais fait. WebP s'appuie sur le format RIFF. Plus précisément, RIFF est un cadre générique qui peut se décliner en divers formats. C'est pour cela que les fichiers WebP commencent par les quatre codes ASCII correspondant à "RIFF".
WebP permet de la compression avec perte ou sans perte, et, d'une manière générale, tout ce qu'on attend d'un format graphique. C'est donc un concurrent de JPEG, PNG (RFC 2083), GIF… L'encodage lors de la compression avec perte est celui de VP8 (RFC 6386), produisant des images plus petites que ses prédécesseurs, ce qui est bon pour le réseau. La compression sans perte est faite en LZ77 et Huffman. WebP permet également le stockage de métadonnées codées en EXIF ou en XMP. Ah, et WebP permet des animations.
Cette image, sur un sujet politique qui est toujours d'actualité,
est au format WebP :
La section 2 du RFC décrit le format en détail. Les trois premiers champs sont la chaine "RIFF", la taille du fichier en binaire et la chaine "WEBP", chacun sur quatre octets. Affichons ces trois champs :
% dd if=how-do-you-like-it-wrapped.webp bs=4 count=3 RIFFXWEBP 3+0 records in 3+0 records out 12 bytes copied…
La section 3 décrit ensuite le stockage des pixels sans perte.
Comme avec tout format, les logiciels qui lisent les fichiers WebP (qu'on télécharge souvent depuis l'Internet, via des sources pas forcément de confiance) doivent être prudents dans l'analyse des fichiers. Les fichiers peuvent être invalides, par accident ou délibérément, et mener le logiciel négligent à lire en dehors d'un tableau ou déréférencer un pointeur invalide. La paranoïa est recommandée quand on lit ces fichiers. Plusieurs failles ont touché la libwebp, l'implémentation de référence, dont la grave CVE-2023-4863 en 2023 qui avait fait, à juste titre, beaucoup de bruit.
Toujours question sécurité, WebP n'a pas de contenu exécutable (contrairement à, par exemple, TrueType). Mais les métadonnées EXIF ou en XMP peuvent poser des problèmes de sécurité et doivent donc être interprétées avec prudence.
Le type
image/webp
est désormais dans
le registre des types de médias (cf. le
formulaire rempli).
Si on regarde des fichiers WebP, on a ce genre d'informations :
% file resolution-dns.webp resolution-dns.webp: RIFF (little-endian) data, Web/P image % file --mime-type resolution-dns.webp resolution-dns.webp: image/webp
Pour finir, produisons un peu d'images WebP pour voir. Si on utilise Asymptote, c'est facile :
% asy -f webp -o resolution-dns.webp resolution-dns.asy
On peut aussi convertir depuis les formats existants, ici avec ImageMagick :
% convert -verbose resolution-dns.png resolution-dns.webp resolution-dns.png PNG 823x508 823x508+0+0 8-bit sRGB 31713B 0.020u 0:00.011 resolution-dns.png=>resolution-dns.webp PNG 823x508 823x508+0+0 8-bit sRGB 16580B 0.040u 0:00.047
Si on fait des graphiques depuis ses programmes en Python avec Matplotlib, il suffit, depuis la version 3.6, de :
plot.savefig(fname="test.webp") # No need to indicate the format, it # is taken from the file extension.
(Voir aussi ce beau résultat sur Wikimedia Commons.)
Date de publication du RFC : Décembre 2024
Auteur(s) du RFC : M.Q.C. van
Beurden, A. Weaver
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cellar
Première rédaction de cet article le 19 décembre 2024
Le format FLAC, normalisé dans ce RFC, permet de stocker et de transmettre du son sans perte (contrairement à des mécanismes de compression comme MP3). Il est donc utile, par exemple pour l'archivage à long terme (mais aussi pour la haute fidélité).
Le multimédia, c'est compliqué. Le RFC fait près de 100 pages, pour un format audio qui se veut pourtant simple. FLAC fait de la compression pour limiter l'espace de stockage nécessaire mais en prenant soin de permettre une décompression qui redonne exactement le fichier originel (d'où le « sans perte » dans son nom, qu'il partage avec d'autres formats, comme le FFV1 du RFC 9043). Je ne connais pas assez le domaine de l'audio pour faire un résumé intelligent du RFC donc je vous invite à le lire si vous voulez comprendre comment FLAC fonctionne (les sections 4 à 6 vous font une description de haut niveau).
Si on prend un fichier FLAC, un programme comme file peut le comprendre :
% file Thélème.flac Thélème.flac: FLAC audio bitstream data, 24 bit, stereo, 44.1 kHz, length unknown
Le fait que l'échantillonage soit à 44,1 kHz est encodé dans les métadonnées (cf. section 9.1.2 du RFC), que file a pu lire (section 9.1.3 pour le fait que le fichier soit en stéréo).
Il y a quelques fichiers FLAC sur Wikimedia Commons. Mais un bon exemple de l'utilisation de FLAC est donné par l'enregistrement des Variations Goldberg par Kimiko Ishizaka, que vous pouvez télécharger en FLAC et sous une licence libre (CC0).
La mise en œuvre de référence de FLAC est en logiciel libre et se nomme libFLAC. Mais il existe beaucoup d'autres programmes qui savent gérer FLAC (j'ai écouté plusieurs fichiers FLAC avec vlc pour cet article), le lire, l'écrire, l'analyser, etc. Une liste est disponible sur le site du groupe de travail IETF. Il faut dire que FLAC, bien qu'il vienne seulement d'être normalisé, est un format ancien, dont le développement a commencé en 2000, et il n'est donc pas étonnant que les programmeur·ses aient eu le temps de travailler.
Si vous voulez ajouter du code à cette liste, lisez bien la section 11 du RFC, sur la sécurité. Un programme qui lit du FLAC doit être paranoïaque (comme avec n'importe quel autre format, bien sûr) et se méfier des cas pathologiques. Ainsi, le RFC note qu'on peut facilement créer un fichier FLAC de 49 octets qui, décomprimé, ferait 2 mégaoctets, ce qui pourrait dépasser la mémoire qui avait été réservée. Ce genre de surprises peut arriver à plusieurs endroits. L'annexe D du RFC contient plusieurs fichiers FLAC intéressants (et vous pouvez les retrouver en ligne) et il existe également une collection de fichiers FLAC, dont certains sont délibérément anormaux, pour que vous puissiez tester la robustesse de votre programme. Autre piège (mais lisez toute la section 11, il y en a beaucoup), FLAC permet de mettre des URL dans les fichiers, URL qu'il ne faut évidemment pas déréférencer bêtement.
Ce RFC est également la documentation pour l'enregistrement
du type MIME audio/flac
(section 12.1).
Pour une analyse technique de la prétention « sans perte » de FLAC, voir cet article de Guillaume Seznec.
Date de publication du RFC : Août 2024
Auteur(s) du RFC : G. Huston (APNIC), N. Buraglio (Energy Sciences Network)
Pour information
Réalisé dans le cadre du groupe de travail IETF v6ops
Première rédaction de cet article le 28 août 2024
Le préfixe IPv6 normalisé pour les
documentations, 2001:db8::/32
était trop petit
pour vous ? Vous aviez du mal à exprimer des architectures réseau
complexes, avec beaucoup de préfixes ? Ne pleurez plus, un nouveau
préfixe a été alloué, c'est désormais un /20, le 3fff::/20
.
Ce RFC modifie
légèrement le RFC 3849, qui normalisait ce
préfixe de documentation. Le but d'un préfixe IP de documentation est
d'éviter que les auteur·es de ces documentations ne prennent des
adresses IP qui existent
par ailleurs, au risque que des administrateurices
réseaux maladroit·es ne copient ces adresses IP (songez
au nombre d'articles qui parlent d'IPv4 en
utilisant des exemples comme les adresses
1.1.1.1
ou 1.2.3.4
, qui
existent réellement). On doit donc utiliser les noms de domaine du RFC 2606, les adresses IPv4 du RFC 5737, et les numéros d'AS du RFC 5398. Pour IPv6, l'espace de
documentation est désormais 3fff::/20
(l'ancien
préfixe 2001:db8::/32
reste réservé et valable donc pas besoin de modifier les
documentations existantes).
Cette nouvelle taille permet de documenter des réseaux réalistes,
par exemple où deux /32
se parlent.
Si ce préfixe est désormais dans le registre des adresses spéciales, il ne semble pas (encore ?) décrit dans la base d'un RIR, contrairement à son prédécesseur.
Date de publication du RFC : Octobre 2024
Auteur(s) du RFC : A.D. Olson, P. Eggert
(UCLA), K. Murchison (Fastmail)
Chemin des normes
Première rédaction de cet article le 31 octobre 2024
Ce nouveau RFC
documente un format déjà ancien et largement déployé, TZif, un
format de description des fuseaux
horaires. Il définit également des types MIME pour ce format,
application/tzif
et
application/tzif-leap
. Il remplace le premier
RFC de normalisation de ce format, le RFC 8536, mais il y a très peu de changements. Bienvenue donc
à la version 4 du format, spécifiée dans ce RFC.
Ce format existe depuis quarante ans (et a pas mal évolué pendant ce temps) mais n'avait apparemment jamais fait l'objet d'une normalisation formelle avant le RFC 8536 en 2019. La connaissance des fuseaux horaires est indispensable à toute application qui va manipuler des dates, par exemple un agenda. Un fuseau horaire se définit par un décalage par rapport à UTC, les informations sur l'heure d'été, des abréviations pour désigner ce fuseau (comme CET pour l'heure de l'Europe dite « centrale ») et peut-être également des informations sur les secondes intercalaires. Le format iCalendar du RFC 5545 est un exemple de format décrivant les fuseaux horaires. TZif, qui fait l'objet de ce RFC, en est un autre. Contrairement à iCalendar, c'est un format binaire.
TZif vient à l'origine du monde Unix et est apparu dans les années 1980, quand le développement de l'Internet, qui connecte des machines situées dans des fuseaux horaires différents, a nécessité que les machines aient une meilleure compréhension de la date et de l'heure. Un exemple de source faisant autorité sur les fuseaux horaires est la base de l'IANA décrite dans le RFC 6557 et dont l'usage est documenté à l'IANA. Pour la récupérer, voir par exemple le RFC 7808.
La section 2 de notre RFC décrit la terminologie du domaine :
time()
, à savoir le nombre de
secondes depuis l'epoch, donc depuis le 1
janvier 1970. Il ne tient pas compte des secondes intercalaires,
donc il existe aussi un « temps Unix avec secondes intercalaires »
(avertissement : tout ce qui touche au temps et aux calendriers
est compliqué).
C'est ce dernier qui est utilisé dans le format TZif, pour
indiquer les dates et heures des moments où se fait une transition
entre heure d'hiver et heure d'été.La section 3 de notre RFC décrit le format lui-même. Un fichier TZif est composé d'un en-tête (taille fixe de 44 octets) indiquant entre autres le numéro de version de TZif. La version actuelle est 4. Ensuite, on trouve les données. Dans la version 1 de TZif, le bloc de données indiquait les dates de début et de fin des passages à l'heure d'été sur 32 bits, ce qui les limitait aux dates situées entre 1901 et 2038. Les versions ultérieures de TZif sont passées à 64 bits, ce qui permet de tenir environ 292 milliards d'années mais le bloc de données de la version 1 reste présent, au cas où il traine encore des logiciels ne comprenant que la version 1. Notez que ces 64 bits permettent de représenter des dates antérieures au Big Bang, mais certains logiciels ont du mal avec des valeurs situées trop loin dans le passé.
Les versions 2, 3 et 4 ont un second en-tête de 44 octets, et un bloc de données à elles. Les vieux logiciels arrêtent la lecture après le premier bloc de données et ne sont donc normalement pas gênés par cet en-tête et ce bloc supplémentaires. Les logiciels récents peuvent sauter le bloc de données de la version 1, qui ne les intéresse a priori pas (voir section 4 et annexe A). C'est au créateur du fichier de vérifier que les blocs de données destinés aux différentes versions sont raisonnablement synchrones, en tout cas pour les dates antérieures à 2038.
Nouveauté apparue avec la version 2, il y a aussi un pied de page à
la fin. Les entiers sont stockés en gros boutien, et en complément à deux. L'en-tête
commence par la chaîne magique « TZif » (U+0054 U+005A U+0069
U+0066), et comprend la longueur du bloc de données (qui dépend du
nombre de transitions, de secondes intercalaires et d'autres
informations à indiquer). Le bloc de données contient la liste des
transitions, le décalage avec UT, le nom du fuseau horaire, la liste des
secondes intercalaires, etc. Vu par le mode hexadécimal
d'Emacs, voici le début d'un fichier Tzif
version 2 (pris sur une Ubuntu, dans
/usr/share/zoneinfo/Europe/Paris
). On voit bien
la chaîne magique, puis le numéro de version, et le début du bloc de
données :
00000000: 545a 6966 3200 0000 0000 0000 0000 0000 TZif2........... 00000010: 0000 0000 0000 000d 0000 000d 0000 0000 ................ 00000020: 0000 00b8 0000 000d 0000 001f 8000 0000 ................ 00000030: 9160 508b 9b47 78f0 9bd7 2c70 9cbc 9170 .`P..Gx...,p...p 00000040: 9dc0 48f0 9e89 fe70 9fa0 2af0 a060 a5f0 ..H....p..*..`.. ...
Avec od, ça donnerait :
% od -x -a /usr/share/zoneinfo/Europe/Paris 0000000 5a54 6669 0032 0000 0000 0000 0000 0000 T Z i f 2 nul nul nul nul nul nul nul nul nul nul nul 0000020 0000 0000 0000 0d00 0000 0d00 0000 0000 nul nul nul nul nul nul nul cr nul nul nul cr nul nul nul nul 0000040 0000 b800 0000 0d00 0000 1f00 0080 0000 nul nul nul 8 nul nul nul cr nul nul nul us nul nul nul nul 0000060 6091 8b50 479b f078 d79b 702c bc9c 7091 dc1 ` P vt esc G x p esc W , p fs < dc1 p ...
Des exemples détaillés et commentés de fichiers TZif figurent en annexe B. À lire si vous voulez vraiment comprendre les détails du format.
Le pied de page indique notamment les extensions à la variable d'environnement TZ. Toujours avec le mode hexadécimal d'Emacs, ça donne :
00000b80: 4345 542d 3143 4553 542c 4d33 2e35 2e30 CET-1CEST,M3.5.0 00000b90: 2c4d 3130 2e35 2e30 2f33 0a ,M10.5.0/3.
On a vu que le format TZif avait une histoire longue et compliquée. La section 4 du RFC est entièrement consacrée aux problèmes d'interopérabilité, liés à la coexistence de plusieurs versions du format, et de beaucoup de logiciels différents. Le RFC conseille (sections 4 et 5) :
application/tzif-leap
ou
application/tzif
(s'il n'indique pas les
secondes intercalaires). Ces types MIME
sont désormais dans le registre
officiel (cf. section 9 du RFC).L'annexe A du RFC en rajoute, tenant compte de l'expérience accumulée ; par exemple, certains lecteurs de TZif n'acceptent pas les noms de fuseaux horaires contenant des caractères non-ASCII et il peut donc être prudent de ne pas utiliser ces caractères. Plus gênant, il existe des lecteurs assez bêtes pour planter lorsque des temps sont négatifs. Or, les entiers utilisant dans TZif sont signés, afin de pouvoir représenter les moments antérieurs à l'epoch. Donc, attention si vous avez besoin de données avant le premier janvier 1970, cela perturbera certains logiciels bogués.
En parlant des noms de fuseaux horaires, le RFC rappelle (section 5) que le sigle utilisé dans le fichier Tzif (comme « CET ») est en anglais et que, si on veut le traduire, cela doit être fait dans l'application, pas dans le fichier. Le RFC donne comme exemple CST, qui peut être présenté comme « HNC », pour « Heure Normale du Centre ».
Autre piège avec ces sigles utilisés pour nommer les fuseaux horaires : ils sont ambigus. PST peut être la côte Ouest des USA mais aussi l'heure du Pakistan ou des Philippines.
La section 7 du RFC donne quelques conseils de sécurité :
Enfin, l'annexe C du RFC liste les changements depuis le RFC 8536, changements qui mènent à cette nouvelle version, la 4. Rien de crucial mais :
Une bonne liste de logiciels traitant ce format figure à l'IANA.
Date de publication du RFC : Janvier 2025
Auteur(s) du RFC : B. Trammell (Google Switzerland), M. Welzl (University of Oslo), R. Enghardt (Netflix), G. Fairhurst (University of Aberdeen), M. Kuehlewind (Ericsson), C. Perkins (University of Glasgow), P. Tiesel (SAP SE), T. Pauly (Apple)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 23 janvier 2025
Voici un des RFC du projet TAPS, qui vise à proposer une vision cohérente des protocoles de transport aux applications. Ce RFC décrit (mais de manière assez abstraite) l'API de TAPS.
Avant de le lire, il est très fortement recommandé de réviser le RFC 9621, qui décrit les buts du projet TAPS, et les principes de l'API Transport Services (autrefois surnommée « post-sockets »). Mais le RFC 9621 restait assez abstrait, et notre RFC 9622 se fait un peu plus concret (sans aller jusqu'à une vraie API puisqu'il reste partiellement indépendant du langage de programmation utilisé).
Rappelons quelques principes de TSAPI (Transport Services API, l'API qui est le but final du projet TAPS). D'abord, l'API est asynchrone (ce qui n'est pas idéal pour les langages avec parallélisme comme Go ou Elixir). On crée des Connexions (la majuscule au début est là pour se distinguer du concept flou de connexion, et pour désigner un type d'objets particulier), voire des Préconnexions si on veut les configurer avant, on leur définit des propriétés (en indiquant si elles sont impératives ou facultatives), on peut ensuite activer les Connexions (ou écouter passivement que l'autre machine les active), puis on envoie et on reçoit des Messages. La section 3 du RFC résume l'API, et, si vous êtes pressé·e, vous pouvez vous contenter de cette section. Elle contient quelques exemples pratiques, en pseudo-code.
À propos de pseudo-code, le RFC note que les conventions de nommage de l'API ne pourront pas forcément être suivies religieusement par toutes les mises en œuvre puisque certains langages de programmation ont des règles différentes. Mais c'est un détail.
Parmi les points dignes d'intérêt dans le RFC, on note que les applications peuvent indiquer le domaine d'avitaillement (PvD, ProVisioning Domain, défini dans le RFC 7556) qu'elles veulent utiliser. Elles peuvent par exemple l'identifier par son nom de domaine (RFC 8801).
Autre point intéressant, les cadreurs (framers). Décrits dans la section 9.1.2, ce sont des couches logicielles qui vont mettre en œuvre les exigences du protocole sous-jacent. Lors d'une opération d'envoi ou de réception d'un Message, les cadreurs vont tenir compte du protocole pour transformer un Message en flux d'octets et réciproquement. (Le RFC suggère de regarder le RFC 9329, pour un exemple.)
Pour mettre en œuvre cette API assez abstraite dans un langage de programmation spécifique, l'annexe A du RFC donne quelques conseils. Il y a des mises en œuvre de TAPS (plusieurs sont listées dans le RFC 9623, annexe C). On peut citer :
Notez un point positif dans le RFC : l'annexe C du RFC 9623, qui décrit les mises en œuvre existantes donne non
seulement les URL « normaux » du projet, et ceux sur
GitHub mais aussi un lien
Software Heritage, pour la pérennité. C'est
une très bonne chose, car certains RFC ont une longue durée de
vie). Par exemple, NEAT est
mais aussi
https://github.com/NEAT-project/neat
.
https://archive.softwareheritage.org/swh:1:dir:737820840f83c4ec9493a8c0cc89b3159e2e1a57;origin=https://github.com/NEAT-project/neat;visit=swh:1:snp:bbb611b04e355439d47e426e8ad5d07cdbf647e0;anchor=swh:1:rev:652ee991043ce3560a6e5715fa2a5c211139d15c
Date de publication du RFC : Janvier 2025
Auteur(s) du RFC : T. Pauly (Apple), B. Trammell (Google Switzerland), A. Brunstrom (Karlstad University), G. Fairhurst (University of Aberdeen), C. Perkins (University of Glasgow)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 23 janvier 2025
Ce RFC s'inscrit dans un projet IETF datant de quelques années : formaliser davantage les services que rend la couche Transport aux applications. Le but est de permettre de développer des applications en indiquant ce qu'on veut de la couche Transport, en rentrant le moins possible dans les détails. Ce RFC décrit l'architecture générale, le RFC 9622 spécifiant quant à lui une API plus concrète.
Des API pour les applications parlant à la couche Transport, il y en a beaucoup. La plus connue est bien sûr l'interface « socket », qui a pour principal avantage d'être ancienne et très documentée, dans des livres (comme le génial Stevens) et dans d'innombrables logiciels libres à étudier (mais, par contre, la norme POSIX qui la spécifie officiellement, n'est toujours pas disponible publiquement). C'est d'ailleurs cette multiplicité d'API, donc beaucoup sont solidement installées dans le paysage, qui fait que je doute du succès du projet et de sa TSAPI (Transport Services API) : la base installée à déplacer est énorme.
Pourtant, ces API traditionnelles ont de nombreux défauts. Le RFC
cite par exemple l'incohérence : pour envoyer des données sur une
prise qui utilise du TCP en clair, on
utilise send()
mais quand
la communication est chiffrée avec TLS, c'est une autre
fonction (non normalisée). Pourtant, pour l'application, c'est la
même chose.
Toujours question cohérence, la terminologie n'est pas fixée et des termes comme flow, stream, connection et message ont des sens très différents selon les cas.
Le but de la nouvelle architecture est donc de permettre le développement d'une API (normalisée dans le RFC 9622) qui unifiera tout cela. (Notez que l'IETF, habituellement, normalise des protocoles et pas des API. Mais il y a déjà eu des exceptions à cette règle.) Un autre objectif est de permettre de mettre dans les bibliothèques du code qui autrefois était souvent dupliqué d'une application à l'autre, comme la mise en œuvre de l'algorithme des « globes oculaires heureux » (RFC 8305).
J'avais déjà présenté le projet TAPS, qui était alors nettement moins avancé, dans un exposé à la conférence MiXiT (dont voici le support).
L'architecture présentée ici est dérivée de l'analyse des protocoles de transport du RFC 8095 et de leur synthèse dans le RFC 8923. Pour les questions de sécurité, il est également conseillé de lire le RFC 8922.
L'interface pour les applications (TSAPI : Transport Services Application Programming Interface) sera fondée sur l'asynchronicité, en mode programmation par évènements. (Opinion personnelle : c'est une mauvaise idée, excessivement favorable aux langages sans vrai parallélisme, comme C. J'aurais préféré un modèle synchrone, fondé sur des fils d'exécution parallèle, plus simple pour la·e programmeur·se.)
Une des idées de base du projet est de développer une API qui soit indépendante du langage de programmation. C'est à mon avis fichu dès le départ. Outre cette question du parallélisme, la gestion des erreurs est très variable d'un langage à l'autre : codes de retour (en C), exceptions, valeurs mixtes comme en Haskell ou en Zig… L'API du RFC 9622 ne plaira donc pas à tout le monde.
La section 2 du RFC expose le modèle utilisé par l'API. D'abord, un résumé de la traditionnelle API « socket » :
SOCK_STREAM
mais, en pratique, son
utilisation nécessite de connaitre le protocole de transport utilisé),La nouvelle API généralisera ce modèle : l'application utilise l'API Transport Services (TSAPI), en indiquant les propriétés souhaitées pour la connexion, et la mise en œuvre de l'API parlera à tous les protocoles de transport (et de chiffrement) possibles, qu'ils soient placés dans le noyau ou pas. La résolution de noms est intégrée dans ce cadre, entre autres parce que certains services sous-jacents, notamment TLS, nécessitent de connaitre le nom du service auquel on accède (ce qui n'est pas le cas avec l'API socket, qui ne manipule que des adresses IP).
Pendant qu'on en parle, cette idée d'intégrer la résolution de
noms est également dans des projets comme Connect by
Name, qui vise à permettre aux applications de juste
demander connect(domain-name, port, tls=true)
et que tout soit fait proprement par l'API, notamment les fonctions
de sécurité (vérifier le nom dans le
certificat, par exemple, ce qui n'est pas
trivial à faire proprement).
On l'a dit plus haut, TSAPI est asynchrone. Le RFC estime que ce mode d'interaction avec le réseau, fondé sur des évènements, est plus fréquent et même « plus naturel ». C'est un des reproches que je ferais au projet. Je préfère le modèle d'interactions synchrone, notamment pour des langages de programmation ayant la notion de fils d'exécution séparés (les goroutines en Go, les processus en Erlang ou Elixir, les tâches d'Ada…), dans l'esprit des Communicating sequential processes (et j'en profite pour recommander le génial livre de Hoare, bien qu'il soit assez abstrait et d'un abord austère). Rien n'est naturel en programmation (ou ailleurs : « la seule interface utilisateur naturelle est le téton ; tout le reste est appris ») et le RFC se débarrasse un peu vite du choix effectué et ne cherche pas vraiment à le justifier.
Donc, l'application qui veut, par exemple, recevoir des données, va appeler l'API pour demander une lecture et l'API préviendra l'application quand des données seront prêtes. Au contraire, dans un modèle synchrone, le fil qui veut lire sera bloqué tant qu'il n'y aura rien à lire, les autres fils continuant leur travail.
Dans l'API socket, on peut avoir des messages séparés lorsqu'on utilise UDP mais, avec TCP, c'est forcément une suite d'octets, sans séparation des éventuels messages. Chaque protocole applicatif doit donc, s'il en a besoin, inventer sa méthode de découpage en messages : DNS et EPP indiquent la longueur du message avant d'envoyer le message, HTTP/1 l'indique dans l'en-tête mais a un mécanisme de découpage fondé sur des délimiteurs (cf. RFC 9112, sections 6.1 et 7), HTTP/2 et HTTP/3 ont leurs ruisseaux (une requête par ruisseau), TLS utilise également une indication de longueur. Au contraire, avec TSAPI, il y a toujours un découpage en messages, qui seront encodés en utilisant les capacités du protocole de transport sous-jacent (par un mécanisme nommé cadreur - framer).
TSAPI permet des choses qui n'étaient pas possibles avec les API classiques, comme d'avoir plusieurs paires d'adresses IP entre les machines qui communiquent, avec changement de la paire utilisée pendant une connexion.
Un des intérêts de cette nouvelle API est de permettre aux applications de fonctionner de manière plus déclarative et moins impérative. Au lieu de sélectionner tel ou tel protocole précis, l'application déclare ses exigences, et TSAPI se débrouillera pour les lui fournir (section 3.1).
Bon, c'est bien joli, de vouloir que les applications ne connaissent pas les protocoles (ce qui permet de les faire évoluer plus facilement) mais, quand même, parfois, il existe des fonctions très spécifiques d'un protocole particulier qu'il serait bien sympa d'utiliser. La section 3.2 de notre RFC couvre donc cette question. Par exemple, l'option User Timeout de TCP (RFC 5482) est bien pratique, même si elle est spécifique à TCP. Donner accès à ces spécificités soulève toutefois un problème : parfois, l'application souhaite une certaine fonction, et peut donc s'en passer, et parfois elle l'exige et la connexion doit échouer si elle n'est pas disponible pour un certain protocole. L'API devra donc distinguer les deux cas.
Une fois que l'application a indiqué ses préférences et ses exigences, l'API devra sélectionner un protocole de transport. Parfois, plusieurs protocoles conviendront. Si l'application demande juste un transport fiable d'une suite d'octets, TCP, SCTP et QUIC conviendront tous les trois (alors qu'UDP serait exclu).
Comme rien n'est gratuit en ce bas monde, le progrès de l'abstraction, et une relative indépendance de l'application par rapport aux particularités des protocoles de transport, ont un inconvénient : le système devient plus complexe. Les développeureuses d'application lorsqu'il faut déboguer, et les administrateurices système qui vont installer (et essuyer les plâtres !) les applications vont avoir des difficultés nouvelles. Des fonctions de déboguage devront donc être intégrées. (Personnellement, je pense aussi à la nécessité que la bibliothèque qui mette en œuvre TSAPI coopère au déboguage pour les protocoles chiffrés, qui ne permettront pas tellement d'utiliser Wireshark. Voir mon exposé à Capitole du Libre.)
Et enfin, après ces généralités, passons aux concepts qui devront être visibles par l'application, rapprochons-nous de la future API (section 4 du RFC). D'abord, la Connexion. Non, ce n'est pas une erreur d'avoir capitalisé le mot. Le RFC utilise la même méthode pour montrer qu'il s'agit d'un concept abstrait, qui ne correspond pas forcément parfaitement aux connexions du protocole de transport (pour ceux qui ont ce concept). La Connexion est donc au cœur de l'API Transport Services. Elle se fait entre deux points, le local et le distant, chacun identifié par un Endpoint Identifier. Elle a un certain nombre de propriétés, qui peuvent s'appliquer avant (et influenceront la sélection du protocole de transport, et/ou des machines auxquelles ont se connecte), juste avant ou pendant la Connexion, par exemple l'utilisation du 0-RTT (RFC 9622, section 6.2.5) ou l'exigence du chiffrement (cf. RFC 8922), mais il y a aussi des propriétés pour chaque message individuel. Ensuite, on va :
Close
ou salement avec Abort
).
Un écoutant va créer une Pré-Connexion et attendre les demandes, ce
n'est pas lui qui va établir les connexions. L'entité active, elle,
va se connecter (fonctions Listen
et
Initiate
). Et le
pair-à-pair ? Il sera également possible via
la fonction Rendezvous
. Ce sera à l'API de
gérer les services comme STUN (RFC 8489),
souvent indispensables en pair-à-pair.
Le transfert de données a les classiques fonctions
Send
et Receive
(qui n'est
pas synchrone, rappelons-le, donc qui ne bloque pas). Mais il y a
aussi un nouveau concept, le cadreur (traduction peu imaginative de
framer). C'est la couche logicielle qui va ajouter et/ou modifier les
données du message pour mettre en œuvre des exigences du protocole,
par exemple indiquer les frontières entre messages dans un protocole
de transport qui n'a pas ce concept (comme TCP). Ainsi, les
protocoles EPP (RFC 5734) et
DNS (RFC 7766) nécessitent que chaque message soit
préfixé d'un seizet indiquant sa longueur. Un
cadreur peut faire cela automatiquement pour chaque message,
simplifiant la tâche de l'application (par exemple la bibliothèque
standard d'Erlang a cette
option - cf. packet
).
Enfin, un peu de sécurité et de vie privée (section 6). D'abord, les implémentations devront faire attention aux risques pour la vie privée si on réutilise l'état des connexions passées. C'est le cas des sessions TLS (RFC 8446, section 2.2) ou des gâteaux HTTP (RFC 6265), qui peuvent permettre de relier deux connexions entre elles, lien qu'on ne souhaite peut-être pas.
Et puis, en sécurité, le RFC rappelle qu'on ne doit pas se replier sur des solutions non sécurisées si la technique sécurisée échoue. Ainsi, si une application demande du chiffrement, mais que la connexion chiffrée échoue, il ne faut pas se rabattre sur une connexion en clair.
Notez que l'ensemble du projet TAPS a bénéficié de divers financements publics, par exemple de l'UE.
Une opinion personnelle sur ce projet ? Outre des critiques sur tel ou tel choix, comme l'asynchronisme, je suis malheureusement sceptique quant aux chances de réussite. Les API actuelles, avec tous leurs défauts, sont solidement installées, et dans le meilleur des cas, il faudra de nombreuses années pour que les formations s'adaptent et enseignent une API comme TSAPI.
Date de publication du RFC : Septembre 2024
Auteur(s) du RFC : G. Grover, N. ten Oever (University of Amsterdam)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF hrpc
Première rédaction de cet article le 17 septembre 2024
Voici un RFC explicitement politique, puisqu'il documente la façon dont les concepteur·rices de protocoles à l'IETF devraient examiner les conséquences de leurs protocoles sur l'exercice des droits humains. Si vous concevez un protocole réseau (IETF ou pas), c'est une lecture recommandée.
Les protocoles ne sont pas neutres puisqu'ils ont des conséquences concrètes sur les utilisateurices, conséquences positives ou négatives. S'ils n'en avaient pas, on ne passerait pas du temps à les développer, et on ne dépenserait pas d'argent à les déployer. Ces conséquences ne sont pas forcément faciles à déterminer, surtout avant tout déploiement effectif, mais ce RFC peut guider la réflexion et permettre d'identifier les points qui peuvent avoir des conséquences néfastes. Il comprend de nombreux exemples tirés de précédents RFC. Il met à jour partiellement le RFC 8280, dont il reprend la section 6, et s'inspire de la méthode du RFC 6973, qui documentait les conséquences des protocoles sur la vie privée.
La question est évidemment complexe. Les protocoles n'ont pas forcément un pouvoir direct sur ce que les utilisateurices peuvent faire ou pas (c'est l'argument central de ceux qui estiment que les techniques sont neutres : HTTP transférera aussi bien un article de la NASA qu'un texte complotiste sur les extra-terrestres). Leur rôle est plutôt indirect, en ce qu'ils encouragent ou découragent certaines choses, plutôt que d'autoriser ou interdire. Et puis, comme le note le RFC, cela dépend du déploiement. Par exemple, pour le courrier électronique, des faits politiques importants ne s'expliquent pas par le protocole. Ce ne sont pas les particularités de SMTP (RFC 5321) qui expliquent la domination de Gmail par exemple. Il ne faut en effet pas tomber dans le déterminisme technologique (comme le font par exemple les gens qui critiquent DoH) : l'effet dans le monde réel d'un protocole dépend de bien d'autres choses que le protocole.
Ah, et autre point dans l'introduction, la définition des droits humains. Notre RFC s'appuie sur la déclaration universelle des droits humains et sur d'autres documents comme le International Covenant on Economic, Social and Cultural Rights.
Parmi les droits humains cités dans cette déclaration universelle, nombreux sont ceux qui peuvent être remis en cause sur l'Internet : liberté d'expression, liberté d'association, droit à la vie privée, non-discrimination, etc. L'absence d'accès à l'Internet mène également à une remise en cause des droits humains, par exemple parce que cela empêche les lanceurs d'alerte de donner l'alerte. Les atteintes aux droits humains peuvent être directes (censure) ou indirectes (la surveillance des actions peut pousser à l'auto-censure, et c'est souvent le but poursuivi par les acteurs de la surveillance ; cf. « Chilling Effects: Online Surveillance and Wikipedia Use »). Et les dangers pour les individus ne sont pas seulement « virtuels », ce qui se passe en ligne a des conséquences physiques quand, par exemple, une campagne de haine contre des gens accusés d'attaques contre une religion mène à leur assassinat, ou quand un État emprisonne ou tue sur la base d'informations récoltées en ligne.
C'est pour cela que, par exemple, le conseil des droits humains de l'ONU mentionne que les droits qu'on a dans le monde physique doivent aussi exister en ligne. [La propagande des médias et des politiciens en France dit souvent que « l'Internet est une zone de non-droit » et que « ce qui est interdit dans le monde physique doit aussi l'être en ligne », afin de justifier des lois répressives. Mais c'est une inversion complète de la réalité. En raison des particularités du numérique, notamment la facilité de la surveillance de masse, et de l'organisation actuelle du Web, avec un petit nombre d'acteurs médiant le trafic entre particuliers, les droits humains sont bien davantage menacés en ligne.] Sur la question de l'application des droits humains à l'Internet, on peut aussi lire les « 10 Internet Rights & Principles> » et le « Catalog of Human Rights Related to ICT Activities ».
Comme les droits humains sont précieux, et sont menacés sur l'Internet, l'IETF doit donc veiller à ce que son travail, les protocoles développés, n'aggravent pas la situation. D'où la nécessité, qui fait le cœur de ce RFC, d'examiner les protocoles en cours de développement. Ces examens (reviews, section 3 du RFC) devraient être systématiques et, évidemment, faits en amont, pas une fois le protocole déployé. [En pratique, ces examens ont assez vite été arrêtés, et ce RFC ne reflète donc pas la situation actuelle.]
Vu la façon dont fonctionne l'IETF, il n'y a pas besoin d'une autorité particulière pour effectuer ces examens. Tout·e participant·e à l'IETF peut le faire. Ce RFC 9620 vise à guider les examinateurs (reviewers). L'examen peut porter sur le contenu d'un Internet Draft mais aussi être complété, par exemple, avec des interviews d'experts de la question (les conséquences de tel ou tel paragraphe dans un Internet Draft ne sont pas forcément évidentes à la première lecture, ou même à la seconde) mais aussi des gens qui seront affectés par le protocole en question. Si un futur RFC parle d'internationalisation, par exemple, il ne faut pas interviewer que des anglophones, et pas que des participants à l'IETF, puisque l'internationalisation concerne tout le monde.
Une grosse difficulté, bien sûr, est que le protocole n'est pas tout. Les conditions effectives de son déploiement, et son évolution dans le temps, sont cruciales. Ce n'est pas en lisant le RFC 5733 (ou le RFC 9083) qu'on va tout comprendre sur les enjeux de la protection des données personnelles des titulaires de noms de domaine ! Le RFC 8980 discute d'ailleurs de cette importante différence entre le protocole et son déploiement.
La plus importante section de notre RFC est sans doute la section 4, qui est une sorte de check-list pour les auteur·es de protocoles et les examinateurices. Idéalement, lors de la phase de conception d'un protocole, il faudrait passer toutes ces questions en revue. Bien évidemment, les réponses sont souvent complexes : la politique n'est pas un domaine simple.
Premier exemple (je ne vais pas tous les détailler, rassurez-vous), les intermédiaires. Est-ce que le protocole permet, voire impose, des intermédiaires dans la communication ? C'est une question importante car ces intermédiaires, s'ils ne sont pas sous le contrôle des deux parties qui communiquent, peuvent potentiellement surveiller la communication (risque pour la confidentialité) ou la perturber (risque pour la liberté de communication). Un exemple est l'interception HTTPS (cf. « The Security Impact of HTTPS Interception »). Le principe de bout en bout (RFC 1958 ou bien « End-to-End Arguments in System Design ») promeut plutôt une communication sans intermédiaires, mais on trouve de nombreuses exceptions dans les protocoles IETF (DNS, SMTP…) ou en dehors de l'IETF (ActivityPub), car les intermédiaires peuvent aussi rendre des services utiles. En outre, ces intermédiaires tendent à ossifier le protocole, en rendant plus difficile le déploiement de tout changement (cf. RFC 8446 pour les problèmes rencontrés par TLS 1.3).
Le RFC fait aussi une différence entre intermédiaires et services. Si vous êtes utilisateurice de Gmail, Gmail n'est pas un intermédiaire, mais un service car vous êtes conscient·e de sa présence et vous l'avez choisi. [L'argument me semble avoir des faiblesses : ce genre de services pose exactement les mêmes problèmes que les intermédiaires et n'est pas forcément davantage maitrisé.]
Un bon moyen de faire respecter le principe de bout en bout est
de chiffrer au
maximum. C'est pour cela que QUIC (RFC 9000) chiffre davantage de données que
TLS. C'est aussi pour cela que l'IETF
travaille au chiffrement du SNI (draft-ietf-tls-esni
).
Autre exemple, la connectivité (section 4.2). Car, si on n'a pas accès à l'Internet du tout, ou bien si on y a accès dans des conditions très mauvaises, tout discussion sur les droits humains sur l'Internet devient oiseuse. L'accès à l'Internet est donc un droit nécessaire (cf. la décision du Conseil Constitutionnel sur Hadopi, qui posait le principe de ce droit d'accès à l'Internet). Pour le protocole, cela oblige à se pencher sur son comportement sur des liens de mauvaise qualité : est-ce que ce protocole peut fonctionner lorsque la liaison est mauvaise ?
Un sujet délicat (section 4.4) est celui des informations que le protocole laisse fuiter (l'« image vue du réseau » du RFC 8546). Il s'agit des données qui ne sont pas chiffrées, même avec un protocole qui fait de la cryptographie, et qui peuvent donc être utilisées par le réseau, par exemple pour du traitement différencié (de la discrimination, pour dire les choses franchement). Comme recommandé par le RFC 8558, tout protocole doit essayer de limiter cette fuite d'informations. C'est pour cela que TCP a tort d'exposer les numéros de port, par exemple, que QUIC va au contraire dissimuler.
L'Internet est mondial, on le sait. Il est utilisé par des gens qui ne parlent pas anglais et/ou qui n'utilisent pas l'alphabet latin. Il est donc crucial que le protocole fonctionne pour tout le monde (section 4.5). Si le protocole utilise des textes en anglais, cela doit être de manière purement interne (le GET de HTTP, le Received: de l'IMF, etc), sans être obligatoirement montré à l'utilisateurice. Ce principe, formulé dans le RFC 2277, dit que tout ce qui est montré à l'utilisateur doit pouvoir être traduit. (En pratique, il y a des cas limites, comme les noms de domaine, qui sont à la fois éléments du protocole, et montrés aux utilisateurs.)
Si le protocole sert à transporter du texte, il doit évidemment utiliser Unicode, de préférence encodé en UTF-8. S'il accepte d'autres encodages et/ou d'autres jeux de caractère (ce qui peut être dangereux pour l'interopérabilité), il doit permettre d'étiqueter ces textes, afin qu'il n'y ait pas d'ambiguité sur leurs caractéristiques. Pensez d'ailleurs à lire le RFC 6365.
Un contre-exemple est le vieux protocole whois (RFC 3912), qui ne prévoyait que l'ASCII et, si on peut l'utiliser avec d'autres jeux de caractères, comme il ne fournit pas d'étiquetage, le client doit essayer de deviner de quoi il s'agit. (Normalement, whois n'est plus utilisé, on a le Web et RDAP, mais les vieilles habitudes ont la vie dure.)
Toujours question étiquetage, notre RFC rappelle l'importance de pouvoir, dans le protocole, indiquer explicitement la langue des textes (RFC 5646). C'est indispensable afin de permettre aux divers logiciels de savoir quoi en faire, par exemple en cas de synthèse vocale.
Le RFC parle aussi de l'élaboration des normes techniques (section 4.7). Par exemple, sont-elles dépendantes de brevets (RFC 8179 et RFC 6701) ? [Personnellement, je pense que c'est une question complexe : les brevets ne sont valables que dans certains pays et, en outre, la plupart des brevets logiciels sont futiles, brevetant des technologies banales et déjà connues. Imposer, comme le proposent certains, de ne normaliser que des techniques sans brevet revient à donner un doit de veto à n'importe quelle entreprise qui brevète n'importe quoi. Par exemple, le RFC 9156 n'aurait jamais été publié si on s'était laissé arrêter par le brevet.]
Mais un autre problème avec les normes techniques concerne leur disponibilité. Si l'IETF, le W3C et même l'UIT publient leurs normes, ce n'est pas le cas de dinosaures comme l'AFNOR ou l'ISO, qui interdisent même la redistribution de normes qu'on a légalement acquises. Si les normes IETF sont de distribution libre, elles dépendent parfois d'autres normes qui, elles, ne le sont pas.
Un peu de sécurité informatique, pour continuer. La section 4.11 traite de l'authentification des données (ce que fait DNSSEC pour le DNS, par exemple). Cette possibilité d'authentification est évidemment cruciale pour la sécurité mais le RFC note qu'elle peut aussi être utilisée négativement, par exemple avec les menottes numériques.
Et il y a bien sûr la confidentialité (section 4.12 mais aussi RFC 6973), impérative depuis toujours mais qui était parfois sous-estimée, notamment avant les révélations Snowden. Les auteur·es de protocoles doivent veiller à ce que les données soient et restent confidentielles et ne puissent pas être interceptées par un tiers. Il y a longtemps que tout RFC doit contenir une section sur la sécurité (RFC 3552), exposant les menaces spécifiques à ce RFC et les contre-mesures prises, entre autre pour assurer la confidentialité. L'IETF refuse, à juste titre, toute limitation de la cryptographie, souvent demandée par les autorités (RFC 1984). Les exigences d'accès par ces autorités (en invoquant des arguments comme la lutte contre le terrorisme ou la protection de l'enfance) ne peuvent mener qu'à affaiblir la sécurité générale puisque ces accès seront aussi utilisés par les attaquants, ou par un État qui abuse de son pouvoir.
Le modèle de menace de l'Internet, depuis longtemps, est que tout ce qui est entre les deux machines situées aux extrémités de la communication doit être considéré comme un ennemi. Pas parce que les intermédiaires sont forcément méchants, loin de là, mais parce qu'ils ont des possibiliés techniques que certains exploiteront et il faut donc protéger la communication car on ne sait jamais ce que tel ou tel intermédiaire fera (RFC 7258 et RFC 7624). Bref, tout protocole doit chiffrer le contenu qu'il transporte (RFC 3365). Aujourd'hui, les principales exceptions à ce principe sont le très vieil whois (RFC 3912) et surtout le DNS qui a, certes, des solutions techniques pour le chiffrement (RFC 7858 et RFC 8484) mais qui sont loin d'être universellement déployées.
Ce chiffrement doit évidemment être fait de bout en bout, c'est-à-dire directement entre les deux machines qui communiquent, afin d'éviter qu'un intermédiaire n'ait accès à la version en clair. Cela pose un problème pour les services store-and-forward comme le courrier électronique (RFC 5321). De même, chiffrer lorsqu'on communique en HTTPS avec Gmail ne protège pas la communication contre Google, seulement contre les intermédiaires réseau ! Relire le RFC 7624 est recommandé.
Question vie privée, le RFC recommande également de faire attention aux métadonnées et à l'analyse de trafic. Les conseils du RFC 6973, section 7, sont ici utiles.
Un sujet encore plus délicat est celui de l'anonymat et du pseudonymat. On sait qu'il n'y a pas réellement d'anonymat sur l'Internet (quoiqu'en disent les politiciens malhonnêtes et les journalistes avides de sensationnalisme), le numérique permettant au contraire de récolter et de traiter beaucoup de traces de la communication. Néanmoins, le protocole doit permettre, autant que possible, de s'approcher de l'anonymat. Par exemple, les identificateurs persistents sont évidemment directement opposés à cet objectif puisqu'ils rendent l'anonymat impossible (rappel important : anonymat ≠ pseudonymat). Au minimum, il faudrait permettre à l'utilisateur de changer facilement et souvent ces identificateurs. Et, bien sûr, ne pas imposer qu'ils soient liés à l'identité étatique. Des exemples d'identificateurs qui sont parfois utilisés sur le long terme sont les adresses IP, les Connection ID de QUIC (un bon exemple d'un protocole qui permet leur changement facilement), les biscuits de HTTP, et les adresses du courrier électronique, certainement très difficiles à changer. Comme le montre l'exemple de ces adresses, les identificateurs stables sont utiles et on ne peut pas forcément les remplacer par des identificateurs temporaires. Ici, le souci de vie privée rentre en contradiction avec l'utilité des identificateurs, une tension courante en sécurité. Le fait qu'on ne puisse en général pas se passer d'identificateurs, à relativement longue durée de vie, est justement une des raisons pour lesquelles il n'y a pas de vrai anonymat sur l'Internet.
Notons que les politiciens de mauvaise foi et les journalistes incompétents parlent parfois d'anonymat dès qu'un identificateur stable n'est pas l'identité étatique (par exemple quand je crée un compte Gmail « anonymous652231 » au lieu d'utiliser le nom qui est sur ma carte d'identité). Mais tout identificateur stable peut finir par se retrouver lié à une autre identité, peut-être aussi à l'identité étatique, par exemple si deux identificateurs sont utilisés dans le même message. Et certains identificateurs sont particulièrement communs, avec plusieurs usages, ce qui les rend encore plus dangereux pour la vie privée. Le numéro de téléphone, que certaines messageries instantanées imposent, est particulièrement sensible et est donc déconseillé.
Donc, s'il faut utiliser des identificateurs stables, ils doivent au moins pouvoir être des pseudonymes.
D'autres façons de désanonymiser existent, par exemple quand les gens ont bêtement cru que condenser un identificateur n'était pas réversible (cf. l'article « Four cents to deanonymize: Companies reverse hashed email addresses »).
Notre RFC rappelle ainsi les discussions animées qu'avait connu
l'IETF en raison d'un mécanisme d'allocation des adresses
IPv6, qui les faisaient dériver d'un
identificateur stable, l'adresse
MAC, qui permettait de suivre à la trace un
utilisateur mobile. Depuis, le RFC 8981 (et
le RFC 7217 pour les cas où on veut une
stabilité limitée dans l'espace) ont résolu ce problème (le RFC 7721 résume le débat). À noter que l'adresse MAC peut aussi
devenir variable (draft-ietf-madinas-mac-address-randomization
).
Autre exemple où un protocole IETF avait une utilisation imprudente des identificateurs, DHCP, avec ses identificateurs stables qui, certes, n'étaient pas obligatoires mais, en pratique, étaient largement utilisés (RFC 7844).
Une autre question très sensible est celle de la
censure. Le protocole en cours de
développement a-t-il des caractéristiques qui rendent la censure
plus facile ou au contraire plus difficile (section 4.16) ? Par
exemple, si le protocole fait passer par des points bien identifiés
les communications, ces points vont certainement tenter les censeurs
(pensez aux résolveurs DNS et à leur rôle dans la censure…). Les RFC 7754 et RFC 9505 décrivent les
techniques de censure. Elles sont très variées. Par exemple, pour le
Web, le censeur peut agir sur les résolveurs DNS mais aussi bloquer
l'adresse IP ou bien, en faisant du DPI,
bloquer les connexions TLS en regardant le
SNI. Certains systèmes d'accès au contenu,
comme Tor, ou de distribution du
contenu, comme BitTorrent, résistent mieux à
la censure que le Web mais ont d'autres défauts, par exemple en
termes de performance. L'exemple du SNI montre
en tout cas très bien une faiblesse de certains protocoles : exposer
des identificateurs aux tierces parties (qui ne sont aucune des
deux parties qui communiquent) facilite la censure. C'est pour cela
que l'IETF développe ECH
(cf. draft-ietf-tls-esni/
).
Le protocole, tel que normalisé dans un RFC, c'est bien joli, mais il faut aussi tenir compte du déploiement effectif. Comme noté au début, ce n'est pas dans le RFC 5321 qu'on trouvera les causes de la domination de Gmail ! Les effets du protocole dans la nature sont en effet très difficiles à prévoir. La section 4.17 se penche sur cette question et demande qu'on considère, non seulement le protocole mais aussi les effets qu'il aura une fois déployé. [Ce qui, à mon avis, relève largement de la boule de cristal, surtout si on veut tenir compte d'effets économiques. Et les exemples du RFC ne sont pas géniaux, comme de reprocher au courrier sa gratuité, qui encourage le spam. L'exemple de l'absence de mécanisme de paiement sur le Web, qui pousse à développer des mécanismes néfastes comme la publicité, est meilleur.] La section 4.21 traite également ce sujet des conséquences, parfois inattendues, du déploiement d'un protocole.
Le RFC a aussi un mot (section 4.18) sur les questions d'accessibilité, notamment aux handicapés. Cette question, très présente dans les discussions autour des couches hautes du Web (cf. les réunions Paris Web) semble plus éloignée de ce que fait l'IETF mais le RFC cite quand même l'exemple du RFC 9071, sur l'utilisation de RTP dans des réunions en ligne, avec une alternative en texte pour les personnes malentendantes.
L'Internet est aujourd'hui très, trop, centralisé, notamment pour ce qui concerne les services (la connectivité, quoique imparfaitement répartie, est moins dépendante d'un petit nombre d'acteurs). Il est donc utile, lors de la conception d'un protocole, de réfléchir aux caractéristiques du protocole qui risquent d'encourager la centralisation (par exemple par la création d'un, ou d'un petit nombre de points de contrôle). Le RFC 3935 donne explicitement à l'IETF un objectif de promotion de la décentralisation.
En conclusion, même si l'activité organisée d'examen des futurs RFC n'a pas pris, ce RFC reste utile pour réfléchir à l'impact de nos protocoles sur les droits des humains.
Date de publication du RFC : Juillet 2024
Auteur(s) du RFC : R. Bellis (ISC), J. Abley
(Cloudflare)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 25 juillet 2024
Dans un message DNS, il y a quatre sections qui peuvent prendre un nombre variable d'enregistrements (resource records). Chaque section est précédée d'un chiffre qui indique ce nombre d'enregistrements. L'une de ces sections indique la question posée. Est-ce que cela a un sens de mettre plusieurs questions dans un message ? Non, répond, ce très court RFC, qui clarifie le RFC 1035 sur ce point.
Regardons une requête DNS avec tshark :
% tshark -V -r dns.pcap … Domain Name System (query) Transaction ID: 0x0dfd Flags: 0x0120 Standard query 0... .... .... .... = Response: Message is a query .000 0... .... .... = Opcode: Standard query (0) … Questions: 1 Answer RRs: 0 Authority RRs: 0 Additional RRs: 1 Queries wylag.de: type A, class IN Name: wylag.de [Name Length: 8] [Label Count: 2] Type: A (Host Address) (1) Class: IN (0x0001) Additional records <Root>: type OPT Name: <Root> Type: OPT (41) UDP payload size: 4096 Higher bits in extended RCODE: 0x00 EDNS0 version: 0 Z: 0x8000 1... .... .... .... = DO bit: Accepts DNSSEC security RRs .000 0000 0000 0000 = Reserved: 0x0000 Data length: 12 Option: COOKIE Option Code: COOKIE (10) Option Length: 8 Option Data: 93c545f2aaf3f12c Client Cookie: 93c545f2aaf3f12c Server Cookie: <MISSING>
Il s'agit d'une requête, pas d'une réponse, donc il est normal que
les sections Answer et Authority soient vides (taille à zéro). La
section Additional n'est pas vide car elle contient l'enregistrement
EDNS. Et la section Question contient un seul
enregistrement, la question (« quelle est l'adresse IPv4 de
wylag.de
? »).
Tout le problème traitée par ce RFC est : que se passe t-il si la section Question d'une requête contient plus d'un enregistrement ? Je divulgâche tout de suite : c'est interdit, il ne faut pas. (Pour le cas des requêtes/réponses ordinaires, avec l'opcode 0 ; d'autres messages DNS peuvent avoir des règles différentes.) La principale raison pour cette interdiction est que, dans la réponse, certains champs sont globaux à toute la réponse (comme le code de réponse, le rcode) et on ne peut donc pas se permettre d'accepter des questions qui risqueraient de nécessiter des réponses différentes. Désormais, un serveur DNS qui voit passer une requête avec un nombre de questions supérieur à 1 doit répondre avec le code de retour FORMERR (format error).
Le RFC 1035, la norme originelle, traitait ce point mais restait flou.
Il y avait une autre façon de régler le problème, en imposant que, s'il y a plusieurs questions, toutes portent sur le même nom de domaine. Cela aurait réglé le doute sur le code de retour et aurait pu être pratique pour des cas comme la demande simultanée de l'adresse IPv4 et IPv6. Mais cette solution a été écartée au profit de la solution plus simple qui était d'interdire les questions multiples. (Et, de toute façon, on ne peut pas garantir que le code de retour sera le même pour tous les types, même si le nom est le même. Pensez aux serveurs DNS générant dynamiquement les données.)
Date de publication du RFC : Septembre 2024
Auteur(s) du RFC : B. Jonglez (ENS Lyon), J. Chroboczek (IRIF, Université Paris Cité)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF babel
Première rédaction de cet article le 14 septembre 2024
Voici un RFC sur le protocole de routage Babel. Il normalise une extension au protocole pour tenir compte du RTT, le temps d'aller-retour sur un segment du réseau. C'est l'occasion de revenir sur ce critère de routage, souvent cité mais rarement utilisé (et pour de bonnes raisons).
On lit parfois des affirmations rapides comme « les routeurs Internet choisissent la route la plus rapide ». Outre que « rapide » n'a pas de définition précise (parle-t-on de la latence ? De la capacité ?), la phrase est fausse : le routage se fait sur des critères nettement moins dynamiques. Dans le cas de BGP, ce sont en bonne partie des critères de business. Das le cas d'un IGP, des critères de performance sont pris en compte, mais en se limitant à des critères assez statiques. En effet, si on prend en compte naïvement une variable très dynamique, qui change souvent, comme l'est le RTT, on risque d'avoir un routage très instable : un lien est peu utilisé, ses performances sont bonnes, les routeurs vont y envoyer tout le trafic, ses performances vont chuter, les routeurs vont tout envoyer ailleurs, les performances vont remonter, les routeurs vont encore changer d'avis, etc. Tenir compte des performances immédiates du lien est donc une fausse bonne idée ou, plus précisément, ne doit pas être fait naïvement.
C'est justement ce que fait ce RFC qui explique comment utiliser intelligemment un critère très mouvant, le RTT (la latence, mais mesurée en aller-retour), pour le choix des routes dans Babel. Babel est un protocole de routage de type IGP, normalisé dans le RFC 8966, surtout prévu pour des réseaux sans administration centrale.
Si par exemple (section 1 du RFC), une machine A à Paris a deux moyens de joindre une machine D également à Paris, une des routes passant par un routeur B à Paris et une autre par un routeur C à Tokyo, il est assez évident qu'il vaut mieux rester à Paris. Mais ce n'est pas forcément ce que feront les mises en œuvre de Babel, qui se disent simplement que les deux routes ont le même nombre de segments (deux). Bien sûr, attribuer manuellement des préférences aux segments résoudrait le problème (un lien vers le Japon est plus « cher ») mais l'un des buts de Babel est de ne pas requérir de configuration manuelle par un administrateur réseaux.
Utiliser le RTT, qui est relativement facile à évaluer (mais continuez la lecture : il y a des pièges) semble une solution évidente. Mais, comme dit plus haut, cela peut mener à de violentes instabilités du routage. Il faut donc évaluer le RTT, puis le traiter avant de l'utiliser, pour éviter des oscillations du routage. Et attention, la latence n'est pas toujours le meilleur critère de choix, d'autres, comme le coût monétaire, peuvent être pris en compte, par exemple pour privilégier un lien WiFi sur un lien 5G. Babel n'impose pas de critère unique de choix des routes.
Cette extension ne nécessite pas que les différents routeurs aient une horloge synchronisée (ce qui serait difficile à faire dans un réseau sans administration), juste que les horloges ne dérivent pas trop vite l'une par rapport à l'autre. D'autre part, comme l'algorithme essaie de limiter les oscillations de routes, il ne s'ajustera pas instantanément aux changements, et ne sera donc pas optimal dans tous les cas.
Commençons par l'évaluation du RTT. Il ne suffit pas de soustraire le temps de départ d'une question au temps d'arrivée d'une réponse, entre autre parce que la génération de la réponse peut prendre du temps (et puis un routeur Babel n'est pas obligé de répondre à tous les messages Hello). Et rappelez-vous que cette extension n'impose pas une synchronisation des horloges. L'algorithme est donc un peu plus compliqué, c'est celui créé par Mills et utilisé entre autres pour NTP. Un routeur nommé Alice qui envoie un message Hello (RFC 8966, section 3.4.1) ajoute à ces messages le temps de départ, appelé t1. Le routeur nommé Bob le reçoit à un temps t1'. Comme les horloges ne sont pas forcément synchronisées, on ne peut pas comparer directement t1 et t1'. Au lieu de cela, Bob, quand il enverra un message Babel IHU (I Heard yoU) indiquera à la fois t1 et t1' dans ce message, ainsi que le temps t2' d'émission. Alice, recevant ce message, n'aura qu'à calculer (t2 - t1) - (t2' - t1') et aura ainsi le RTT, même si les horloges sont différentes (tant qu'elles ne dérivent pas trop dans l'intervalle entre t1 et t2). Bon, j'ai simplifié, mais vous avez tous les détails dans le RFC, section 3.2.
Notez que cet algorithme impose aux deux routeurs de garder des informations supplémentaires sur leurs voisins, dans la table documentée dans la section 3.2.4 du RFC 8966. Celle-ci devra stocker les estampilles temporelles reçues.
Ces estampilles étant stockées sur 32 bits et ayant une
résolution en microsecondes, elles reviendront à zéro toutes les 71
minutes. Le routeur doit donc prendre garde à ignorer les
estampilles situées dans son futur (par exemple s'il a redémarré et
perdu la notion du temps) ou trop éloignées. Sur un système
POSIX, le routeur peut donc utiliser
clock_gettime(CLOCK_MONOTONIC)
.
Bon, désormais, nous avons le RTT. Qu'en faire ? Comme indiqué plus haut, il ne faut pas l'utiliser tel quel comme critère de sélection des routes, sous peine d'osccilations importantes des tables de routage. Il y a plusieurs opérations à faire (section 4). D'abord, il faut lisser le RTT, éliminant ainsi les cas extrêmes. Ainsi, on va utiliser la formule RTT ← α RTT + (1 - α) RTTn, où RTTn désigne la mesure qu'on vient juste de faire, et où la constante α a une valeur recommandée de 0,836. (Les détails figurent dans l'article « A delay-based routing metric ».)
Ensuite, pour nourrir l'algorithme de choix des routes, il faut convertir ce RTT en un coût. La fonction est simple : de 0 à une valeur rtt-min, le coût est constant, il augmente ensuite linéairement vers une valeur maximale, atteinte pour un RTT égal à rtt-max, et constante ensuite. Les valeurs par défaut recommandées sont de 10 ms pour rtt-min et 150 ms pour rtt-max. En d'autres termes, sur l'Internet, tout RTT inférieur à 10 ms est bon, tout RTT supérieur à 150 ms est à éviter autant que possible.
Enfin, car il reste encore des variations, la section 4 sur les traitements du RTT se termine en demandant l'application d'un mécanisme d'hystérésis, comme celui du RFC 8966, annexe A.3.
Le format des paquets, maintenant. Rappelons que Babel encode les données en TLV et permet des sous-TLV (la valeur d'un TLV est elle-même un TLV). Un routeur doit ignorer les sous-TLV qu'il ne comprend pas, ce qui permet d'ajouter des nouvelles informations sans risque (section 5).
Donc, notre RFC normalise le sous-TLV Timestamp (type 3), qui permet de stocker les estampilles temporelles, et peut apparaitre sous les sous-TLV Hello et IHU. Dans le premier cas, il stockera juste une valeur (t1 ou t2' dans l'exemple plus haut), dans le second, deux valeurs (t1 et t1' dans l'exemple plus haut).
La mise en œuvre « officielle » de Babel a, dans sa version de
développement, la capacité de déterminer ces RTT (option
enable-timestamps
dans la configuration).
Date de publication du RFC : Février 2025
Auteur(s) du RFC : P. Koch (DENIC
eG), M. Larson, P. Hoffman
(ICANN)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 12 février 2025
Un résolveur DNS ne connait au début, rien du contenu du DNS. Rien ? Pas tout à fait, il connait une liste des serveurs de noms faisant autorité pour la racine, car c'est par eux qu'il va commencer le processus de résolution de noms. Cette liste est typiquement en dur dans le code du serveur, ou bien dans un de ses fichiers de configuration. Mais peu d'administrateurs système la maintiennent à jour. Il est donc prudent, au démarrage du résolveur, de chercher une liste vraiment à jour, et c'est le priming (amorçage, comme lorsqu'on amorce une pompe pour qu'elle fonctionne seule ensuite), opération que décrit ce RFC, qui remplace l'ancienne version, le RFC 8109 (les changements sont nombreux mais sont surtout des points de détail).
Le problème de départ d'un résolveur est un problème d'œuf et de
poule. Le résolveur doit interroger le DNS pour avoir
des informations mais comment trouve-t-il les serveurs DNS à
interroger ? La solution est de traiter la racine du DNS de manière
spéciale : la liste de ses serveurs est connue du résolveur au
démarrage. Elle peut être dans le code du serveur lui-même, ici un
Unbound qui contient les adresses IP des
serveurs de la racine (je ne montre que les trois premiers,
A.root-servers.net
,
B.root-servers.net
et
C.root-servers.net
) :
% strings /usr/sbin/unbound | grep -i 2001: 2001:503:ba3e::2:30 2001:500:84::b 2001:500:2::c ...
Ou bien elle est dans un fichier de configuration (ici, sur un Unbound) :
server: directory: "/etc/unbound" root-hints: "root-hints"
Ce fichier peut être téléchargé via
l'IANA, il peut être spécifique au logiciel résolveur, ou bien fourni
par le système d'exploitation (cas du
paquetage dns-root-data
chez
Debian). Il contient la liste des serveurs
de la racine et leurs adresses :
. 3600000 NS A.ROOT-SERVERS.NET. . 3600000 NS B.ROOT-SERVERS.NET. ... A.ROOT-SERVERS.NET. 3600000 A 198.41.0.4 A.ROOT-SERVERS.NET. 3600000 AAAA 2001:503:ba3e::2:30 B.ROOT-SERVERS.NET. 3600000 A 192.228.79.201 B.ROOT-SERVERS.NET. 3600000 AAAA 2001:500:84::b ...
Tous les résolveurs actuels mettent en œuvre le priming. En effet, les configurations locales tendent à ne plus être à jour au bout d'un moment. (Sauf dans le cas où elles sont dans un paquetage du système d'exploitation, mis à jour avec ce dernier, comme dans le bon exemple Debian ci-dessus.)
Les changements des serveurs racines sont rares. Si on regarde sur le site des opérateurs des serveurs racine, on voit :
Bref, peu de changements. Ils sont en général annoncés sur les listes de diffusion opérationnelles (comme ici). Mais les fichiers de configuration ayant une fâcheuse tendance à ne pas être mis à jour et à prendre de l'âge, les anciennes adresses des serveurs racine continuent à recevoir du trafic des années après (comme le montre cette étude de J-root). Notez que la stabilité de la liste des serveurs racine n'est pas due qu'au désir de ne pas perturber les administrateurs système : il y a aussi des raisons politiques (aucun mécanisme en place pour choisir de nouveaux serveurs, ou pour retirer les « maillons faibles »). C'est pour cela que la liste des serveurs (mais pas leurs adresses) n'a pas changé depuis 1997 ! (Une histoire - biaisée - des serveurs racine a été publiée par l'ICANN.)
Notons aussi que l'administrateur système d'un résolveur peut changer la liste des serveurs de noms de la racine pour une autre liste, par exemple s'ielle veut utiliser une racine alternative.
Le priming, maintenant. Le principe du priming est, au démarrage, de faire une requête à un des serveurs listés dans la configuration et de garder sa réponse (certainement plus à jour que la configuration) :
% dig +bufsize=4096 +norecurse +nodnssec @i.root-servers.net . NS ; <<>> DiG 9.18.28-0ubuntu0.24.04.1-Ubuntu <<>> +bufsize +norecurse +nodnssec @i.root-servers.net . NS ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54419 ;; flags: qr aa; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 27 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: c0a47c1200bf6bfc0100000067377b40981cc05cdb33b736 (good) ;; QUESTION SECTION: ;. IN NS ;; ANSWER SECTION: . 518400 IN NS k.root-servers.net. . 518400 IN NS h.root-servers.net. . 518400 IN NS m.root-servers.net. . 518400 IN NS l.root-servers.net. . 518400 IN NS b.root-servers.net. . 518400 IN NS e.root-servers.net. . 518400 IN NS d.root-servers.net. . 518400 IN NS f.root-servers.net. . 518400 IN NS g.root-servers.net. . 518400 IN NS i.root-servers.net. . 518400 IN NS a.root-servers.net. . 518400 IN NS j.root-servers.net. . 518400 IN NS c.root-servers.net. ;; ADDITIONAL SECTION: m.root-servers.net. 518400 IN A 202.12.27.33 l.root-servers.net. 518400 IN A 199.7.83.42 k.root-servers.net. 518400 IN A 193.0.14.129 j.root-servers.net. 518400 IN A 192.58.128.30 i.root-servers.net. 518400 IN A 192.36.148.17 h.root-servers.net. 518400 IN A 198.97.190.53 g.root-servers.net. 518400 IN A 192.112.36.4 f.root-servers.net. 518400 IN A 192.5.5.241 e.root-servers.net. 518400 IN A 192.203.230.10 d.root-servers.net. 518400 IN A 199.7.91.13 c.root-servers.net. 518400 IN A 192.33.4.12 b.root-servers.net. 518400 IN A 170.247.170.2 a.root-servers.net. 518400 IN A 198.41.0.4 m.root-servers.net. 518400 IN AAAA 2001:dc3::35 l.root-servers.net. 518400 IN AAAA 2001:500:9f::42 k.root-servers.net. 518400 IN AAAA 2001:7fd::1 j.root-servers.net. 518400 IN AAAA 2001:503:c27::2:30 i.root-servers.net. 518400 IN AAAA 2001:7fe::53 h.root-servers.net. 518400 IN AAAA 2001:500:1::53 g.root-servers.net. 518400 IN AAAA 2001:500:12::d0d f.root-servers.net. 518400 IN AAAA 2001:500:2f::f e.root-servers.net. 518400 IN AAAA 2001:500:a8::e d.root-servers.net. 518400 IN AAAA 2001:500:2d::d c.root-servers.net. 518400 IN AAAA 2001:500:2::c b.root-servers.net. 518400 IN AAAA 2801:1b8:10::b a.root-servers.net. 518400 IN AAAA 2001:503:ba3e::2:30 ;; Query time: 6 msec ;; SERVER: 192.36.148.17#53(i.root-servers.net) (UDP) ;; WHEN: Fri Nov 15 17:48:00 CET 2024 ;; MSG SIZE rcvd: 851
(Les raisons du choix des trois options données à dig sont indiquées plus loin.)
La section 3 de notre RFC décrit en détail à quoi ressemblent les
requêtes de priming. Le type de données demandé
(QTYPE) est NS (Name Servers,
type 2) et le nom demandé (QNAME) est « . » (oui,
juste la racine). D'où le dig . NS
ci-dessus. Le bit RD (Recursion Desired) est
typiquement mis à zéro (d'où le +norecurse
dans
l'exemple avec dig). La taille de la réponse dépassant les 512
octets (limite très ancienne du DNS), il faut utiliser
EDNS (cause du
+bufsize=4096
dans l'exemple). On peut utiliser
le bit DO (DNSSEC OK) qui indique qu'on demande
les signatures DNSSEC mais ce n'est pas habituel (d'où le
+nodnssec
dans l'exemple). En effet, si la
racine est signée, permettant d'authentifier l'ensemble
d'enregistrements NS, la zone root-servers.net
,
où se trouvent actuellement tous les serveurs de la racine, ne l'est
pas, et les enregistrements A et AAAA ne peuvent donc pas être
validés avec DNSSEC.
Cette requête de priming est envoyée lorsque le résolveur démarre, et aussi lorsque la réponse précédente a expiré (regardez le TTL dans l'exemple : six jours). Si le premier serveur testé ne répond pas, on essaie avec un autre. Ainsi, même si le fichier de configuration n'est pas parfaitement à jour (des vieilles adresses y trainent), le résolveur finira par avoir la liste correcte.
Et comment choisit-on le premier serveur qu'on interroge ? Notre RFC recommande (section 3.2) un tirage au sort, pour éviter que toutes les requêtes de priming ne se concentrent sur un seul serveur (par exemple le premier de la liste). Une fois que le résolveur a démarré, il peut aussi se souvenir du serveur le plus rapide, et n'interroger que celui-ci, ce qui est fait par la plupart des résolveurs, pour les requêtes ordinaires (mais n'est pas conseillé pour le priming).
Et les réponses au priming ? Il faut bien
noter que, pour le serveur racine, les requêtes
priming sont des requêtes comme les autres, et ne
font pas l'objet d'un traitement particulier. Normalement, la
réponse doit avoir le code de retour NOERROR
(c'est bien le cas dans mon exemple). Parmi les
flags, il doit y avoir AA (Authoritative
Answer). La section de réponse doit évidemment contenir
les NS de la racine, et la section additionnelle les adresses IP. Le
résolveur garde alors cette réponse dans son cache, comme il le
ferait pour n'importe quelle autre réponse. Notez que là aussi, il
ne faut pas de traitement particulier. Par exemple, le résolveur ne
doit pas compter sur le fait qu'il y aura exactement 13 serveurs,
même si c'est le cas depuis longtemps (ça peut changer).
Normalement, le serveur racine envoie la totalité des adresses IP
(deux par serveur, une en IPv4 et une en
IPv6). S'il ne le fait pas (par exemple par
manque de place parce qu'on a bêtement oublié
EDNS), le résolveur va devoir envoyer des
requêtes A et AAAA explicites pour obtenir les adresses IP (ici, on
interroge k.root-servers.net
) :
% dig @2001:7fd::1 g.root-servers.net AAAA ; <<>> DiG 9.18.33-1~deb12u2-Debian <<>> @2001:7fd::1 g.root-servers.net AAAA ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10640 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 1232 ;; QUESTION SECTION: ;g.root-servers.net. IN AAAA ;; ANSWER SECTION: g.root-servers.net. 3600000 IN AAAA 2001:500:12::d0d ;; Query time: 3 msec ;; SERVER: 2001:7fd::1#53(2001:7fd::1) (UDP) ;; WHEN: Thu Feb 13 17:26:51 CET 2025 ;; MSG SIZE rcvd: 75
Vous pouvez voir ici les requêtes et réponses de priming d'un Unbound. D'abord, décodées par tcpdump :
17:57:14.439382 IP (tos 0x0, ttl 64, id 26437, offset 0, flags [none], proto UDP (17), length 56) 10.10.156.82.51893 > 198.41.0.4.53: [bad udp cksum 0x6cbf -> 0x49dc!] 63049% [1au] NS? . ar: . OPT UDPsize=1232 DO (28) 17:57:14.451715 IP (tos 0x40, ttl 52, id 45305, offset 0, flags [none], proto UDP (17), length 1125) 198.41.0.4.53 > 10.10.156.82.51893: [udp sum ok] 63049*- q: NS? . 14/0/27 . [6d] NS l.root-servers.net., . [6d] NS j.root-servers.net., . [6d] NS f.root-servers.net., . [6d] NS h.root-servers.net., . [6d] NS d.root-servers.net., . [6d] NS b.root-servers.net., . [6d] NS k.root-servers.net., . [6d] NS i.root-servers.net., . [6d] NS m.root-servers.net., . [6d] NS e.root-servers.net., . [6d] NS g.root-servers.net., . [6d] NS c.root-servers.net., . [6d] NS a.root-servers.net., . [6d] RRSIG ar: l.root-servers.net. [6d] A 199.7.83.42, l.root-servers.net. [6d] AAAA 2001:500:9f::42, j.root-servers.net. [6d] A 192.58.128.30, j.root-servers.net. [6d] AAAA 2001:503:c27::2:30, f.root-servers.net. [6d] A 192.5.5.241, f.root-servers.net. [6d] AAAA 2001:500:2f::f, h.root-servers.net. [6d] A 198.97.190.53, h.root-servers.net. [6d] AAAA 2001:500:1::53, d.root-servers.net. [6d] A 199.7.91.13, d.root-servers.net. [6d] AAAA 2001:500:2d::d, b.root-servers.net. [6d] A 170.247.170.2, b.root-servers.net. [6d] AAAA 2801:1b8:10::b, k.root-servers.net. [6d] A 193.0.14.129, k.root-servers.net. [6d] AAAA 2001:7fd::1, i.root-servers.net. [6d] A 192.36.148.17, i.root-servers.net. [6d] AAAA 2001:7fe::53, m.root-servers.net. [6d] A 202.12.27.33, m.root-servers.net. [6d] AAAA 2001:dc3::35, e.root-servers.net. [6d] A 192.203.230.10, e.root-servers.net. [6d] AAAA 2001:500:a8::e, g.root-servers.net. [6d] A 192.112.36.4, g.root-servers.net. [6d] AAAA 2001:500:12::d0d, c.root-servers.net. [6d] A 192.33.4.12, c.root-servers.net. [6d] AAAA 2001:500:2::c, a.root-servers.net. [6d] A 198.41.0.4, a.root-servers.net. [6d] AAAA 2001:503:ba3e::2:30, . OPT UDPsize=4096 DO (1097)
Et ici par tshark :
1 0.000000 10.10.156.82 → 198.41.0.4 DNS 70 Standard query 0xf649 NS <Root> OPT 2 0.012333 198.41.0.4 → 10.10.156.82 DNS 1139 Standard query response 0xf649 NS <Root> NS l.root-servers.net NS j.root-servers.net NS f.root-servers.net NS h.root-servers.net NS d.root-servers.net NS b.root-servers.net NS k.root-servers.net NS i.root-servers.net NS m.root-servers.net NS e.root-servers.net NS g.root-servers.net NS c.root-servers.net NS a.root-servers.net RRSIG A 199.7.83.42 AAAA 2001:500:9f::42 A 192.58.128.30 AAAA 2001:503:c27::2:30 A 192.5.5.241 AAAA 2001:500:2f::f A 198.97.190.53 AAAA 2001:500:1::53 A 199.7.91.13 AAAA 2001:500:2d::d A 170.247.170.2 AAAA 2801:1b8:10::b A 193.0.14.129 AAAA 2001:7fd::1 A 192.36.148.17 AAAA 2001:7fe::53 A 202.12.27.33 AAAA 2001:dc3::35 A 192.203.230.10 AAAA 2001:500:a8::e A 192.112.36.4 AAAA 2001:500:12::d0d A 192.33.4.12 AAAA 2001:500:2::c A 198.41.0.4 AAAA 2001:503:ba3e::2:30 OPT
Et un décodage plus détaillé de tshark dans ce fichier.
Enfin, la section 6 de notre RFC traite des problèmes de sécurité du priming. Évidemment, si un attaquant injecte une fausse réponse aux requêtes de priming, il pourra détourner toutes les requêtes ultérieures vers des machines de son choix. À part le RFC 5452, et l'utilisation de cookies (RFC 7873, mais seuls les serveurs racine C, F, G et I les gèrent aujourd'hui), la seule protection est DNSSEC : si le résolveur valide (et a donc la clé publique de la racine), il pourra détecter que les réponses sont mensongères (voir aussi la section 3.3). Cela a l'avantage de protéger également contre d'autres attaques, ne touchant pas au priming, comme les attaques sur le routage.
Notez que DNSSEC est recommandé pour valider les réponses
ultérieures mais, comme on l'a vu, n'est pas important pour valider
la réponse de priming elle-même, puisque
root-servers.net
n'est pas signé. Si un
attaquant détournait, d'une manière ou d'une autre, vers un faux
serveur racine, servant de fausses données, ce ne serait qu'une
attaque par déni de
service, puisque le résolveur validant pourrait détecter
que les réponses sont fausses.
L'annexe A du RFC liste les changements depuis le RFC 8109. Rien de vraiment crucial, juste une longue liste de clarifications et de précisions.
Date de publication du RFC : Juin 2024
Auteur(s) du RFC : T. Reddy.K (Nokia), M. Boucadair
(Orange)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF add
Première rédaction de cet article le 18 août 2024
Traditionnellement, tous les résolveurs DNS fournissaient un service équivalent. Le client avait un résolveur configuré, soit statiquement, soit via DHCP, et ne se souciait pas vraiment du résolveur ainsi désigné, tous étaient pareils. Aujourd'hui, ce n'est plus vraiment le cas : les résolveurs fournissent des services très différents, par exemple en matière de chiffrement, ou de blocage de certains noms. Il est donc utile que le client puisse se renseigner sur son résolveur, ce que permet cette nouvelle technique, le type de données RESINFO, que le client va récupérer dans le DNS.
Voici une liste (non-limitative !) des caractéristiques d'un résolveur et qui peuvent être présentes ou pas :
Avant la solution de ce RFC, la seule manière pour le client DNS de savoir ce que son résolveur proposait était manuelle, en lisant des documentations (cf. par exemple celle de mon résolveur). Ce n'est pas pratique quand la configuration du résolveur est automatique ou semi-automatique, via DHCP, ou avec les solutions des RFC 9462 et RFC 9463.
Ce nouveau RFC propose donc un mécanisme qui permet au client de découvrir les caractéristiques d'un résolveur et de les analyser, avant de décider quel résolveur choisir. (Un point important est que ce RFC se veut neutre : il ne dit pas quelles sont les bonnes caractéristiques d'un résolveur, le client reçoit une information, il est libre de l'utiliser comme il veut.)
Place à la technique (section 3 du RFC) : un nouveau type de
données DNS est défini, RESINFO (code
261). Son contenu est l'information recherchée, sous forme de
couples clé=valeur. Le nom de domaine auquel il est rattaché est le
nom du résolveur, récupéré par les méthodes des RFC 9462 et RFC 9463, ou manuellement
configuré. Ce nom est désigné par le sigle ADN, pour
Authentication Domain Name (RFC 9463, section 3.1.1). Si on a utilisé le nom spécial
resolver.arpa
(RFC 9462,
section 4), on peut lui demander son RESINFO.
Le format du RESINFO (section 4 du RFC) est copié sur celui des enregistrements TXT. Chaque chaine de caractères suit le modèle clé=valeur du RFC 6763, section 6.3. Les clés inconnues doivent être ignorées, ce qui permettra dans le futur d'ajouter de nouvelles clés au registre des clés. Un exemple d'enregistrement RESINFO :
resolver IN RESINFO "qnamemin" "exterr=15,17" "infourl=https://resolver.example.com/guide"
Il indique (section 5, sur la signification des clés) que ce résolveur fait de la minimisation des requêtes (RFC 9156), et notez que cette clé n'a pas de valeur, c'est juste un booléen dont la présence indique la QNAME minimisation. L'enregistrement continue en donnant les codes EDE (Extended DNS Errors, RFC 8914) que peut renvoyer le résolveur. C'est surtout utile pour indiquer ce qu'il bloque (15 = bloqué par décision de l'administrateurice du résolveur, 17 = bloqué par demande de l'utilisateurice). Et enfin il donne un URL où on peut aller chercher davantage d'information en langue naturelle.
La section 7 du RFC donne quelques conseils de sécurité : avoir
un lien sécurisé avec le résolveur qu'on interroge (par exemple avec
DoT), pour éviter qu'un méchant ne modifie le
RESINFO, et valider la réponse avec DNSSEC (sauf pour
resolver.arpa
, qui est un cas spécial).
La section 8 précise le registre
des clés disponibles. Pour ajouter des clés (on note qu'à
l'heure actuelle, il n'y en a pas pour indiquer la disponibilité de
DoT ou DoH, ou pour la politique de conservation des
requêtes), la procédure est « spécification nécessaire » (RFC 8126). Si on veut des clés non normalisées, on
doit les préfixer par temp-
.
RESINFO est récent et donc pas forcément mis en œuvre dans tous les logiciels DNS que vous utilisez. Un dig récent fonctionne :
% dig dot.bortzmeyer.fr RESINFO … ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34836 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 … ;; ANSWER SECTION: dot.bortzmeyer.fr. 86369 IN CNAME radia.bortzmeyer.org. radia.bortzmeyer.org. 86369 IN RESINFO "qnamemin" "infourl=https://doh.bortzmeyer.fr/policy" ;; Query time: 0 msec ;; SERVER: 192.168.2.254#53(192.168.2.254) (UDP) ;; WHEN: Sun Aug 18 08:15:55 UTC 2024 ;; MSG SIZE rcvd: 142
Si vous avez un dig plus ancien, il faudra demander TYPE261 et pas RESINFO. Notez que, physiquement, un RESINFO est juste un TXT, ce qui facilite sa mise en œuvre (dans le futur dnspython, la classe RESINFO hérite simplement de TXT).
Trouve-t-on beaucoup de RESINFO dans la nature ? La plupart des grands résolveurs DNS publics ne semblent pas en avoir. Une exception est DNS4ALL :
% dig +short dot.dns4all.eu RESINFO "qnamemin exterr=0-1,3,5-12,18,20 infourl=https://dns4all.eu"
Et, comme vous le voyez plus haut, j'en ai mis un dans mon résolveur. Le logiciel du serveur primaire ne connaissant pas encore ce type, j'ai utilisé la technique des types inconnus du RFC 3597 :
; Pour le résolveur public : ; Type RESINFO (RFC 9606), enregistré à l'IANA mais pas encore connu des logiciels radia IN TYPE261 \# 50 08716e616d656d696e 28696e666f75726c3d68747470733a2f2f646f682e626f72747a6d657965722e66722f706f6c696379
Cette série de chiffres hexadécimaux ayant été produite à partir de
la version texte et du programme
. On note que, comme
ce résolveur public n'est pas menteur, je n'indique pas d'EDE
(Extended DNS Errors, RFC 8914).
text-to-unknown-txt-type.py
Date de publication du RFC : Août 2024
Auteur(s) du RFC : C. Bormann (Universität Bremen TZI), B. Gamari (Well-Typed), H. Birkholz (Fraunhofer SIT)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cbor
Première rédaction de cet article le 17 août 2024
Ce RFC ajoute au format de données binaire CBOR la possibilité de stocker des données temporelles plus détaillées, incluant par exemple l'échelle utilisée (UTC ou TAI), ou bien ayant une précision supérieure.
Petit rappel : CBOR est normalisé dans le RFC 8949, il incluait deux étiquettes pour les données temporelles, permettant d'indiquer date et heure en format lisible par un humain, et sous forme d'un nombre de secondes depuis l'epoch, avec une résolution d'une seconde. Le RFC 8943 y ajoute les dates (sans indication de l'heure). Au passage, le concept d'étiquette en CBOR est normalisé dans la section 3.4 du RFC 8949.
Notre nouveau RFC 9581 ajoute :
Ces nouvelles étiquettes ont été ajoutées au registre des étiquettes CBOR. Les clés possibles pour les dictionnaires indiquant les temps étendus sont dans un nouveau registre à l'IANA. Ajouter une entrée à ce registre nécessite un RFC et un examen par un expert.
Un service sur ce blog,
permet d'obtenir plusieurs valeurs
utilisant ces formats. Si nous utilisons le gem
Ruby cbor-diag, nous
voyons :
https://www.bortzmeyer.org/apps/date-in-cbor
% curl -s https://www.bortzmeyer.org/apps/date-in-cbor | cbor2diag.rb ["Current date in CBOR, done with Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0] and the flunn library <https://github.com/funny-falcon/flunn>", 0("2024-04-17T14:21:07Z"), 1(1713363667), 100(19830), 1004("2024-04-17"), 1001({1: 1713363667, -1: 0, -9: 193986759}), 1001({1: 1713363704, -1: 1}), "Duration since boot: ", 1002({1: 1742903})]
On voit alors successivement :
Question programmation, le service a été écrit en
Python. Ce langage a une fonction
time_ns
,
qui permet d'obtenir les nanosecondes. Pour le TAI, cela a été un
peu plus difficile, la machine doit être configurée spécialement.
Date de publication du RFC : Juillet 2024
Auteur(s) du RFC : P. Wouters (Aiven), D. Huigens
(Proton AG), J. Winter
(Sequoia-PGP), Y. Niibe (FSIJ)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF openpgp
Première rédaction de cet article le 1 août 2024
Dernière mise à jour le 4 janvier 2025
Le logiciel PGP est synonyme de cryptographie pour beaucoup de gens. Un des plus anciens et des plus utilisés pour les fonctions de confidentialité mais aussi d'authentification. Le format de PGP, OpenPGP, est normalisé dans ce RFC, qui remplace le RFC 4880 (il n'y a pas de changement crucial, juste une mise à jour longtemps attendue).
PGP a vu son
format de données normalisé pour la première fois en août 1996, dans
le RFC 1991. Cette norme a été révisée par la suite, dans
le RFC 2440, puis le RFC 4880, puis par des RFC ponctuels (comme les RFC 5581 et RFC 6637, désormais inclus)
et notre RFC est la dernière version, qui synthétise tout. Sa
gestation a été longue et douloureuse et a suscité des controverses,
menant à un projet concurrent, LibrePGP (qui prévoit son propre
RFC, actuellement draft-koch-librepgp
,
que j'avoue n'avoir pas lu).
Cette normalisation permet à diverses mises en œuvre de PGP d'interopérer. La plus connue aujourd'hui est le logiciel libre, GNU Privacy Guard (qui n'existait pas encore au moment de la publication du premier RFC) mais il y en a d'autres comme Sequoia (qui a annoncé qu'il suivrait notre récent RFC). Il ne faut donc pas confondre le logiciel PGP, écrit à l'origine par Phil Zimmermann, et qui est non-libre, avec le format OpenPGP que décrit notre RFC (cf. section 1.1) et que des logiciels autres que PGP peuvent lire et écrire.
Le principe du chiffrement avec PGP est simple. Une clé de session (le terme est impropre puisqu'il n'y a pas de session au sens de TLS mais c'est celui utilisé par le RFC) est créée pour chaque destinataire, elle sert à chiffrer le message et cette clé est chiffrée avec la clé publique du destinataire (section 2.1 du RFC).
Pour l'authentification, c'est aussi simple conceptuellement. Le message est condensé et le condensé est chiffré avec la clé privée de l'émetteur (section 2.2 du RFC).
Le format OpenPGP permet également la compression (qui améliore la sécurité en supprimant les redondances) et l'encodage en Base64 (RFC 4648), baptisé ASCII armor, pour passer à travers des logiciels qui n'aiment pas le binaire (la section 6 détaille cet encodage).
La section 3 explique les éléments de base utilisés par le format PGP. L'un des plus importants est le concept d'entier de grande précision (MPI pour Multi-Precision Integers), qui permet de représenter des entiers de très grande taille, indispensables à la cryptographie, sous forme d'un doublet longueur + valeur.
Enfin les sections 4 et 5 expliquent le format lui-même. Un message PGP est constitué de paquets (rien à voir avec les paquets réseau). Chaque paquet a un type, une longueur et un contenu. Par exemple, un paquet de type 1 est une clé de session chiffrée, un paquet de type 2 une signature, un paquet de type 9 du contenu chiffré, etc.
La section 7 du RFC décrit un type de message un peu particulier, qui n'obéit pas à la syntaxe ci-dessus, les messages en clair mais signés. Ces messages ont l'avantage de pouvoir être lus sans avoir de logiciel PGP. Ils nécessitent donc des règles spéciales.
On notera que gpg
permet d'afficher les paquets présents dans un message PGP, ce qui
est pratique pour l'apprentissage ou le débogage. Voyons un exemple
avec un fichier test.txt
de 17 octets, signé
mais non chiffré (j'ai un peu simplifié la sortie du logiciel) :
% gpg --list-packets test.gpg :compressed packet: algo=2 :onepass_sig packet: keyid 3FA836C996A4A254 version 3, sigclass 0x00, digest 10, pubkey 1, last=1 :literal data packet: mode b (62), created 1721800388, name="test.txt", raw data: 17 bytes :signature packet: algo 1, keyid 3FA836C996A4A254 version 4, created 1721800388, md5len 0, sigclass 0x00 digest algo 10, begin of digest 2b d9 hashed subpkt 33 len 21 (issuer fpr v4 C760CAFC6387B0E8886C823B3FA836C996A4A254) hashed subpkt 2 len 4 (sig created 2024-07-24) subpkt 16 len 8 (issuer key ID 3FA836C996A4A254) data: [4096 bits]
Malheureusement, gpg n'affiche pas les valeurs numériques des types, telles que listées par le RFC. Mais les noms qu'il utilise sont les mêmes que dans le RFC, on peut donc facilement trouver la section qui explique ce qu'est un "onepass_sig packet" (section 5.4).
Avec un message chiffré, on obtient :
% gpg --list-packets test.txt.gpg gpg: encrypted with 4096-bit RSA key, ID 9045E02757F02AA1, created 2014-02-09 "Stéphane Bortzmeyer (Main key) <stephane@bortzmeyer.org>" gpg: encrypted with 2048-bit RSA key, ID 516CB37B336525BB, created 2009-12-15 "ISC Security Officer <security-officer@isc.org>" gpg: decryption failed: No secret key :pubkey enc packet: version 3, algo 1, keyid 516CB37B336525BB data: [2046 bits] :pubkey enc packet: version 3, algo 1, keyid 9045E02757F02AA1 data: [4096 bits] :encrypted data packet: length: 78 mdc_method: 2
Conformément au principe d'agilité cryptographique (RFC 7696), le format OpenPGP n'est pas lié à un algorithme cryptographique particulier, et permet d'en ajouter de nouveaux. La section 15 détaille comment demander à l'IANA d'ajouter de nouveaux paramètres dans les registres PGP.
L'annexe B, quant à elle, énumère les principaux changements depuis le RFC 4880. Ce dernier RFC a été publié il y a plus de seize ans mais son remplacement par notre nouveau RFC 9580 a été une opération longue et difficile, et les changements se sont accumulés (Daniel Huigens en a fait un bon résumé). Ceci dit, le format OpenPGP ne change pas radicalement, et l'interopérabilité avec les anciens programmes est maintenue. Parmi les principales modifications :
Enfin, un grand nombre d'errata ont été traités (la liste complète est dans l'annexe D).
Date de publication du RFC : Avril 2024
Auteur(s) du RFC : R. Arends (ICANN), M. Larson
(ICANN)
Chemin des normes
Première rédaction de cet article le 27 avril 2024
Lorsqu'un résolveur DNS détecte un problème avec une zone, l'empêchant de résoudre les noms dans cette zone, il n'avait pas de moyen simple et automatique de prévenir les gérants des serveurs faisant autorité pour cette zone. Leur envoyer un message en utilisant l'information dans l'enregistrement SOA ou les adresses classiques du RFC 2142 ? Mais, justement, si la zone ne marche pas, le courrier ne partira pas forcément. Ce nouveau RFC propose un nouveau système. Les serveurs faisant autorité annoncent un domaine (qui marche, espérons-le), qui acceptera des requêtes DNS spéciales signalant le problème.
Cela dépend évidemment du problème pratique qui se pose. Si la zone n'a aucun serveur faisant autorité qui marche, il n'y a évidemment rien à faire. Mais s'ils marchent, tout en servant des données problématiques (par exemple des signatures DNSSEC expirées), alors, le résolveur pourra agir. Les serveurs faisant autorité mettent dans leurs réponses une option EDNS qui indique le domaine qui recevra les rapports (cela doit être un autre domaine, qui n'a pas de problème), le résolveur fera alors une requête DNS se terminant par le nom du domaine de signalement, et encodant le problème. L'agent, le domaine de signalement, pourra alors récolter ces requêtes et savoir qu'il y a un problème. Cela ne traite pas tous les cas (il faudra toujours utiliser RDAP ou whois pour récolter des informations sur les contacts du domaine erroné, puis leur écrire depuis un autre réseau) mais c'est simple, léger et automatisable. Les gérants de domaine sérieux, qui prennent au sérieux les signalements de problèmes techniques (soit 0,00001 % des domaines) pourront alors agir. (Note si vous gérez un résolveur et que vous constatez un problème avec un domaine et que les contacts ne répondent pas : un message méchant sur Twitter est souvent plus efficace.)
Donc, les détails techniques : le domaine qui veut recevoir les éventuels signalements va devoir configurer ses serveurs faisant autorité pour renvoyer une option EDNS, de numéro 18 (section 5 du RFC), indiquant l'agent, c'est-à-dire le domaine qui va recevoir les signalements (il faut évidemment veiller à ce qu'il n'ait pas de point de défaillance commun avec le domaine surveillé). Notez que cette option est systématiquement envoyée, le client (le résolveur) n'a pas à dire quoi que ce soit (la question avait fait l'objet d'un sérieux débat à l'IETF).
En cas de problème, notamment DNSSEC, le résolveur qui a noté le problème va alors construire un nom de domaine formé, successivement (section 6.1.1) par :
_er
,_er
(oui, encore),
Par exemple, si le domaine dyn.bortzmeyer.fr
annonce comme agent report.dyn.sources.org
, et
qu'un résolveur découvre des signatures DNSSEC expirées (EDE 7) en
cherchant à résoudre hello.dyn.bortzmeyer.fr /
TXT
(TXT a la valeur 16), la
requête de signalement du résolveur sera
_er.16.hello.dyn.bortzmeyer.fr.7._er.report.dyn.sources.org
(ouf). Le type demandé est TXT. Lorsque cette requête
arrivera au serveur faisant autorité pour
report.dyn.sources.org
, il pourra enregistrer
qu'il y a eu un problème, et mettre cette information à la
disposition de son administrateur système.
Ce serveur faisant autorité est censé répondre au signalement avec une réponse de type TXT comme ici :
% dig _er.16.hello.dyn.bortzmeyer.fr.7._er.report.dyn.sources.org TXT … ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12032 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 … ;; ANSWER SECTION: _er.16.hello.dyn.bortzmeyer.fr.7._er.report.dyn.sources.org. 30 IN TXT "Thanks for the report of error 7 on hello.dyn.bortzmeyer.fr" …
L'agent peut ensuite être interrogé, par des méthodes propres à la mise en œuvre utilisée :
% echo report | socat - UNIX-CONNECT:/home/drink/drink.sock REPORT state: hello.dyn.bortzmeyer.fr, 7 ip.dyn.bortzmeyer.fr, 7
Ici, on voit que deux domaines ont été signalés comme ayant des signatures expirées (rassurez-vous, c'était juste des tests). Le nombre de signalements n'est pas indiqué, ni la source des signalements (travail futur).
Quelques petits points de sécurité à garder en tête (section 9 du RFC) :
Cette technique a été mise en œuvre dans Drink lors d'un hackathon IETF. Drink peut à la fois signaler un domaine agent, et être serveur pour un domaine agent.
Un exemple de signalisation EDNS de cette option, vu la version
de développement de Wireshark (merci à Alexis La Goutte) :
Date de publication du RFC : Mai 2024
Auteur(s) du RFC : K. Davis (Cisco Systems), B. Peabody (Uncloud), P. Leach (University of Washington)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF uuidrev
Première rédaction de cet article le 12 mai 2024
Ce RFC normalise les UUID, une famille d'identificateurs uniques, obtenus sans registre central. Il remplace l'ancienne norme, le RFC 4122, avec pas mal de nouveautés et un RFC complètement refait.
Les UUID, également connus autrefois sous le nom de GUID (Globally Unique IDentifiers), sont issus à l'origine du système des Apollo, adopté ensuite dans la plate-forme DCE. Les UUID ont une taille fixe, 128 bits, et sont obtenus localement, par exemple à partir d'un autre identificateur unique comme un nom de domaine, ou bien en tirant au hasard, la grande taille de leur espace de nommage faisant que les collisions sont très improbables (section 6.7 du RFC). Ce RFC reprend la spécification (bien oubliée aujourd'hui) de DCE de l'Open Group (ex-OSF) et ajoute la définition d'un espace de noms pour des URN (sections 4 et 7). (Il existe aussi une norme ITU sur les UUID et un registre des UUID, pour ceux qui y tiennent.)
Les UUID peuvent donc convenir pour identifier une entité sur le réseau, par exemple une machine mais aussi, vu leur nombre, comme identificateur unique pour des transactions (ce qui était un de leurs usages dans DCE). En revanche, ils ne sont pas résolvables, contrairement aux noms de domaine. Mais ils sont présents dans beaucoup de logiciels (Windows, par exemple, les utilise intensivement). On les utilise comme clé primaire dans les bases de données, comme identificateur de transaction, comme nom de machine, etc.
Les UUID peuvent se représenter sous forme d'un nombre binaire de
128 bits (la section 5 du RFC décrit les différents champs qui
peuvent apparaitre) ou bien sous forme texte. Sur
Unix, on peut fabriquer un UUID avec la
commande uuidgen
, qui affiche la représentation
texte standard que normalise notre RFC (section 4) :
% uuidgen 317e8ed3-1428-4ef1-9dce-505ffbcba11a % uuidgen ec8638fd-c93d-4c6f-9826-f3c71436443a
Sur Linux, vous pouvez aussi simplement
faire cat /proc/sys/kernel/random/uuid
. Sur une machine FreeBSD, un UUID de la
machine est automatiquement généré (par le script
/etc/rc.d/hostid
) et stocké dans le fichier
/etc/hostid
.
Pour l'affichage sous forme d'URN (RFC 8141), on ajoute
juste l'espace uuid
par exemple
urn:uuid:ec8638fd-c93d-4c6f-9826-f3c71436443a
. Il
a été ajouté au registre
IANA des espaces de noms des URN.
La section 4 du RFC détaille le format de l'UUID. En dépit des apparences, l'UUID n'est pas plat, il a une structure, mais il est très déconseillé aux applications de l'interpréter (section 6.12). Un des champs les plus importants est le champ Version (qui devrait plutôt s'appeler Type) car il existe plusieurs types d'UUID :
Ces différents types / versions figurent dans un registre IANA. Ce registre ne peut être modifié que par une action de normalisation (cf. RFC 8126).
uuidgen, vu plus haut, peut générer des UUID de
version 1 option -t
, de version 3
(-m
), de version 4 (c'est son comportement par
défaut, mais on peut utiliser l'option -r
si on
veut être explicite) ou de version 5 (-s
). Ici,
on voit les UUID fondés sur une estampille temporelle (version 1)
augmenter petit à petit :
% uuidgen -t 42ff1626-0fc7-11ef-8162-49e9505fb2f3 % uuidgen -t 4361fae8-0fc7-11ef-8162-49e9505fb2f3 % uuidgen -t 45381d02-0fc7-11ef-8162-49e9505fb2f3
Ici, dans le cas d'un UUID fondé sur un nom (version 3), l'UUID est stable (essayez chez vous, vous devriez obtenir le même résultat que moi), une propriété importante des UUID de version 3 et 5 :
% uuidgen -m -n @dns -N foobar.example 8796bf1a-793c-3c44-9ec5-a572635cd3d4 % uuidgen -m -n @dns -N foobar.example 8796bf1a-793c-3c44-9ec5-a572635cd3d4
Les espaces de noms sont enregistrés dans un
registre IANA, d'autres peuvent être ajoutés si on écrit une spécification
(cf. RFC 8126). Notez que chaque espace a son
UUID (6ba7b810-9dad-11d1-80b4-00c04fd430c8
pour
l'espace DNS).
Les UUID de version 6 et 7, nouveautés de ce RFC 9562, ne sont pas mis en œuvre par uuidgen, ni d'ailleurs par beaucoup d'autres programmes.
Les sections 6.1 et 6.2, elles, décrivent le processus de génération d'un UUID à base temporelle. Idéalement, il faut utiliser une graine enregistrée sur le disque (pour éviter de générer des UUID identiques) ainsi que l'instant de la génération. Mais lire sur le disque prend du temps (alors qu'on peut vouloir générer des UUID rapidement, par exemple pour identifier des transactions) et l'horloge de la machine n'a pas toujours une résolution suffisante pour éviter de lire deux fois de suite le même instant. Ces sections contiennent donc également des avis sur la génération fiable d'UUID, par exemple en gardant en mémoire le nombre d'UUID générés, pour les ajouter à l'heure.
La section 8, consacrée à la sécurité, rappelle qu'un UUID ne doit pas être utilisé comme capacité (car il est trop facile à deviner) et qu'il ne faut pas demander à un humain de comparer deux UUID (ils se ressemblent trop pour un œil humain).
Il est évidemment recommandé d'utiliser les UUID de version 5 plutôt que de version 3 (RFC 6151) mais SHA-1 a aussi ses problèmes (RFC 6194) et, de toute façon, pour la plupart des utilisations des UUID, les faiblesses cryptographiques de MD5 et de SHA-1 ne sont pas gênantes.
La section 2.1 du RFC détaille les motivations pour la mise à jour du RFC 4122 et quels sont les changements effectués. Certaines utilisations des UUID ont remis en cause des suppositions originales. Ainsi, les UUID sont souvent utilisés dans un contexte réparti, où leur capacité à être uniques sans registre central est très utile. Mais quelques points manquaient au RFC 4122 :
Seize mises en œuvre des UUID ont été étudiées pour préparer le nouveau RFC (vous avez la liste dans la section 2.1), menant aux constations suivantes :
En Python, il existe un module UUID qui offre des fonctions de génération d'UUID de différentes versions (mais pas les plus récentes) :
import uuid myuuid = uuid.uuid1() # Version 1, Time-based UUID heruuid = uuid.uuid3(uuid.NAMESPACE_DNS, "foo.bar.example") # Version # 3, Name-based ("hash-based") UUID, a name hashed by MD5 otheruuid = uuid.uuid4() # Version 4, Random-based UUID yetanotheruuid = uuid.uuid5(uuid.NAMESPACE_DNS, "www.example.org") # Version 5, a name hashed by SHA1 if (myuuid == otheruuid or \ myuuid == heruuid or \ myuuid == yetanotheruuid or \ otheruuid == yetanotheruuid): raise Exception("They are equal, PANIC!") print(myuuid) print(heruuid) # Will always be the same print(otheruuid) print(yetanotheruuid) # Will always be the same
Et comme le dit la documentation, Note that uuid1() may compromise privacy since it creates a UUID containing the computer’s network address. (méthode de génération des UUID version 1 qui est désormais déconseillée).
Le SGBD PostgreSQL inclut un type UUID. Cela évite de stocker les UUID sous leur forme texte, ce qui est techniquement absurde et consomme 288 bits au lieu de 128 (section 6.13 du RFC).
essais=> CREATE TABLE Transactions (id uuid, value INT); CREATE TABLE essais=> INSERT INTO Transactions VALUES ('74738ff5-5367-5958-9aee-98fffdcd1876', 42); INSERT 0 1 essais=> INSERT INTO Transactions VALUES ('88e6441b-5f5c-436b-8066-80dca8222abf', 6); INSERT 0 1 essais=> INSERT INTO Transactions VALUES ('Pas correct', 3); ERROR: invalid input syntax for type uuid: "Pas correct" LINE 1: INSERT INTO Transactions VALUES ('Pas correct', 3); ^ -- PostgreSQL peut seulement générer la version 4, les aléatoires essais=> INSERT INTO Transactions VALUES (gen_random_uuid () , 0); INSERT 0 1 essais=> SELECT * FROM Transactions; id | value --------------------------------------+------- 74738ff5-5367-5958-9aee-98fffdcd1876 | 42 88e6441b-5f5c-436b-8066-80dca8222abf | 6 41648aef-b123-496e-8a4c-52e573d17b6a | 0 (3 rows)
Attention, le RFC (section 6.13) déconseille l'utilisation des UUID fondés sur un nom pour servir de clé primaire dans la base de données, sauf si on est absolument certain (mais c'est rare) que les noms ne changeront pas.
Il existe plusieurs exemples d'utilisation des UUID. Par exemple,
Linux s'en sert pour identifier les disques
attachés à la machine, de préférence à l'ancien système où l'ajout
d'un nouveau disque pouvait changer l'ordre des numéros sur le bus et
empêcher le système de trouver un disque. Un
/etc/fstab
typique sur Linux contient donc
des :
UUID=da8285a0-3a70-413d-baed-a1f48d7bf7b2 /home ext3 defaults ...
plutôt que les anciens :
/dev/sda3 /home ext3 defaults
car sda3
n'est pas un identificateur
stable. L'UUID, lui, est dans le système de fichiers et ne changera
pas avec les changements sur le bus. On peut
l'afficher avec dumpe2fs :
# dumpe2fs -h /dev/sda3 ... Filesystem UUID: da8285a0-3a70-413d-baed-a1f48d7bf7b2 ...
Un exemple d'utilisation de la nouvelle version 7 est décrit dans l'excellent article « Goodbye integers. Hello UUIDv7! ». Une mise en œuvre de cette version 7 apparait dans « UUIDv7 in 20 languages ».
La section 6 décrit les bonnes pratiques de génération d'un UUID. Ainsi :
Date de publication du RFC : Octobre 2024
Auteur(s) du RFC : S. Lhomme, M. Bunkus, D. Rice
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cellar
Première rédaction de cet article le 15 octobre 2024
Matroska est un format de conteneur pour du contenu multimédia (son, image, sous-titres, etc). Ce n'est pas un format de données (format utilisé par un codec), mais un moyen de regrouper de manière structurée des données différentes.
Je vous préviens tout de suite, le RFC fait 167 pages, et, en prime, le multimédia n'est pas ma spécialité. Donc, je vais faire court. Matroska est fondé sur un langage qui utilise le modèle de données XML, EBML (normalisé dans le RFC 8794). Notre nouveau RFC 9559 est donc surtout une très longue liste des éléments XML qu'on peut y trouver (Matroska est riche !), encodés en binaire. Matroska n'est pas nouveau (il en est à la version 4), mais c'est sa première normalisation. Notez que vous trouverez de l'information sur la page Web du projet.
Le caractère hiérarchique d'un fichier Matroska (des éléments dans d'autres éléments) est bien rendu par cet outil (qui fait partie de MKVToolNix, cité plus loin) :
% mkvinfo ./format/matroska/testdata/sweep-with-DC.mkvmerge13.mka + EBML head |+ EBML version: 1 |+ EBML read version: 1 |+ Maximum EBML ID length: 4 |+ Maximum EBML size length: 8 |+ Document type: matroska |+ Document type version: 4 |+ Document type read version: 2 + Segment: size 18507 |+ Seek head (subentries will be skipped) |+ EBML void: size 4031 |+ Segment information | + Timestamp scale: 22674 | + Multiplexing application: libebml v1.4.2 + libmatroska v1.6.2 | + Writing application: mkvmerge v52.0.0 ('Secret For The Mad') 64-bit | + Duration: 00:00:02.607714066 | + Date: 2022-08-28 18:32:21 UTC | + Segment UID: 0x3b 0x9f 0x28 0xc7 0xc4 0x90 0x8a 0xe0 0xcd 0x66 0x8f 0x11 0x8f 0x7c 0x2f 0x54 |+ Tracks | + Track | + Track number: 1 (track ID for mkvmerge & mkvextract: 0) | + Track UID: 3794791294650729286 | + Track type: audio | + Codec ID: A_FLAC | + Codec's private data: size 133 | + Default duration: 00:00:00.040634920 (24.609 frames/fields per second for a video track) | + Language: und | + Language (IETF BCP 47): und | + Audio track | + Sampling frequency: 44100 | + Bit depth: 16 |+ EBML void: size 1063 |+ Cluster
On voit un document de la version 4 (celle du RFC), commençant
(forcément, cf. section 4.5 du RFC) par un en-tête EBML, et
comportant une seule piste (l'élément
Tracks
est décrit en section 5.1.4), un son au
format FLAC
(RFC 9639). L'extension
.mka
est souvent utilisée pour des conteneurs
n'ayant que de l'audio (et .mkv
s'il y a de la
vidéo). La piste audio est dans une langue indéterminée,
und. Plusieurs éléments existent pour noter la
langue car Matroska n'utilisait autrefois que la norme
ISO 639 mais la version 4 recommande les bien
plus riches étiquettes de langue IETF (RFC 5646, alias « IETF BCP 47 »).
Un autre document a deux pistes, le son et l'image :
% mkvinfo ~/Videos/2022-01-22\ 13-17-28.mkv … |+ Tracks | + Track | + Track number: 1 (track ID for mkvmerge & mkvextract: 0) | + Track UID: 1 | + "Lacing" flag: 0 | + Language: und | + Codec ID: V_MPEG4/ISO/AVC | + Track type: video | + Default duration: 00:00:00.033333333 (30.000 frames/fields per second for a video track) | + Video track | + Pixel width: 852 | + Pixel height: 480 | + Display unit: 4 | + Codec's private data: size 41 (H.264 profile: High @L3.1) | + Track | + Track number: 2 (track ID for mkvmerge & mkvextract: 1) | + Track UID: 2 | + "Lacing" flag: 0 | + Name: simple_aac_recording | + Language: und | + Codec ID: A_AAC | + Track type: audio | + Audio track | + Channels: 2 | + Sampling frequency: 44100 | + Bit depth: 16 | + Codec's private data: size 5
L'arborescence des fichiers Matroska peut être très profonde et ça se reflète dans la numérotation des sections du RFC (il y a une section 5.1.4.1.28.18 !).
Les types MIME à utiliser sont audio/matroska
et video/matroska
(section 27.3 du RFC).
Il existe plusieurs mises en œuvre de Matroska en logiciel libre comme MKVToolNix, qui sait faire beaucoup de choses (comme afficher les éléments Matroska comme vu plus haut) et qui existe en ligne de commande ou GUI. Et VLC sait évidemment jouer les fichiers Matroska. OBS, quant à lui, permet d'en produire. Par exemple, cette vidéo sans intérêt a été faite avec OBS.
Date de publication du RFC : Avril 2024
Auteur(s) du RFC : B. Makarenko (The Technical center of Internet, LLC), V. Dolmatov (JSC "NPK Kryptonite")
Pour information
Première rédaction de cet article le 21 octobre 2024
Ce RFC marque l'arrivée d'un nouvel algorithme de signature dans les enregistrements DNSSEC, algorithme portant le numéro 23. Bienvenue au GOST R 34.10-2012 (alias ECC-GOST12), algorithme russe, spécifié en anglais dans le RFC 7091, une légère mise à jour de GOST R 34.10-2001.
La liste des algorithmes DNSSEC est un registre à l'IANA, https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml#dns-sec-alg-numbers-1
. Elle
comprend désormais GOST R 34.10-2012 (qui succède au R 34.10-2001 du
RFC 5933). Notez que GOST désigne en fait une
organisation de
normalisation, le terme correct serait donc de ne jamais
dire « GOST » tout court, mais plutôt « GOST R 34.10-2012 » pour
l'algorithme de signature et « GOST R 34.11-2012 » pour celui de
condensation,
décrit dans le RFC 6986 (voir la section 1 de notre RFC 9558).
La section 2 décrit le format des enregistrements
DNSKEY
avec GOST, dans lequel
on publie les clés GOST R 34.10-2012. Le champ Algorithme vaut 23,
le format de la clé sur le réseau suit le RFC 7091. GOST
est un algorithme à courbes elliptiques, courbes décrites par Q
= (x,y). Les 32 premiers octets de la clé sont x et les 32 suivants
y (en petit-boutien,
attention, contrairement à la majorité des protocoles Internet).
Parmi les bibliothèques cryptographiques existantes, au moins OpenSSL met en œuvre GOST R 34.10-2012 (testé avec la version 3.3.2). Voir RFC 9215 pour de l'aide à ce sujet. Sinon, on trouve parfois seulement l'ancienne version dans certains logiciels et certaines bibliothèques.
La section 2.2 donne un exemple de clé GOST publiée dans le DNS,
je n'ai pas trouvé d'exemple réel dans la nature, même en
.ru
.
La section 3 décrit le format des enregistrements RRSIG
, les signatures (avec un
exemple). On suit les RFC 5958 et RFC 7091.
Attention, une particularité de GOST fait que deux signatures des
mêmes données peuvent donner des résultats différents, car un
élément aléatoire est présent dans la signature.
La section 4
décrit le format des enregistrements DS
pour GOST. La clé publique de
la zone fille est condensée par GOST R 34.11-2012, algorithme de
numéro
5.
Les sections 5 et 6 couvrent des questions pratiques liées au développement et au déploiement de systèmes GOST, par exemple un rappel sur la taille de la clé (512 bits) et sur celle du condensat cryptographique (256 bits).
GOST peut se valider avec Unbound si la bibliothèque de cryptographie utilisée gère GOST. Et, comme indiqué plus haut, ce ne sera sans doute que l'ancienne version, celle du RFC 5933. Pour les programmeurs Java, DNSjava a le dernier GOST depuis la version 3.6.2. Pour le statut (recommandé ou non) de l'algorithme GOST pour DNSSEC, voir le RFC 8624. En Python, dnspython en version 2.7.0 n'a que l'ancien algorithme.
Date de publication du RFC : Avril 2024
Auteur(s) du RFC : U. Sharma (Igalia, S.L.), C. Bormann (Universität Bremen TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sedate
Première rédaction de cet article le 29 avril 2024
Ce RFC modifie légèrement le format des estampilles temporelles du RFC 3339, changeant à la marge une définition et, surtout, permettant d'y attacher des informations supplémentaires.
Le RFC 3339 décrit un des formats d'estampilles temporelles possibles sur l'Internet (car, malheureusement, toutes les normes Internet ne l'utilisent pas). Un exemple, sur Unix :
% date --rfc-3339=seconds 2024-04-29 06:22:33+00:00
Le format du RFC 3339 est du type « YYYY-MM-DD HH:MM:SS » avec, à la fin, ajout d'une information sur le décalage avec le temps de référence (on verra que notre nouveau RFC 9557 change un peu cette dernière information). Un format gros boutien, donc, qui permet notamment de trier les dates-et-heures uniquement en suivant l'ordre lexicographique des caractères.
Mais certaines applications voudraient en savoir plus, et ajouter
à ce format des détails. D'ailleurs, c'est déjà fait dans des
solutions non normalisées, comme le
format de Java, très populaire, qui permet des estampilles
comme 2011-12-03T10:15:30+01:00[Europe/Paris]
(fuseau horaire, ajouté après la date au
format du RFC 3339 ; lisez le RFC 6557 pour en savoir davantage sur les fuseaux
horaires).
Ce nouveau RFC prévoit donc une extension optionnelle (les dates-et-heures qui suivaient le format de l'ancien RFC restent parfaitement valdies), compatible avec celle de Java, et généraliste (on pourra indiquer autre chose que le fuseau horaire). Ce format étendu est baptisé IXDTF pour Internet Extended Date/Time Format. En revanche, le nouveau format ne gère pas des cas compliqués (la gestion du temps en informatique est toujours compliquée) comme les dates futures lorque la définition du fuseau horaire changera, par exemple en supprimant l'heure d'été, ou des échelles de temps différentes, comme TAI (le format IXDTF ne marche que pour l'échelle d'UTC).
Donc, concrètement, notre RFC commence par changer un peu la définition du décalage à la fin des estampilles. Dans le RFC 3339, il y avait trois cas subtilement différents pour une estampille indiquant l'absence de décalage :
+00:00
indiquait qu'on utilisait UTC
comme référence (identique au cas précédent),-00:00
indiquait qu'on n'avait
aucune idée sur la référence (typiquement parce qu'on voudrait
connaitre l'heure locale mais qu'on ne la
connait pas).
(Au passage, si vous ne connaissiez pas ces trois cas et leurs
différences, ne vous inquiétez pas, vous n'étiez pas seul·e.) Notre
RFC change cela (section 2) en décidant que Z est désormais synonyme
de -00:00
plutôt que de
+00:00
, un changement qui aura sans doute peu
d'importance en pratique.
L'autre nouveauté de ce RFC 9557 est plus marquante, c'est le format étendu IXDTF (section 3). Il consiste à ajouter à la fin de l'estampille une série (facultative) de couples {clé, valeur}, entre crochets. Si la clé est absente, c'est que la valeur est un fuseau horaire, suivant la syntaxe de la base TZ. Voici un exemple :
2022-07-08T12:14:37+02:00[Europe/Paris][u-ca=hebrew]
Cet exemple indique que le fuseau horaire était celui de
Paris (cf. le format de
ces noms de fuseaux) et que le calendrier préféré (clé
u-ca
), pour afficher cette date, serait le
calendrier hébreu (ce qui indiquera 9 Tammouz
5782, sauf erreur).
Un point d'exclamation avant la clé indiquerait que la clé doit être comprise par le lecteur, sinon, il faut qu'il ignore l'estampille temporelle (la plupart du temps, l'application peut ignorer les clés et leurs valeurs). Un trait bas indique une clé expérimentale, non officiellement enregistrée.
Car notre RFC crée aussi un registre
des clés. Pour l'instant, il ne compte qu'une seule clé,
u-ca
, pour indiquer le calendrier préféré. Pour
enregistrer une nouvelle clé, il faut une spécification écrite
(cf. RFC 8126), mais il y a aussi des
enregistrements temporaires, plus légers.
Et on termine par un petit mot sur la sécurité (section 7). Le RFC rappelle que plus on donne d'informations, plus on risque d'en donner trop. Ainsi, l'indication du calendrier préféré peut indiquer une origine ethnique ou religieuse, qui peut être considérée comme privée, surtout s'il y a des risques d'attaques racistes.
Date de publication du RFC : Mars 2024
Auteur(s) du RFC : R. Housley (Vigil Security)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lamps
Première rédaction de cet article le 22 novembre 2024
Ce court RFC ajoute aux certificats PKIX du RFC 5280 la possibilité de contenir des adresses de courrier électronique dont la partie locale est en Unicode. Et il modifie légèrement les règles pour les noms de domaine en Unicode dans les certificats. Il remplace le RFC 8399, avec peu de changements.
Les certificats sur
l'Internet sont normalisés dans le RFC 5280, qui décrit un profil de
X.509 nommé PKIX
(définir un profil était nécessaire car la norme X.509 est bien trop
riche et complexe). Ce RFC 5280 permettait des
noms de domaine en
Unicode (sections 4.2.1.10 et 7 du RFC 5280) mais il suivait l'ancienne norme IDN, celle des
RFC 3490 et suivants. Depuis, les IDN sont
normalisés dans le RFC 5890 et suivants, et
notre nouveau RFC 9549 modifie très légèrement le RFC 5280 pour s'adapter à cette nouvelle norme de
noms de domaines en Unicode. Les noms de domaine dans un certificat
peuvent être présents dans les champs Sujet (titulaire du
certificat) et Émetteur (AC ayant signé le certificat) mais aussi
dans les contraintes sur le nom (une autorité de certification peut être
limitée à des noms se terminant en example.com
,
par exemple).
Notez que, comme avant, ces noms sont exprimés dans le certificat
en Punycode (RFC 3492,
xn--caf-dma.fr
au lieu de
café.fr
), appelé A-label
dans notre RFC. C'est un bon exemple du fait que les
limites qui rendaient difficiles d'utiliser des noms de domaine en
Unicode n'avaient rien à voir avec le DNS (qui n'a jamais été limité à
ASCII, contrairement à ce qu'affirme une légende
courante). En fait, le problème venait des applications (comme
PKIX), qui ne s'attendaient pas à des noms en
Unicode. Un logiciel qui traite des certificats aurait été bien
étonné de voir des noms de domaines non-ASCII, et
aurait peut-être planté. D'où ce choix du
Punycode (A-label) par opposition à la forme plus
lisible du U-label.
Nouveauté plus importante du RFC 8399, mis à jour par notre RFC 9549, les adresses de courrier électronique en Unicode (EAI pour Email Address Internationalization). Elles étaient déjà permises par la section 7.5 du RFC 5280, mais seulement pour la partie domaine (à droite du @). Désormais, elles sont également possibles dans la partie locale (à gauche du @). Le RFC 8398 donne tous les détails sur ce sujet.
Reste à savoir quelles AC acceptent
Unicode. En 2018, j'avais testé avec
Let's Encrypt (avec le
client Dehydrated, en
mettant le Punycode dans domains.txt
) et ça
marchait, regardez le certificat de
. Le voici, affiché
par GnuTLS :
https://www.potamochère.fr/
% gnutls-cli www.potamochère.fr ... - subject `CN=www.xn--potamochre-66a.fr', issuer `CN=R10,O=Let's Encrypt,C=US', serial 0x03a34cfc017b4311da0b21797cd250ebd3c0, RSA key 4096 bits, signed using RSA-SHA256, activated `2024-11-01 05:26:59 UTC', expires `2025-01-30 05:26:58 UTC', pin-sha256="6y…
D'autres AC acceptent ces noms en Unicode : Gandi le fait aussi, par exemple. On
notera que le célèbre service de test de la qualité des
configurations TLS, SSLlabs, gère bien les IDN :
Enfin, le registre du
.ru
a participé au développement de
logiciels pour traiter l'Unicode dans les certificats.
La section 1.2 résume les changements depuis le RFC 8399. Notamment :
♚.example
ne
serait pas légal. Ce point est désormais explicite.xn--potamochre-66a.fr
et pas
potamochère.fr
). Cela permet d'éviter de
déclencher certaines
failles de sécurité.Date de publication du RFC : Février 2024
Auteur(s) du RFC : J. Arkko, C. S. Perkins, S. Krishnan
Pour information
Première rédaction de cet article le 24 avril 2024
La question de l'empreinte environnementale du numérique suscite beaucoup de débats. L'IAB avait organisé en décembre 2022 un atelier sur le cas de l'empreinte environnementale de l'Internet dont ce RFC est le compte-rendu (tardif, oui, je sais, mais mon propre article de résumé du RFC est aussi en retard).
Le sujet est très complexe, relativement nouveau, et surtout noyé sous beaucoup d'approximations, voire de franches bêtises (l'ADEME s'en est fait une spécialité, avec des chiffres tirés du chapeau et à la méthodologie inconnue). Il y a donc du travail sur la planche. L'IAB commence par estimer que l'Internet a certes une empreinte environnementale mais peut aussi servir à diminuer l'empreinte environnementale globale, ce qui n'est franchement pas étayé (le RFC cite l'exemple de réunions physiques remplacées par des réunions en ligne, sans citer de calcul détaillé qui permettrait de voir s'il y a vraiment un gain, et en oubliant que de toute façon une réunion en ligne ne rend pas les mêmes services). Mais l'IAB note aussi que l'Internet a des effets indirects, et pas forcément positifs : il cite l'exemple de l'augmentation de la consommation de biens matériels que produit le commerce en ligne.
Clairement, l'Internet n'est pas virtuel, contrairement à ce que prétend le marketing qui abuse de termes comme cloud pour faire croire que le numérique est immatériel. A contrario, l'Internet dépend de machines, de l'électricité (et des humains qui font fonctionner ces machines). Que peut-on faire pour diminuer l'empreinte environnementale de l'Internet ? (Sans pour autant suivre les conseils débiles de l'ADEME, comme de supprimer ses messages.)
Comme tous les ateliers de l'IAB, celui-ci a fonctionné en demandant aux participants des position papers expliquant leur point de vue. Ne participent à l'atelier que des gens ayant écrit un de ces articles, ce qui garantit que tout le monde a dû travailler le sujet. Ces articles sont disponibles en ligne, plus exactement à cet endroit. (Tous les documents liés à cet atelier sont également disponibles.) Parmi les papiers acceptés :
La section 2 du RFC détaille les sujets qui étaient dans le programme de l'atelier :
Ah et, si vous vous le demandez, l'atelier a été entièrement en ligne (section 2.1 du RFC).
La première des quatre sessions de l'atelier essayait d'aborder le problème de manière générale. Le problème du réchauffement climatique est évidemment bien plus vaste que l'Internet seul et n'a pas de solution simple et unique. Et les solutions ne sont pas toutes techniques, donc il y a des limites à ce que l'IETF peut faire (ce qui ne veut pas dire qu'il ne faut rien faire !). Même la publicité est mentionnée dans cette section du RFC, avec un très prudent « davantage d'études sont nécessaires » (opinion personnelle : son attitude au sujet de la publicité est un bon moyen de savoir si votre interlocuteur veut sérieusement lutter contre le réchauffement climatique, ou bien s'il veut juste faire des discours).
Ensuite, deuxième session sur les mesures et la récolte des faits. Par exemple, où sont les gros postes de consommation électrique dans le numérique ? Les serveurs ? Les routeurs ? Les terminaux ? C'est d'autant plus important que le RFC note la quantité de fausses informations qui circulent (citant par exemple un article qui confondait MB/s et Mb/s, soit un facteur 8 de différence). De même, contrairement à ce qui est encore souvent dit, la session a mis en évidence le fait que la consommation électrique n'est pas du tout proportionnelle au trafic. Des phrases comme « envoyer un courrier dégage autant de dioxyde de carbone qu'un vol Paris-Quelquepart » n'ont donc aucun sens. (Un des papiers acceptés, « Towards a power-proportional Internet » expliquait pourquoi il fallait changer cela et comment le faire.) Par contre, les usages impactent la consommation car ils peuvent nécessiter des mises à jour du réseau.
La troisième session regardait du côté des pistes d'amélioration,
plus précisement de celles sur lesquelles l'IETF pouvait agir. Le
premier point est celui des mesures (insuffisantes et parfois
contradictoires). Le deuxième point concernait l'influence de
phénomènes comme la gigue (RFC 4689) ou l'élongation du
trajet (RFC 7980) sur la consommation énergétique (si on
réduit ces phénomènes grâce à de meilleurs protocoles, est-ce qu'on
diminue la consommation ?). Parmi les autres optimisations
possibles, le choix de meilleurs formats, plus optimisés
(CBOR - RFC 8949 - est
cité). Notez qu'un des articles acceptés pour l'atelier faisait le
point sur toutes les activités de l'IETF liées à l'énergie,
draft-eckert-ietf-and-energy-overview
.
Et la quatrième et dernière session portait sur les étapes suivantes du travail ; en résumé, il y a du travail et, même si l'IETF ne peut pas tout, elle doit en prendre sa part. Il faut toujours garder en tête que le but n'est pas de réduire l'empreinte environnementale de l'Internet mais de réduire celle de l'ensemble de la société. Éteindre l'Internet diminuerait certainement son empreinte environnementale mais pourrait avoir des effets négatifs sur d'autres secteurs, comme les transports. Pour améliorer l'Internet sans le supprimer, plusieurs axes ont été mis en avant :
Et les actions concrètes (section 2.4.3) ?
Si vous voulez participer au nouveau programme de l'IAB E-IMPACT, tout est là.
Puisqu'on parlait de la section sur la sécurité qui est obligatoire dans les RFC, notre RFC en a une, qui rappelle que :
Date de publication du RFC : Mars 2024
Auteur(s) du RFC : J. Gould, D. Smith (VeriSign), J. Kolker, R. Carney (GoDaddy)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 14 juillet 2024
RDAP est le protocole recommandé pour accéder aux données sociales sur un nom de domaine, comme le nom du titulaire ou son adresse postale. Pour d'évidentes raisons de vie privée, certains registres ne renvoient pas la totalité de l'information dont ils disposent. Que doit-on mettre dans RDAP dans ce cas ? La question n'était pas tranchée et chaque registre faisait différemment. Désormais, il existe une solution normalisée.
Au passage, oui, d'accord, il n'y a pas que
RDAP pour obtenir les données sociales (cet
article vous en indiquera d'autres). Mais c'est le service le
plus moderne et le plus adapté
aux programmeur·ses. Contacté en HTTPS, le serveur RDAP va
renvoyer du JSON que le client n'aura plus qu'à filtrer et
formatter. Voici par exemple une partie de la réponse RDAP obtenue
en se renseignant sur
nouveaufrontpopulaire.fr
:
% curl -s https://rdap.nic.fr/domain/nouveaufrontpopulaire.fr | jq … [ "fn", {}, "text", "Parti Socialiste" ], [ "org", {}, "text", "Parti Socialiste" ], [ "adr", {}, "text", [ "", "", "99 Rue Moliere", "Ivry-sur-Seine", "", "94200", "FR" ] ], …
Ici, il s'agit d'une personne morale donc les données sont toutes envoyées. Et s'il s'agissait d'une personne physique, pour laquelle la loi Informatique & et Libertés s'applique, depuis 1978 ? La solution évidente est de ne pas envoyer les données qu'on ne veut pas diffuser mais attention, il y a un piège, il ne faut pas casser la syntaxe JSON. Par exemple, RDAP utilise (c'est en cours de changement, cf. RFC 9553) jCard pour formater les adresses (RFC 7095) et les champs dans jCard ne sont pas étiquetés, c'est leur position dans le tableau qui indique leur rôle (c'est un des nombreux inconvénients de jCard). On ne peut donc pas supprimer, par exemple, la rue, en indiquant :
[ "adr", {}, "text", [ "", "", "Ivry-sur-Seine", "", "94200", "FR" ] ], [ "email", {}, "text", "e5d92838d5f0268143ac47d86880b5f7-48916400@contact.gandi.net" ],
Car, alors, on ne saurait plus si "Ivry-sur-Seine" est la rue ou bien la ville.
Le principe de notre RFC est donc : si on peut, retirer le membre
JSON. Si on ne peut pas (cas du tableau de taille fixe), mettre une
valeur vide ou null
.
Petit point de terminologie : comment traduire le redacted du titre ? « Censuré » est inadapté (l'intervention ne vient pas d'un tiers mais d'une des deux parties). Je vais dire « élidé », mais « caviardé », « biffé » et « expurgé » sont également de bonnes solutions. Le nom correspondant est « élision ». Évidemment, il ne faut surtout pas dire « anonymisé », il n'y a rien d'anonyme ici, puisque le registre connait toute l'information, il refuse simplement de la diffuser.
La section 1 du RFC expose les grands principes de
l'élision. Elle explique notamment qu'en cas d'élision, il faut
ajouter un membre (nommé redacted
) à la réponse
JSON, expliquant les raisons et utilisant le langage
JSONPath (RFC 9535)
pour désigner de manière formelle la partie élidée.
Compte-tenu des contraintes sur la syntaxe de la réponse JSON (RFC 9083), le RFC normalise quatre façons d'élider (section 3) :
"label":"123 Maple
Ave\nSuite 901\nVancouver BC\nCanada"
(un
exemple du RFC 7095) peut être remplacée par une valeur
moins précise comme "label":"Vancouver\nBC\nCanada\n"
.e5d92838d5f0268143ac47d86880b5f7-48916400@contact.gandi.net
dans l'exemple plus haut.Et le RFC insiste qu'il ne faut pas utiliser de texte bidon (« XXX », « lorem ipsum dolor » ou « Ano Nymous ») car ce texte ne correspond pas forcément aux règles de syntaxe du champ (et, j'ajoute, peut être difficile à identifier pour le lecteur, qui peut ne pas avoir la référence).
Pour la première méthode, la suppression d'un champ, si on
supprime le titulaire, on aura un membre nommé
redacted
(élidé) ajouté ainsi :
"redacted": [ { "name": { "description": "Remove registrant" }, "prePath": "$.entities[?(@.roles[0]=='registrant')]", "method": "removal" } ]
Notez le (difficile à lire) code JSONPath
$.entities[?(@.roles[0]=='registrant')]
.
Le deuxième cas, celui d'une valeur vide, donnerait, pour le cas où on supprime juste le nom du titulaire (qui est en position 1 dans le jCard, et son nom en position 3 - sachant qu'on part de 0) :
[ "fn", {}, "text", "" ] … "redacted": [ { "name": { "description": "Registrant Name" }, "postPath": "$.entities[?(@.roles[0]=='registrant')]. vcardArray[1][?(@[0]=='fn')][3]", "pathLang": "jsonpath", "method": "emptyValue", "reason": { "description": "Server policy" } } ]
Troisième technique d'élision, réduire une valeur. Le
redacted
devient :
"redacted": [ { "name": { "description": "Home Address Label" }, "postPath": "$.vcardArray[1][?(@[0]=='adr')][1].label", "pathLang": "jsonpath", "method": "partialValue", "reason": { "description": "Server policy" } } ]
Et pour finir, la quatrième et dernière méthode, le remplacement :
"redacted": [ { "name": { "description": "Registrant Email" }, "postPath": "$.entities[?(@.roles[0]=='registrant')]. vcardArray[1][?(@[0]=='email')][3]", "pathLang": "jsonpath", "method": "replacementValue", } ]
Ce membre appelé redacted
est spécifié en
détail dans la section 4 du RFC. (Et il est enregistré à l'IANA parmi les extensions RDAP.) Pour signaler qu'il peut
apparaitre, le membre rdapConformance
de la
réponse JSON va l'indiquer :
{ "rdapConformance": [ "itNic", "redacted", "rdap_level_0" ], …
Dès qu'il y a élision, redacted
doit être
ajouté. Il contient un tableau JSON d'objets, dont les membres
peuvent être (seul le premier est obligatoire) :
name
: un terme qui décrit le champ
élidé,prePath
et
postPath
: des expressions JSONPath (RFC 9535) qui
dénotent le membre retiré ou modifié,method
: la technique d'élision
utilisée (suppression, nettoyage, remplacement, etc),reason
: texte libre décrivant la
raison de l'élision.Date de publication du RFC : Avril 2024
Auteur(s) du RFC : M. Loffredo, M. Martinelli (IIT-CNR/Registro.it)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 28 octobre 2024
Ce RFC normalise une extension au protocole d'accès à l'information RDAP pour permettre des recherches inversées, des recherches par le contenu (« quels sont tous les noms de domaine de cette personne ? »).
Alors, tout d'abord, un avertissement. L'extension normalisée dans ce RFC est dangereuse pour la vie privée. Je détaille ce point plus loin mais ne réclamez pas tout de suite le déploiement de cette extension : lisez tout d'abord. C'est dès sa section 1 que le RFC met en garde contre un déploiement hâtif !
RDAP (normalisé dans les RFC 9082 et RFC 9083), comme son prédécesseur whois, permet d'obtenir des informations dites sociales (nom, adresse, numéro de téléphone, etc) sur les personnes (morales ou physiques) associées à une ressource Internet réservée, comme un nom de domaine ou un adresse IP. Une limite importante de ces deux protocoles est qu'il faut connaitre l'identifiant (nom de domaine, adresse IP) de la ressource. Or, certaines personnes seraient intéressés à faire l'inverse, découvrir les ressources à partir d'informations sociales. C'est le cas par exemple de juristes cherchant le portefeuille de noms de domaine de quelqu'un qu'ils soupçonnent de menacer leur propriété intellectuelle. Ou de chercheurs en sécurité informatique étudiant toutes les adresses IP utilisées par le C&C d'un botnet et voulant en découvrir d'autres. Ce RFC normalise justement un moyen de faire des recherches inverses. whois n'avait jamais eu une telle normalisation, à cause des risques pour la vie privée, risques qui sont peut-être moins importants avec RDAP.
(Notez quand même que le RFC 9082, section 3.2.1, prévoyait déjà certaines recherches inverses, d'un domaine à partir du nom ou de l'adresse d'un de ses serveurs de noms.)
Passons aux détails techniques (section 2). L'URL
d'une requête inverse va inclure dans son chemin
/reverse_search
et, bien sûr, des critères de
recherche. Par exemple,
/domains/reverse_search/entity?fn=Jean%20Durand&role=registrant
donnera tous les domaines dont le titulaire
(registrant) se nomme Jean Durand
(fn
est défini dans la section 6.2.1 du RFC 6350). Le terme après
/reverse_search
indique le type des données
auxquelles s'appliquent les critères de recherche (actuellement,
c'est forcément entity
, une personne morale ou
physique).
La plupart des recherches inverses porteront sans doute sur quelques champs comme l'adresse de courrier électronique ou le handle (l'identifiant d'une entité). La section 8 décrit les possibilités typiques mais un serveur RDAP choisit de toute façon ce qu'il permet ou ne permet pas.
La sémantique exacte d'une recherche inverse est décrite en
JSONPath (RFC 9535). Vous trouvez le JSONPath dans la réponse (section
5 du RFC), dans le membre
reverse_search_properties_mapping
. Par exemple :
"reverse_search_properties_mapping": [ { "property": "fn", "propertyPath": "$.entities[*].vcardArray[1][?(@[0]=='fn')][3]" } ]
L'expression JSONPath
$.entities[*].vcardArray[1][?(@[0]=='fn')][3]
indique que le serveur va faire une recherche sur le membre
fn
du tableau vCard
(RFC 7095). Oui, elle est compliquée, mais
c'est parce que le format vCard est compliqué. Heureusement, on
n'est pas obligé de connaitre JSONPath pour utiliser les recherches
inverses, uniquement pour leur normalisation (section 3).
Pour savoir si le serveur RDAP que vous interrogez gère cette
extension et quelles requêtes il permet, regardez sa réponse à la
question help
(RFC 9082,
section 3.1.6, et RFC 9083, section 7) ; s'il
accepte les requêtes inverses, vous y trouverez la valeur
reverse_search
dans le membre
rdapConformance
(section 9), par exemple :
"rdapConformance": [ "rdap_level_0", "reverse_search" ]
Pour les recherches acceptées, regardez le membre
reverse_search_properties
. Par exemple :
"reverse_search_properties": [ { "searchableResourceType": "domains", "relatedResourceType": "entity", "property": "fn" } ]
Ici, le serveur indique qu'il accepte les requêtes inverses pour trouver des noms de domaine en fonction d'un nom d'entité (ce que nous avons fait dans l'exemple plus haut). Voir la section 4 pour les détails. Si vous tentez une requête inverse sur un serveur, et que le serveur n'accepte pas les requêtes inverses, ou tout simplement n'accepte pas ce type particulier de recherche inverse que vous avez demandé, il répondra avec le code de retour HTTP 501 (section 7). Vous aurez peut-être aussi un 400 si votre requête déplait au serveur pour une raison ou l'autre.
L'extension est désormais placée dans le registre des extensions RDAP. En outre, deux nouveaux registres sont créés, RDAP Reverse Search, pour les recherches possibles et RDAP Reverse Search Mapping pour les règles JSONPath. Pour y ajouter des valeurs, la politique (RFC 8126) est « spécification nécessaire ».
Revenons maintenant aux questions de vie privée (RFC 6973), que je vous avais promises. La section 12 du RFC détaille le problème. La puissance des recherches inverses les rend dangereuses. Une entreprise concurrente pourrait regarder vos noms de domaine et ainsi se tenir au courant, par exemple, d'un nouveau projet ou d'un nouveau produit. Un malveillant pourrait regarder les noms de domaine d'une personne et identifier ainsi des engagements associatifs que la personne ne souhaitait pas forcément rendre très visibles. Les données stockées par les registres sont souvent des données personnelles et donc protégées par des lois comme le RGPD. Le gestionnaire d'un serveur RDAP doit donc, avant d'activer la recherche inverse, bien étudier la sécurité du serveur (section 13) mais aussi (et ce point n'est hélas pas dans le RFC) se demander si cette activation est vraiment une bonne idée.
En théorie, RDAP pose moins de problème de sécurité que whois pour ce genre de recherches, car il repose sur HTTPS (le chiffrement empêche un tiers de voir questions et réponses) et, surtout, il permet l'authentification ce qui rend possible, par exemple, de réserver les recherches inverses à certains privilégiés, avec un grand choix de contrôle d'accès (cf. annexe A). Évidemment, cela laisse ouvertes d'autres questions comme « qui seront ces privilégiés ? » et « comment s'assurer qu'ils n'abusent pas ? » (là, les journaux sont indispensables pour la traçabilité, cf. l'affaire Haurus).
Notons qu'outre les problèmes de vie privée, la recherche inverse pose également des problèmes de performance (section 10). Attention avant de la déployer, des requêtes apparemment innocentes pourraient faire ramer sérieusement le serveur RDAP. Si vous programmez un serveur RDAP ayant des recherches inverses, lisez bien les recommandations d'optimisation de la section 10, par exemple en ajoutant des index dans votre SGBD. Et le serveur ne doit pas hésiter, en cas de surcharge, à répondre seulement de manière partielle (RFC 8982 ou RFC 8977).
Je n'ai pas trouvé de code public mettant en œuvre ces recherches inverses. De même, je ne connais pas encore de serveur RDAP déployé qui offre cette possibilité.
Date de publication du RFC : Février 2024
Auteur(s) du RFC : S. Gössner (Fachhochschule Dortmund), G. Normington, C. Bormann (Universität Bremen TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF jsonpath
Première rédaction de cet article le 14 février 2025
Le langage JSONPath sert à désigner une partie d'un document JSON. Il est donc utile pour, par exemple, extraire des valeurs qui nous intéressent.
Il existe de nombreux langages pour cela (une liste partielle figure à la fin de l'article), pas forcément normalisés mais mis en œuvre dans tel ou tel logiciel (dont, évidemment, jq). Ce RFC est, je crois, la première normalisation formelle d'un tel langage. Voici tout de suite un exemple, utilisant le logiciel jpq, sur la la base des arbres parisiens :
% jpq '$[*].libellefrancais' arbres.json [ "Marronnier", "Aubépine", "Cerisier à fleurs", …
Cela se lit « affiche les noms d'espèce
[libellefrancais
, dans le fichier] de tous
[vous avez vu l'astérisque] les
arbres ». (Avec jq, on aurait écrit jq
'.[].libellefrancais' arbres.json
.) Voyons maintenant les
détails.
JSONPath travaille sur des
documents JSON, JSON étant normalisé dans le RFC 8259. Il n'est sans doute pas utile de
présenter JSON, qui est utilisé partout aujourd'hui. Notons
simplement que l'argument d'une requête JSONPath, écrit en JSON, est
ensuite modélisé comme un arbre, composé de
nœuds. Le JSON {"foo": 3, "bar": 0}
a ainsi une
racine de l'arbre sous laquelle se trouvent deux nœuds, avec comme
valeur 3 et 0 (les noms des membres JSON, foo
et bar
, ne sont pas sélectionnables par
JSONPath et donc pas considérés comme nœuds).
Vous êtes impatient·e de vous lancer ? On va utiliser le logiciel jpq cité plus haut. Pensez à installer un Rust très récent puis :
cargo install jpq
Ou bien, si vous voulez travailler à partir des sources :
git clone https://codeberg.org/KMK/jpq.git cd jpq cargo install --path .
Puis pensez à modifier votre chemin de recherche d'exécutables si ce n'est pas déjà fait (notez que l'excellente Julia Evans vient justement de faire un très bon article sur ce sujet). Testez :
% echo '["bof"]' | jpq '$[0]' [ "bof" ]
Les choses utiles à savoir pour écrire des expressins JSONPath :
[0]
est un segment sélectionnant
le premier élément d'un tableau, ici, le seul).La grammaire complète (en ABNF, RFC 5234) figure dans l'annexe A du RFC.
Allez, passons aux exemples, avec les arbres parisiens comme document JSON. Le quatrième arbre du document (on part de zéro) :
% jpq '$[3]' arbres.json [ { "idbase": 143057, "typeemplacement": "Arbre", "domanialite": "CIMETIERE", "arrondissement": "PARIS 20E ARRDT", "complementadresse": null, "numero": null, "adresse": "CIMETIERE DU PERE LACHAISE / DIV 41", "idemplacement": "D00000041050", "libellefrancais": "Erable", …
Tous les genres (dans la classification de Linné) :
% jpq '$[*].genre' arbres.json [ "Aesculus", "Crataegus", "Prunus", …
Les sélecteurs par nom peuvent s'écrire avec un point, comme ci-dessus, ou bien entre crochets avec des apostrophes (notez comme c'est moins pratique pour le shell Unix) :
% jpq "\$[*]['genre']" arbres.json [ "Aesculus", "Crataegus", "Prunus", …
La forme avec les crochets est la forme canonique pour une expression JSONPath. La notation avec un point n'est acceptée que pour les noms, pas pour les indices :
% echo '[1, 2, 3]' | jpq '$[1]' [ 2 ] % echo '[1, 2, 3]' | jpq '$.1' Error: × Invalid JSONPath: at position 2, in dot member name, must start with lowercase alpha or '_' ╭──── 1 │ $.1 · ▲ · ╰── in dot member name, must start with lowercase alpha or '_' ╰────
Tous les arbres qui font plus de 50 mètres de haut :
% jpq '$[?@.hauteurenm > 50].hauteurenm' < arbres.json [ 120, 69, 100, 67, 58, 74, 911 ]
L'arobase indique le nœud en cours d'évaluation. Et, oui, l'arbre de 911 mètres de haut est une erreur, chose courante dans les données ouvertes.
Comme vous avez vu, la requête JSONPath renvoie toujours une liste de nœuds, affichée par jpq dans la syntaxe des tableaux JSON.
Vous avez vu, dans l'exemple où on cherchait les plus grands arbres, que JSONPath permet de faire des comparaisons. Vous avez droit à tous les opérateurs booléens classiques. Ici, l'identificateur de tous les arbres situés dans un cimetière :
% jpq '$[?@.domanialite == "CIMETIERE"].idbase' < arbres.json [ 143057, 165296, 166709, …
JSONPath permet d'ajouter des fonctions, qui ne sont utilisables
que pour les comparaisons (je ne crois pas qu'on puisse mettre leur
résultat dans la valeur retournée par JSONPath). Plusieurs d'entre
elles sont définies par ce RFC, comme length()
,
mais pas forcément mises en œuvre dans tous les programmes. Et pour
compliquer les choses, des implémentations de JSONPath ajoutent
leurs propres fonctions non enregistrées et qui rendent l'expression
non portable.
Deux de ces fonctions,
search()
et match()
,
prennent en paramètre une expression
rationnelle, à la syntaxe du RFC 9485.
Les expressions JSONPath, quand on les envoie via l'Internet,
peuvent être marquées avec le type application/jsonpath
,
désormais enregistré
(section 3 du RFC). En plus, un
nouveau registre IANA est créé, pour stocker les fonctions
mentionnées au paragraphe précédent. Pour ajouter des fonctions à ce
registre, il faut passer par la procédure « Examen par un expert »
du RFC 8126.
La section 4, sur la sécurité, est très détaillée. JSONPath était souvent mis en œuvre en passant directement l'expression ou une partie d'entre elle au langage sous-jacent (typiquement JavaScript). Inutile de détailler les énormes problèmes de sécurité que cela pose si l'expression évaluée est, en tout ou en partie, fournie par un client externe (par exemple via un formulaire Web), permettant ainsi l'injection de code. Une validation de ces expressions avant de les passer étant irréaliste, le RFC insiste sur le fait qu'une mise en œuvre de JSONPath doit avoir son propre code et ne pas compter sur une évaluation par le langage de programmation sous-jacent.
Même ainsi, une expression JSONPath peut permettre une attaque par déni de service, si une expression mène à une consommation de ressources excessive. (Cf. la section 10 du RFC 8949 et la section 8 du RFC 9485.)
La section 1.2 de notre RFC décrit l'histoire un peu
compliquée de JSONPath. (Une des raisons pour lesquelles il
existe plusieurs langages de requête dans JSON est que JSONPath a
mis du temps à être terminé.) L'origine de JSONPath date de
2007, dans cet article de
Gössner (un des auteurs du RFC). L'interprétation de ce premier
JSONPath était souvent déléguée au langage sous-jacent (le
eval()
de JavaScript…)
avec les problèmes de sécurité qu'on imagine. Le JSONPath actuel se
veut portable, au sens où il ne dépend pas d'un langage
d'implémentation particulier, ni d'un moteur
d'expressions rationnelles
particulier. L'inconvénient est qu'il n'est pas forcément compatible
avec l'ancien JSONPath.
JSONPath a aussi été inspiré par le XPath de XML (cf. annexe B de notre RFC, qui détaille cette inspiration, et compare les deux langages). On peut aussi comparer JSONPath à JSON Pointer (RFC 6901), ce qui est fait dans l'annexe C.
Il existe bien sûr d'autres mises en œuvre de JSONPath que jpq. Mais attention : certaines sont très anciennes (comme Python JSONPath RW) et ne correspondent pas à ce qui est normalisé dans le RFC. Essayons Python JSONPath Next-Generation :
% python Python 3.13.2 (main, Feb 5 2025, 01:23:35) [GCC 14.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from jsonpath_ng.ext import filter, parse >>> import json >>> data = json.load(open("arbres.json")) >>> expr = parse("$[?@.hauteurenm > 50].hauteurenm") >>> result = expr.find(data) >>> for r in result: ... print(r.value) ... 120 69 100 67 58 74 911
D'autres langages de requête dans le JSON existent, chacun avec sa syntaxe :
Date de publication du RFC : Mars 2024
Auteur(s) du RFC : P. Hoffman (ICANN), K. Fujiwara
(JPRS)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 3 mai 2024
Comme beaucoup de protocoles très utilisés sur l'Internet, le DNS est ancien. Très ancien (la première norme, le RFC 882, date de 1983). Les normes techniques vieillissent, avec l'expérience, on comprend mieux, on change les points de vue et donc, pour la plupart des protocoles, on se lance de temps en temps dans une révision de la norme. Mais le DNS est une exception : la norme actuelle reste fondée sur des textes de 1987, les RFC 1034 et RFC 1035. Ces documents ne sont plus à jour, modifiés qu'ils ont été par des dizaines d'autres RFC. Bref, aujourd'hui, pour comprendre le DNS, il faut s'apprêter à lire de nombreux documents. En attendant qu'un courageux et charismatique participant à l'IETF se lance dans la tâche herculéenne de faire un jeu de documents propre et à jour, ce RFC 9499, successeur du RFC 8499 (il y a peu de modifications), se limite à une ambition plus modeste : fixer la terminologie du DNS. Ce n'est pas un tutoriel : vous n'y apprendrez pas le DNS. C'est plutôt une encyclopédie.
En effet, chacun peut constater que les discussions portant sur le DNS sont difficiles : on manque de terminologie standard, et celle des RFC officielles ne suffit pas, loin de là. Souvent, le DNS a tellement changé que le RFC officiel est même trompeur : les mots ne veulent plus dire la même chose. D'autres protocoles ont connu des mises à jour de la norme. Cela a été le cas de SMTP, passé successivement du RFC 772 à l'actuel RFC 5321, en passant par plusieurs révisions successives. Ou de XMPP, qui a vu sa norme originale mise à jour dans le RFC 6120. Et bien sûr de HTTP, qui a connu plusieurs toilettages complets (cf. RFC 9110). Mais personne n'a encore osé faire pareil pour le DNS. Au moins, ce RFC 9499, comme son prédécesseur RFC 8499, traite l'un des problèmes les plus criants, celui du vocabulaire. Le RFC est évidemment en anglais, les traductions proposées dans cet article, et qui n'ont pas de valeur « officielle » sont de moi seul.
Notre RFC 9499 rassemble donc des définitions pour des termes qui étaient parfois précisément définis dans d'autres RFC (et il fournit alors un lien vers ce RFC original), mais aussi qui n'étaient définis qu'approximativement ou parfois qui n'étaient pas définis du tout (et ce RFC fournit alors cette définition). Du fait du flou de certains RFC anciens, et des changements qui ont eu lieu depuis, certaines définitions sont différentes de l'original. Le document a fait l'objet d'un consensus relatif auprès des experts DNS mais quelques termes restent délicats. Notez aussi que d'autres organisations définissent des termes liés au DNS par exemple le WHATWG a sa définition de ce qu'est un domaine, et le RSSAC a développé une terminologie.
Ironiquement, un des termes les plus difficiles à définir est
« DNS » lui-même (section 1 de notre RFC). D'accord, c'est le sigle
de Domain Name System mais ça veut dire quoi ?
« DNS » peut désigner le schéma de nommage (les noms de domaine comme
signal.eu.org
, leur syntaxe, leurs
contraintes), la base de données répartie (et faiblement cohérente)
qui associe à ces noms des informations (comme des certificats, des
adresses IP, etc), ou le
protocole
requête/réponse (utilisant le port 53) qui permet d'interroger cette
base. Parfois, « DNS » désigne uniquement le protocole, parfois,
c'est une combinaison des trois éléments indiqués plus haut
(personnellement, quand j'utilise « DNS », cela désigne uniquement
le protocole, un protocole relativement simple, fondé sur l'idée
« une requête => une réponse »).
Bon, et ces définitions rigoureuses et qui vont mettre fin aux discussions, ça vient ? Chaque section du RFC correspond à une catégorie particulière. D'abord, en section 2, les noms eux-même, ces fameux noms de domaine. Un système de nommage (naming system) a plusieurs aspects, la syntaxe des noms, la gestion des noms, le type de données qu'on peut associer à un nom, etc. D'autres systèmes de nommage que celui présenté dans ce RFC existent, et sont distincts du DNS sur certains aspects. Pour notre système de nommage, le RFC définit :
www.madmoizelle.com
), le vocabulaire s'en
ressent. Par exemple, on va dire que com
est « au-dessus de
madmoizelle.com
» (vision arborescente) ou
bien « à la fin de www.madmoizelle.com
»
(vision texte). Notez aussi que la représentation des noms de
domaine dans les paquets IP n'a rien à voir avec leur représentation
texte (par exemple, les points n'apparaissent pas). Enfin, il faut
rappeler que le vocabulaire « standard » n'est pas utilisé
partout, même à l'IETF, et qu'on voit parfois « nom de domaine »
utilisé dans un sens plus restrictif (par exemple uniquement pour
parler des noms pouvant être résolus avec le DNS, pour lesquels il
avait été proposé de parler de DNS
names).ldap.potamochère.fr.
est un FQDN alors que
ldap
tout court ne l'est pas). En toute
rigueur, un FQDN devrait toujours s'écrire avec un point à la fin
(pour représenter la racine) mais ce n'est pas toujours le
cas. (Notre RFC parle de « format de présentation » et de « format
courant d'affichage » pour distinguer le cas où on met
systématiquement le point à la fin et le cas où on
l'oublie.)www.laquadrature.net
, il y a trois
composants, www
,
laquadrature
et
net
.brienne.tarth.got.example
peut être un nom
de machine mais www.&#$%?.example
ne
peut pas l'être. Le terme de « nom de machine » est parfois
aussi utilisé pour parler du premier composant d'un nom de
domaine (brienne
dans
brienne.tarth.got.example
).fr
ou
name
sont des TLD. N'utilisez surtout pas le terme erroné
d'« extension ». Et ne dites pas que le TLD est le
composant le plus à droite, ce n'est pas vrai dans
l'alphabet arabe. La distinction courante
entre gTLD, gérés par l'ICANN, et
ccTLD, indépendants de l'ICANN, est
purement politique et ne se reflète pas dans le DNS.www.cl.cam.ac.uk
est un sous-domaine de
cl.cam.ac.uk
, qui est un sous-domaine de
cam.ac.uk
et ainsi de suite, jusqu'à la
racine, le seul domaine à n'être sous-domaine de
personne. Quand le RFC parle de suffixe, il s'agit d'un suffixe
de composants, pas de caractères :
foo.example.net
n'est pas un sous-domaine
de oo.example.net
.vader IN CNAME anakin
, l'alias est
vader
(et c'est une erreur de dire que
c'est « le CNAME »).anakin
est le CNAME,
le « nom canonique ».Fini avec les noms, passons à l'en-tête des messages DNS et aux codes qu'il peut contenir. Cet en-tête est défini dans le RFC 1035, section 4.1. Il donne des noms aux champs mais pas forcément aux codes. Ainsi, le code de réponse 3 indiquant qu'un domaine demandé n'existe pas est juste décrit comme name error et n'a reçu son mnémonique de NXDOMAIN (No Such Domain) que plus tard. Notre RFC définit également, dans sa section 3 :
Voici un renvoi depuis la racine vers .fr
:
% dig @l.root-servers.net A blog.imirhil.fr ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16572 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 5, ADDITIONAL: 11 ... ;; AUTHORITY SECTION: fr. 172800 IN NS d.ext.nic.fr. fr. 172800 IN NS d.nic.fr. fr. 172800 IN NS e.ext.nic.fr. fr. 172800 IN NS f.ext.nic.fr. fr. 172800 IN NS g.ext.nic.fr.
La section 4 s'intéresse aux transactions DNS. Un des termes définis est celui de QNAME (Query NAME), nouveauté du RFC 8499. Il y a trois sens possibles (le premier étant de très loin le plus commun) :
Le RFC 2308 est clairement en tort ici. Il aurait dû utiliser un terme nouveau, pour le sens nouveau qu'il utilisait.
Passons maintenant aux enregistrements DNS, stockés dans cette base de données répartie (section 5 du RFC) :
Voici un ensemble d'enregistrements (RRset), comptant ici deux enregistrements :
rue89.com. 600 IN MX 50 mx2.typhon.net. rue89.com. 600 IN MX 10 mx1.typhon.net.
(Depuis, ça a changé.)
Et voici un pseudo-enregistrement OPT, tel qu'affiché par dig, avec une indication de la taille maximale et l'option client subnet (RFC 7871) :
;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 512 ; CLIENT-SUBNET: 13.176.144.0/24/0
Ensuite, un sujet chaud où le vocabulaire est particulièrement peu défini, et très mal utilisé (voir les forums grand public sur le DNS où les discussions prennent un temps fou car les gens utilisent mal les mots) : les différents types de serveurs et clients DNS (section 6). Pour commencer, il est crucial de se méfier quand un texte parle de « serveur DNS » tout court. Si le contexte ne permet pas de savoir de quel genre de serveur DNS on parle, cela indique un manque de compréhension du DNS par l'auteur du texte. Serveurs faisant autorité et serveurs résolveurs, quoique utilisant le même protocole, sont en effet très différents.
getaddrinfo()
ou
getnameinfo()
. Sur
Unix, le résolveur minimum fait en
général partie de la libc et trouve l'adresse du ou des résolveurs
complets dans /etc/resolv.conf
.f.root-servers.net
fait autorité pour la
racine, d.nic.fr
fait autorité pour
pm
,
etc. Des logiciels comme NSD ou Knot assurent cette fonction. Les serveurs
faisant autorité sont gérés par divers acteurs, les registres, les
hébergeurs DNS (qui sont souvent en même temps bureaux
d'enregistrement), mais aussi parfois par
M. Michu. La commande dig NS $ZONE
vous
donnera la liste des serveurs faisant autorité pour la zone
$ZONE
. Ou bien vous pouvez utiliser un
service sur le Web en visitant
https://dns.bortzmeyer.org/DOMAIN/NS
où
DOMAIN est le nom de domaine qui vous intéresse.NS .
» à un des serveurs de sa
liste. Ainsi, tant qu'au moins un des serveurs de la vieille
liste répond, le résolveur est sûr d'apprendre la liste
actuelle./etc/resolv.conf
le
serveur primaire ») n'a pas de sens.forwarders
). La définition de notre RFC est
le premier sens.www.organisation.example
aille sur le site
public quand on vient de l'Internet mais sur un site interne de
la boîte quand on est sur le réseau local des employés.Voici, vu par tcpdump, un exemple d'initialisation d'un résolveur BIND utilisant la racineYeti (RFC 8483) :
15:07:36.736031 IP6 2a01:e35:8bd9:8bb0:21e:8cff:fe76:29b6.35721 > 2001:6d0:6d06::53.53: \ 21476% [1au] NS? . (28) 15:07:36.801982 IP6 2001:6d0:6d06::53.53 > 2a01:e35:8bd9:8bb0:21e:8cff:fe76:29b6.35721: \ 21476*- 16/0/1 NS yeti-ns.tisf.net., NS yeti-ns.lab.nic.cl., NS yeti-ns.wide.ad.jp., NS yeti.ipv6.ernet.in., NS yeti-ns.as59715.net., NS ns-yeti.bondis.org., NS yeti-dns01.dnsworkshop.org., NS dahu2.yeti.eu.org., NS dahu1.yeti.eu.org., NS yeti-ns.switch.ch., NS bii.dns-lab.net., NS yeti.bofh.priv.at., NS yeti-ns.conit.co., NS yeti.aquaray.com., NS yeti-ns.ix.ru., RRSIG (619)
La question était « NS .
» (quels sont les
serveurs de la racine) et la réponse contenait les noms des seize
serveurs racine qu'avait Yeti à l'époque.
Voici aussi des exemples de résultats avec un résolveur ou bien avec un serveur faisant autorité. Si je demande à un serveur faisant autorité (ici, un serveur racine), avec mon client DNS qui, par défaut, demande un service récursif (flag RD, Recursion Desired) :
% dig @2001:620:0:ff::29 AAAA www.iab.org ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54197 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 9, ADDITIONAL: 13 ;; WARNING: recursion requested but not available ... ;; AUTHORITY SECTION: org. 172800 IN NS b0.org.afilias-nst.org. ...
C'est pour cela que dig affiche WARNING: recursion requested but not available. Notez aussi que le serveur, ne faisant autorité que pour la racine, n'a pas donné la réponse mais juste un renvoi aux serveurs d'Afilias. Maintenant, interrogeons un serveur récursif (le service de résolveur public Yandex DNS) :
% dig @2a02:6b8::feed:0ff AAAA www.iab.org ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63304 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ... ;; ANSWER SECTION: www.iab.org. 1800 IN AAAA 2001:1900:3001:11::2c
Cette fois, j'ai obtenu une réponse, et avec le
flag RA, Recursion
Available. Si je pose une question sans le
flag RD (Recursion Desired,
avec l'option +norecurse
de dig) :
% dig +norecurse @2a02:6b8::feed:0ff AAAA www.gq.com ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59438 ;; flags: qr ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ... ;; ANSWER SECTION: www.gq.com. 293 IN CNAME condenast.map.fastly.net.
J'ai obtenu ici une réponse car l'information était déjà dans le cache (la mémoire) de Yandex DNS (on le voit au TTL, qui n'est pas un chiffre rond, il a été décrémenté du temps passé dans le cache). Si l'information n'est pas dans le cache :
% dig +norecurse @2a02:6b8::feed:0ff AAAA blog.keltia.net ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19893 ;; flags: qr ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 ...
Je n'obtiens alors pas de réponse (ANSWER: 0, donc NODATA). Si je demande au serveur faisant autorité pour cette zone :
% dig +norecurse @2a01:e0d:1:3:58bf:fa61:0:1 AAAA blog.keltia.net ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62908 ;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 6, ADDITIONAL: 15 ... ;; ANSWER SECTION: blog.keltia.net. 86400 IN AAAA 2a01:240:fe5c:1::2 ...
J'ai évidemment une réponse et, comme il s'agit d'un serveur faisant autorité, elle porte le flag AA (Authoritative Answer, qu'un résolveur ne mettrait pas). Notez aussi le TTL qui est un chiffre rond (et qui ne change pas si on rejoue la commande).
Passons maintenant à un concept relativement peu connu, celui de zones, et le vocabulaire associé (section 7) :
gouv.fr
n'est pas une zone séparée, il
est dans la même zone que fr
(cela se teste
facilement : gouv.fr
n'a pas
d'enregistrement NS ou de SOA).wikipedia.org
est org
, le parent de
.org
est la racine.ns1.mazone.example
, le résolveur doit
passer par les serveurs de mazone.example
,
qui est déléguée à ns1.mazone.example
et
ainsi de suite... On rompt ce cercle vicieux en ajoutant, dans
la zone parente, des données qui ne font pas autorité sur les
adresses de ces serveurs (RFC 1034,
section 4.2.1). Il faut donc bien veiller à les garder
synchrones avec la zone fille. (Tanguy Ortolo me suggère
d'utiliser « enregistrement de raccord » plutôt que
« colle ». Cela décrit bien leur rôle, en effet.)ip6.arpa
ou sous les
domaines très longs de certains CDN. Cela se trouve aussi avec les
enregistrements de service : dans
_sip._tcp.example.com
,
_tcp.example.com
est probablement un
ENT. La réponse correcte à une requête DNS pour un ENT est
NODATA (code de réponse NOERROR, liste des répoonses vide) mais
certains serveurs bogués, par exemple, à une époque, ceux
d'Akamai, répondaient NXDOMAIN.
Voyons ici la colle retournée par un serveur faisant autorité (en
l'occurrence un serveur de .net
) :
% dig @a.gtld-servers.net AAAA labs.ripe.net ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18272 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 8, ADDITIONAL: 9 ... ;; AUTHORITY SECTION: ripe.net. 172800 IN NS ns3.nic.fr. ripe.net. 172800 IN NS sec1.apnic.net. ripe.net. 172800 IN NS sec3.apnic.net. ripe.net. 172800 IN NS tinnie.arin.net. ripe.net. 172800 IN NS sns-pb.isc.org. ripe.net. 172800 IN NS pri.authdns.ripe.net. ... ;; ADDITIONAL SECTION: sec1.apnic.net. 172800 IN AAAA 2001:dc0:2001:a:4608::59 sec1.apnic.net. 172800 IN A 202.12.29.59 sec3.apnic.net. 172800 IN AAAA 2001:dc0:1:0:4777::140 sec3.apnic.net. 172800 IN A 202.12.28.140 tinnie.arin.net. 172800 IN A 199.212.0.53 tinnie.arin.net. 172800 IN AAAA 2001:500:13::c7d4:35 pri.authdns.ripe.net. 172800 IN A 193.0.9.5 pri.authdns.ripe.net. 172800 IN AAAA 2001:67c:e0::5
On notera :
pri.authdns.ripe.net
: ce serveur étant
dans la zone qu'il sert, sans son adresse IP, on ne pourrait
jamais le joindre.sec1.apnic.net
. Ce n'est pas
strictement indispensable (on pourrait l'obtenir par une
nouvelle requête), juste une optimisation.ns3.nic.fr
et
sns-pb.isc.org
ne sont pas renvoyées. Le
serveur ne les connait probablement pas et, de toute façon,
elles seraient hors-bailliage, donc ignorées par un résolveur prudent.Voyons maintenant, un ENT,
gouv.fr
(notez que, depuis, ce domaine n'est
plus un ENT) :
% dig @d.nic.fr ANY gouv.fr ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42219 ;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 1
Le serveur fait bien autorité pour ce domaine (flag AA dans la réponse), le domaine existe (autrement, le status aurait été NXDOMAIN, pas NOERROR) mais il n'y a aucun enregistrement (ANSWER: 0).
Et, ici, une délégation boiteuse, pour
.ni
:
% check-soa ni dns.nic.cr. 2001:13c7:7004:1::d100: ERROR: SERVFAIL 200.107.82.100: ERROR: SERVFAIL ns.ideay.net.ni. 186.1.31.8: OK: 2013093010 ns.ni. 165.98.1.2: ERROR: read udp 10.10.86.133:59569->165.98.1.2:53: i/o timeout ns.uu.net. 137.39.1.3: OK: 2013093010 ns2.ni. 200.9.187.2: ERROR: read udp 10.10.86.133:52393->200.9.187.2:53: i/o timeout
Le serveur dns.nic.cr
est déclaré comme
faisant autorité pour .ni
mais il ne le sait
pas, et répond donc SERVFAIL.
Les jokers ont une section à eux, la section 8 du RFC. S'appuyant sur le RFC 4592, elle définit, entre autres :
*
dans
une zone déclenche la synthèse automatique de réponses pour les
noms qui n'existent pas dans la zone. Si la zone
foo.example
contient
bar.foo.example
et
*.foo.example
, une requête pour
thing.foo.example
renverra le contenu de
l'enregistrement avec le joker, une requête pour
bar.foo.example
donnera les données de
bar.foo.example
. Attention,
l'astérisque n'a son rôle particulier
que s'il est le composant le plus spécifique (le premier). Dans
foo.*.bar.example
, il n'y a pas de
joker.foo.bar.baz.example
n'existe pas, que
bar.baz.example
n'existe pas non plus,
mais que baz.example
existe, alors
baz.example
est l'ancêtre le plus proche
de foo.bar.baz.example
. Ce concept est
nécessaire pour le RFC 5155.Allez courage, ne faiblissons pas, il reste encore la question de l'enregistrement des noms de domaine (section 9) :
bortzmeyer.org
😁.
C'est le registre qui décide de la politique d'enregistrement,
qui peut être très variable selon les zones (sauf dans celles
contrôlées par l'ICANN, où une certaine
uniformité est imposée). Mes lecteurs français noteront que,
comme le terme « registre » est court et largement utilisé, le
gouvernement a inventé
un nouveau mot, plus long et jamais vu auparavant,
« office d'enregistrement »..aquarelle
se trouve dans la
Public Suffix List alors qu'il
s'agit d'un « .marque
», un de ces TLD où
une seule entreprise peut enregistrer des noms. Le terme est ancien mais est apparu
pour la première fois dans un RFC avec le RFC 6265, section 5.3. com
, co.uk
et
eu.org
sont des suffixes publics. Rien dans la syntaxe du nom n'indique
qu'un nom de domaine est un suffixe public, puisque ce statut ne
dépend que d'une politique d'enregistrement (qui peut
changer). Il est parfaitement possible qu'un domaine, et un de
ses enfants, soient tous les deux un suffixe public (c'est le
cas de .org
et
eu.org
).
Prenons par exemple le domaine eff.org
. Au
moment de la publication du RFC :
Enfin, pour terminer, les sections 10 et 11 de notre RFC couvrent DNSSEC. Pas grand'chose de nouveau ici, DNSSEC étant plus récent et donc mieux défini.
L'annexe A de notre RFC indique quelles définitions existaient dans de précédents RFC mais ont été mises à jour par le nôtre. (C'est rare, puisque le but de ce RFC de terminologie est de rassembler les définitions, pas de les changer.) Par exemple, la définition de QNAME du RFC 2308 est corrigée ici.
L'annexe B liste les termes dont la première définition formelle se trouve dans ce RFC (ou dans un de ses prédécesseurs, les RFC 7719 et "RFC 8499). Cette liste est bien plus longue que celle de l'annexe A, vu le nombre de termes courants qui n'avaient jamais eu l'honneur d'une définition stricte.
Notre RFC ne contient pas une liste exhaustive des changements depuis son prédécesseur, le RFC 8499, mais ils sont peu importants. Parmi les changements sérieux :
Date de publication du RFC : Janvier 2024
Auteur(s) du RFC : M. Knodel, W. Hardaker, T. Pauly
Pour information
Première rédaction de cet article le 9 mai 2024
Aujourd'hui, l'essentiel du trafic sur l'Internet est chiffré, et pour d'excellentes raisons. Pas question de revenir là-dessus, mais, ceci dit, il va falloir adapter certaines pratiques de gestion des réseaux. L'IAB a organisé en 2022 un atelier sur ce sujet, dont ce RFC est le compte-rendu.
Comme les autres ateliers de l'IAB, il s'agit de faire s'exprimer diverses personnes, pas forcément d'arriver à une position commune (surtout sur un sujet… sensible). Tenu entièrement en ligne, cet atelier commençait par la soumission d'articles, qui devaient être lus d'abord, et discutés lors de l'atelier. La liste des articles figure dans l'annexe A du RFC, et les articles sont disponibles en ligne.
D'abord, notre RFC réaffirme que la cryptographie est absolument nécessaire à la protection de la vie privée. Les difficultés réelles qu'elle peut poser ne doivent jamais être un prétexte pour réduire ou affaiblir le chiffrement. Qu'il y ait une tension entre l'impératif du chiffrement et certaines méthodes de gestion du réseau, OK, mais la priorité reste la lutte contre la surveillance (RFC 7258).
Ensuite (section 1), le RFC pose le paysage : le trafic étant largement chiffré, des méthodes traditionnelles de gestion du réseau ne fonctionnent plus. tcpdump ne montre plus les données, on ne peut donc pas distinguer différentes méthodes HTTP, par exemple. Avec QUIC (RFC 9000), c'est même une partie de la couche transport qui est chiffrée donc un observateur extérieur ne peut plus, par exemple, évaluer le RTT d'une session qu'il observe (ce qui était possible avec TCP). Il va donc falloir s'adapter notamment en coopérant davantage avec les applications qui, elles, ont accès à tout. Pour l'instant, on a vu surtout passer des récriminations d'acteurs habitués à surveiller le trafic et qui se plaignent que le chiffrement les en empêchent (le RFC 8404 est un bon exemple de ces récriminations). Au contraire, il faut chercher à résoudre une contradiction : permettre aux réseaux d'appliquer leur politique sans céder d'un millimètre sur le principe du chiffrement.
Outre le cas des opérateurs réseau qui veulent examiner le trafic, à des fins louables (améliorer le réseau) ou critiquables (défavoriser certains usages), on peut aussi citer, parmi les intermédiaires qui voudraient observer et/ou interférer avec le trafic réseau, les réseaux d'entreprise qui veulent, par exemple, empêcher les employés d'accéder à certains services, ou bien les mécanismes de contrôle des enfants (appelés à tort « contrôle parental », alors que leur but est justement de remplacer les parents).
Trois thèmes importants étaient discutés à l'atelier : la situation actuelle, les futures techniques et la façon dont on pourrait arriver à déployer ces futures techniques.
Pour la situation actuelle, on sait que, depuis longtemps, les administrateurices réseau comptent sur de l'observation passive, par exemple pour classer le trafic (tant de HTTP, tant de SSH, etc). C'est à la base de techniques comme IPFIX (RFC 7011), qui permet de produire de beaux camemberts. Outre la production de rapports, cette observation passive peut (mais n'est pas forcément) être utilisée pour prévoir les évolutions futures, prioriser certains types de trafic, détecter des comportements malveillants, etc. Les protocoles Internet étaient en effet traditionnellement trop bavards, faisant fuiter des informations vers des observateurs passifs (cf. le concept de « vue depuis le réseau », RFC 8546).
La lutte
de l'épée et de la cuirasse étant éternelle, il y a évidemment eu
des travaux sur des mécanismes pour empêcher ce genre d'analyses
comme l'article soumis pour l'atelier « WAN
Traffic Obfuscation at Line Rate ». (Au passage,
sur cette idée d'obfuscation, je recommande le
livre de Brunton et Nissenbaum.)
Le RFC mentionne aussi une discussion sur l'idée d'exiger une
permission explicite des utilisateurs pour les analyses du trafic
(je ne vous dis pas les difficultés pratiques…). Voir
l'Internet-Draft
draft-irtf-pearg-safe-internet-measurement
.
Dans un monde idéal, le réseau et les applications coopéreraient (et, dans un monde idéal, licornes et bisounours s'embrasseraient tous les jours) mais on voit plutôt une lutte, le réseau essayant d'en savoir plus, les applications de préserver leur intimité. Pourtant, le RFC note qu'une coopération pourrait être dans leur intérêt réciproque. Bien sûr, des applications comme Tor refuseraient certainement toute coopération puisque leur but est de laisser fuiter le moins d'information possible, mais d'autres applications pourraient être tentées, par exemple en échange d'informations du réseau sur la capacité disponible. Après tout, certaines techniques sont justement conçues pour cela, comme ECN (RFC 3168).
OK, maintenant, quelles sont les techniques et méthodes pour résoudre le problème de la gestion des réseaux chiffrés (section 2.2) ? Le RFC exprime bien le fait que le but du chiffrement est d'empêcher tout tiers d'accéder aux informations. Donc, sauf faille de sécurité, par exemple suite à une faiblesse du chiffrement, tout accès à l'information impose la coopération d'une des deux parties qui communiquent. Cette question du consentement est cruciale. Si on n'a pas le consentement d'une des deux extrémités de la communication, on n'a pas le droit d'accéder aux données, point. Cf. la contribution « What’s In It For Me? Revisiting the reasons people collaborate ». Comme le répète souvent Eric Rescorla « Sur l'Internet, tout ce qui n'est pas une des extrémités est un ennemi. » (Cela inclut donc tous les équipements réseaux et les organisations qui les contrôlent.) Bref, les utilisateurs doivent pouvoir donner un consentement éclairé, et juger si les bénéfices de la visibilité l'emportent sur les risques. Évidemment, ce principe correct va poser de sérieuses difficultés d'application, car il n'est pas évident d'analyser proprement les bénéfices, et les risques (songez aux bandeaux cookies ou aux permissions des application sur votre ordiphone). Développer des nouveaux mécanismes de communication avec l'utilisateurice sont nécessaires, sinon, comme le note le RFC, « les ingénieurs devront choisir pour les utilisateurs ». Le RFC estime que les utilisateurs donneront toujours la priorité à leur vie privée (notamment parce qu'ils ne comprennent pas l'administration de réseaux et ses exigences), mais cela ne me semble pas si évident que cela. Personnellement, il me semble que le choix par défaut est crucial, car peu d'utilisateurices le modifieront.
Une des techniques qui permettent d'avoir accès à de l'information sans avoir toute l'information est celle des relais (cf. « Relying on Relays: The future of secure communication ». Le principe est de séparer les fonctions : l'utilisateur parle à un relais qui parle au serveur final. Le relais ne connait pas le contenu de la demande, le serveur ne connait pas le demandeur. C'est utilisé dans des techniques comme Oblivious HTTP (RFC 9458) ou Oblivious DNS (RFC 9230). La préservation de la vie privée est donc bien meilleure qu'avec, par exemple, un VPN, où l'opérateur du VPN voit tout, le demandeur et la demande (contrairement à ce que prétendent les publicités pour NordVPN que de nombreux youtubeurs transmettent avec leurs vidéos). Le relais permet donc un accès à une information limitée, ce qui permet d'assurer certaines fonctions (comme le filtrage de contenu malveillant) sans avoir un accès complet.
Un exemple souvent cité par les opérateurs de l'intérêt d'accéder à la communication et de la modifier est celui de l'optimisation des flux TCP. Des PEP (Performance Enhancing Proxies) violant le modèle en couches (car, normalement, TCP est de bout en bout) sont souvent déployés, notamment sur les liaisons satellites, dont les performances sont plus mauvaises, et les caractéristiques techniques très spéciales. (Cf. l'exposé de Nicolas Kuhn, « QUIC for satellite communications » à la Journée du Conseil Scientifique de l'Afnic en 2021.) Le PEP va modifier les en-têtes TCP, voire parfois boucler la connexion TCP et en faire une autre lui-même. Cette technique ne marche évidemment plus avec QUIC, qui chiffre même la couche transport. Et elle mène à l'ossification de l'Internet puisqu'elle rend plus difficile de faire évoluer TCP, car toute évolution risque de perturber les PEP. QUIC avait en partie été explicitement développé pour empêcher ces intermédiaires de bricoler la session. Une autre approche est celle proposée dans « The Sidecar: "Opting in" to PEP Functions ».
Bon, maintenant, comment arriver à déployer de meilleures méthodes ? Dans un environnement fermé ou semi-fermé, cela doit être possible de faire en sorte que, par exemple, toutes les machines mettent en œuvre telle fonction (cf. RFC 8520 pour un exemple). Mais cela ne marche clairement pas pour l'Internet.
Parmi les moyens proposés de résolution de la contradiction entre visibilité par le réseau et protection de la vie privée, le RFC mentionne les Zero-Knowledge Middleboxes. Décrites dans l'article du même nom, cette méthode utilise les preuves à divulgation nulle de connaissance pour prouver à l'intermédiaire qui fait le filtrage qu'on a bien respecté sa politique de filtrage, sans lui dire ce qu'on a fait. L'article détaille par exemple comment cela peut s'appliquer au DNS, qui est le principal outil de censure de l'Internet dans l'Union européenne. Ces preuves à divulgation nulle de connaissance ayant toujours été mystérieuses pour moi, je ne vous expliquerai pas comment elles marchent, mais notez que les auteurs ont fait un article pédagogique.
Enfin, le RFC mentionne la proposition Red Rover, qui propose encore un arrangement bisounours où les utilisateurs et les opérateurs collaboreraient pour un filtrage géré en commun. Les auteurs disent que ça marcherait car les utilisateurs « ne veulent probablement pas violer les CGU » (ben si : ils veulent utiliser Sci-Hub même si c'est censuré).
En conclusion, le RFC note en effet qu'on peut être sceptique quant aux chances d'une solution négociée. Mais il met aussi en avant le fait qu'il va être très difficile de trouver une solution qui marche pour toute la variété des réseaux. Et que l'expérience prouve largement que toute nouvelle technique va avoir des effets inattendus et que, par exemple, une solution qui visait à donner aux opérateurs des informations utiles pour la gestion de réseaux, va parfois être détournée pour d'autres buts.
Sinon, sur la question du déboguage des applications dans un monde où tout est chiffré, j'avais fait un exposé à Capitole du Libre.
Date de publication du RFC : Septembre 2023
Auteur(s) du RFC : W. Kumari (Google), P. Hoffman (ICANN)
Chemin des normes
Première rédaction de cet article le 30 septembre 2023
Le TLD
.alt
a été réservé pour les utilisations
non-DNS. Si demain
je crée une chaine de blocs nommée Catena et
que j'enregistre des noms dessus, il est recommandé qu'ils se
terminent par catena.alt
(mais comme toujours
sur l'Internet, il n'y a pas de police mondiale chargée de faire
respecter les RFC).
Ce nouveau RFC
est l'occasion de rappeler que noms
de domaine et DNS, ce
n'est pas pareil. Les noms de domaine existaient avant le DNS
et, même aujourd'hui, peuvent être résolus par d'autres techniques
que le DNS (par exemple votre fichier local
/etc/hosts
ou équivalent).
Mais le DNS est un tel succès que cette « marque » est utilisée
partout. On voit ainsi des systèmes de résolution de noms n'ayant
rien à voir avec le DNS se prétendre « DNS pair-à-pair » ou « DNS
sur la blockchain », ce
qui n'a aucun sens. Ce nouveau RFC, lui, vise clairement uniquement
les systèmes non-DNS. Ainsi, des racines alternatives ou des
domaines privés comme le
.local
du RFC 6762 ne
rentrent pas dans son champ d'application. De plus, il ne s'applique
qu'aux noms de domaine ou en tout cas aux identificateurs qui leur
ressemblent suffisamment. Si vous créez un système complètement
disruptif où les identificateurs ne ressemblent pas à des noms de
domaine, ce RFC ne vous concerne pas non plus. Mais si, pour des
raisons techniques (être compatible avec les applications
existantes) ou marketing (les noms de domaine, c'est bien, tout le
monde les reconnait), vous choisissez des noms de domaine comme
identificateurs, lisez ce RFC.
En effet, il existe plusieurs de ces systèmes. Ils permettent, en
indiquant un nom de domaine (c'est-à-dire une
série de composants séparés par des points comme
truc.machin.chose.example
) d'obtenir des
informations techniques, permettant, par exemple, de trouver un
serveur ou de s'y connecter. Un exemple d'un tel système est le
mécanisme de résolution utilisé par Tor. Les
identificateurs sont construits à partir d'une clé
cryptographique et suffixés d'un
.onion
(réservé par le RFC 7686). Ainsi, ce blog est en
http://sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion/
. N'essayez
pas ce nom dans le DNS : vous ne le trouverez pas, il se résout via
Tor.
Pendant longtemps, cette pratique de prendre, un peu au hasard,
un nom et de l'utiliser comme TLD a été la façon la plus courante de créer des
noms de domaine dans son espace à soi. C'est ainsi que
Namecoin a utilisé le
.bit
, ENS (Ethereum Name Service) le
.eth
et GNUnet le
.gnu
. Chacun prenait son nom comme il voulait,
sans concertation (il n'existe pas de forum ou d'organisation pour
discuter de ces allocations). Cela entraine deux risques, celui de
collision (deux systèmes de nommage utilisent le même TLD, ou bien
un système de nommage utilise un TLD qui est finalement alloué dans
le DNS, ce qui est d'autant plus probable qu'il n'existe pas de
liste de ces TLD non-DNS). Il y a plusieurs solutions à ce problème,
et l'IETF en a longuement discuté (cf. par exemple
l'atelier
EName de 2017, ou bien le très contestable RFC 8244). Ce RFC a donc mis des années à sortir. L'ICANN, par exemple, a essayé de diaboliser ces
noms, attirant surtout l'attention sur leurs dangers. Autre méthode,
il a été suggéré de créer un mécanisme de concertation pour éviter
les collisions, création qui n'a jamais eu lieu, et pas pour des
raisons techniques. Ce RFC 9476 propose simplement de
mettre les noms non-DNS sous un TLD unique, le
.alt
. Ce TLD est réservé dans le registre
des noms spéciaux (créé par le RFC 6761). Ainsi, si le système de résolution de Tor était
créé aujourd'hui, on lui proposerait d'être en
.onion.alt
. Est-ce que les concepteurs de
futurs systèmes de résolution non-DNS se plieront à cette demande ?
Je suis assez pessimiste à ce sujet. Et il serait encore plus
utopique de penser que les TLD non-DNS existants migrent vers
.alt
.
Comme .alt
, par construction, regroupe des
noms qui ne sont pas résolvables dans le DNS, un résolveur purement
DNS ne peut que répondre tout de suite NXDOMAIN (ce nom n'existe
pas) alors qu'un résolveur qui parle plusieurs protocoles peut
utiliser le suffixe du nom comme une indication qu'il faut utiliser
tel ou tel protocole. Si jamais des noms sous
.alt
sont réellement utilisés, ils ne devraient
jamais apparaitre dans le DNS (par exemple dans les requêtes aux
serveurs racine) mais, compte-tenu de
l'expérience, il n'y a aucun doute qu'on les verra fuiter dans le
DNS. Espérons que des techniques comme celles du RFC 8020, du RFC 8198 ou du RFC 9156 réduiront la
charge sur les serveurs de la racine; et préserveront un peu la vie
privée (section 4 du RFC).
Le nom .alt
est évidemment une référence à
alternative mais il rappelera des souvenirs aux utilisateurs
d'Usenet. D'autres noms avaient été
sérieusement discutés comme de préfixer
tous les TLD non-DNS par un tiret
bas. Mon catena.alt
aurait été
_catena
:-) Tout ce qui touche à la
terminologie est évidemment très sensible, et le RFC prend soin de
souligner que le terme de « pseudo-TLD », qu'il utilise pour
désigner les TLD non-DNS n'est pas péjoratif…
On note que le risque de collision existe toujours, mais sous
.alt
. Notre RFC ne prévoit pas de registre des
noms sous .alt
(en partie parce que
l'IETF
ne veut pas s'en mêler, son protocole, c'est le DNS, et en partie
parce que ce milieu des mécanismes de résolution différents est très
individualiste et pas du tout organisé).
Date de publication du RFC : Novembre 2023
Auteur(s) du RFC : B. Schwartz (Meta Platforms), M. Bishop, E. Nygren (Akamai Technologies)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 8 avril 2024
Ces deux nouveaux types d'enregistrement DNS, SVCB et sa variante HTTPS, permettent de donner des informations supplémentaires à un client réseau avant qu'il ne tente de se connecter à un serveur. On peut envoyer ainsi des indications sur les versions des protocoles gérées, des clés cryptographiques ou des noms de serveurs supplémentaires.
Un client d'un service réseau a en effet plein de questions à se poser avant de tenter une connexion. Quelle adresse IP utiliser ? Quel port ? Chiffrement ou pas ? Les anciens mécanismes traitent la question de l'adresse IP (on la trouve par une requête DNS) et celle du port, si on se limite aux ports bien connus (comme 43 pour whois). Mais cela ne dit pas, par exemple, si le serveur HTTP distant accepte ou non HTTP/3 (RFC 9114). Par contre, cet enregistrement HTTPS de Cloudflare va bien nous dire que ce serveur accepte HTTP/2 et 3 :
% dig cloudflare.com HTTPS … ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28399 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 … ;; ANSWER SECTION: cloudflare.com. 300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=104.16.132.229,104.16.133.229 ipv6hint=2606:4700::6810:84e5,2606:4700::6810:85e5 … ;; WHEN: Mon Apr 08 09:27:01 CEST 2024 ;; MSG SIZE rcvd: 226
Bon, en quoi consiste cet enregistrement SVCB ? Il a deux modes de fonctionnement, alias et service. Le premier mode sert à faire d'un nom une version canonique d'un autre, un peu comme le CNAME mais en étant utilisable à l'apex d'une zone. Le second mode sert à indiquer les paramètres techniques de la connexion. Un enregistrement SVCB (ou HTTPS) a trois champs dans ses données :
SvcPriority
: quand il vaut zéro, il
indique le mode alias. Autrement (par exemple dans le cas
ci-dessus), il indique la priorité de ces paramètres.TargetName
: en mode alias, il indique
le nom canonique, ou autrement un nom alternatif (pour un service
accessible via plusieurs noms). Dans l'exemple Cloudflare
ci-desssus, il valait la racine (un point) ce qui indique
l'absence de nom alternatif (section 2.5).SvcParams
: une liste de couples
{clé,valeur} pour les paramètres de connexion (uniquement en mode
service). Dans le cas avec Cloudflare, c'était
alpn="h3,h2" ipv4hint=104.16.132.229,104.16.133.229
ipv6hint=2606:4700::6810:84e5,2606:4700::6810:85e5
. (Si
vous vous intéressez aux débats à l'IETF, la question de la
syntaxe de ces paramètres avait suscité une
longue discussion.)Les enregistrements SVCB ont le type 64 (enregistré à l'IANA) et les HTTPS, qui ont la même syntaxe et le même contenu, mais sont spécifiques à HTTP, ont le 65 (SVCB est générique). Les enregistrements HTTPS (et de futurs enregistrements pour d'autres protocoles) sont dits « compatibles avec SVCB » car ils ont la même syntaxe et la même sémantique.
Notre RFC définit (section 7) une liste de paramètres possibles mais d'autres peuvent être ajoutés dans un registre IANA, via la procédure « Examen par un expert » (RFC 8126). Pour l'instant, il y a, entre autres :
L'enregistrement peut (cela dépend des protocoles qui
l'utilisent, HTTP ne le fait pas) être placé sur un sous-domaine
indiquant le service, par exemple
_8765._baz.api.example.com
(section
10.4.5).
Idéalement, un serveur faisant autorité devrait renvoyer les SVCB et les HTTPS, s'ils sont présents, dans la section additionnelle de la réponse, lorsque le type demandé était une adresse IP. Mais ceux de Cloudflare ne semblent pas le faire actuellement. (PowerDNS le fait.)
Si vous vous intéressez aux questions opérationnelles, et que vous voulez mettre des enregistrements SVCB/HTTPS dans votre zone, la section 10 du RFC est faite pour vous. J'ai des enregistrements HTTPS pour ce blog :
# Un alias à l'apex (la priorité 0 indique le mode alias) % dig +short +nodnssec bortzmeyer.org HTTPS 0 www.bortzmeyer.org. # J'ai HTTP/2 (mais pas encore HTTP/3) % dig +short +nodnssec www.bortzmeyer.org HTTPS 1 . alpn="h2"
Pour cela, j'ai mis dans le fichier de zone :
; Enregistrements SVCB (HTTPS). ; HTTP/2 (mais pas encore - au 2024-04-08 - de HTTP/3) www IN HTTPS 1 . alpn="h2" ; alias @ IN HTTPS 0 www.bortzmeyer.org.
Les clients HTTP récents, qui gèrent SVCB/HTTPS vont alors se
connecter directement en HTTP/2 à
https://www.bortzmeyer.org/
même si
l'utilisateur demandait originellement
http://bortzmeyer.org/
(le type
d'enregistrement HTTPS, comme son nom l'indique, sert aussi à
annoncer qu'on accepte HTTPS, ce qui permettra d'abandonner HSTS). Les clients
HTTP plus anciens, évidemment, ne connaissent pas le système
SVCB/HTTPS et il faut donc garder une configuration pour eux (par
exemple des adresses IP à l'apex). Il y a aussi les autres méthodes,
comme le Alt-Svc:
du RFC 7838. La section 9.3 du RFC décrit le comportement attendu
lorsque les différentes méthodes coexistent.
Faites attention toutefois, lorsque vous mettez ce type d'enregistrements dans votre zone, je ne connais pas encore d'outils de test permettant de vérifier la syntaxe des enregistrements, encore moins leur correspondance avec la réalité (par exemple, SSLLabs ne semble pas le faire). C'est un problème général de la signalisation sur l'Internet, quand on signale (notamment via le DNS) les capacités d'un serveur : le logiciel client doit de toute façon être prêt à tout, car il ne peut jamais être sûr que le signal est conforme aux faits.
En parlant d'anciens logiciels (clients et serveurs), vous pouvez trouver une liste de mises en œuvre de SVCB/HTTPS. Attention, elle est incomplète et pas à jour. Notez qu'il y a parfois des contraintes particulières, ainsi, il semble que Firefox ne demande des enregistrements HTTPS que s'il utilise DoH. iOS envoie des requêtes HTTPS depuis iOS 14, publié en septembre 2020, ce qui avait étonné, à l'époque.
En parlant de Firefox, s'il est assez
récent, et s'il est configuré
pour faire du DoH, vous pouvez tester le SVCB/HTTPS en allant dans
about:networking#dnslookuptool
. En entrant un
nom de domaine, le champ « RR HTTP » doit renvoyer l'enregistrement
HTTPS.
Avec un tcpdump récent, voici le trafic
DNS utilisant le nouvel enregistrement DNS, qu'on peut observer sur
un serveur faisant autorité pour
bortzmeyer.org
:
09:49:23.354974 IP6 2a04….31362 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 13024% [1au] HTTPS? www.bortzmeyer.org. (47) 09:52:06.094314 IP6 2a00….56551 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 40948% [1au] HTTPS? wWw.bOrTZmEyER.ORg. (62) 10:06:21.501437 IP6 2400….11624 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 59956 [1au] HTTPS? doh.bortzmeyer.fr. (46) 10:06:21.999608 IP6 2400….36887 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 17231 [1au] HTTPS? radia.bortzmeyer.org. (49) 10:25:53.947096 IP6 2001….54476 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 26123% [1au] HTTPS? www.bortzmeyer.org. (47)
Si votre tcpdump est plus ancien, vous verrez Type65 au lieu de HTTPS.
Sinon, si vous aimez les bricolages (et celui-ci sera de moins en moins utile avec le temps, au fur et à mesure que les serveurs géreront ce type), pour fabriquer les enregistrements, vous pouvez utiliser cet outil, qui va fabriquer la forme binaire, directement chargeable par les serveurs faisant autorité :
% perl type65_https.pl 'example.net HTTPS 1 . alpn="h3,h2" ipv4hint="192.0.2.42" ipv6hint="2001:db8::42"' example.net. TYPE65 ( \# 41 00010000010006026833026832000400 04c000022a0006001020010db8000000 000000000000000042 )
(Il faut un Net::DNS
récent sinon « unknown type "HTTPS" at /usr/share/perl5/Net/DNS/RR.pm line 671.
in new Net::DNS::RR( www.bortzmeyer.org HTTPS 1 . alpn="h2" )
at type65_https.pl line 30. ».)
Quelques articles pas mal :
Date de publication du RFC : Juillet 2023
Auteur(s) du RFC : P. van Dijk (PowerDNS), L. Peltan
(CZ.NI), O. Sury (Internet Systems
Consortium), W. Toorop (NLnet
Labs), C.R. Monshouwer, P. Thomassen
(deSEC, SSE - Secure Systems
Engineering), A. Sargsyan (Internet Systems
Consortium)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 29 mars 2024
L'idée de base de ces « zones catalogue » est d'automatiser la configuration d'une nouvelle zone DNS sur les serveurs secondaires, en publiant dans le DNS les caractéristiques des zones qu'ils devront servir. Cela concerne donc surtout les gros hébergeurs qui ont beaucoup de zones.
Petit rappel : une zone, dans le DNS, est une partie contigüe de l'arbre des
noms de domaine, gérée
comme un tout (mêmes serveurs faisant
autorité). Ainsi, si vous venez de louer le nom
machin.example
, un hébergeur DNS va configurer
ses serveurs pour faire autorité pour ce nom. Par exemple, avec le
logiciel NSD, sur
le primaire (serveur maitre) :
zone: name: "machin.example" zonefile: "primary/machin.example" # Les serveurs secondaires : notify: 2001:db8:1::53 provide-xfr: 2001:db8:1::53 …
Et, sur un serveur secondaire (serveur esclave), qui transférera la zone depuis le primaire (RFC 5936) :
zone: name: "machin.example" # Le primaire : allow-notify: 2001:db8:cafe::1 NOKEY request-xfr: AXFR 2001:db8:cafe::1 NOKEY
Si on gère beaucoup de zones, avec des ajouts et des retraits tout le temps, l'avitaillement manuel est long et risqué (et si on oublie un serveur ?). Éditer ces fichiers sur tous les serveurs secondaires devient vite pénible. Et si les logiciels sur les secondaires sont différents les uns des autres (ce qui est recommandé, pour la robustesse), il faut se souvenir des différentes syntaxes. Pourquoi faire manuellement ce qu'on peut automatiser ? C'est le principe des zones catalogue.
Le principe est simple : une zone catalogue est une zone comme
une autre, produite par les mêmes mécanismes (par exemple
emacs) et qui sera servie par un serveur
primaire à tous les secondaires, qui changeront alors
automatiquement leur configuration en fonction du contenu de la zone
catalogue. Chaque zone à configurer est un enregistrement de type
PTR, dont la partie gauche est une étiquette interne et la partie
droite indique le nom de la zone. Ici, on configure la zone
rutaba.ga
, l'étiquette (qui doit être unique)
label1
est à usage interne (section 4.1 du RFC) :
label1.zones.catalog.example. IN PTR rutaba.ga.
Le reste est listé sous forme de propriétés (section 4.2). Une
propriété évidente est l'adresse IP du primaire. Pour l'instant,
elle doit être indiquée via le composant ext
qui désigne les propriétés pas encore normalisées :
primaries.ext.catalog.example. IN AAAA 2001:db8:bad:dcaf::42
La liste des propriétés figure dans un registre IANA.
À l'heure actuelle, de nombreux logiciels gèrent ces zones catalogues. Le site Web du projet (pas mis à jour depuis très longtemps) en liste plusieurs.
Voici un exemple complet de zone catalogue :
; -*- zone -*- catalog.example. IN SOA ns4.bortzmeyer.org. stephane.bortzmeyer.org. 2023120902 900 600 86400 1 catalog.example. IN NS invalid. ; Le NS est inutile mais ; obligatoire et "invalid" est la valeur ; recommandée (section 4). version.catalog.example. IN TXT "2" ; Obligatoire, section 4.2.1 label1.zones.catalog.example. IN PTR rutaba.ga. primaries.ext.catalog.example. IN AAAA 2001:db8:bad:dcaf::42
La configuration de BIND pour l'utiliser :
# La zone catalogue se charge comme n'importe quelle zone. Ceci dit, # vu le caractère critique de la zone catalogue, la section 7 du RFC # insiste sur l'importance de sécuriser ce transfert, par exemple avec # TSIG (RFC 8945) : zone "catalog.example" { type slave; file "catalog.example"; masters { 2001:db8:666::; }; }; # Et, ici, on la désigne comme spéciale : options { ... catalog-zones { zone "catalog.example" in-memory no; }; };
Naturellement, comme toujours lorsque on automatise, on risque le syndrome de l'apprenti sorcier. Attention donc en générant la zone catalogue. Comme le note le RFC (section 6) : « Great power comes with great responsibility. Catalog zones simplify zone provisioning by orchestrating zones on secondary name servers from a single data source: the catalog. Hence, the catalog producer has great power and changes must be treated carefully. For example, if the catalog is generated by some script and this script generates an empty catalog, millions of member zones may get deleted from their secondaries within seconds, and all the affected domains may be offline in a blink of an eye. »
Date de publication du RFC : Février 2024
Auteur(s) du RFC : A. Backman (Amazon), J. Richer (Bespoke Engineering), M. Sporny (Digital Bazaar)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 17 janvier 2025
Ce RFC spécifie comment on peut signer cryptographiquement et vérifier des messages HTTP, afin de s'assurer de leur authenticité. Des signatures HTTP conceptuellement proches, mais syntaxiquement différentes sont notamment utilisées par le fédivers.
Alors, attention, vous allez me dire « mais tout le monde utilise HTTPS, de toute façon » mais ce n'est pas tout à fait la même chose. Comme l'explique la section 1 de notre RFC, TLS protège une connexion mais ne fonctionne pas de bout en bout, s'il y a sur le trajet des relais qui terminent une session TLS et ouvrent une autre connexion (la section 7.1.2 détaille les limites de TLS pour les scénarios envisagés). Et TLS ne permet pas d'authentifier le client, sauf à utiliser des certificats client, que beaucoup trouvent peu pratique, et qui dépendent d'un système centralisé, les AC. En prime, dans un contexte d'utilisation serveur-serveur, comme dans le fédivers, on n'a pas envie de confier sa clé privée à son serveur. Donc, il y a un besoin pour une signature des messages (TLS n'est pas inutile pour autant, il fournit notamment la confidentialité, cf. section 8.2 de notre RFC). Voilà pourquoi Mastodon, par exemple, a commencé à utiliser des signatures HTTP pour la sécurité. Le format ActivityPub ne prévoyait en effet aucune sécurité, d'où la nécessité de développer des techniques ad hoc. Mais notez que la technique de Mastodon, non normalisée et même pas spécifiée quelque part, sauf dans le code source, n'est pas compatible avec celle du RFC. Un travail est en cours pour documenter cela.
Notons qu'il existe aussi des solutions pour signer le corps d'un message HTTP, comme JWS (RFC 7515) si le corps est en JSON (RFC 8259). Mais les signatures HTTP de notre RFC 9421 protègent non seulement le corps (quel que soit son format) mais aussi une partie des champs de l'en-tête. Il n'y a pas de solution générale de protection des contenus en HTTP, juste une protection du canal de communication, avec HTTPS.
Donc, le principe est simple : le signeur canonicalise certains champs de l'en-tête (et, indirectement, le corps, via la condensation du RFC 9530), les signe et met la signature dans l'en-tête. Par exemple, avec ce message HTTP (une simple demande de lecture, qui n'a donc probablement pas besoin d'être authentifiée mais c'est juste un exemple) :
GET /9421.html HTTP/1.1 Host: www.bortzmeyer.org Date: Fri, 17 Jan 2025 10:53:17 +0100 Content-Type: text/plain Content-Digest: sha-256=:591b6607e9e257e26808e2ccf3984c23a5742b78defad9ec7b2966ddcef29909=: Hello, HTTP
La signature ajoutera ces deux champs :
Signature-Input: sig=("date" "host";sf "content-type";sf "content-digest" "@method" "@target-uri" "@authority" "@scheme" "@request-target" "@path" "@query");alg="ecdsa-p256-sha256" Signature: sig=:0dnCVeCDOJ9fQiLqUXLE8hwBDNpAVlO3TJZgqm/FBJ1x2h5/g5qU20UM87BkJt0iK9vpRRgPF9fmWobf6Y5iYQ==:
(« sig » est un identificateur arbitraire, qui sert à faire la correspondance entre une signature et ses paramètres, dans le cas où il y a plusieurs signatures.) Le vérificateur devra récupérer la clé publique (notez que le RFC ne précise pas comment : chaque application utilisant les signatures HTTP devra décrire sa méthode de récupération des clés), canonicaliser et refaire les calculs cryptographiques, validant (ou non) la signature.
Voilà, vous connaissez l'essentiel du RFC, maintenant, place aux
détails. D'abord, un peu de terminologie : en dépit de leur nom, les
signatures HTTP peuvent être aussi bien de « vraies » signatures, faites avec de la
cryptographie asymétrique que des
MAC incluant une clé secrète (avec de la
cryptographie symétrique, donc). Ensuite,
relisez bien le RFC 9651 : les champs
structurés dans l'en-tête sont un cas particulier, en raison de la
canonicalisation qu'ils vont subir. (Vous
avez remarqué le sf
dans le champ
Signature-Input:
plus haut ? Il veut dire
Structured Field et indique un traitement
particulier.)
Ensuite, la question évidemment compliquée de la
canonicalisation. HTTP permet et même parfois impose des
transformations du message faites par des intermédiaires (la section
1.3 donne une liste partielle des transformations possibles). Il est
très difficile, voire impossible, de définir quels champs de
l'en-tête vont être respectés et lesquels vont être modifiés. Il n'y
a donc pas de liste officielle des champs à signer, chaque
application va indiquer les siens. Dans l'exemple ci-dessus, on
signe les quatre champs présents, et on l'indique dans le
Signature-Input:
. Si un intermédiaire ajoute un
champ quelconque (par exemple le Forwarded:
du
RFC 7239), la signature restera valide. Le
récepteur ne devra donc pas considérer ce champ comme
authentifié. Même chose pour le corps si on ne signe pas son
condensat (RFC 9530). La section 7.2.8 du RFC
rappelle l'importance de signer le corps, pour la plupart des
cas. Notez également que le Signature-Input:
plus haut incluait aussi des composants qui ne sont pas des champs
de l'en-tête mais qu'on veut signer, comme la méthode (GET
) ou
les composants de l'URL. Leur nom est précédé d'un arobase.
Autre problème du monde réel : les programmes n'ont pas un contrôle complet du message HTTP produit. Par exemple, le programme va utiliser une bibliothèque qui va formater les champs à sa manière, en ajouter, etc. C'est pour cela qu'il est important de laisser ouverte la question de la liste des champs à signer. Chaque application choisira.
Les signatures HTTP ne sont pas une solution complète prête à l'emploi : bien des points sont délibérément laissés vides dans le RFC. Une solution complète de sécurité doit donc spécifier :
Date:
soit présent et signé, pour détecter
les messages trop anciens que quelqu'un essaierait de rejouer,
cf. la section 7.2.2),Les noms des composants signés (champs de l'en-tête et autres)
sont décrits dans la section 2 de notre RFC. Pour un champ, c'est
simplement son nom en minuscules (comme "date"
dans le Signature-Input:
plus haut). Autrement,
les noms commencent par un arobase par exemple
@method
indique la méthode HTTP
utilisée. Les méthodes de canonicalisation (complexes, surtout pour
les champs structurés !) sont dans la même section.
Les différents paramètres de la signature, comme un
identificateur pour la clé utilisée, ou comme la date de signature,
peuvent également être inclus dans le
Signature-Input:
.
Tous ces paramètres forment ce qu'on appelle la base (section 2.5). Celle du message donné comme exemple plus haut est :
"date": Fri, 17 Jan 2025 10:53:17 +0100 "host";sf: www.bortzmeyer.org "content-type";sf: text/plain "content-digest": sha-256=:591b6607e9e257e26808e2ccf3984c23a5742b78defad9ec7b2966ddcef29909=: "@method": GET "@target-uri": https://www.bortzmeyer.org/9421.html "@authority": www.bortzmeyer.org "@scheme": https "@request-target": /9421.html "@path": /9421.html "@query": ? "@signature-params": ("date" "host";sf "content-type";sf "content-digest" "@method" "@target-uri" "@authority" "@scheme" "@request-target" "@path" "@query");alg="ecdsa-p256-sha256"
Ici, elle est relativement courte, la plupart des métadonnées n'étant pas mentionnée.
Une fois la signature générée, le signeur ajoute deux champs à l'en-tête (section 4 du RFC) :
Signature-Input:
qui indique les
paramètres de la signature (liste des champs signés, et
optionnellement métadonnées sur la signature),Signature:
, qui contient la
signature elle-même.
Les deux champs sont structurés selon la syntaxe du RFC 9651. (Notez que ces deux champs sont une des différences
avec l'ancienne version des signatures HTTP, utilisée dans le
fédivers. Cette ancienne version n'avait qu'un champ,
Signature:
. Un vérificateur qui veut gérer les
deux versions peut donc utiliser la présence du champ
Signature-Input:
comme indication que la
version utilisée est celle du RFC. L'annexe A expose cette
heuristique, qui figure également dans le
projet d'intégration avec ActivityPub.)
Voici un exemple de signature avec des métadonnées (date de signature et identifiant de la clé :
Signature-Input: reqres=("@status" "content-digest" "content-type" \ "@authority";req "@method";req "@path";req "content-digest";req)\ ;created=1618884479;keyid="test-key-ecc-p256" Signature: reqres=:dMT/A/76ehrdBTD/2Xx8QuKV6FoyzEP/I9hdzKN8LQJLNgzU\ 4W767HK05rx1i8meNQQgQPgQp8wq2ive3tV5Ag==:
Notez que notre RFC décrit aussi une méthode pour demander qu'un
correspondant signe ses messages : inclure un champ
Accept-Signature:
(section 5 du RFC).
Les signatures HTTP nécessitent la modification ou la création de plusieurs registres IANA :
Accept-Signature:
,
Signature:
et Signature-Input:
ont été ajoutés au registre des champs
d'en-tête HTTP.ecdsa-p256-sha256
, que j'ai
utilisé dans mon exemple (algorithme
ECDSA). On peut y ajouter des algorithmes
via la politique « Spécification nécessaire » du RFC 8126. created
(la date de signature) ou
keyid
(l'identificateur de la clé, selon un
schéma de nommage spécifique à l'application). On peut y ajouter des valeurs
via la politique « Examen par un expert » du RFC 8126.@method
.
On peut y ajouter des noms
via la politique « Examen par un expert » du RFC 8126.sf
, par exemple, qui
indique qu'un champ de l'en-tête est un champ structuré et doit
donc être traité de manière spéciale.
On peut y ajouter des noms
via la politique « Examen par un expert » du RFC 8126.Si vous voulez un article d'introduction :
Questions mises en œuvre, on dispose désormais de bibliothèques pour de nombreux langages de programmation (je ne les ai pas testées) :
https://github.com/superseriousbusiness/httpsig
,https://imrannazar.com/articles/http-signatures-in-php
,https://docs.rs/crate/http-signatures/latest
,https://pypi.org/project/http-message-signatures/
.
Une discussion est en
cours pour ajouter ces signatures à
curl.
Si vous programmez, pour tester votre code, je recommande fortement le service en ligne
.
Notez qu'il ne vérifie pas
le condensat du corps du message. Pour fabriquer les clés
pour ce service, si
vous voulez faire de l'ECDSA, vous pouvez
utiliser les commandes OpenSSL suivantes
(oui, il doit y avoir une version plus simple) :
https://httpsig.org/
openssl ecparam -out server.pem -name prime256v1 -genkey openssl req -new -key server.pem -nodes -days 1000 -out server.csr openssl x509 -in server.csr -out server.crt -req -signkey server.pem -days 2001
La clé privée sera dans server.pem
et la
publique dans server.crt
.
Concernant l'ancienne version des signatures HTTP, vous pouvez consulter :
https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/
et https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/
.Mobilizon.Federation.HTTPSignatures.Signature
et il vient à l'origine de Pleroma.Cette technique des signatures HTTP a eu une histoire longue et
compliquée. Née chez Amazon, elle avait d'abord été
décrite dans un Internet draft, draft-cavage-http-signatures
en 2013, et déployée, notamment dans le secteur financier. En 2017,
Mastodon avait utilisé la technique telle que décrite dans ce
document (forçant le reste du fédivers à s'aligner sur « ce que fait
Mastodon »). C'est après l'adoption par le groupe de travail
httpbis que le projet avait pris sa forme finale. (Regardez
les
supports de présentation à l'IETF en 2019 et la vidéo de la
réunion.) L'auteur du projet initial avait écrit un
bon résumé en 2020, décrivant de l'intérieur comment se passe
la normalisation dans l'Internet. Les différences principales avec ce que fait le fédivers :
Signature-Input:
et
Signature:
,Date de publication du RFC : Mars 2023
Auteur(s) du RFC : W. Kozlowski, S. Wehner
(QuTech), R. Van Meter (Keio
University), B. Rijsman, A. S. Cacciapuoti, M. Caleffi
(University of Naples Federico II), S. Nagayama
(Mercari)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF qirg
Première rédaction de cet article le 3 avril 2024
Voici un RFC assez futuriste qui explore à quoi pourrait ressembler un futur « Internet » quantique. Je divulgâche tout de suite : ce ne sera pas de si tôt.
Quelques avertissements s'imposent d'abord. Avant tout, rappelez-vous que la quantique produit des résultats qui sont parfaitement cohérents théoriquement et très bien vérifiés expérimentalement mais qui sont hautement non-intuitifs. Avant d'aborder le monde merveilleux de la quantique, n'oubliez pas d'oublier tout ce que vous croyez savoir sur le monde physique. Et ne comptez pas trop sur moi comme guide, on sort nettement ici de mon domaine de compétence. Ensuite, « quantique » est un terme à très forte charge marketing (moins que « IA » mais davantage que « métavers » ou même « blockchain », qui semblent bien passés de mode). Il faut donc être prudent chaque fois qu'un commercial ou un éditorialiste va dire que « c'est quantique » ou que « la quantique va bouleverser tel ou tel domaine ». Enfin, il y a loin de la coupe aux lèvres : de même qu'on n'a pas encore d'ordinateur quantique utile, on n'a pas encore de réseau quantique. Le RFC est à juste titre prudent, pointant les différents obstacles qui restent sur le chemin de l'Internet quantique.
Bon, ces précautions étant posées, qu'est-ce qu'un réseau quantique et pourquoi y consacrer un RFC ? La section 1 du RFC le résume : un réseau quantique est un réseau qui ferait communiquer des dispositifs quantiques pour faire des choses inimaginables avec un réseau classique. Il s'appuierait sur des propriétés spécifiques au monde quantique, notamment l'intrication, propriétés qui n'ont pas d'équivalent dans le monde classique. Attention, et le RFC insiste bien là-dessus, personne n'envisage de remplacer l'Internet classique par un Internet quantique (de la même façon que les futurs ordinateurs quantiques, étant loin d'être généralistes, ne remplaceront pas les ordinateurs classiques). Au contraire, le scénario envisagé est celui d'un réseau hybride, partiellement quantique. (Une lecture recommandée par le RFC est « The quantum internet ».)
Un exemple typique qui ne serait pas possible avec un réseau classique est celui de la distribution quantique de clés (parfois appelée du terme erroné de « cryptographie quantique »), dont l'utilité pratique est douteuse mais qui est assez spectaculaire et, contrairement à d'autres applications, est assez avancée techniquement. D'autres applications sont envisageables à plus long terme. C'est le cas par exemple du blind quantum computation, qui n'a pas encore d'article Wikipédia mais est expliqué dans cet article.
En laboratoire, beaucoup de résultats ont été obtenus. Les chercheurs et chercheuses ont déjà mis au point bien des dispositifs physiques étonnants. Mais à l'échelle du réseau, il n'y a pas encore eu beaucoup de travaux. Le RFC compare cette situation à celle d'un réseau classique où on aurait des fibres optiques et des lasers pour les illuminer mais aucun protocole de transport, aucun mécanisme de routage, encore moins de moyens de gérer le réseau. Développer une application pour un réseau quantique revient à toucher directement au matériel, comme, pour un réseau classique, s'il fallait que chaque application parle aux interfaces physiques, sans avoir d'interface de plus haut niveau comme les prises.
La section 2 du RFC est un rappel sur la quantique. Comme dit plus haut, c'est un domaine riche et complexe, où l'intuition ordinaire ne sert pas à grand'chose. Donc, lire ce rappel est une bonne idée mais n'espérez pas tout comprendre si vous n'êtes pas spécialiste de la question. Cette section est conçue pour des gens qui ne connaissent rien à la physique quantique, elle recommande, pour aller plus loin, le livre de Sutor Dancing with Qubits ou bien celui de Nielsen et Chuang, Quantum Computation and Quantum Information.
Le rappel commence avec la notion d'état quantique. Vous avez sans doute déjà entendu dire qu'un bit classique peut prendre deux valeurs, 0 ou 1, alors que son équivalent quantique, le qubit, a un état qui est une superposition de valeurs possibles, avec des probabilités. Lorsqu'on le mesure, on trouve un 0 ou un 1. (Oui, comme le célèbre chat qui est à la fois vivant et mort.) Attention, ces non-certitudes ne sont pas la conséquence d'un manque d'information mais sont une propriété fondamentale du monde quantique (Alain Aspect a eu un prix Nobel pour avoir prouvé cela). Notez que les versions HTML ou PDF du RFC sont recommandées ici, car il y a quelques équations. Comme un qubit est dans un état qui superpose les deux valeurs possibles, les opérations quantiques agissent sur tout l'état, par exemple l'équivalent quantique d'une porte NOT va inverser les probabilités du 0 et du 1 mais pas transformer un 0 en 1.
Le terme « qubit » (et cette distinction revient souvent dans le RFC) peut désigner aussi bien le concept abstrait que le truc physique qui va le mettre en œuvre (il existe plusieurs techniques pour fabriquer un engin qui gérera des qubits).
On peut ensuite assembler des qubits et, très vite, le nombre de possibilités croît. Mais l'intérêt de mettre des qubits ensemble est qu'on peut les intriquer et ce concept est au cœur de beaucoup de solutions quantiques, notamment du réseau quantique. Une fois intriqués, les deux qubits verront leur sort lié. Une mesure sur l'un affectera l'autre. (Rappel : la quantique n'est pas intuitive et l'intrication n'a pas d'équivalent dans le monde non-quantique, celui sur lequel a été bâtie notre intuition.) La mesure, comme toujours en quantique, est « destructive » au sens où elle ramène à un système classique (le qubit vaut 0 ou 1 quand on le mesure, pas un mélange des deux, et le chat est vivant ou mort quand on ouvre la boite).
Cette intrication est au cœur des réseaux quantiques (section 3 du RFC). Tous les projets de réseaux quantiques utilisent cette propriété (qui, rappelons-le, n'a pas d'équivalent non-quantique). L'intrication permet de corréler deux nœuds du réseau. Par exemple, pour se mettre d'accord sur une valeur, deux machines n'ont pas besoin de faire tourner des algorithmes de consensus, elles peuvent utiliser deux qubits intriqués, chacune en gardant un. Quand une machine lira la valeur de son qubit, elle sera certaine de la valeur lue par l'autre. Et l'intrication ne peut pas être partagée : un tiers ne peut pas s'intriquer avec une intrication existante, ce qui peut avoir des applications en sécurité.
Un réseau quantique est donc défini par notre RFC comme un ensemble de nœuds qui peuvent échanger des qubits intriqués.
Bon, tout ça, c'est très joli, mais comment on le réalise, ce réseau quantique ? La section 4 se penche sur les défis :
Distribuer sur le réseau des qubits quelconques n'est pas forcément facile, donc le RFC suggère de plutôt distribuer des paires de Bell. On peut alors plus facilement (tout est relatif) faire de la téléportation, c'est-à-dire « transporter » un qubit d'un point à un autre. Ce n'est pas une violation du théorème d'impossibilité du clonage puisque le qubit n'est pas copié (il disparait de son point de départ). Notez que le terme de « téléportation » est surtout marketing : vous ne pourrez pas déplacer votre chat ou vous-même de cette façon.
Dernier problème, amplifier le signal (sans le copier !) pour tenir compte de sa dégradation avec la distance. Il existe une astuce, l'échange d'intrication, que je ne vais pas essayer d'expliquer, mais qui permet des réseaux quantiques sur des distances importantes.
Revenons à la correction d'erreurs. Les réseaux quantiques ne sont pas complètement démunis, et ont des solutions possibles, comme les codes quantiques.
OK, on a vu que le monde quantique était très spécial. Donc, le réseau quantique va être bizarre aussi, aux yeux de quelqu'un qui a l'habitude des réseaux classiques (section 5 du RFC). Par exemple, il fera face à ces problèmes :
Répétons-le, chaque nœud du réseau quantique devra également être relié à un réseau classique. Le réseau sera donc complexe et son administration pas évidente.
Une fois qu'on a accepté cela, le réseau classique pourra s'occuper d'opérations comme la construction des tables de routage, pour laquelle les algorithmes et méthodes classiques semblent suffire. On n'aura donc peut-être qu'un seul plan de contrôle (le classique) mais deux plans de données, le classique et le quantique.
Que faut-il construire comme machines pour le plan de données quantique ? D'abord, des répéteurs quantiques qui vont pouvoir créer les intrications, les échanger et contrôler la fidélité. Ensuite :
Facile, me direz-vous ? Non, construire ces machines va nécessiter de s'attaquer à quelques problèmes physiques :
Si vous n'êtes pas découragé·e (mais il ne faut pas l'être : même si les difficultés sont colossales, le chemin est rigolo), il faut maintenant, en supposant qu'on aura les composants de base d'un réseau, les assembler. (À moins que le choix décrit dans le RFC des paires de Bell et de l'échange d'intrication ne soit remis en cause par les futurs progrès…) La section 6 se penche sur la question. Elle démarre par un bel excès d'optimisme, en expliquant que, contrairement à ce qui s'est passé avec l'Internet classique, on a de l'expérience sur la construction de réseau, et qu'on pourra donc ne pas faire d'erreur comme la taille trop réduite des adresses IPv4.
Des services essentiels pour un réseau réel seront difficiles à assurer sur un réseau quantique. Par exemple, l'impossibilité du clonage interdira d'utiliser un logiciel équivalent à tcpdump (remarquez, pour la sécurité, c'est un plus). Le RFC liste les principes de base d'un réseau quantique :
Et le RFC se termine par une exploration d'une architecture de réseau quantique possible, inspirée de MPLS. Dans ce réseau (pour l'instant) imaginaire, tout fonctionne en mode connecté (comme MPLS) : on doit d'abord créer un circuit virtuel (car créer les paires de Bell et les utiliser va nécessiter de la coordination, donc il vaut mieux établir d'abord une connexion). Ce QVC (Quantum Virtual Circuit) a des caractéristiques comme une qualité de service choisie, qui se décline en, par exemple, une capacité mesurée en nombre de paires de Bell par seconde et bien sûr une fidélité (toutes les applications des réseaux quantiques n'ont pas les mêmes exigences en terme de fidélité). La signalisation peut être décentralisée (comme avec RSVP) ou centralisée (comme avec OpenFlow). Comme vous le verrez en lisant cette conclusion du RFC, les détails sont encore approximatifs.
Ce RFC a mis longtemps à être écrit, vous pouvez trouver une description ancienne du projet sur le blog de l'IETF. Notez que l'écriture de ce RFC a été en partie financée par la Quantum Internet Alliance européenne.
N'hésitez pas à vous plonger dans la bibliographie très détaillée de ce RFC, vous y trouverez beaucoup de lectures passionnantes. Il y a même déjà des livres entiers sur les réseaux quantiques comme celui de Van Meter.
Date de publication du RFC : Septembre 2022
Auteur(s) du RFC : C. Eckel (Cisco Systems)
Pour information
Réalisé dans le cadre du groupe de travail IETF shmoo
Première rédaction de cet article le 31 octobre 2024
L'IETF, l'organisation qui normalise les protocoles de l'Internet a toujours prôné le pragmatisme et le réalisme : ce n'est pas tout d'écrire des normes, il faut encore qu'elles fonctionnent en vrai. Un des outils pour cela est le hackathon qui se déroule traditionnellement le week-end avant les réunions physiques de l'IETF. Ce RFC, écrit par le responsable de ces hackathons, décrit l'organisation de ces événements.
La salle (en cours d'installation) du hackathon à
Yokohama en 2023 :
Paradoxalement, alors que cette ambition de réalisme (« we believe in rough consensus and running code ») est ancienne à l'IETF, les hackathons sont relativement récents. Le premier était en 2015 à Dallas. Mais les hackathons font désormais partie de la culture IETF et on n'imagine plus de réunion sans eux. Ils accueillent désormais systématiquement plusieurs centaines de participant·es (cf. section 6 du RFC). (Mon premier était à Chicago.) Au passage, si vous êtes programmeur·se et que vous allez à une réunion IETF, ne ratez pas le hackathon, il en vaut la peine. Le prochain commence ce samedi, le 2 novembre 2024, à Dublin.
Donc, le but du hackathon à l'IETF (section 1 du RFC) est de tester les normes en cours d'élaboration, de voir si la future spécification est claire et réaliste, et de faire en sorte que les gens qui écrivent les normes (qui ne sont pas forcément des développeur·ses) interagissent avec ceux et celles qui mettent en œuvre les normes. (Cela peut paraitre étonnant, mais beaucoup de SDO ne fonctionnent pas comme cela ; la norme est écrite par des gens qui sont très loin du terrain et qui se moquent du caractère réaliste et effectif de leurs textes.) Le code qui tourne est souvent plus significatif qu'une opinion exprimée pendant la réunion. Et le hackathon est aussi l'occasion de travailler en commun (surtout si on est présent·e physiquement), ce qui fait avancer les projets.
Ah, et puisqu'on parle de collaboration, contrairement à certains hackathons, ceux de l'IETF n'ont plus aucune forme de compétition : pas de classement, pas de prix. (Voir la section 7.6 sur le rôle qu'avaient les juges de la compétition et pourquoi cela a été vite supprimé.) En outre, la plupart du code écrit pendant les hackathons est sous une licence libre (la principale exception concerne les gens qui ont modifié un logiciel privateur, par exemple le code de certains routeurs). Du point de vue « juridique », notez que le hackathon est un événement de l'IETF et donc soumis aux règles IETF (voir aussi la section 5.3).
Les hackathons de l'IETF sont gratuits (et il n'est pas forcément nécessaire de payer son voyage, on peut le faire à distance, voir plus loin) et ouverts à tous (cf. section 5.3). On s'inscrit en ligne puis on y va. Si vous êtes débutant·e, ne vous inquiétez pas, on a tous été débutant·es et les hackathons sont justement l'occasion de mêler étudiant·es, expert·es, etc. Une large participation est encouragée.
Comment organise t-on un tel événement ? Charles Eckel, l'auteur du RFC, est pour l'instant le pilier de ces hackathons. Le RFC a été écrit, entre autres, pour transmettre son expérience et permette à d'autres de le seconder ou de lui succéder. La section 2 du RFC détaille les choix effectués. Le hackathon se tient le week-end pour faciliter la participation des gens qui travaillent, notamment de ceux et celles qui ne viennent pas à la réunion IETF la semaine suivante. Et puis ça renforce le côté informel.
Questions horaires, le hackathon de l'IETF n'essaie pas de faire travailler les gens 24 heures sur 24 tout le week-end. La majorité des participant·es au hackathon viennent à l'IETF ensuite et doivent donc rester frai·ches. Le hackathon dure typiquement le samedi de 09:00 à 22:00 et le dimanche de 09:00 à 16:00 (il y a déjà des réunions IETF le dimanche en fin d'après-midi). Si des gens veulent travailler toute la nuit, ielles doivent le faire dans leur chambre.
La nourriture est fournie sur place pour éviter qu'on doive sortir (mais on a évidemment le droit de sortir, les participant·es ne sont pas attaché·es et peuvent estimer plus important d'aller discuter avec d'autres participant·es en dehors du hackathon). Et, comme le note le RFC, c'est une motivation supplémentaire pour faire venir les gens (on mange bien).
Et pour les gens qui sont à distance ? Le problème avait commencé à se poser pour la réunion IETF 107 prévue à Vancouver et annulée au dernier moment pour cause de Covid. Il y avait eu un effort pour maintenir un hackathon en distanciel mais cela n'avait pas intéressé grand'monde. (Un des plus gros problèmes du distanciel est le décalage horaire. Les gens qui n'étaient pas en PST n'avaient montré aucun enthousiasme pour le hackathon.) Cela s'est mieux passé par la suite, avec des réunions entièrement en ligne pendant la pandémie. Aujourd'hui, les réunions ont repris en présentiel mais une partie des participant·es, aussi bien à l'IETF qu'au hackathon, sont à distance. (Mon opinion personnelle est que c'est peu utile ; l'intérêt du hackathon est la collaboration intense avec les gens qui sont juste là. En travaillant à distance, on perd cette collaboration. Autant travailler tout seul dans son coin.) Une approche intermédiaire, très utilisée à Maurice, est d'avoir une réunion en présentiel locale, où les gens travaillent à distance sur le hackathon. Cela permet de garder un côté collectif sympa.
Tout cela (à commencer par les repas, et bien sûr la salle, mais elle est parfois incluse dans le contrat global de l'IETF pour sa réunion) coûte de l'argent. La section 3 décrit les sources de financement, qui permettent de garder le hackathon gratuit pour les participant·es. Il y a, comme souvent dans les conférences techniques, des sponsors. (Pour le prochain hackathon, ce seront Ericsson, Meta et l'ICANN.) Mais il y a aussi un financement par l'IETF elle-même (car on ne trouve pas toujours des sponsors). Le RFC précise même quels repas seront sacrifiés s'il n'y a pas assez d'argent (le diner du samedi est considéré comme moins important que les déjeuners, car les participant·es pourront toujours diner en rentrant).
Ah, et les T-shirts ? Pas de hackathon sans T-shirt. Le RFC donne des statistiques intéressantes, comme la répartition par taille aux précédents hackathons.
La participation à distance dispense du coût des repas mais il faut ajouter celle des systèmes de communication utilisés, notamment Meetecho et Gather. (Avant de proposer un autre système, rappelez-vous qu'il y a des centaines de participant·es actif·ves au hackathon et qu'il faut une solution qui tienne la charge.)
À la fin du hackathon, les participant·es présentent leur travail, les résultats obtenus et les conclusions qu'on peut en tirer pour les normes en cours de développement (section 4). Au début, il y avait également une session de présentation des projets au début du hackathon mais elle a dû être abandonnée au fur et à mesure que le hackathon grossissait. La présentation initiale se fait désormais sur le Wiki (voir par exemple la prochaine et la page où on peut aller si on cherche une équipe ou bien des volontaires). C'est sur ce Wiki que les champions (les gens qui ont une idée et organisent une activité particulière au hackathon, cf section 7.4) essaient de recruter. Quant aux présentations finales, elles sont publiées sur Github. C'est aussi là qu'on trouvera le code produit lorsqu'il n'est pas sur un dépôt extérieur (section 5.6).
La liste des projets affichés dans la salle du hackathon de
Yokohama en 2023 :
On a parlé des outils utilisés, lors de la section sur le financement. La section 5 dresse une liste plus complète des outils logiciels qui servent pour le hackathon. Il y a évidemment, comme pour tout travail IETF, le Datatracker (qui a une section sur le hackathon). Tout aussi évidemment, puisque l'IETF travaille largement avec des listes de diffusion, il y a une liste des participant·es au hackathon.
Pour profiter de tous ces outils, il faut un réseau à forte capacité, fiable, et évidemment sans aucun filtrage. Lors de la réunion de Beijing, les négociations avec les autorités avaient été serrées et l'accord final imposait un contrôle d'accès aux salles de l'IETF, pour éviter que des citoyens chinois n'en profitent. (Normalement, il n'y a aucun contrôle à l'entrée de l'IETF, personne ne vérifie les badges.) Le groupe qui s'occupe de monter et de démonter le réseau de l'IETF a donc en plus la charge du réseau du hackathon. Celui-ci peut en outre nécessiter des services spéciaux, afin de tester en présence de tels services (NAT64, prefix delegation, etc). Il y a même un accès VPN, pour celles et ceux qui travaillent à distance.
Date de publication du RFC : Septembre 2022
Auteur(s) du RFC : M. Koster, G. Illyes, H. Zeller, L. Sassman
(Google)
Chemin des normes
Première rédaction de cet article le 14 septembre 2022
Quand vous gérez un serveur Internet (pas forcément un serveur
Web) prévu pour des humains, une bonne partie du trafic, voire la
majorité, vient de robots. Ils ne sont pas
forcément malvenus mais ils peuvent être agaçants, par exemple s'ils
épuisent le serveur à tout ramasser. C'est pour cela qu'il existe
depuis longtemps une convention, le fichier
robots.txt
, pour dire aux robots gentils ce
qu'ils peuvent faire et ne pas faire. La spécification originale
était très limitée et, en pratique, la plupart des robots comprenait
un langage plus riche que celui du début. Ce nouveau RFC documente plus ou moins
le format actuel.
L'ancienne spécification, qui date de
1996, est toujours en ligne
sur le site de
référence (qui ne semble plus maintenu, certaines
informations ont des années de retard). La réalité des fichiers
robots.txt
d'aujourd'hui est différente, et
plus riche. Mais, comme souvent sur
l'Internet, c'est assez le désordre, avec
différents robots qui ne comprennent pas la totalité du langage
d'écriture des robots.txt
. Peut-être que la
publication de ce RFC aidera à uniformiser les choses.
Donc, l'idée de base est la suivante : le
robot qui veut ramasser des ressources sur un
serveur va d'abord télécharger un fichier situé sur le chemin
/robots.txt
. (Au passage, si cette norme était
faite aujourd'hui, on utiliserait le
/.well-known
du RFC 8615.) Ce fichier va contenir des instructions pour le
robot, lui disant ce qu'il peut récupérer ou pas. Ces instructions
concernent le chemin dans l'URI (et robots.txt
n'a
donc de sens que pour les serveurs utilisant ces URI du RFC 3986, par exemple les serveurs Web). Un exemple très simple
serait ce robots.txt
:
User-Agent: * Disallow: /private
Il dit que, pour tous les robots, tout est autorisé sauf les chemins
d'URI commençant par /private
. Un robot qui
suit des liens (RFC 8288) doit donc ignorer
ceux qui mènent sous /private
.
Voyons maintenant les détails pratiques (section 2 du RFC). Un
robots.txt
est composé de
groupes, chacun s'appliquant à un robot ou un
groupe de robots particulier. Les groupes sont composés de
règles, chaque règle disant que telle partie de
l'URI est interdite ou autorisée. Par défaut, tout est autorisé
(même chose s'il n'y a pas de robots.txt
du
tout, ce qui est le cas de ce blog). Un groupe commence par une
ligne User-Agent
qui va identifier le robot (ou
un astérisque pour désigner tous les
robots) :
User-Agent: GoogleBot
Le robot va donc chercher le ou les groupes qui le concernent. En
HTTP,
normalement, l'identificateur que le robot cherche dans le
robots.txt
est une sous-chaine de
l'identificateur envoyé dans le champ de l'en-tête HTTP
User-Agent
(RFC 9110,
section 10.1.5).
Le robot doit combiner tous les groupes qui s'appliquent à lui,
donc on voit parfois plusieurs groupes avec le même
User-Agent
.
Le groupe est composé de plusieurs règles, chacune commençant par
Allow
ou Disallow
(notez
que la version originale de la spécification n'avait pas
Allow
). Si plusieurs règles correspondent, le
robot doit utiliser la plus spécifique, c'est-à-dire celle dont le
plus d'octets correspond. Par exemple, avec :
Disallow: /private Allow: /private/notcompletely
Une requête pour /private/notcompletely/foobar
sera autorisée. L'ordre des règles ne compte pas (mais certains
robots sont bogués sur ce point). C'est du fait de cette notion de
correspondance la plus spécifique qu'on ne peut pas compiler un
robots.txt
en une simple expression
rationnelle, ce qui était possible avec la
spécification originelle. Si une règle Allow
et une Disallow
correspondent, avec le même
nombre d'octets, l'accès est autorisé.
Le robot a le droit d'utiliser des instructions supplémentaires, par exemple pour les sitemaps.
En HTTP, le robot peut rencontrer une redirection
lorsqu'il essaie de récupérer le robots.txt
(code HTTP 301, 302, 307 ou 308). Il devrait dans ce cas la suivre
(mais pas infinement : le RFC recommande cinq essais au
maximum). S'il n'y a pas de robots.txt
(en
HTTP, code de retour 404), tout est autorisé (c'est le cas de mon
blog). Si par contre il y a une erreur (par exemple 500 en HTTP), le
robot doit attendre et ne pas se croire tout permis. Autre point
HTTP : le robot peut suivre les instructions de mémorisation du
robots.txt
données, par exemple, dans le champ
Cache-control
de l'en-tête.
Avant de passer à la pratique, un peu de sécurité. D'abord, il
faut bien se rappeler que le respect du
robots.txt
dépend de la bonne volonté du
robot. Un robot malveillant, par exemple, ne tiendra certainement
pas compte du robots.txt
. Mais il peut y avoir
d'autres raisons pour ignorer ces règles. Par exemple, l'obligation
du dépôt légal fait que la
BNF
annonce explicitement qu'elle ignore ce
fichier. (Et un programme comme wget a
une option, -e robots=off
, pour débrayer la vérification
du robots.txt
.) Bref, un robots.txt
ne remplace pas
les mesures de sécurité, par exemple des règles d'accès aux
chemins définies dans la configuration de votre serveur HTTP. Le
robots.txt
peut même diminuer la sécurité, en
indiquant les fichiers « intéressants » à un éventuel attaquant.
Passons maintenant à la pratique. On trouve plein de mises en
œuvre de robots.txt
un peu partout mais en
trouver une parfaitement conforme au RFC est bien plus
dur. Disons-le clairement, c'est le bordel, et l'auteur d'un
robots.txt
ne peut jamais savoir comment il va
être interprété. Il ne faut donc pas être trop subtil dans son
robots.txt
et en rester à des règles
simples. Du côté des robots, on a un problème analogue : bien des
robots.txt
qu'on rencontre sont bogués. La
longue période sans spécification officielle est largement
responsable de cette situation. Et tout n'est pas clarifié par le
RFC. Par exemple, la grammaire en section 2.2 autorise un
Disallow
ou un Allow
à
être vide, mais sans préciser clairement la sémantique associée.
Pour Python, il y a un module standard, mais qui est loin de suivre le RFC. Voici un exemple d'utilisation :
import urllib.robotparser import sys if len(sys.argv) != 4: raise Exception("Usage: %s robotstxt-file user-agent path" % sys.argv[0]) input = sys.argv[1] ua = sys.argv[2] path = sys.argv[3] parser = urllib.robotparser.RobotFileParser() parser.parse(open(input).readlines()) if parser.can_fetch(ua, path): print("%s can be fetched" % path) else: print("%s is denied" % path)
Et, ici, on se sert de ce code :
% cat ultra-simple.txt User-Agent: * Disallow: /private % python test-robot.py ultra-simple.txt foobot /public/test.html /public/test.html can be fetched % python test-robot.py ultra-simple.txt foobot /private/test.html /private/test.html is denied
Mais ce module ne respecte pas la précédence des règles :
% cat precedence.txt User-Agent: * Disallow: / Allow: /bar.html % python3 test-robot.py precedence.txt foobot /bar.html /bar.html is denied
En application de la règle la plus spécifique,
/bar.html
aurait dû être autorisé. Si on
inverse Allow
et Disallow
,
on obtient le résultat attendu. Mais ce n'est pas normal, l'ordre
des règles n'étant normalement pas significatif.
Autre problème du module Python, il ne semble pas gérer les
jokers, comme l'exemple *.gif$
du
RFC. Il existe des modules Python alternatifs pour traiter les
robots.txt
mais aucun ne semble vraiment mettre
en œuvre le RFC.
La situation n'est pas forcément meilleure dans les autres langages de programmation. Essayons avec Elixir. Il existe un module pour cela. Écrivons à peu près le même programme qu'en Python :
usage = "Usage: test robotstxtname useragent path" filename = case Enum.at(System.argv(), 0) do nil -> raise RuntimeError, usage other -> other end content = case File.read(filename) do {:ok, data} -> data {:error, reason} -> raise RuntimeError, "#{filename}: #{reason}" end ua = case Enum.at(System.argv(), 1) do nil -> raise RuntimeError, usage other -> other end statuscode = 200 {:ok, rules} = :robots.parse(content, statuscode) path = case Enum.at(System.argv(), 2) do nil -> raise RuntimeError, usage other -> other end IO.puts( case :robots.is_allowed(ua, path, rules) do true -> "#{path} can be fetched" false -> "#{path} can NOT be fetched" end)
Et testons-le :
% mix run test-robot.exs ultra-simple.txt foobot /public/test.html /public/test.html can be fetched % mix run test-robot.exs ultra-simple.txt foobot /private/test.html /private/test.html can NOT be fetched
Il semble avoir moins de bogues que le module Python mais n'est quand même pas parfait.
Comme dit plus haut, le robots.txt
n'est pas
réservé au Web. Il est utilisé par exemple pour
Gemini. Ainsi, le robot de
collecte Lupa lit les robots.txt
et ne
va pas ensuite récupérer les URI interdits. En septembre 2022, 11 %
des capsules Gemini avaient un robots.txt
.
Date de publication du RFC : Octobre 2022
Auteur(s) du RFC : D. Farinacci (lispers.net), F. Maino (Cisco Systems), V. Fuller (vaf.net Internet Consulting), A. Cabellos (UPC/BarcelonaTech)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lisp
Première rédaction de cet article le 23 août 2024
Comme pour tous les protocoles de séparation de l'identificateur et du localisateur, le protocole LISP, normalisé dans le RFC 9300, doit faire face au problème de la correspondance (mapping) entre les deux informations. Comment trouver un localisateur, en ne connaissant que l'identificateur ? LISP n'a pas de solution unique et plusieurs protocoles de correspondance peuvent être utilisés. La stabilité du logiciel des routeurs imposait une interface stable avec le système de résolution des identificateurs en localisateurs. C'est ce que fournit notre RFC 9301, qui spécifie l'interface, vue du routeur, et qui ne devrait pas changer, même si de nouveaux systèmes de correspondance/résolution apparaissent. Ce RFC remplace le RFC 6833. L'interface change assez peu mais le texte est sérieusement réorganisé, et la spécification a désormais le statut de norme et plus simplement d'expérimentation.
LISP prévoit deux sortes de machines impliquées dans la résolution d'identificateurs (les EID, Endpoint ID) en localisateurs (les RLOC, Routing Locator). Ces deux machines sont les Map-Resolver et les Map-Server. Pour ceux qui connaissent le DNS, on peut dire que le Map-Server est à peu près l'équivalent du serveur faisant autorité et le Map-Resolver joue quasiment le même rôle que celui du résolveur. Toutefois, il ne faut pas pousser la comparaison trop loin, LISP a ses propres règles. Pour résumer en deux phrases, un routeur LISP d'entrée de tunnel (un ITR, Ingress Tunnel Router), ayant reçu un paquet à destination d'une machine dont il connait l'identificateur (l'EID), va interroger un Map-Resolver pour connaître le localisateur (le RLOC, auquel l'ITR enverra le paquet). Pour accomplir sa tâche, le Map-Resolver fera suivre les requêtes au Map-Server, qui la transmettra finalement au routeur de sortie du tunnel (l'ETR, Egress Tunnel Router), qui est la vraie source faisant autorité.
C'est entre le Map-Resolver et le Map-Server que se trouvent les détails du système de correspondance. Ils peuvent être connectés par ALT (RFC 6836), par CONS (RFC jamais publié), par NERD (RFC 6837), par DDT (RFC 8111) ou bien par tout autre système de résolution, existant ou encore à créer (ils ne peuvent pas être connectés avec simplement LISP, puisqu'on aurait alors un problème d'œuf et de poule, LISP ayant besoin de ALT qui aurait besoin de LISP… cf. section 8.1). Rappelez-vous que notre RFC 9301 ne décrit qu'une interface, celle des ITR et ETR avec les Map-Resolver et Map-Server. Il est donc relativement court.
Comme avec toute technique nouvelle, il est prudent d'apprendre
le vocabulaire (section 3, puis section 4 pour un survol général du
système). Il y a deux types d'acteurs, les
Map-Server et les Map-Resolver
que nous avons déjà vu, et trois types
importants de messages, Map-Register
(un
ETR l'envoie au Map-Server pour indiquer les RLOC
des EID dont il est responsable), Map-Request
(un ITR l'envoie à un Map-Resolver pour obtenir
les RLOC ; le Map-Resolver fait suivre jusqu'au
Map-Server, puis à l'ETR) et enfin
Map-Reply
, la réponse au précédent. Notons que
ces types de messages ont leur description complète (avec leur
format) dans le RFC 9300. Notez aussi
que Map-Resolver et Map-Server
sont des fonctions, et que les deux peuvent être assurés par la même
machine, qui serait à la fois Map-Resolver et
Map-Server (dans le DNS, un tel mélange est
déconseillé).
Schéma général du système de correspondance LISP :
La section 8 de notre RFC plonge dans les
détails. Accrochez-vous. Voyons d'abord le premier routeur LISP que
rencontrera le paquet. On le nomme ITR pour Ingress Tunnel
Router. Les routeurs précédents traitaient l'adresse de
destination du paquet comme une adresse IP
ordinaire. L'ITR, lui, va la traiter comme un identificateur (EID
pour Endpoint IDentification). L'EID n'est pas
routable sur l'Internet. Il faut donc encapsuler le paquet en LISP
pour l'envoyer dans le tunnel. La nouvelle adresse IP de destination
est le localisateur (RLOC pour Routing
LOCator). Pour trouver le localisateur, l'ITR va demander
à un ou plusieurs Map-Resolver. Il a été
configuré (typiquement, à la main) avec leurs adresses IP (qui
doivent être des localisateurs, pour éviter un amusant problème
d'œuf et de poule; notez que plusieurs
Map-Resolver peuvent avoir la même adresse, grâce
à l'anycast). L'ITR ne
connait que le protocole de résolution, envoi d'une
Map-Request
et récupération d'une
Map-Reply
(en termes DNS, l'ITR est un
stub resolver). L'ITR ne connait donc
pas les protocoles utilisés en interne par le
système de correspondance, il ne connait pas ALT (ou ses
concurrents). Cette communication avec le
Map-Resolver peut être testée et déboguée avec
l'outil lig (RFC 6835).
La réponse du Map-Resolver ne sera pas
forcément positive. L'ITR recevra peut-être une
negative Map-Reply
, envoyée
en réponse si un Map-Resolver ne trouve pas de
localisateur pour l'identificateur qu'on lui a passé. Cela veut dire
que le site final n'utilise pas LISP, et qu'il faut alors router le
paquet avec les méthodes habituelles d'IP. (Il n'est évidemment pas prévu que tout
l'Internet passe à LISP du jour au lendemain, le routeur LISP doit
donc aussi pouvoir joindre les sites non-LISP.)
Si la réponse est positive, l'ITR peut alors encapsuler le paquet et le transmettre. Comment le Map-Resolver a-t-il trouvé la réponse qu'il a envoyé ? Contrairement aux routeurs LISP comme l'ITR, le Map-Resolver et le Map-Server connaissent le système de correspondance utilisé (si c'est ALT, ils sont tous les deux routeurs ALT) et c'est celui-ci (non traité dans ce RFC) qu'ils utilisent pour savoir s'il y a une réponse et laquelle.
Et, à l'autre bout du tunnel, que s'était-il passé ? Le routeur
de fin de tunnel (l'ETR, pour Egress Tunnel
Router), avait été configuré par un administrateur réseaux
avec une liste d'EID dont il est responsable. Pour que le reste du
monde connaisse ces EID, il les publie auprès d'un
Map-Server en envoyant à ce dernier des messages
Map-Register
. Pour d'évidentes raisons de
sécurité, ces messages doivent être authentifiés (champ
Authentication Data
du message
Map-Register
, avec clés gérées à la main pour
l'instant, avec SHA-256 au minimum), alors
que les Map-Request
ne l'étaient pas (la base
de données consultée par les routeurs LISP est publique, pas besoin
d'authentification pour la lire, seulement pour y écrire). Ces
Map-Request
sont renvoyés périodiquement (le
RFC suggère toutes les minutes) pour que le
Map-Server sache si l'information est toujours à
jour. Ainsi, si un ETR est éteint, l'information obsolète dans les
Map-Server disparaîtra en trois minutes maximum
(des messages peuvent être perdus, le RFC demande donc de patienter
un peu en cas de non-réception). Cela veut aussi dire que LISP ne
convient pas forcément tel quel pour les situations où on exige une
mobilité très rapide.
Notez que je ne décris pas tous les détails (comme la possibilité pour un ETR de demander un accusé de réception au Map-Server, chose que ce dernier ne fait pas par défaut), voyez le RFC si vous êtes curieux.
Arrivés là, nous avons un Map-Server qui
connait les EID que gère l'ETR. Désormais, si ce
Map-Server reçoit une demande
Map-Request
, il peut la faire suivre à l'ETR
(si vous connaissez le DNS, vous avez vu que le
Map-Register
n'est pas tout à fait l'équivalent
des mises à jour dynamiques du RFC 2136 : avec
ces dernières, le serveur de noms qui a reçu la mise à jour répondra
ensuite lui-même aux requêtes). Le Map-Server ne
sert donc que de relais, il ne modifie pas la requête
Map-Request
, il la transmet telle quelle à
l'ETR. Le rôle des Map-Resolver et des
Map-Server est donc simplement de trouver l'ETR
responsable et de lui faire suivre (sans utiliser l'encapsulation
LISP) les requêtes, pas de répondre à sa place. Cela se fera
peut-être dans le futur lorsque des mécanismes de
cache seront ajoutés. Pour le moment, les
Map-Resolver n'ont pas de cache, de mémoire
(section 4), une
grosse différence avec le DNS (section 1).
La section 9 fait le tour des questions de sécurité liées au service de résolution. Comme les requêtes sont faites avec le format de paquets de LISP, elles héritent des services de sécurité de LISP comme le nonce qui permet de limiter les risques d'usurpation ou comme la sécurité LISP du RFC 9303. Par contre, comme pour les protocoles utilisés dans l'Internet actuel, il n'y a pas de vraie protection contre les annonces faites à tort (un Map-Server qui annoncerait un EID qui n'est pas à lui). C'est un problème très proche de celui de la sécurité de BGP et qui utilisera peut-être le même genre de solutions.
Notez qu'en théorie, l'interface spécifiée dans ce RFC pourrait servir à d'autres protocoles que celui du RFC 9300, comme par exemple GRE (RFC 2890) ou VXLAN (RFC 7348). Mais, pour l'instant, ce n'est pas le cas.
Il y a apparemment trois mises en œuvre. Outre l'outil de débogage lig (RFC 6835), il y a celle de Cisco pour ses routeurs, mais je ne connais pas les autres, sans doute dans des Unix.
Et les changements depuis le précédent RFC ? Ils sont résumés dans la section 11 :
Map-Notify-Ack
, un accusé de
réception,Date de publication du RFC : Octobre 2022
Auteur(s) du RFC : D. Farinacci (lispers.net), V. Fuller (vaf.net Internet Consulting), D. Meyer (1-4-5.net), D. Lewis (Cisco Systems), A. Cabellos (UPC/BarcelonaTech)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lisp
Première rédaction de cet article le 14 décembre 2024
Le protocole LISP (qui n'a rien à voir avec le langage de programmation du même nom), vise à résoudre un problème documenté dans le RFC 4984 : les difficultés que rencontre le système de routage de l'Internet devant les pressions imposées par la croissance du réseau et par le désir des utilisateurs de ne pas être lié à un unique fournisseur d'accès. Actuellement, tout changement de routage sur n'importe quel site se propage à tous les autres routeurs de la DFZ. Un tel système ne passe pas réellement à l'échelle (il ne peut pas croitre indéfiniment). LISP propose une solution, dans un ensemble de RFC dont ce RFC 9300 (qui succède au RFC 6830) est le principal. LISP n'est désormais plus considéré comme expérimental et ce nouveau RFC est sur le chemin des normes (Standards Track) mais, en pratique, le projet LISP semble plutôt en panne.
Avant de plonger dans ce RFC, voyons les motivations pour LISP et ses principes essentiels (si vous préférez les lire dans le RFC et pas dans mon article, c'est en section 4 mais la lecture recommandée est le RFC 9299). Aujourd'hui, les adresses IP ont deux rôles, localisation (où suis-je connecté au réseau) et identité (qui suis-je). Une adresse IP est un localisateur car changer de point d'attachement (par exemple parce qu'on change de FAI) vous fait changer d'adresse IP, et c'est un identificateur car c'est ce qui est utilisé par les protocoles de transport comme TCP pour identifier une session en cours : changer d'adresse IP casse les connexions existantes.
Le principal problème de cette approche concerne le routage. Un routage efficace nécessiterait une cohérence entre les adresses et la topologie du réseau, par exemple que deux sites proches sur le réseau aient des adresses proches. Mais on n'a pas cette cohérence actuellement. On notera qu'IPv6 ne résolvait pas ce problème, et délibérément (le cahier des charges d'IPv6 ne prévoyait pas de changer le modèle de routage).
Résultat, les routeurs doivent gérer bien plus de routes que nécessaire, augmentant leur prix (en attendant le jour où, même en payant, on n'arrivera pas à manipuler autant de routes, avec leurs changements fréquents). Le RFC 4984 insistait sur ce problème en affirmant que « The workshop participants believe that routing scalability is the most important problem facing the Internet today and must be solved, although the time frame in which these problems need solutions was not directly specified. »
Cette croissance de la table de routage peut être suivie sur le célèbre site de Geoff Huston. Notez que la taille n'est pas le seul aspect, le rythme de changement (le nombre d'updates BGP par seconde) est au moins aussi important.
LISP vise donc à résoudre ce problème par une technique connue sous le nom de séparation du localisateur et de l'identificateur (son nom, « Locator/ID Separation Protocol », en dérive d'ailleurs, bien qu'il soit loin d'être le seul protocole dans cette catégorie). Au lieu que tous les préfixes IP aillent dans la DFZ, seuls les localisateurs, les RLOC (Routing LOCators) y iront. Les identificateurs, les EID (Endpoint IDentifiers) sont dans un nouveau système, le système de correspondance (mapping, RFC 9301), qui permettra de trouver un RLOC à partir d'un EID. LISP est donc l'application d'un vieux principe de l'informatique : « Tout problème peut être résolu en ajoutant un niveau d'indirection. »
À quoi ressemblent RLOC et EID ? Physiquement, ce sont juste des adresses IP (v4 ou v6), avec une nouvelle sémantique. Le RFC 8060 décrit plus en détail comment représenter les EID.
Par rapport aux autres solutions de séparation de l'identificateur et du localisateur (la plus achevée étant HIP), LISP s'identifie par les points suivants :
Comment se passe l'envoi d'un paquet avec LISP ? Supposons qu'une machine
veuille écrire à www.example.com
. Elle utilise
le DNS comme
aujourd'hui, pour trouver que l'adresse est
2001:db8:110:2::e9
(c'est un EID, un
identificateur, mais la machine n'a pas besoin de le savoir, les
machines terminales ne connaissent pas LISP et ne savent même pas
qu'elles l'utilisent). Elle envoie le paquet à son routeur
habituel. À un moment dans le réseau, le paquet va arriver dans un
routeur LISP (qui, lui, a été modifié pour gérer LISP). Il va alors
chercher le RLOC (le localisateur). Il demande au système de
correspondance (l'équivalent LISP du DNS) qui va lui dire « le RLOC de
2001:db8:110:2::e9
est
198.51.100.178
» (notez au passage que RLOC et
EID peuvent être des adresses v4 ou v6). (L'information est stockée
dans un cache du
routeur, pour le prochain paquet.) Le paquet est alors encapsulé dans un paquet
LISP qui est transmis en UDP (port 4341) à
198.51.100.178
. (En raison de ces deux étapes,
correspondance puis encapsulation, LISP fait partie des protocoles
nommés Map and Encap.)
198.51.100.178
décapsule et le paquet suit
ensuite un chemin normal vers la machine
2001:db8:110:2::e9
. Pendant le trajet dans le
tunnel,
le paquet aura donc deux en-têtes, l'interne
(celui placé par la machine d'origine et qui utilise des EID dans
les champs « Adresse IP source » et « Adresse IP destination ») et
l'externe (celui placé par le routeur d'entrée du tunnel, et qui
utilise des RLOC).
Si le système de correspondance avait répondu négativement « Ce
n'est pas un EID, je n'ai pas de RLOC pour
2001:db8:110:2::e9
» (Negative map
reply) ? Cela aurait voulu dire que le site cible
n'utilise pas LISP et, dans ce cas, on lui transmet le paquet par
les mécanismes habituels d'IP.
Ainsi, pour prendre le scénario d'usage principal de LISP, un site qui veut être multi-homé n'aura pas besoin de BGP et d'annoncer ses routes dans la DFZ. Il aura ses identificateurs, ses EID, et les paquets entrant ou sortant de son réseau seront encapsulés en LISP (le système de correspondance peut renvoyer plusieurs RLOC pour un EID, avec des préférences différentes, pour faire de l'ingénierie de trafic). Pour les routeurs de la DFZ, ce seront juste des paquets IP ordinaires. Seules les deux extrémités du tunnel sauront que LISP est utilisé.
Le système de correspondance de LISP n'est pas unique : plusieurs choix sont possibles comme ALT (RFC 6836). Comme le DNS, il fonctionne en tirant les informations nécessaires (pas en les poussant vers tout le monde, comme le fait BGP), ce qui devrait lui donner une bonne capacité de croissance. De toute façon, LISP spécifie une interface vers le système de correspondance (RFC 9301) et les différents systèmes ont juste à mettre en œuvre cette interface pour qu'on puisse en changer facilement. Ainsi, ALT pourra être remplacé par un de ses concurrents, CONS, EMACS ou NERD (leurs noms sont des références au langage de programmation). NERD est documenté dans le RFC 6837.
Ce RFC est un gros morceau (d'autant plus que d'autres parties de LISP sont dans d'autres RFC). Je ne vais pas le couvrir en entier. Mais quelques points méritent d'être gardés en tête :
Pour les fanas de format de paquets, la section 5 décrit l'encapsulation. LISP est indépendant de la famille d'adresses, donc on peut avoir un paquet IP où les EID sont IPv4 qui soit tunnelé avec des RLOC IPv6 ou bien le contraire. Devant le paquet normal, LISP va ajouter un en-tête IP standard pour les RLOC, où la source sera l'ITR (routeur d'entrée du tunnel) et la destination l'ETR (routeur de sortie du tunnel), puis un en-tête UDP (l'UDP a davantage de chances de passer les middleboxes que de l'IP mis directement dans l'IP, des protocoles comme QUIC ont le même problème), avec le port de destination à 4341, puis un en-tête spécifique à LISP et enfin le paquet original. Donc, pour résumer :
L'en-tête spécifique à LISP contient notamment (section 5 si vous voulez tout connaître) :
Comme toutes les solutions à base de tunnel, LISP va souffrir de la mauvaise gestion de la PMTUD dans l'Internet d'aujourd'hui (cf. RFC 4459), l'en-tête LISP supplémentaire réduisant la MTU (cf. section 7 pour des solutions possibles).
La section 5 décrivait les paquets de données, ceux encapsulant les données envoyées par le site original. La section 6 couvre les paquets de contrôle, utilisés par LISP pour ses propres besoins, notamment le système de correspondance (cf. RFC 9301 pour les détails). On y retrouve l'utilisation d'UDP :
Il est évidemment essentiel qu'on sache si son correspondant est joignable ou pas. Comment cette « joignabilité » est-elle vérifiée ? La section 6.3 énumère les mécanismes disponibles. Entre autres :
Mais on peut aussi tester explicitement, par le mécanisme Echo Nonce de la section 10.1. Le testeur émet un message LISP avec les bits N (numnique présent) et E (écho demandé), met le numnique à une valeur aléatoire (RFC 4086), et envoie le paquet. (La section 4.1 précise toutefois que cela ne doit pas être utilisé sur l'Internet public.) L'ETR à l'autre bout doit normalement renvoyer ce numnique dans son prochain paquet. Notons que cela teste la bidirectionnalité de la connexion. Si on n'obtient pas de réponse, cela peut signifier que la connexion est complètement coupée ou tout simplement qu'elle ne marche que dans un seul sens. Mais, surtout, l'absence de réponse peut indiquer le cas où l'ETR qui reçoit le trafic pour un site n'est pas le même routeur que l'ITR qui génère le trafic sortant. Dans ce cas, l'absence de réponse n'est pas un problème. Enfin, le routeur en face peut tout simplement être configuré pour ignorer les demandes d'écho.
Une autre solution pour tester est donc d'utiliser les messages du système de correspondance EID->RLOC, les Map Request et Map Reply. Ces messages ont un bit P (pour probe) qui indique que le demandeur est intéressé par la joignabilité du site demandé.
LISP impose donc des traitements supplémentaires, par rapport à ceux de l'IP classique. Est-ce que cela ne ralentit pas trop les routeurs ? La section 15 explore le problème et explique pourquoi LISP ne nécessite pas de changement du matériel de forwarding (les ASIC du routeur). La plupart des routeurs ont déjà du code prévu pour gérer des tunnels (encapsuler et décapsuler) rapidement.
Comment sera déployé LISP ? Le RFC 7215 décrit plusieurs scénarios possibles et les détails. Principal problème : combien d'ITR et d'ETR pour un opérateur ? Grâce aux tunnels, on peut n'avoir qu'un seul ITR et un seul ETR pour tout le trafic. Cela poserait évidemment des problèmes de redondance et de performance. Mais avoir beaucoup de xTR peut poser des problèmes d'administration. Si les ITR sont largement automatiques (leur cache des correspondance EID->RLOC est bâti dynamiquement), avoir beaucoup d'ETR risque d'être compliqué à maintenir (car l'ETR doit avoir dans sa configuration une liste des EID qu'il va gérer).
Un des gros problèmes que pose la séparation de l'identificateur et du localisateur est la sécurité : nouvelles attaques (par exemple contre le système de correspondance), nouveaux pièges (une machine qui utiliserait le vrai RLOC mais mentirait sur l'EID, par exemple). La section 16 du RFC examine les risques théoriquement présents dans LISP, mais lisez aussi les RFC 7835 et RFC 9303.
Comme avec toutes les techniques de tunnel, un émetteur peut facilement tricher sur l'adresse source interne (celle qui sera utilisée après décapsulation par l'ETR). Pour se protéger, un ITR devrait n'encapsuler un paquet que si l'adresse source est un EID qu'il contrôle. Et un ETR ne devrait transmettre un paquet que si la destination est un EID sous sa responsabilité.
Le test de la réversibilité (via les numniques, cf. section 3) est essentiel contre ces risques. Sans ce test, un ETR pirate pourrait par exemple envoyer un Map Reply en réponse aveugle à un Map Request, et le voir accepté, avec des données mensongères (naturellement, l'ITR n'accepte que des Map Replies qui sont en réponse à un Map Request de sa part). Avec ce système de numnique que le récepteur doit mettre dans sa réponse, un attaquant aveugle (qui n'est pas situé sur le chemin des paquets et ne peut donc pas les observer) aura donc peu de chances de réussir à faire accepter ses paquets.
En revanche, un attaquant situé sur le chemin, et qui peut observer les paquets qui passent, a la possibilité de commettre bien plus de dégâts. Mais c'est déjà le cas avec les protocoles actuels (par exemple, les numéros de séquence difficiles à deviner du RFC 6528 ne protègent pas TCP contre des attaquants situés sur le chemin).
Les attaques par déni de service sont évidemment possibles avec LISP : une des précautions recommandées est de limiter le trafic des Map Requests et Map Replies. Autre attaque par déni de service, un ITR peut être victime d'une machine qui essaie de remplir la table des correspondances EID->RLOC du routeur. Il est donc important d'envisager ce cas, par exemple en permettant de garder dans le cache les entrées les plus fréquemment accédées (pour éviter qu'elles ne soient retirées automatiquement pour faire de la place). Mais il n'existe pas de solution miracle contre ce problème d'envahissement de cache.
Le fonctionnement de LISP est schématisé sur ce dessin : Alice a l'identificateur (EID)
2001:db8:1::1
et veut écrire à Bob qui a le
2001:db8:2::42
(dans la plupart des cas, Alice
aura trouvé l'adresse de Bob dans le DNS, comme aujourd'hui). Ni Alice, ni Bob n'ont
à connaître LISP, chacun croit utiliser de l'IP normal. Alice envoie
donc un paquet standard, à destination de
2001:db8:2::42
. Mais, sur le trajet, il y a un
ITR, un routeur LISP. Celui-ci va chercher (dans le système de
correspondance, non montré ici) le RLOC (le localisateur) de Bob
(ou, plus exactement, de son site). Une fois qu'il a trouvé
2001:db8:ff::666
, il encapsule le paquet
derrière un en-tête LISP et envoie ce paquet à l'ETR, l'autre
routeur LISP en face, 2001:db8:ff::666
. Les
routeurs de l'Internet, entre l'ITR et l'ETR, ne connaissent pas
LISP non plus et routent ce qui leur semble un paquet IP
normal. Arrivé à l'ETR, le paquet est décapsulé et poursuit son
chemin par du routage IP classique. Sur tout le schéma, seuls l'ITR
et l'ETR sont des nouveautés LISP.
Modifions légèrement le schéma pour y mettre le système de
correspondance : On y voit l'ITR
demander à son résolveur « Quel est le localisateur de
2001:db8:2::42
? » et son résolveur lui
répondre. Le résolveur avait demandé au serveur qui avait reçu de
l'ETR un enregistrement disans « Le localisateur de
2001:db8:2::42
est
2001:db8:ff::666
». Entre le résolveur et le
serveur se trouve le cœur du système de correspondance. LISP
en a plusieurs possibles, comme le ALT du RFC 6836.
Où trouve-t-on du code LISP aujourd'hui ?
Comme pour tous les protocoles fondés sur le principe de la séparation de l'identificateur et du localisateur, il est toujours utile, si on veut en comprendre les principes, de (re)lire l'article fondateur de Chiappa, « Endpoints and Endpoint names: A Proposed Enhancement to the Internet Architecture ». Autres articles à lire :
La section 18 de notre RFC décrit les changements depuis le RFC 6830. Le principal est bien sûr que LISP n'est plus considéré comme expérimental, on peut le déployer sur l'Internet public, modulo les précautions de la section 4.1. Sinon :
La section 4.1 décrit ce qu'il faut faire et ne pas faire lorsqu'on déploie LSIP « en vrai » sur l'Internet public. Quand on n'est plus expérimental, il faut faire attention. Bien des techniques de LISP (comme la mise à jour automatique des données des routeurs juste en regardant un paquet qui passe, le gleaning, clairement dangereux) ne sont sûres que dans un environnement fermé.
Date de publication du RFC : Octobre 2022
Auteur(s) du RFC : A. Cabellos
(UPC-BarcelonaTech), D. Saucez
(INRIA)
Pour information
Réalisé dans le cadre du groupe de travail IETF lisp
Première rédaction de cet article le 3 novembre 2022
Le protocole réseau LISP (Locator/ID Separation Protocol, rien à voir avec le langage de programmation du même nom) est normalisé dans le RFC 6830 et plusieurs autres qui l'ont suivi. Ce RFC 6830 est un peu long à lire et ce nouveau RFC propose donc une vision de plus haut niveau, se focalisant sur l'architecture de LISP. Cela peut donc être un bon point de départ vers les RFC LISP.
L'idée de base de LISP est de séparer l'identificateur du localisateur, comme recommandé par le RFC 4984. Un identificateur désigne une machine, un localisateur sa position dans l'Internet. Aujourd'hui, les adresses IP servent pour les deux, ne satisfaisant parfaitement aucun des deux buts : les identificateurs devraient être stables (une machine qui change de réseau ne devrait pas en changer), les localisateurs devraient être efficaces et donc être liés à la topologie, et agrégeables.
LISP a donc deux classes : les identificateurs, ou EID (End-host IDentifier) et les localisateurs, ou RLOC (Routing LOCators). Les deux ont la syntaxe des adresses IP (mais pas leur sémantique). Un système de correspondance permet de passer de l'un à l'autre (par exemple, je connais l'EID de mon correspondant, je cherche le RLOC pour lui envoyer un paquet). LISP est plutôt prévu pour être mis en œuvre dans les routeurs, séparant un cœur de l'Internet qui n'utilise que les RLOC, d'une périphérie qui utiliserait les EID (avec, entre les deux, les routeurs LISP qui feraient la liaison).
En résumé (accrochez-vous, c'est un peu compliqué) :
C'est ce côté « solution dans les routeurs » et donc le fait que les machines terminales ne savent même pas qu'elles font du LISP, qui distingue LISP des autres solutions fondées sur la séparation de l'identificateur et du localisateur, comme ILNP (RFC 6740).
Au passage, pourquoi avoir développé LISP ? Quel était le problème à résoudre ? Outre la beauté conceptuelle de la chose (il n'est pas esthétique de mêler les fonctions d'identificateur et de localisateur), le principal problème à traiter était celui du passage à l'échelle du système de routage de l'Internet, décrit dans le RFC 4984. En gros, le nombre de routes distinctes augmente trop vite et menace la stabilité des routeurs de la DFZ. Le but était donc, par une nouvelle architecture, de rendre inutile certains choix qui augmentent la taille de la table de routage (comme la désagrégation des préfixes IP, afin de faire de l'ingénierie de trafic). Le RFC 7215 décrit comment LISP aide dans ce cas.
La section 7 du RFC décrit les différents scénarios d'usage de LISP :
La section 3 du RFC décrit en détail l'architecture de LISP (après, vous serez mûr·e·s pour lire les RFC LISP eux-mêmes, en commençant par le RFC 6830). Elle repose sur quatre principes :
La séparation entre identificateur et localisateur n'est pas faite au niveau de la machine individuelle, comme avec ILNP, mais à la frontière entre la périphérie de l'Internet (the edge) et son cœur (the core, en gros, la DFZ et quelques routeurs en plus). La périphérie travaille avec des EID (elle ne sait même pas que LISP est utilisé), le cœur avec des RLOC. Contrairement à ILNP, il n'y a donc pas une stricte séparation entre identificateurs et localisateurs : ils ont la même syntaxe (qui est celle d'une adresse IP, v4 ou v6) et, à part les routeurs d'entrée et de sortie des tunnels LISP, personne ne sait s'il utilise un EID ou un RLOC : les machines terminales manipulent des EID, les routeurs du cœur des RLOC, tout en croyant que ce sont des adresses IP ordinaires. Seuls les routeurs à la frontière entre les deux mondes connaissent LISP (et auront donc besoin d'un logiciel adapté).
Un Internet LISP est donc une série de « sites LISP » (des
réseaux de périphérie accessibles par LISP) connectés par des
tunnels entre eux, au-dessus du cœur
actuel. Le routeur d'entrée du tunnel se nomme ITR (pour
Ingress Tunnel Router) et celui de sortie ETR
(pour Egress Tunnel Router). Le terme de xTR
(pour « ITR ou bien ETR ») est parfois utilisé pour désigner un
routeur LISP, qu'il soit d'entrée ou de sortie
Le sous-système des données (data plane) se charge d'encapsuler et de décapsuler les paquets, puis de les transmettre au bon endroit. (Sa principale qualité est donc la rapidité : il ne faut pas faire attendre les paquets.) Les ITR encapsulent un paquet IP qui vient d'un site LISP (dans un paquet UDP à destination du port 4341), puis l'envoient vers l'ETR. À l'autre bout du tunnel, les ETR décapsulent le paquet. Dans le tunnel, les paquets ont donc un en-tête intérieur (un en-tête IP normal, contenant les EID source et destination), qui a été placé par la machine d'origine, et un en-tête extérieur (contenant le RLOC source, celui de l'ITR, et le RLOC de destination, celui de l'ETR puis, après l'en-tête UDP, l'en-tête spécifique de LISP). Rappelez-vous que les routeurs du cœur ne connaissent pas LISP, ils font suivre ce qui leur semble un paquet IP ordinaire. Les routeurs LISP utilisent des tables de correspondance entre EID et RLOC pour savoir à quel ETR envoyer un paquet.
Ces tables ont été apprises du sous-système de contrôle (control plane, qui contient la fonction de correspondance - mapping), le routeur ayant un cache des correspondances les plus récentes. Cette fonction de correspondance, un des points les plus délicats de LISP (comme de tout système de séparation de l'identificateur et du localisateur) est décrite dans le RFC 6833. Son rôle peut être comparé à celui du DNS et de BGP dans l'Internet classique.
Une correspondance est une relation entre un préfixe d'identificateurs (rappelez-vous que les EID sont, syntaxiquement, des adresses IP ; on peut donc utiliser des préfixes CIDR) et un ensemble de localisateurs, les RLOC des différents routeurs possibles pour joindre le préfixe convoité.
Le RFC 6833 normalise une interface avec ce système de correspondance. Il y a deux sortes d'entités, le Map Server, qui connait les correspondances pour certains préfixes (car les ETR lui ont raconté les préfixes qu'ils servent), et le Map Resolver, qui fait les requêtes (il est typiquement dans l'ITR, ou proche). Quatre messages sont possibles (les messages de contrôle LISP sont encpasulés en UDP, et vers le port 4342) :
Map-Register
: un ETR informe son
Map Server des préfixes EID qu'il sait
joindre,Map-Notify
: la réponse de l'ETR au
message précédent,Map-Request
: un ITR (ou bien un
outil de débogage comme lig, cf. RFC 6835) cherche les RLOC correspondant à un EID,Map-Reply
: un Map
Server ou un ETR lui répond.Un point important de LISP est qu'il peut y avoir plusieurs mécanismes de correspondance EID->RLOC, du moment qu'ils suivent les messages standard du RFC 6833. On pourra donc, dans le cadre de l'expérience LISP, changer de mécanisme pour voir, pour tester des compromis différents. Notre RFC rappele l'existence du système ALT (RFC 6836, fondé, comme BGP sur un graphe. Mais aussi celle d'un mécanisme utilisant une base « plate » (NERD, RFC 6837), un mécanisme arborescent nommé DDT (RFC 8111), des DHT, etc. On pourrait même, dans des déploiements privés et locaux, avoir une base centralisée avec un seul serveur.
ALT, normalisé dans le RFC 6836, est le système de correspondance « historique » de LISP, et il est souvent présenté comme le seul dans les vieux documents. Il repose sur BGP, protocole bien maitrisé par les administrateurs de routeurs, ceux qui auront à déployer LISP. L'idée de base est de connecter les serveurs ALT par BGP sur un réseau virtuel au-dessus de l'Internet.
DDT, dans le RFC 8111, lui, ressemble beaucoup plus au DNS, par sa structuration arborescente des données, et sa racine.
Évidemment, tout l'Internet ne va pas migrer vers LISP instantanément. C'est pour cela que notre RFC mentionne les problèmes de communication avec le reste du monde. Les EID ne sont typiquement pas annoncés dans la table de routage globale de l'Internet. Alors, comment un site pourra-t-il communiquer avec un site LISP ? Le mécanisme décrit dans le RFC 6832 utilise deux nouvelles sortes de routeurs : les PITR (Proxy Ingress Tunnel Router) et les PETR (Proxy Egress Tunnel Router). Le PITR annonce les EID en BGP vers l'Internet, en les agrégeant le plus possible (l'un des buts de LISP étant justement d'éviter de charger la table de routage globale). Il recevra donc les paquets envoyés par les sites Internet classiques et les fera suivre par les procédures LISP normales. A priori, c'est tout : le site LISP peut toujours envoyer des paquets vers l'Internet classiques en ayant mis un EID en adresse IP source. Mais cela peut échouer pour diverse raisons (uRPF, par exemple) donc on ajoute le PETR : il recevra le paquet du site LISP et le transmettra.
Voici pour les principes de LISP. Mais, si vous travaillez au quotidien comme administrateur d'un réseau, vous avez sans doute à ce stade plein de questions concrètes et opérationnelles. C'est le moment de lire la section 4 de ce RFC. Par exemple, la gestion des caches : un routeur LISP ne peut pas faire appel au système de correspondance pour chaque paquet qu'il a à transmettre. Le sous-système des données tuerait complètement le sous-système de contrôle, si un routeur s'avisait de procéder ainsi. Il faut donc un cache, qui va stocker les réponses aux questions récentes. Qui dit cache dit problèmes de cohérence des données, puisque l'information a pu changer entre la requête, et l'utilisation d'une réponse mise en cache. Pour gérer cette cohérence, LISP dispose de divers mécanismes, notamment un TTL (Time To Live) : l'ETR le définit, indiquant combien de temps les données peuvent être utilisées (c'est typiquement 24 h, aujourd'hui).
Autre problème pratique cruciale, la joignabilité des
RLOC. C'est bien joli de savoir que telle machine a tel RLOC mais
est-ce vrai ? Peut-on réellement lui parler ou bien tous les
paquets vont-ils finir dans un trou noir ? Un premier mécanisme
pour transporter l'information de joignabilité est les LSB
(Locator Status Bits). Transportés dans les
paquets LISP, ces bits indiquent si un RLOC donné est joignable
par l'ETR qui a envoyé le paquet. Évidemment, eux aussi peuvent
être faux, donc, s'il existe une communication bi-directionnelle,
il est préférable d'utiliser le mécanisme des numniques. Quand un ITR écrit à un ETR, il
met un numnique dans le paquet, que l'ETR renverra dans son
prochain paquet. Cette fois, plus de doute, l'ETR est bien
joignable. Si l'ITR est impatient et veut une réponse tout de
suite, il peut tester activement la joignabilité, en envoyant des Map-Request
.
LISP est souvent présenté avec un modèle simplifié où chaque site est servi par un seul ETR, qui connait les EID du site et les annonce au Map Server. Mais, dans la réalité, les sites sérieux ont plusieurs ETR, pour des raisons de résilience et de répartition de charge. Cela soulève le problème de leur synchronisation : ces ETR doivent avoir des configurations compatibles, pour annoncer les mêmes RLOC pour leurs EID. Pour l'instant, il n'existe pas de protocole pour cela, on compte sur une synchronisation manuelle par l'administrateur réseaux.
Enfin, comme LISP repose sur des tunnels, il fait face à la malédiction habituelle des tunnels, les problèmes de MTU. Du moment qu'on encapsule, on diminue la MTU (les octets de l'en-tête prennent de la place) et on peut donc avoir du mal à parler avec les sites qui ont une MTU plus grande, compte-tenu de la prévalence d'erreurs grossières de configuration, comme le filtrage d'ICMP. La section 4.4 de notre RFC décrit le traitement normal de la MTU dans LISP et ajoute que des mécanismes comme celui du RFC 4821 seront peut-être nécessaires.
Dans l'Internet d'aujourd'hui, une préoccupation essentielle est bien sûr la sécurité : d'innombrables menaces pèsent sur les réseaux (section 8 du RFC). Quelles sont les problèmes spécifiques de LISP en ce domaine ? Par exemple, certains systèmes de correspondance, comme DDT, sont de type pull : on n'a pas l'information à l'avance, on va la chercher quand on en a besoin. Cela veut dire qu'un paquet de données (sous-système des données) peut indirectement déclencher un événement dans le sous-système de contrôle (par la recherche d'une correspondance EID->RLOC afin de savoir où envoyer le paquet). Cela peut affecter la sécurité.
D'autant plus que le sous-système de contrôle sera typiquement
mis en œuvre dans le processeur généraliste des routeurs, beaucoup
moins rapide que les circuits électroniques spécialisés qui
servent à la transmission des données. Un attaquant qui enverrait
des tas de paquets vers des EID différents pourrait, à un coût
très faible pour lui, déclencher plein de demandes à DDT,
ralentissant ainsi sérieusement les routeurs LISP. Une mise en œuvre naïve de LISP où toute requête pour
un EID absent du cache déclencherait systématiquement une
MAP-Request
serait très vulnérable. Une
limitation du trafic est donc
nécessaire.
En parlant du système de correspondance, il représente évidemment un talon d'Achille de LISP. Si son intégrité est compromise, si des fausses informations s'y retrouvent, les routeurs seront trahis et enverront les paquets au mauvais endroit. Et si le système de correspondance est lent ou en panne, par exemple suite à une attaque par déni de service, le routage ne se fera plus du tout (à part pour les EID encore dans les caches des routeurs). On peut donc comparer ce système de correspondance au DNS dans l'Internet classique, par son côté crucial pour la sécurité.
Il faut donc des « bons » Map Server, qui suivent bien le RFC 6833 (notamment sa section 6) et, peut-être dans le futur, des Map Servers qui gèrent l'extension de sécurité LISP-Sec (si son RFC est publié un jour).
Dernier point de sécurité, le fait que LISP puisse faire du routage asymétrique (le chemin d'Alice à Bob n'est pas le même que celui de Bob à Alice). Rien d'extraordinaire à cela, c'est pareil pour lee routage Internet classique, mais il faut toujours se rappeler que cela a des conséquences de sécurité : par exemple, un pare-feu ne verra, dans certains cas, qu'une partie du trafic.
On trouvera plus de détails sur les attaques qui peuvent frapper LISP dans le RFC 7835.
Pour ceux qui sont curieux d'histoire des technologies, l'annexe A du RFC contient un résumé de LISP. Tout avait commencé à Amsterdam en octobre 2006, à l'atelier qui avait donné naissance au RFC 4984. Un groupe de participants s'était alors formé, avait échangé, et le premier Internet-Draft sur LISP avait été publié en janvier 2007. En même temps, et dans l'esprit traditionnel de l'Internet (running code), la programmation avait commencé et les premiers routeurs ont commencé à gérer des paquets LISP en juin 2007.
Le groupe de travail IETF officiel a été ensuite créé, en mars 2009. Les premiers RFC sont enfin sortis en 2013.
LISP n'a pas toujours été comme aujourd'hui ; le protocole initial était plutôt une famille de protocoles, désignés par des numéros, chacun avec des variantes sur le concept de base. Cela permettait de satisfaire tous les goûts mais cela compliquait beaucoup le protocole. On avait LISP 1, où les EID étaient routables dans l'Internet normal (ce qui n'est plus le cas), LISP 1.5 où ce routage se faisait dans un réseau séparé, LISP 2 où les EID n'étaient plus routables, et où la correspondance EID->RLOC se faisait avec le DNS, et enfin LISP 3 où le système de correspondance était nouveau (il était prévu d'utiliser une DHT...). Le LISP final est proche de LISP 3.
Date de publication du RFC : Août 2022
Auteur(s) du RFC : D. Schinazi (Google), L. Pardue (Cloudflare)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF masque
Première rédaction de cet article le 8 septembre 2022
Ce RFC est le premier du groupe de travail MASQUE qui développe des techniques pour relayer du trafic IP. Dans ce RFC, il s'agit de faire passer des datagrammes sur HTTP.
Pourquoi, sur HTTP ? Parce que c'est le seul protocole dont on est raisonnablement sûr qu'il passera partout. En bâtissant un service de datagrammes sur HTTP (et non pas directement sur IP), on arrivera à le faire fonctionner à tous les endroits où HTTP fonctionne.
En fait, ce RFC décrit deux mécanismes assez différents, mais qui visent le même but, la transmission de datagrammes sur HTTP. Le premier mécanisme est le plus simple, utiliser HTTP juste pour lancer une session qui utilisera les datagrammes de QUIC, normalisés dans le RFC 9221. (QUIC seul ne passe pas partout, d'où la nécessité d'insérer une couche HTTP.) Ce premier mécanisme dépend de QUIC donc ne marche qu'avec HTTP/3 (RFC 9114). Un second mécanisme est normalisé, dans notre RFC ; nommé Capsule, il permet de faire passer des datagrammes (ou à peu près) sur HTTP/2 (RFC 9113) et HTTP/1 (RFC 9112). Capsule est plus général, mais moins efficace.
Ces mécanismes ne sont pas vraiment prévus pour des utilisations par des applications, mais plutôt pour des extensions à HTTP (RFC 9110, section 16). Par exemple, le service CONNECT (RFC 9110, section 9.3.6) pourrait être doublé par un service équivalent, mais utilisant des datagrammes, au lieu du transfert fiable que fait CONNECT (cf. RFC 9298). Même chose pour les Web sockets du RFC 6455.
La section 2 de notre RFC explique de quels datagrammes on parle. Il s'agit de
paquets de données dont l'ordre
d'arrivée et l'acheminement ne sont pas garantis, et qui vont sans
doute consommer moins de ressources. Sur HTTP/3 (qui utilise QUIC),
ils vont utiliser les trames QUIC de type
DATAGRAM
(RFC 9221). Ce
sont les « meilleurs » datagrammes HTTP, ceux qui collent le plus à
la sémantique des datagrammes. Sur HTTP/2, tout acheminement de
données est garanti. Si on veut faire des datagrammes, il va falloir
utiliser le protocole Capsule, décrit dans la
section 3. (Notez que HTTP/2 ne garantit pas l'ordre d'arrivée si
les datagrammes sont transportés dans des ruisseaux différents.) Et
pour HTTP/1 ? Le protocole ne convient guère puisqu'il garantit
l'ordre d'arrivée et l'acheminement, justement les propriétés
auxquelles on était prêt à renoncer. Là aussi, on se servira de
Capsule.
HTTP utilise différentes méthodes pour faire ses requêtes (les deux plus connues sont GET et POST). Les datagrammes ne sont utilisables qu'avec des méthodes qui les acceptent explicitement. Donc, pas de GET ni de POST, plutôt du CONNECT.
Sur HTTP/3, le champ de données du datagramme QUIC aura le format :
HTTP/3 Datagram { Quarter Stream ID (i), HTTP Datagram Payload (..), }
Le quarter stream ID est l'identificateur du ruisseau QUIC du client où a été envoyée la requête HTTP, divisé par 4 (ce qu'on peut toujours faire, vu la façon dont sont générés ces identificateurs, RFC 9000, section 2.1).
Ah, et comme les datagrammes ne sont pas acceptés par défaut en
HTTP/3, il faudra envoyer au début de la session un paramètre
SETTINGS_H3_DATAGRAM
(enregistré à
l'IANA).
Les capsules, maintenant. Inutiles en HTTP/3, elles sont la seule
façon de faire du datagramme en HTTP/1 et HTTP/2. Le protocole
Capsule permet d'envoyer des données encodées en
TLV sur une connexion HTTP. On indique qu'on
va l'utiliser, en HTTP/1 avec le champ Upgrade:
(RFC 9110, section 7.8) et en HTTP/2 avec un
CONNECT étendu (cf. RFC 8441). Les
upgrade tokens (les identificateurs de protocoles
utilisés, enregistrés à
l'IANA) sont décrits dans la section 16.7 du RFC 9110.
Le format des capsules est décrit en section 3.2 :
Capsule { Capsule Type (i), Capsule Length (i), Capsule Value (..), }
Les différents types possibles de capsules figurent dans un registre IANA. Une fois le protocole Capsule sélectionné via un upgrade token, on passe du HTTP classique au protocole Capsule. Un premier type de capsule est DATAGRAM (type 0) dont le nom indique bien la sémantique. D'autres types pourront être ajoutés en suivant la procédure « spécification nécessaire » du RFC 8126, sauf les valeurs basses du type (de 0 à 63) qui exigent une action de normalisation, ou bien une approbation par l'IESG.
Si on utilise Capsule, la requête HTTP est accompagnée du champ
Capsule-Protocol
(un champ structuré, cf. RFC 9651), champ désormais enregistré
à l'IANA.
Il existe plusieurs mises en œuvre de ces datagramms HTTP. En logiciel libre, on a :
Notez que le protocole Capsule n'est a priori pas accessible au code JavaScript chargé dans le navigateur Web (il faut pouvoir accéder aux upgrade tokens). Mais rappelez-vous que tout ce mécanisme de datagrammes sur HTTP est conçu pour des extensions de HTTP, pas pour l'application finale.
Date de publication du RFC : Août 2022
Auteur(s) du RFC : W. Eddy (MTI Systems)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 20 août 2022
Le protocole de transport TCP est l'un des piliers de l'Internet. La suite des protocoles Internet est d'ailleurs souvent appelée TCP/IP, du nom de ses deux principaux protocoles. Ce nouveau RFC est la norme technique de TCP, remplaçant l'antique RFC 793, qui était vieux de plus de quarante ans, plus vieux que la plupart des lecteurices de ce blog. Il était temps de réviser et de rassembler en un seul endroit tous les détails sur TCP.
TCP est donc un protocole de transport. Rappelons brièvement ce qu'est un protocole de transport et à quoi il sert. L'Internet achemine des paquets IP de machine en machine. IP ne garantit pas leur arrivée : les paquets peuvent être perdus (par exemple parce qu'un routeur n'avait plus de place dans ses files d'attente), peuvent être dupliqués, un paquet peut passer devant un autre, pourtant envoyé avant, etc. Pour la grande majorité des applications, ce n'est pas acceptable. La couche de transport est donc chargée de remédier à cela. Première (en partant du bas) couche à être de bout en bout (les routeurs et autres équipements intermédiaires n'y participent pas, ou plus exactement ne devraient pas y participer), elle se charge de suivre les paquets, de les remettre dans l'ordre, et de demander aux émetteurs de ré-émettre si un paquet s'est perdu. C'est donc un rôle crucial, puisqu'elle évite aux applications de devoir gérer ces opérations très complexes. (Certaines applications, par exemple de vidéo, n'ont pas forcément besoin que chaque paquet arrive, et n'utilisent pas TCP.) Le RFC 8095 contient une comparaison détaillée des protocoles de transport de l'IETF.
Avant de décrire TCP, tel que spécifié dans ce RFC 9293, un petit mot sur les normes techniques de l'Internet. TCP avait originellement été normalisé dans le RFC 761 en 1980 (les couches 3 - IP et 4 étaient autrefois mêlées en une seule). Comme souvent sur l'Internet, le protocole existait déjà avant sa normalisation et était utilisé. La norme avait été rapidement révisée dans le RFC 793 en 1981. Depuis, il n'y avait pas eu de révision générale, et les RFC s'étaient accumulés. Comprendre TCP nécessitait donc de lire le RFC 793 puis d'enchainer sur plusieurs autres. Un RFC, le RFC 7414, avait même été écrit dans le seul but de fournir un guide de tous ces RFC à lire. Et il fallait également tenir compte de l'accumulation d'errata. Désormais, la situation est bien plus simple, tout TCP a été consolidé dans un RFC central, notre RFC 9293. (Notez que ce travail de consolidation a été aussi fait pour HTTP et SMTP, qui ne sont plus décrits par les RFC d'origine mais par des versions à jour, mais pas pour le DNS, qui reste le principal ancien protocole qui aurait bien besoin d'une remise à jour complète des documents qui le spécifient.) Ne vous faites toutefois pas d'illusion : malgré ses 114 pages, ce RFC 9293 ne couvre pas tout, et la lecture d'autres RFC reste nécessaire. Notamment, TCP offre parfois des choix multiples (par exemple pour les algorithmes de contrôle de la congestion, une partie cruciale de TCP), qui ne sont pas décrits intégralement dans la norme de base. Voyez par exemple le RFC 7323, qui n'a pas été intégré dans le RFC de base, et reste une extension optionnelle.
Attaquons-nous maintenant à TCP. (Bien sûr, ce sera une présentation très sommaire, TCP est forcément plus compliqué que cela.) Son rôle est de fournir aux applications un flux d'octets fiable et ordonné : les octets que j'envoie à une autre machine arriveront dans l'ordre et arriveront tous (ou bien TCP signalera que la communication est impossible, par exemple en cas de coupure du câble). TCP découpe les données en segments, chacun voyageant dans un datagramme IP. Chaque octet est numéroté (attention : ce sont bien les octets et pas les segments qui sont numérotés) et cela permet de remettre dans l'ordre les paquets, et de détecter les pertes. En cas de perte, on retransmet. Le flux d'octets est bidirectionnel, la même connexion TCP permet de transmettre dans les deux sens. TCP nécessite l'établissement d'une connexion, donc la mémorisation d'un état, par les deux pairs qui communiquent. Outre les tâches de fiabilisation des données (remettre les octets dans l'ordre, détecter et récupérer les pertes), TCP fournit du démultiplexage, grâce aux numéros de port. Ils permettent d'avoir plusieurs connexions TCP entre deux machines, qui sont distinguées par ces numéros de port (un pour la source et un pour la destination) dans l'en-tête TCP. Le bon fonctionnement de TCP nécessite d'édicter un certain nombre de règles et le RFC les indique avec MUST-n où N est un entier. Par exemple, MUST-2 et MUST-3 indiqueront que la somme de contrôle est obligatoire, aussi bien en envoi qu'en vérification à la réception.
Après ces concepts généraux, la section 3 de notre RFC rentre dans les détails concrets, que je résume ici. D'abord, le format de cet en-tête que TCP ajoute derrière l'en-tête IP et devant les données du segment. Il inclut les numéros de port, le numéro de séquence (le rang du premier octet dans les données), celui de l'accusé de réception (le rang du prochain octet attendu), un certain nombre de booléens (flags, ou bits de contrôle, qui indiquent, par exemple, s'il s'agit d'une connexion déjà établie, ou pas encore, ou la fin d'une connexion), la taille de la fenêtre (combien d'octets peuvent être envoyés sans accusé de réception, ce qui permet d'éviter de surcharger le récepteur), une somme de contrôle, et des options, de taille variable (l'en-tête contient un champ indiquant à partir de quand commencent les données). Une option de base est MSS (Maximum Segment Size) qui indique quelle taille de segment on peut gérer. Il existe d'autres options comme les accusés de réception sélectifs du RFC 2018 ou comme le facteur multiplicatif de la taille de fenêtre du RFC 7323.
Avant d'expliquer la dynamique de TCP, le RFC définit quelques
variables indispensables, qui font partie de l'état de la connexion
TCP. Pour l'envoi de données, ce sont par exemple
SND.UNA
(ces noms sont là pour comprendre le
RFC, mais un programme qui met en œuvre TCP peut évidemment nommer
ces variables comme il veut), qui désigne le numéro de séquence du
début des données envoyées, mais qui n'ont pas encore fait l'objet
d'un accusé de réception, ou SND.NXT
, le numéro
de séquence du début des données pas encore envoyées ou encore
SND.WND
, la taille de la fenêtre
d'envoi. Ainsi, on ne peut pas envoyer d'octets dont le rang serait
supérieur à SND.UNA
+
SND.WND
, il faut attendre des accusés de
réception (qui vont incrémenter SND.UNA
) ou une
augmentation de la taille de la fenêtre. Pour la réception, on a
RCV.NXT
, le numéro de séquence des prochains
paquets si tout est normal, et RCV.WND
, la
taille de la fenêtre de réception.
TCP est un protocole à état et il a donc une machine à
états, décrite en section 3.3.2. Parmi les états,
LISTEN
, qui indique un TCP en train d'attendre
un éventuel pair, ESTAB
(ou ESTABLISHED
), où TCP envoie et reçoit des
données, et plusieurs états qui apparaissent lors de l'ouverture ou
de la fermeture d'une connexion.
Et les numéros de séquence ? Un point important de TCP est qu'on ne numérote pas les segments mais les octets. Chaque octet a son numéro et les accusés de réception référencent ces numéros. On n'accuse évidemment pas réception de chaque octet individuel. Les accusés de réception sont cumulatifs ; ils désignent un octet et, implicitement, tous les octets précédents. Quand un récepteur prévient qu'il a bien reçu l'octet N, cela veut dire que tous ceux avant N ont aussi été reçus.
Les numéros ne partent pas de 1 (ni de zéro), ils sont relatifs à un ISN (Initial Sequence Number, rappelez-vous qu'il y a un glossaire, en section 4 du RFC) qui est choisi aléatoirement (pour des raisons de sécurité, cf. RFC 6528) au démarrage de la session. Des logiciels comme Wireshark savent cela et peuvent calculer puis afficher des numéros de séquence qui partent de 1, pour faciliter la lecture.
Les numéros de séquence sont stockés sur 32 bits et il n'y a donc que quatre milliards (et quelques) valeurs possibles. C'était énorme quand TCP a été créé mais cela devient de plus en plus petit, relativement aux capacités des réseaux modernes. Comme TCP repart de zéro quand il atteint le numéro de séquence maximum, il y a un risque qu'un « vieux » paquet soit considéré comme récent. À 1 Gb/s, cela ne pouvait se produire qu'au bout de 34 secondes. Mais à 100 Gb/s, il suffit d'un tiers de seconde ! Les extensions du RFC 7323 deviennent alors indispensables.
Tout le monde sait bien que TCP est un protocole orienté connexion. Cela veut dire qu'il faut pouvoir créer, puis supprimer, les connexions. La section 3.5 de notre RFC décrit cet établissement de connexion, via la fameuse « triple poignée de mains ». En gros, un des TCP (TCP n'est pas client-serveur, et la triple poignée de mains marche également quand les deux machines démarrent la connexion presque en même temps) envoie un paquet ayant l'option SYN (pour synchronization), le second répond avec un paquet ayant les options ACK (accusé de réception du SYN) et SYN, le premier TCP accuse réception. Avec trois paquets, les deux machines sont désormais d'accord qu'elles sont connectées. En d'autres termes (ici, machine A commence) :
Pour mettre fin à la connexion, un des deux TCP envoie un paquet ayant l'option FIN (pour finish), auquel l'autre répond de même.
Conceptuellement, TCP gère un flux d'octets sans séparation. TCP
n'a pas la notion de message. Si une application envoie (en appelant
print
, write
ou autre
fonction du langage de programmation utilisé) les octets "AB" puis
"CD", TCP transmettra puis livrera à l'application distante les
quatre octets "ABCD" dans l'ordre, sans indiquer qu'il y avait eu
deux opérations d'écriture sur le réseau. Mais, pour envoyer les
données, TCP doit les segmenter en paquets IP distincts. La
fragmentation IP n'étant pas bonne pour les
performances, et étant peu fiable de toute façon, l'idéal est que
ces paquets IP aient une taille juste inférieure à la MTU du chemin. Cela
nécessite de découvrir cette MTU (section 3.7.2), ou bien d'adopter
la solution paresseuse de faire des paquets de 1 280 octets (la MTU
minimum, en IPv6). Comme la solution paresseuse ne serait pas
optimale en performances, les mises en œuvre de TCP essaient toutes
de découvrir la MTU du chemin. Cela peut se faire avec la méthode
PMTUD, décrite dans les RFC 1191 et RFC 8201, mais qui a l'inconvénient de nécessiter
qu'ICMP ne soit pas bloqué. Or, beaucoup de
middleboxes programmées
avec les pieds, ou configurées par des incompétents, bloquent ICMP,
empêchant le fonctionnement de PMTUD. Une alternative est PLPMTUD,
normalisée dans le RFC 4821, qui ne dépend
plus d'ICMP.
Une fois la connexion établie, on peut envoyer des données. TCP va les couper en segments, chaque segment voyageant dans un paquet IP.
Une mise en œuvre de TCP
n'envoie pas forcément immédiatement les octets qu'on lui a
confiés. Envoyer des petits paquets n'est pas efficace : s'il y a
peu de données, les en-têtes du paquet forment la majorité du trafic
et, de toute façon, le goulet d'étranglement dans le réseau n'est
pas forcément lié au nombre d'octets, il peut l'être au nombre de
paquets. Un des moyens de diminuer le nombre de paquets contenant
peu d'octets est l'algorithme de Nagle. Le
principe est simple : quand TCP reçoit un petit nombre d'octets à
envoyer, il ne les transmet pas tout de suite mais attend un peu
pour voir si l'application ne va pas lui confier des octets
supplémentaires. Décrit dans le RFC 896, et
recommandé dans le RFC 1122, cet algorithme
est aujourd'hui présent dans la plupart des mises en œuvres de
TCP. Notre RFC 9293 le recommande, mais en ajoutant que les
applications doivent pouvoir le débrayer. C'est notamment
indispensable pour les applications interactives, qui n'aiment pas
les délais, même courts. (Sur Unix, cela se
fait avec l'option TCP_NODELAY
passée à
setsockopt.)
Un des rôles les plus importants de TCP est de lutter contre la congestion. Il ne faut pas envoyer le plus d'octets possible, ou alors on risque d'écrouler le réseau (RFC 2914). Plusieurs mécanismes doivent être déployés : démarrer doucement (au début de la connexion, TCP ne connait pas le débit que peut supporter le réseau et doit donc être prudent), se calmer rapidement si on constate que des paquets sont perdus (cela peut être un signe de congestion en aval), etc. Ces mesures pour éviter la congestion sont absolument obligatoires (comme tous les biens communs, l'Internet est vulnérables aux parasites, un TCP égoïste qui ne déploierait pas ces mesures anti-congestion serait un mauvais citoyen du réseau). Elles sont décrites plus précisément dans les RFC 1122, RFC 2581, RFC 5681 et RFC 6298. Notez qu'ils énoncent des principes, mais pas forcément les algorithmes exacts à utiliser, ceux-ci pouvant évoluer (et ils l'ont fait depuis la sortie du RFC 793, voir les RFC 5033 et RFC 8961).
Un point qui surprend souvent les programmeur·ses qui écrivent
des applications utilisant TCP est que rien n'indique qu'une
connexion TCP est coupée, par exemple si un câble est
sectionné. C'est seulement lorsqu'on essaie d'écrire qu'on
l'apprendra (ce comportement est une conséquence de la nature sans
connexion d'IP ; pourquoi prévenir d'une coupure alors qu'IP va
peut-être trouver un autre chemin ?). Si on veut être mis au courant
tout de suite, on peut utiliser des keep-alives,
des segments vides qui seront envoyés de temps en temps juste pour
voir s'ils arrivent. Cette pratique est contestée (entre autre parce
qu'elle peut mener à abandonner une connexion qui n'est que
temporairement coupée, et aussi parce que, même si les
keep-alives passent, cela ne garantit pas que la
prochaine écriture de données passera). Notre RFC demande donc que
les keep-alives soient désactivés par défaut, et
que l'application puisse contrôler leur activation (sur Unix, c'est
l'option SO_KEEPALIVE
à utiliser avec
setsockopt).
Pendant les quarante ans écoulés depuis la sortie du RFC 793, bien des choses ont changé. Des options ont été ajoutées mais on a vu aussi certaines options qui semblaient être des bonnes idées être finalement abandonnées. C'est le cas du mécanisme de données urgentes, qui n'a jamais vraiment marché comme espéré. Une mise en œuvre actuelle de TCP doit toujours le gérer, pour pouvoir parler avec les anciennes, mais on ne peut pas compter dessus (RFC 6093).
On a parlé plusieurs fois de l'utilisation de TCP par les applications. Pour cela, il faut que TCP offre une API à ces applications. Notre RFC ne décrit pas d'API concrète (cela dépend de toute façon du langage de programmation, et sans doute du système d'exploitation). Il se contente d'une description fonctionnelle de l'interface : les services qu'elle doit offrir à l'application. On y trouve :
write
a écrit et seulement cela.Rappelez-vous que l'API « socket » n'est qu'un des moyens pour l'application de parler à TCP. Et, à propos de cette API, notez que le RFC utilise le terme de socket avec un sens très différent (cf. le glossaire en section 4).
Et l'autre interface, « sous » TCP, l'interface avec la couche réseau (section 3.9.2) ? TCP peut fonctionner avec des couches réseau variées mais, en pratique, c'est toujours IP, dans une de ses deux versions, v4 ou v6. Un des points délicats est le traitement des messages ICMP que TCP peut recevoir. Au minimum, ils doivent être assignés à une connexion existante. Mais cela ne veut pas dire qu'il faut systématiquement agir lors de la réception d'un de ces messages (RFC 5461). Par exemple, il faut ignorer les messages ICMP de répression de la source (RFC 6633). Et il ne faut pas fermer la connexion juste parce qu'on a reçu une erreur ICMP (MUST-56), car elles peuvent être temporaires.
TCP étant un protocole orienté connexion, la connexion va avoir
un état, représenté par un automate
fini. Ainsi, l'ouverture d'une connexion par le premier
TCP va le faire passer
par les états CLOSED, SYN-SENT puis enfin ESTABLISHED. Récupérée
sur
Wikimedia Commons, voici une représentation de cet automate :
La section 5 de notre RFC décrit les changements depuis le RFC 793. TCP reste le même et des TCP d'« avant » peuvent interopérer avec ceux qui suivent notre RFC récent. Les principaux changements sont :
Le RFC 793 avait été écrit par Jon Postel (comme tant de RFC de cette époque), et notre nouveau RFC 9293 lui rend hommage, en estimant que la longue durée de vie du RFC 793 montre sa qualité.
En application des changements de ce nouveau RFC sur TCP, un registre IANA, celui des bits de l'en-tête comme URG (dont on a vu qu'il était désormais démobilisé), a été mis à jour.
La section 7 de notre nouveau RFC 9293 n'avait pas d'équivalent dans l'ancien RFC 793. Elle concerne la sécurité de TCP. TCP, par lui-même, fournit peu de sécurité (il protège quand même un peu contre l'usurpation d'adresse IP et l'injection de trafic, surtout contre un attaquant qui n'est pas sur le chemin, cf. RFC 5961). Par exemple, il ne fournit pas de services cryptographiques, donc pas de confidentialité ou de vraie garantie d'intégrité. (Son concurrent QUIC, lui, intègre systématiquement la cryptographie.) Si on veut davantage de sécurité pour TCP, il faut mettre IPsec en dessous de TCP ou bien TLS au-dessus. TCP a aussi des mécanismes de sécurité qui lui sont propres, comme AO, normalisé dans le RFC 5925 (mais très peu déployé en pratique). Autre expérience qui n'a pas été un succès, tcpcrypt (RFC 8548).
Il reste des attaques contre lesquelles la cryptographie ne fournit pas vraiment de solution. C'est le cas du SYN flooding, ou d'autres attaques par déni de service.
Mëme si on chiffre les données applicatives avec TLS, TCP expose quand même au réseau tout son fonctionnement. Cette vue depuis le réseau (RFC 8546) soulève plusieurs problèmes (elle peut par exemple faciliter des actions contraires à la neutralité du réseau) et c'est pour cela que le plus récent QUIC masque une grande partie de son fonctionnement (ce qui n'a pas été sans entrainer des polémiques). Toujours côté vie privée, le comportement des mises en œuvre de TCP est suffisamment différent pour qu'il soit possible dans certains cas d'identifier le système d'exploitation d'une machine à distance (ce n'est pas forcément grave, mais il faut le savoir).
Pour les programmeurs et programmeuses, l'annexe A du RFC contient différentes observations utiles pour la mise en œuvre concrète de TCP. Par exemple, cette annexe explique que la validation des numéros de séquence des segments TCP entrants, validation qui est nécessaire pour empêcher un grand nombre d'attaques, peut rejeter des segments légitimes. Ce problème n'a pas de solution idéale.
Un autre point intéressant concerne l'algorithme de
Nagle. Cet algorithme représente un compromis entre la
réactivité de l'application (qui nécessite d'envoyer les données le
plus vite possibles) et l'optimisation de l'occupation du réseau
(qui justifie d'attendre un peu pour voir si on ne va pas recevoir
de nouvelles données à envoyer). Une modification de l'algorithme de
Nagle, décrite dans le document
draft-minshall-nagle
peut rendre le choix moins
douloureux.
Enfin, l'annexe B résume sous forme d'un tableau quelles sont les parties de la norme TCP qui doivent être mises en œuvre et quelles sont celles qu'on peut remettre à plus tard.
Si vous voulez sérieusement apprendre TCP, vous pouvez bien sûr lire le RFC en entier, mais il est sans doute plus sage de commencer par un bon livre comme le CNP3.
Un pcap d'une connexion TCP typique est
utilisé ici pour illustrer le fonctionnement de TCP (fichier tcp-typique.pcap
). Vu par tshark, cela donne :
8.445772 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 94 33672 → 443 [SYN] Seq=0 Win=64800 Len=0 MSS=1440 SACK_PERM=1 TSval=285818465 TSecr=0 WS=128 8.445856 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TCP 94 443 → 33672 [SYN, ACK] Seq=0 Ack=1 Win=64260 Len=0 MSS=1440 SACK_PERM=1 TSval=3778761402 TSecr=285818465 WS=128 8.534636 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [ACK] Seq=1 Ack=1 Win=64896 Len=0 TSval=285818554 TSecr=3778761402 8.534897 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 SSLv3 236 Client Hello 8.534989 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TCP 86 443 → 33672 [ACK] Seq=1 Ack=151 Win=64128 Len=0 TSval=3778761491 TSecr=285818554 8.548101 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TLSv1.2 1514 Server Hello 8.548111 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TCP 1514 443 → 33672 [PSH, ACK] Seq=1429 Ack=151 Win=64128 Len=1428 TSval=3778761504 TSecr=285818554 [TCP segment of a reassembled PDU] 8.548265 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TLSv1.2 887 Certificate, Server Key Exchange, Server Hello Done 8.636922 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [ACK] Seq=151 Ack=2857 Win=63616 Len=0 TSval=285818656 TSecr=3778761504 8.636922 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [ACK] Seq=151 Ack=3658 Win=62848 Len=0 TSval=285818656 TSecr=3778761504 8.649899 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [FIN, ACK] Seq=151 Ack=3658 Win=64128 Len=0 TSval=285818669 TSecr=3778761504 8.650160 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TCP 86 443 → 33672 [FIN, ACK] Seq=3658 Ack=152 Win=64128 Len=0 TSval=3778761606 TSecr=285818669 8.739117 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [ACK] Seq=152 Ack=3659 Win=64128 Len=0 TSval=285818758 TSecr=3778761606
Ici, 2a01:4f8:c010:7eb7::106
(une sonde RIPE Atlas) veut faire du
HTTPS (donc, port 443)
avec le serveur 2605:4500:2:245b::42
. Tout
commence par la triple poignée de mains (les trois premiers paquets,
SYN, SYN+ACK, ACK). Une fois la connexion établie, tshark reconnait
que les machines font du TLS mais, pour TCP, ce ne sont que des données
applicatives (contrairement à QUIC, TCP sépare connexion et
chiffrement). On note qu'il n'y a pas eu
d'optimisation des accusés de réception. Par exemple, l'accusé de
réception du ClientHello TLS voyage dans un paquet séparé, alors
qu'il aurait pu être inclus dans la réponse applicative (le
ServerHello), probablement parce que celle-ci a mis un peu trop de
temps à être envoyée. À la fin de la connexion, chaque machine
demande à terminer (FIN). Une analyse automatique plus complète, avec
tshark -V
figure dans tcp-typique.txt
.
Ah, au fait, si vous avez pu lire cet article, c'est certainement grâce à TCP… (Ce blog ne fait pas encore de HTTP sur QUIC…).
Date de publication du RFC : Août 2022
Auteur(s) du RFC : M. Thomson (Mozilla)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF quic
Première rédaction de cet article le 30 août 2022
Dans un monde idéal, nos flux de données sur l'Internet passeraient sans problème et sans être maltraitées par les éléments intermédiaires du réseau. Mais dans le monde réel, de nombreuses middleboxes examinent le trafic et interfèrent avec lui. Les machines émettrices de trafic doivent donc se protéger et une des techniques les plus courantes est le chiffrement : si la middlebox ne peut pas comprendre ce qui est envoyé, elle ne pourra pas interférer. Le protocole de transport QUIC chiffre donc le plus possible. Mais une partie du trafic reste non chiffrée. Une des techniques pour empêcher que les middleboxes ne le lisent et ne prennent des décisions déplorables fondées sur ces informations transmises en clair est le graissage : on fait varier les données le plus possible. Ce RFC normalise une possibilité de graisser un bit de l'en-tête QUIC qui était fixe auparavant.
L'un des principes de conception de QUIC est de diminuer le plus possible la vue depuis le réseau (RFC 8546) ; les éléments intermédiaires, comme les routeurs ne doivent avoir accès qu'à ce qui leur est strictement nécessaire, le reste étant chiffré, pour leur éviter toute tentation. QUIC définit (dans le RFC 8999) des invariants, des informations qui seront toujours vraies même pour les futures versions de QUIC (le RFC 9000 définit QUIC version 1, pour l'instant la seule version). En dehors de ces invariants, tout peut… varier et les équipements intermédiaires du réseau ne doivent pas en tenir compte. Si tous suivaient ce principe, on préserverait la neutralité du réseau, et on éviterait l'ossification (cf. RFC 9170). Mais, en pratique, on sait que ce n'est pas le cas et que toute information visible depuis le réseau sera utilisée par les middleboxes, ce qui peut rendre difficile les évolutions futures (TLS a par exemple beaucoup souffert de ce problème, d'où le RFC 8701, le premier à avoir formalisé ce concept de graissage).
Or, QUIC a un bit qui n'est pas cité dans les invariants du RFC 8999 mais qui est fixe et peur servir à différencier QUIC d'autres protocoles. On l'appele d'ailleurs souvent le « bit QUIC » (même si ça n'est pas son nom officiel, qui est fixed bit, cf. RFC 9000, sections 17.2 et 17.3). Si les middleboxes s'en servent pour appliquer un traitement particulier à QUIC, les futures versions de QUIC ne pourront pas utiliser ce bit pour autre chose (rappelez-vous qu'il ne fait pas partie des invariants).
Notre RFC
ajoute donc à QUIC une option pour graisser ce bit, c'est-à-dire
pour le faire varier au hasard, décourageant ainsi les
middleboxes d'en tenir compte. Un nouveau
paramètre de transport QUIC (ces paramètres sont définis dans le
RFC 9000, section 7.4) est créé,
grease_quic_bit
(et ajouté au registre
IANA). Lorsqu'il est annoncé à l'ouverture de la connexion
QUIC, il indique que ce bit est ignoré et que le pair à tout intérêt
à le faire varier.
Les paquets initiaux de la connexion
(Initial
et Handshake
)
doivent garder le bit QUIC à 1 (puisqu'on n'a pas encore les
paramètres de la connexion), sauf si on reprend une ancienne
connexion (en envoyant un jeton, cf. RFC 9000,
section 19.7).
Tout le but de ce graissage est de permettre aux futures versions
de QUIC (v2, 3, etc) d'utiliser ce bit qui était originellement
fixe. Ces futures versions, ou tout simplement des extensions à QUIC
négociées au démarrage de la connexion, pourront donc utiliser ce
bit (lui donner une sémantique). Le RFC leur recommande d'annoncer
quand même le paramètre grease_quic_bit
, ce qui
permettra de graisser même si la future extension n'est pas gérée
par le partenaire.
Le RFC note enfin que ce graissage rend plus difficile d'identifier les flux de données QUIC au milieu de tout le trafic. C'est bien sûr le but, mais cela peut compliquer certains activités d'administration réseau. Un futur RFC explique plus en détail cette situation.
Apparemment, plusieurs mises en œuvre de QUIC gèrent déjà ce graissage.
Date de publication du RFC : Août 2022
Auteur(s) du RFC : P. Faltstrom (Netnod), F. Ljunggren (Kirei), D. van Gulik (Webweaving)
Pour information
Première rédaction de cet article le 10 août 2022
Ce RFC décrit un encodage en texte de données binaires, l'encodage Base45. Proche conceptuellement d'encodages comme Base64, il est, par exemple, beaucoup utilisé pour le code QR.
Ces codes QR ne permettent pas de stocker directement des données binaires quelconques car le contenu sera toujours interprété comme du texte. Il est donc nécessaire d'encoder et c'est le rôle de Base45. Il existe bien sûr d'innombrables autres mécanismes d'encodage en texte, comme Base16, Base32 et Base64, spécifiés dans le RFC 4648 mais Base45 est plus efficace pour les codes QR, qui réencodent ensuite.
Base45 utilise un sous-ensemble d'ASCII, de 45 caractères (d'où son nom). Deux octets du contenu binaire sont encodés en trois caractères de ce sous-ensemble. Par exemple, deux octets nuls donneront la chaine de caractères "000". Regardons tout de suite avec le module Python base45 :
% pip3 install base45 % python3 >>> import base45 >>> base45.b45encode(b'Bonjour, tout le monde') b'.H86/D34ENJES4434ESUETVDL44-3E6VC' >>>
Et décodons avec le module Elixir base45 :
% iex -S mix iex(1)> Base45.decode(".H86/D34ENJES4434ESUETVDL44-3E6VC") "Bonjour, tout le monde"
C'est parfait, tout le monde est d'accord. Ici, évidemment, la chaine originale n'avait pas vraiment besoin d'être encodée donc on va essayer avec du binaire, les trois octets 42, 1 et 6, encodage en Elixir, décodage en Python :
iex(1)> Base45.encode(<<42, 1, 6>>) "/D560" >>> base45.b45decode("/D560") b'*\x01\x06'
Encore une fois, tout va bien, Elixir a encodé les trois octets du binaire en quatre caractères, que Python a su décoder (l'astérisque est affiché car son code ASCII est 42).
Je vous laisse découvrir l'algorithme complet (il est assez simple) dans la section 3 du RFC. Si vous le programmez vous-même (ce qui n'est sans doute pas une bonne idée, il existe déjà de nombreuses mises en œuvre), attention aux cas limites comme un binaire d'un seul octet, ou comme une chaine à décoder qui compte des caractères invalides. Ici, un essai avec un caractère invalide, le signe égal :
% python3 >>> import base45 >>> base45.b45decode("AAAA=") Traceback (most recent call last): File "/home/stephane/.local/lib/python3.8/site-packages/base45/__init__.py", line 30, in b45decode buf = [BASE45_DICT[c] for c in s.rstrip("\n")] File "/home/stephane/.local/lib/python3.8/site-packages/base45/__init__.py", line 30, in <listcomp> buf = [BASE45_DICT[c] for c in s.rstrip("\n")] KeyError: '=' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/stephane/.local/lib/python3.8/site-packages/base45/__init__.py", line 54, in b45decode raise ValueError("Invalid base45 string") ValueError: Invalid base45 string
Pensez à tester votre code avec de nombreux cas variés. Le RFC cite
l'exemple des chaines "FGW" (qui se décode en une paire d'octets
valant chacun 255) et "GGW" qui, quoique proche de la précédente et
ne comportant que des caractères de l'encodage Base45 est néanmoins
invalide (testez-la avec votre décodeur). Le code de test dans le module Elixir
donne des idées de tests utiles (certains viennent de bogues
détectées pendant le développement). Regardez
test/base45_test.exs
.
Et, comme toujours, lorsque vous recevez des données venues de l'extérieur, soyez paranoïaques dans le décodage ! Un code QR peut être malveillant et chercher à activer une bogue dans le décodeur (section 6 du RFC).
Enfin, le RFC rappelle qu'une chaine encodée n'est pas forcément directement utilisable comme URL, elle peut comporter des caractères qui ne sont pas sûrs, il faut donc encore une étape d'encodage si on veut en faire un URL.
Compte tenu de l'importance des codes QR, on trouve des mises en œuvre de Base45 un peu partout. J'ai par exemple cité celle en Python. Si vous programmez en Elixir, il y a une bibliothèque sur Hex (écrite en Erlang) mais aussi ma bibliothèque (aux performances très basses).
Sinon, si vous cherchez un bon article d'explication sur Base45, je recommande celui-ci.
Date de publication du RFC : Août 2022
Auteur(s) du RFC : W. Hardaker (USC/ISI), V. Dukhovni
(Bloomberg)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 26 août 2024
Si vous êtes responsable d'une zone DNS, et que vous la testez régulièrement avec des outils comme Zonemaster ou DNSviz (ce que font tous les responsables sérieux), vous avez peut-être eu des avertissements comme quoi vos « paramètres NSEC3 » n'étaient pas ceux conseillés. C'est parce que les recommandations en ce sens ont changé avec ce RFC. Lisez-le donc si vous voulez comprendre les recommandations actuelles.
D'abord, un peu de contexte. Ce RFC concerne les zones qui sont
signées avec DNSSEC
et qui utilisent les enregistrements NSEC3 du
RFC 5155. Aujourd'hui, par exemple, c'est le
cas de .fr
,
.com
mais aussi de
bortzmeyer.org
grâce à qui vous êtes arrivés
sur cet article. Mais ce n'est pas le cas de la racine des
noms de domaine, qui
utilise NSEC (RFC 4035). Pour comprendre la
dfifférence entre les deux, je vous renvoie à mon article sur le
RFC 5155.
Un exemple où Zonemaster proteste, sur
icann.org
:
Ce RFC 5155 donnait des conseils de sécurité cryptographiques qui, avec le recul et l'expérience se sont avérés sous-optimaux. Ce nouveau RFC 9276 les modifie donc et suggère fortement de ne plus utiliser de sel, ni d'itérations successives, dans le calcul des condensats pour NSEC3.
Lorsqu'une zone est signée avec utilisation de NSEC3, elle comprend un enregistrement de type NSEC3PARAM qui indique quatre choses :
.fr
ou
.com
utilisent NSEC3,
même s'il n'y a pas de problème avec l'énumération des noms
(.fr
distribue la
liste). (Notez que si l'option est à 0 dans le NSEC3PARAM,
cela ne signifie pas qu'il n'y a pas d'opt-out,
celui-ci est typiquement indiqué uniquement dans les
enregistrements NSEC3.)
Voici par exemple l'enregistrement de icann.org
en août 2024 :
% dig +short icann.org NSEC3PARAM 1 0 5 A4196F45E2097176
Utilisation de SHA-1 (le 1 est le code de SHA-1), pas
d'opt-out (mais prudence, son utilisation n'est
pas obligatoirement signalée dans les options, voir plus haut), cinq
itérations supplémentaires (donc six au total) et un sel apparemment
aléatoire, A4196F45E2097176
.
La première recommandation du RFC concerne le nombre
d'itérations. Comme le sel, le but est de rendre plus difficile
l'utilisation de tables calculées à l'avance par un attaquant. Sans
sel et avec une seule itération, un attaquant qui a à l'avance
calculé tout un dictionnaire et sait donc que le condensat de
foobar
est
8843d7f92416211de9ebb963ff4ce28125932878
pourra
donc facilement inverser le condensat dans un enregistrement
NSEC3. C'est pour cela que le RFC 5155
recommandait un nombre variable d'itérations, indiqué par
l'enregistrement NSEC3PARAM. Mais, en pratique, la protection contre
l'énumération n'est pas si solide que ça. Bien des noms peuvent être
devinés (www
étant le plus évident mais il y a
aussi les mots d'un dictionnaire de la langue), d'autant plus qu'on
choisit en général un nom de domaine pour être simple
et facilement mémorisable. Et que ces noms se retrouvent à
plein d'endroits comme les journaux Certificate
Transparency (RFC 9162). L'opinion
d'aujourd'hui est que le jeu (la protection contre l'énumération)
n'en vaut pas la chandelle (le coût de signature
et de validation). Notez aussi une externalité négative : les résolveurs aussi devront effectuer ces
itérations et sont donc concernés. Bon, en prime, les techniques
modernes rendent la protection peu efficace de toute façon
(cf. « GPU-Based NSEC3 Hash
Breaking »). La recommandation du RFC
est donc de ne pas avoir d'itérations supplémentaires, donc de
mettre ce nombre à zéro.
Et la deuxième recommandation concerne le sel. Il y a dans NSEC3 un sel implicite, c'est le nom de domaine (RFC 5155, section 5). D'ailleurs, mon exemple de condensat de foobar était faux, puisque j'avais omis cette étape. Si on l'inclut, le sel supplémentaire indiqué dans l'enregistrement NSEC3PARAM perd de son intérêt. En outre, en pratique, on change rarement le sel (cela nécessite de modifier toute la chaine NSEC3) ce qui diminue la protection qu'il offre. La recommandation actuelle est donc de ne pas utiliser de sel (ce qui se note avec un tiret, pas avec une chaine vide).
Si on suit les recommandations du RFC, le NSEC3PARAM aura cette allure :
% dig +short fr NSEC3PARAM 1 0 0 -
Et un des NSEC3 sera du genre :
% dig nexistesurementpas.fr qu7kmgn3e….fr. 594 IN NSEC3 1 1 0 - ( QU7MMK1… NS DS RRSIG )
Notez aussi que le RFC recommande (section 3), avant de réfléchir
aux paramètres de NSEC3, de réfléchir à NSEC3 lui-même. Sur une
grosse zone de délégation, changeant souvent, comme
.fr
, NSEC3 est tout à fait justifié en raison
des avantages de l'opt-out. Mais sur la zone DNS
typique d'une petite organisation, qui ne compte souvent que des
noms prévisibles (l'apex, www
et
mail
), NSEC3 peut avantageusement être remplacé
par NSEC, qui consomme moins de ressources. (NSEC3, ou d'ailleurs
les couvertures minimales du RFC 4470, peut, dans le
pire des cas, faciliter certaines attaques par déni de
service.)
Les recommandations précédentes s'appliquaient aux signeurs de zone (côté serveurs faisant autorité, donc). Mais la section 3 a aussi des recommandations pour les résolveurs : compte-tenu du coût que représente pour eux les itérations NSEC3, ils ont le droit d'imposer un maximum, et de le diminuer petit à petit. Ces résolveurs peuvent refuser de répondre (réponse SERVFAIL) ou bien traiter la zone come n'étant pas signée (cf. section 6). Un nouveau code d'erreur étendu (RFC 8914), le numéro 27, Unsupported NSEC3 iterations value, a été réservé pour qu'ils puissent informer leurs clients.
Revenons aux serveurs faisant autorité : le RFC précise aussi qu'un hébergeur DNS devrait informer clairement ses utilisateurs des paramètres NSEC3 qu'il accepte. Il ne faudrait pas qu'on choisisse N itérations et qu'on s'aperçoive au déploiement qu'un des secondaires n'accepte pas d'en faire autant.
Aujourd'hui, la grande majorité des zones utilisant NSEC3 est
passée aux recommandations de ce RFC (comme par
exemple .fr en 2022). Notons que .org
a
un sel mais pas d'itérations supplémentaires.
% dig +short org NSEC3PARAM 1 0 0 332539EE7F95C32A
Si vous utilisez OpenDNSSEC pour automatiser les opérations DNSSEC sur vos zones, voici la configuration conforme au RFC que j'utilise :
<Denial> <NSEC3> <!-- <OptOut/> --> <Resalt>P100D</Resalt> <Hash> <Algorithm>1</Algorithm> <Iterations>0</Iterations> <Salt length="0"/> </Hash> </NSEC3> </Denial>
Date de publication du RFC : Juillet 2022
Auteur(s) du RFC : S. Dashevskyi, D. dos Santos, J. Wetzels, A. Amri (Forescout Technologies)
Pour information
Première rédaction de cet article le 14 septembre 2022
Comme chacun·e sait, l'Internet est une jungle (les politiciens ajouteraient « une jungle Far-West de non-droit qu'il faut civiliser »). Par exemple, les logiciels qui parlent avec les vôtres ne sont pas toujours correctement écrits, voire ils sont franchement malveillants. Le code de votre côté doit donc être robuste, voire paranoïaque, et penser à tout. Ce RFC décrit quelques problèmes qui ont été observés dans des logiciels mettant en œuvre le DNS et explique comment ne pas refaire la même erreur.
Le RFC ne parle pas de failles DNS mais de failles dans les programmes DNS, ce qui est très différent (mais la différence est souvent oubliée dans les médias). Le protocole lui-même n'était pas en cause dans ces cas, ce sont juste les logiciels qui avaient des bogues. Les cas sont nombreux, par exemple SIGRed (CVE-2020-1350) ou DNSpooq (CVE-2020-25681 à CVE-2020-25687). Ces problèmes frappent notamment souvent dnsmasq (personnellement, je n'ai jamais compris pourquoi ce logiciel était si utilisé, mais c'est une autre histoire).
Plusieurs vulnérabilités ont concerné l'analyse des
enregistrements DNS (RR = Resource Record). Les
risques lors de cette analyse devraient être bien connus, la
première faille documentée l'ayant été en 2000 (CVE-2000-0333) !
Tout logiciel qui analyse des enregistrements DNS (client, serveurs,
mais aussi pare-feux, IDS, etc) peut
tomber dans ces pièges, d'où l'importance de les documenter. C'était
déjà fait dans
l'Internet-Draft
draft-ietf-dnsind-local-compression
et dans le RFC 5625 mais c'était perdu au
milieu d'autres choses donc notre RFC choisit d'enfoncer le
clou.
Il commence par le grand classique des bogues de logiciels DNS : le traitement des pointeurs de compression. Pour gagner quelques octets, à l'époque où ça comptait, le DNS prévoit (RFC 1035, section 4.1.4) qu'on peut, dans un enregistrement DNS, remplacer tout ou partie d'un nom de domaine par un pointeur vers un autre endroit du paquet. Ainsi, si on a tous ses serveurs qui se terminent par les mêmes composants (comme c'est le cas, par exemple, de la racine), on peut ne mettre ces composants qu'une fois, et pointer vers eux partout ailleurs. Évidemment, si vous êtes programmeuse ou programmeur, vous avez déjà vu le piège : les pointeurs, c'est dangereux. Ils peuvent pointer en dehors du paquet, ou pointer vers eux-même, par exemple. Si on suit aveuglément un pointeur, un déni de service est possible, si on tape en dehors de la mémoire allouée ou bien si on se lance dans une boucle sans fin.
Rentrons dans les détails. Le RFC 1035 nous dit que l'octet qui indique la longueur d'un composant doit valoir moins de 64 (la taille maximale d'un composant), donc avoir les deux bits de plus fort poids à zéro. S'ils sont tous les deux à un, cela indique qu'on a un pointeur. Cet octet et le suivant (privés des deux bits de plus fort poids) sont alors un pointeur, le nombre d'octets depuis le début du message. On voit qu'on peut atteindre 16 383 octets (2 ** 14 - 1), largement assez pour sortir de la mémoire allouée pour le paquet, avec les conséquences qu'on imagine. Cela s'est produit en vrai (CVE-2020-25767, CVE-2020-24339 et CVE-2020-24335).
Autre exemple amusant cité par le RFC, le cas d'un pointeur pointant sur lui-même. Soit un message DNS minimal. Le premier composant est à 12 octets du début du message. Si on y met les octets 0xC0 et 0x0C, on aura un pointeur (0xC0 = 11000000, les deux bits de plus fort poids à un) qui vaut 12 (0xC0 0x0C moins les deux bits les plus significatifs = 0000000000001100 = 12). Le pointeur pointera alors sur lui-même, entrainant le logiciel DNS imprudent dans une boucle sans fin. Ça aussi, ça s'est déjà produit (CVE-2017-9345).
Dernier exemple amusant avec des pointeurs, le pointeur qui va
mener à un nombre infini de composants. L'attaquant (ou le
programmeur maladroit) met dans le message un composant suivi d'un
pointeur qui revient au début de ce composant. Si le composant était
test
, un analyseur DNS imprudent va créer le
nom de domaine test.test.test.test…
avant de
tomber à court de mémoire, ou bien, dans un langage de programmation
comme C, d'écrire dans une
autre zone de la mémoire, ce qui peut mener à un
RCE. Notez que les pointeurs ne devraient pas
pointer vers un pointeur : ça n'a aucun intérêt pratique et c'est
dangereux, mais le RFC 1035 ne l'interdit pas
explicitement. Une alternative est de tester le nombre de fois qu'on
a suivi un pointeur ou, encore mieux, de vérifier que le pointeur ne
pointe qu'en avant de lui-même.
Notez que le problème de la répétition infinie d'un composant pourrait également être évité en s'assurant que le nom de domaine reste en dessous de sa taille maximale, 255 octets (section 3 du RFC).
Mais il n'y a pas que les pointeurs. Un nom de domaine doit être
terminé par un octet nul, indiquant la racine. Ainsi, le nom
afnic.fr
est encodé 0x05 0x61 0x66 0x6E 0x69
0x63 ("afnic") 0x02 0x66 0x72 ("fr") 0x00 (la racine). Que se
passe-t-il si l'octet nul final manque ? Certains programmes en
C ont été assez imprudents
pour tenter de traiter le nom de domaine avec des routines comme
strlen
, qui comptent sur un octet nul pour
terminer les données (section 4 de notre RFC). Le développeur qui ne
veut pas refaire des failles comme CVE-2020-25107, CVE-2020-17440,
CVE-2020-24383 ou CVE-2020-27736 devrait utiliser
strnlen
ou une autre méthode sûre.
On n'a pas terminé. Lorsqu'on traite une réponse DNS, les
enregistrements incluent des données. Leur longueur est donné par un
champ RDLENGTH de deux octets, qui précède les données (section
5). Si un analyseur DNS ne fait pas attention, la longueur indiquée
peut être supérieure à la taille du paquet. Un programme qui,
bêtement, lirait RDLENGTH puis ferait un read()
de la longueur indiquée aurait de fortes chances de taper en dehors
de la mémoire accessible (CVE-2020-25108, CVE-2020-24336 et
CVE-2020-27009).
Ah, et le RFC n'en parle pas, mais l'analyse des options EDNS (RFC 6891) offre également ce genre de pièges. Une option est encodée en TLV et une longueur invalide peut mener à lire les valeurs en dehors du paquet.
Un problème du même genre est décrit dans la section 6 du RFC. Un message DNS comprend plusieurs sections (Question, Réponse, Autorité, Additionnelle) et le nombre d'enregistrements par section est indiqué au début du message DNS. Ces nombres (count) peuvent également être faux et ne doivent pas être utilisés aveuglément, sinon, on aura des malheurs du genre CVE-2020-25109, CVE-2020-24340, CVE-2020-24334 ou CVE-2020-27737.
C'est en raison de tous ces risques qu'il faut tester que ses
programmes résistent bien à des messages DNS mal formés. Par
exemple, le serveur faisant autorité
Drink, écrit en Elixir, a dans ses jeux de tests de tels
messages. Regardez par exemple drink_parsing_test.exs
. Un
exemple d'un tel test, pour le problème de boucle sans fin sur des
pointeurs, décrit en section 2 du RFC, est :
test "pointerloop1" do # RFC 9267, section 2 result = Drink.Parsing.parse_request(<<@id::unsigned-integer-size(16), @request::unsigned-integer-size(16), 1::unsigned-integer-size(16), # QDcount 0::unsigned-integer-size(16), # ANcount 0::unsigned-integer-size(16), # NScount 0::unsigned-integer-size(16), # ARcount 0xc0::unsigned-integer-size(8), # First label of the question # section starts with a # compression pointer to itself. 0x0c::unsigned-integer-size(8), @mx::unsigned-integer-size(16), @in_class::unsigned-integer-size(16) >>, false) assert result == {:error, :badDNS} end
On fabrique un paquet DNS avec le pointeur invalide, et on
l'analyse. Le sous-programme parse_request
ne
doit pas tourner sans fin, et doit renvoyer une erreur.
Un test du même genre en Python, où on envoie un message DNS avec un pointeur pointant sur lui-même (et on espère que ça n'aura pas tué le serveur qui le reçoit) :
id = 12345 misc = 0 # opcode 0, all flags zero data = struct.pack(">HHHHHHBB", id, misc, 1, 0, 0, 0, 0xc0, 0x0c) s.sendto(data, sockaddr)
Date de publication du RFC : Juin 2022
Auteur(s) du RFC : R. Stewart (Netflix), M. Tüxen
(Münster Univ. of Appl. Sciences), K. Nielsen (Kamstrup A/S)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF twvwg
Première rédaction de cet article le 6 juin 2022
Une des particularités du protocole IP est que vous avez plusieurs protocoles de transport disponibles. TCP et UDP sont les plus connus mais SCTP, normalisé dans notre RFC, est également intéressant. C'est un protocole déjà ancien (il date de 2000) et ce RFC remplace son prédécesseur, le RFC 4960. Beaucoup de changements de détails, mais rien de crucial.
SCTP ressemble plutôt à TCP, notamment par le fait qu'il fournit un transport fiable. Mais il a plusieurs différences :
Cette dernière possibilité le rapproche des protocoles qui séparent l'identificateur et le localisateur et lui permet de gérer proprement le multihoming. Cela se fait en indiquant, au début du contact, toutes les adresses IP (autrefois, il y avait même les noms de domaines, cf. la section 3.3.2.1.4) de la machine.
SCTP tient également compte de l'expérience acquise avec TCP. par exemple, l'établissement d'une connexion (que SCTP nomme association) se fait avec un échange de quatre paquets (et non pas trois comme avec TCP), pour offrir une meilleure protection contre les dénis de service. Les SYN cookies, un ajout plus ou moins bancal en TCP, sont ici partie intégrante du protocole.
SCTP est surtout issu des demandes du monde de la téléphonie sur IP, qui avait besoin d'un tel protocole pour la signalisation mais il peut être aussi utilisé dans d'autres domaines.
Un excellent article du Linux Journal explique bien SCTP.
SCTP est depuis longtemps mis en œuvre dans Linux et dans FreeBSD. De même, des programmes de débogage comme Wireshark sont capables de décoder et d'afficher le SCTP. Voici par exemple un pcap entre deux machines dont l'une a envoyé (trame 3) la chaîne de caractères « toto ». (Vous avez également la version texte de ce dialogue.) Des exemples de programmes de tests SCTP se trouvent dans mon article sur le RFC 3286.
Comme tout « nouveau » protocole de transport, SCTP est handicapé par des coupe-feux mal programmés et/ou mal configurés. L'Internet s'ossifiant de plus en plus, il devient très difficile de déployer un nouveau protocole de transport. D'où le RFC 6951, pour faire tourner SCTP sur UDP (comme ce que fait QUIC).
Les changements qu'introduit ce nouveau RFC ne modifient pas en profondeur le protocole mais corrigent les nombreux problèmes survenus pendant ses premières années d'utilisation. Le RFC 8540 donne la liste complète des problèmes corrigés.
Date de publication du RFC : Juin 2022
Auteur(s) du RFC : R. Bush (Arrcus & IIJ Research), R. Housley (Vigil Security)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidrops
Première rédaction de cet article le 15 juin 2022
Un très court RFC pour un simple rappel, qui ne devrait même pas être nécessaire : les identités utilisées dans la RPKI, la base des identités qui sert aux techniques de sécurisation de BGP, n'ont pas de lien avec les identités attribuées par l'État et ne doivent pas être utilisées pour, par exemple, signer des documents « officiels » ou des contrats commerciaux.
Le RFC 6480 décrit cette RPKI : il s'agit d'un ensemble de certificats et de signatures qui servent de base pour des techniques de sécurité du routage comme les ROA du RFC 6482 ou comme le BGPsec du RFC 8205. Les objets de la RPKI servent à établir l'autorité sur des ressources Internet (INR, Internet Number Resources, comme les adresses IP et les numéros d'AS). Le RFC 6480, section 2.1, dit clairement que cela ne va pas au-delà et que la RPKI ne prétend pas être la source d'identité de l'Internet, ni même une source « officielle ».
Prenons un exemple concret, un certificat
choisi au hasard dans les données du RIPE-NCC (qu'on peut récupérer avec
rsync en
rsync://rpki.ripe.net/repository
). Il est au
format DER donc ouvrons-le :
% openssl x509 -inform DER -text -in ./DEFAULT/ztLYANxsM7afpHKR4vFbM16jYA8.cer Certificate: Data: ... Serial Number: 724816122963 (0xa8c2685453) Validity Not Before: Jan 1 14:04:04 2022 GMT Not After : Jul 1 00:00:00 2023 GMT Subject: CN = ced2d800dc6c33b69fa47291e2f15b335ea3600f ... X509v3 extensions: ... sbgp-ipAddrBlock: critical IPv4: 51.33.0.0/16 ... sbgp-autonomousSysNum: critical Autonomous System Numbers: 206918 ...
Je n'ai pas tout montré, mais seulement les choses importantes :
51.33.0.0/16
, utilisant les extensions du
RFC 3779.Ce certificat dit simplement que l'entité qui a la clé privée (RFC 5280) correspondante (une administration britannique, dans ce cas) a autorité sur des ressources comme l'AS 206918. Rien d'autre.
Mais, apparemment, certaines personnes n'avaient pas bien lu le RFC 6480 et croyaient que cet attirail PKIesque leur permettait également de signer des documents sans lien avec les buts de la RPKI, voire n'ayant rien à voir avec le routage. Et d'autant plus que des gens croient que le I dans RPKI veut dire Identity (il veut en fait dire Infrastructure). Il a ainsi été suggéré que les clés de la RPKI pouvaient être utilisées pour signer des LOA demandant l'installation d'un serveur dans une baie.
Pourquoi est-ce que cela serait une mauvaise idée ? Il y a
plusieurs raisons mais la principale est qu'utiliser la RPKI pour
des usages en dehors du monde restreint du routage Internet est que
cela exposerait les AC de cette RPKI à des
risques juridiques qu'elles n'ont aucune envie d'assumer. Et cela
compliquerait les choses, obligeant sans doute ces AC à déployer des
processus bureaucratiques bien plus rigides. Si on veut connaitre
l'identité officielle (que le RFC nomme à tort « identité dans le
monde réel ») d'un titulaire de ressources Internet, on a les bases
des RIR,
qu'on interroge via RDAP ou autres
protocoles. (C'est ainsi que j'ai trouvé le titulaire de
51.33.0.0/16
.) Bien sûr, il y a les
enregistrements Ghostbusters du RFC 6493 mais ils sont uniquement là pour aider à trouver un
contact opérationnel, pas pour indiquer l'identité étatique du
titulaire. Même les numéros d'AS ne sont pas l'« identité » d'un acteur de
l'Internet (certains en ont plusieurs).
Notons qu'il y a d'autres problèmes avec l'idée de se servir de la RPKI pour signer des documents à valeur légale. Par exemple, dans beaucoup de grandes organisations, ce ne sont pas les mêmes personnes qui gèrent le routage et qui gèrent les commandes aux fournisseurs. Et, au RIPE-NCC, les clés privées sont souvent hébergées par le RIPE-NCC, pas par les titulaires, et le RIPE-NCC n'a évidemment pas le droit de s'en servir pour autre chose que le routage.
Bref, n'utilisez pas la RPKI pour autre chose que ce pour quoi elle a été conçue.
Date de publication du RFC : Mai 2022
Auteur(s) du RFC : C. Huitema (Private Octopus), S. Dickinson (Sinodun IT), A. Mankin (Salesforce)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dprive
Première rédaction de cet article le 22 mai 2022
Ce nouveau RFC complète la série des mécanismes de protection cryptographique du DNS. Après DoT (RFC 7858) et DoH (RFC 8484), voici DoQ, DNS sur QUIC. On notera que bien que QUIC ait été conçu essentiellement pour les besoins de HTTP, c'est le DNS qui a été la première application normalisée de QUIC.
Fonctionnellement, DoQ est très proche de ses prédécesseurs, et offre les mêmes services de sécurité, confidentialité, grâce au chiffrement, et authentification du serveur, via le certificat de celui-ci. L'utilisation du transport QUIC permet quelques améliorations, notamment dans le « vrai » parallélisme. Contrairement à DoT (RFC 7858) et DoH (RFC 8484), DoQ peut être utilisé pour parler à un serveur faisant autorité aussi bien qu'à un résolveur.
Notez que le terme de DoQ (DNS over QUIC) n'apparait pas dans le RFC de terminologie DNS, le RFC 8499. Il sera normalement dans son successeur.
Logiquement, DoQ devrait faire face aux mêmes problèmes politiques que ceux vécus par DoH (voir aussi mon article dans Terminal) mais cela n'a pas été encore le cas. Il faut dire qu'il y a peu de déploiements (ne comptez pas installer DoQ comme serveur ou client tout de suite, le code est loin d'être disponible partout).
Le cahier des charges de DoQ est (sections 1 et 3 du RFC) :
En revanche, DoQ n'esssaie pas de :
DoQ utilise QUIC. QUIC est un protocole de transport généraliste, un concurrent de TCP. Plusieurs protocoles applicatifs peuvent utiliser QUIC et, pour chacun de ces protocoles, cela implique de définir comment sont utilisés les ruisseaux (streams) de QUIC. Pour HTTP/3, cela a été fait dans le RFC 9113, publié plus tard. Ironiquement, le DNS est donc le premier protocole à être normalisé pour une utilisation sur QUIC. Notez qu'une autre façon de faire du DNS sur QUIC est de passer par DoH et HTTP/3 (c'est parfois appelé DoH3, terme trompeur) mais cette façon n'est pas couverte dans notre RFC, qui se concentre sur DoQ, du DNS directement sur QUIC, sans HTTP.
La spécification de DoQ se trouve en section 4. Elle est plutôt
simple, DoQ étant une application assez directe de QUIC. Comme DoQ
utilise QUIC, il y a forcément un ALPN, en l'occurrence via
l'identificateur doq
.
Le port, quant à lui, est par défaut 853,
également nommé domain-s
. C'est donc le même
que DoT, et aussi le même que le DNS sur DTLS (RFC 8094, protocole expérimental, jamais vraiment mis en œuvre
et encore moins déployé, et qui est de toute façon distinguable du
trafic QUIC, cf. section 17.2 du RFC 9000). Bien sûr, un client et un serveur DNS majeurs et
vaccinés peuvent toujours se mettre d'accord sur un autre port (mais
pas 53, réservé au DNS classique). Cette utilisation d'un port
spécifique à DoQ est un des points qui le rend vulnérable au
filtrage, comme vu plus haut. Utiliser 443 peut aider. (Le point a
été chaudement discuté à l'IETF, entre
défenseurs de la vie privée et gestionnaires de réseau qui voulaient
pouvoir filtrer facilement les protocoles de leur choix. Si on
utilise le port 443, il faut se rappeler que l'ALPN est pour
l'instant en clair, la lutte de l'épée et de la cuirasse va donc continuer.)
Plus important, la question de la correspondance entre les messages DNS et les ruisseaux QUIC. La règle est simple : pour chaque requête DNS, un ruisseau est créé. La première requête sur une connexion donnée sera donc sur le ruisseau 0 (RFC 9000, section 2.1), la deuxième sur le ruisseau 4, etc. Si les connexions QUIC sont potentiellement de longue durée, en revanche, les ruisseaux ne servent qu'une fois. Le démultiplexage des réponses est assuré par QUIC, pas par le DNS (et l'identificateur de requête DNS est donc obligatoirement à zéro, puisqu'inutile). Le parallélisme est également fourni par QUIC, client et serveur n'ont rien de particulier à faire. Il est donc recommandé de ne pas attendre une réponse pour envoyer les autres questions. Les messages DNS sont précédés de deux octets indiquant leur longueur, comme avec TCP.
Comme avec TCP, client et serveur ont le droit de garder un œil sur les ruisseaux et les connexions inactifs, et de les fermer d'autorité. La section 5.5 donne des détails sur cette gestion de connexion, mais le RFC 7766 est également une bonne lecture (voir aussi le RFC 9103 pour le cas des transferts de zones).
DoQ introduit quelques codes
d'erreur à lui (RFC 9000, section
20.2), comme DOQ_INTERNAL_ERROR
(quelque chose
s'est mal passé), DOQ_PROTOCOL_ERROR
(quelqu'un
n'a pas lu le RFC, par exemple l'identificateur de requête était
différent de zéro) ou DOQ_EXCESSIVE_LOAD
(trop
de travail tue le travail).
Un des gros avantages de QUIC est le 0-RTT, où des données sont envoyées dès le premier paquet, ce qui réduit nettement la latence. Cela implique que le client ait déjà contacté le serveur avant, mémorisant les informations qui seront données au serveur pour qu'il retrouve la session à reprendre. C'est cool, mais cela pose d'évidents problèmes de vie privée (ces informations mémorisées sont une sorte de cookie, et permettent le suivi à la trace d'un client). D'ailleurs, en parlant de vie privée, le RFC signale aussi (section 5.5.4) que la possibilité de migrer une connexion QUIC d'une adresse IP à l'autre a également des conséquences pour la traçabilité.
Autre piège avec le 0-RTT, il ne protège pas contre le rejeu et
il ne faut donc l'utiliser que pour des requêtes DNS
idempotentes comme QUERY
(le cas le plus courant) ou NOTIFY
(qui change
l'état du serveur, mais est idempotent). Bon, de toute façon, un
serveur peut toujours être configuré pour ne pas accepter de 0-RTT,
et répondre alors REFUSED
(avec un code
d'erreur étendu - RFC 8914 - Too
Early).
La section 5 du RFC liste les exigences auxquelles doivent se soumettre les mises en œuvre de DoQ. Le client doit authentifier le serveur (cf. RFC 7858 et RFC 8310, mais aussi RFC 8932, sur les bonnes pratiques). Comme tous les serveurs ne géreront pas DoQ, en tout cas dans un avenir prévisible, les clients doivent être préparés à ce que ça échoue et à réessayer, par exemple en DoT.
QUIC ne dissimule pas forcément la taille des messages échangés, et celle-ci peut être révélatrice. Malgré le chiffrement, la taille d'une requête ou surtout d'une réponse peut donner une idée sur le nom demandé. Le RFC impose donc l'utilisation du remplissage, soit par la méthode QUIC générique de la section 19.1 du RFC 9000, soit par la méthode spécifique au DNS du RFC 7830. La première méthode est en théorie meilleure car elle dissimule d'autres métadonnées, et qu'elle tient compte de davantage d'éléments, mais les bibliothèques QUIC n'exposent pas forcément dans leur API de moyen de contrôler ce remplissage. Le RFC recommande donc aux clients et serveurs DoQ de se préparer à faire le remplissage eux-mêmes (en relisant bien le RFC 8467 avant).
Un petit retour sur la protection de la vie privée en section 7. Cette section rappele l'importance de lire et suivre le RFC 8932 si vous gérez un service de résolution DNS sécurisé (DoQ ou pas DoQ). Et elle insiste sur le fait que des fonctions de QUIC très pratiques pour diminuer la latence à l'établissement de la connexion, notamment le 0-RTT, ont des conséquences pour la vie privée, en permettant de suivre un même utilisateur (plus exactement une même machine). Et cela marche même si la machine a changé d'adresse IP entretemps. On peut aussi dire que le problème de QUIC est de permettre des sessions de très longue durée (que ce soit une vraie session unique, ou bien une « session virtuelle », formée en reprenant une session existante avec le 0-RTT) et que de telles sessions longues, excellentes pour les performances, le sont forcément moins pour l'intimité.
Les mises en œuvre de DoQ, maintenant. La société AdGuard a produit du code, dont un serveur et un client en Go, dnslookup. (Regardez l'exposé du directeur technique de cette société à l'OARC.) Voici un exemple de compilation, puis d'utilisation de dnslookup :
% git clone https://github.com/ameshkov/dnslookup.git % cd dnslookup % make % ./dnslookup www.bortzmeyer.org quic://dns.adguard.com
On utilisait le serveur DoQ public d'AdGuard. Officiellement, NextDNS en a également un mais je n'arrive pas à l'utiliser :
% ./dnslookup www.bortzmeyer.org quic://3e4935.dns.nextdns.io ... 2022/05/22 08:16:33 Cannot make the DNS request: opening quic session to quic://3e4935.dns.nextdns.io:853: timeout: no recent network activity
Notez que si vous voulez utiliser dnslookup avec un serveur dont le
certificat n'est pas valide, il faut mettre la variable
d'environnement VERIFY
à 0 (cela ne semble pas documenté).
Il existe une autre mise en œuvre de DoQ, quicdoq, écrite en C. Elle dépend de la bibliothèque QUIC picoquic qui, à son tour, dépend de picotls, dont la compilation n'est pas toujours évidente. D'une manière générale, les bibliothèques QUIC sont souvent récentes, parfois expérimentales, et ne marchent pas toujours du premier coup. Mais si vous arrivez à compiler les dépendances de quicdoq, vous pouvez lancer le serveur ainsi :
% ./quicdoq_app -p 8053 -d 9.9.9.9
Puis le client :
% ./quicdoq_app ::1 8053 foobar:SOA bv:NS www.bortzmeyer.org:AAAA
Le logiciel de test de performances Flamethrower, écrit en C++ a une branche DoQ en cours de développement.
Le logiciel en Python aioquic ne semble pouvoir interagir qu'avec lui-même. Vu les messages d'erreur (quelque chose à propos de not enough bytes), je soupçonne qu'il utilise l'encodage des débuts de DoQ, quand il n'y avait pas encore le champ de deux octets au début des messages. Même avec lui-même, il a des exigences pénibles en matière de certificat (pas de certificats auto-signés, obligation que le nom du serveur soit dans le SAN - Subject Alternative Name, pas seulement dans le sujet).
Pour PowerDNS, il n'y a pour l'instant qu'un ticket. Et pour Unbound, c'est en cours, ainsi que pour Knot.
Dans les bibliothèques DNS, un travail est en cours pour go-dns.
Pour finir, vous pouvez regarder la présentation de DoQ par une des auteures du RFC au RIPE 84. Et un des premiers articles de recherche sur DoQ est « One to Rule them All? A First Look at DNS over QUIC ».
Date de publication du RFC : Mai 2022
Auteur(s) du RFC : M. Miller, M. Borins (GitHub), M. Bynens (Google), B. Farias
Pour information
Réalisé dans le cadre du groupe de travail IETF dispatch
Première rédaction de cet article le 11 mai 2022
Ce RFC
documente les types de médias pour le langage
de programmation JavaScript (dont le nom
officiel est ECMAScript, qu'on retrouve dans le titre de ce RFC). Il
remplace le RFC 4329 et le principal
changement est que le type recommandé est désormais
text/javascript
et non plus
application/javascript
.
Si vous voulez vous renseigner en détail sur
JavaScript, notre RFC recommande de lire la norme
ECMA, en
. Cette
norme est développée en dehors de l'IETF et le choix des
types de médias (aussi appelés types
MIME, cf. RFC 2046)
n'est donc pas forcément en accord avec les règles de l'IETF (RFC 6838). C'est pour cela que l'IESG a ajouté
une note d'avertissement au RFC. Mais, bon, ce n'est pas trop grave
en pratique. Le type recommandé est donc désormais
https://262.ecma-international.org/12.0/
text/javascript
. D'autres types existent,
application/ecmascript
,
application/javascript
, etc, mais ils sont
maintenant considérés comme dépassés.
Il existe plusieurs versions de la norme JavaScript et d'autres
apparaitront peut-être dans le futur. Mais le type officiel
n'indique pas de version (il a existé des propositions comme
text/javascript1.4
) et compte que toutes ces
versions sont suffisamment compatibles pour qu'on ne gère pas la
complexité d'avoir plusieurs types. Normalement,
ECMA s'engage à ne pas bouleverser le
langage.
Le choix de text/
est contestable car la
définition originale de text/
dans le RFC 2045 prévoyait plutôt
application/
pour les programmes. Le RFC 4329 enregistrait les types
text/javascript
et
application/javascript
, et recommandait le
application/javascript
(en accord avec ce que
dit le RFC 6838). Aujourd'hui, c'est le
contraire, text/javascript
est le
préféré. D'une manière générale, application/
n'a guère été utilisé (pas seulement dans le cas de
JavaScript). (Personnellement, cela me convient très bien : pour
moi, si ça peut s'afficher avec cat et
s'éditer avec vi, c'est du texte.)
JavaScript est donc officiellement du texte, et la section 4 de
notre RFC précise : du texte en UTF-8. Le
paramètre charset
est donc facultatif (même si
le RFC 6838 dit le contraire). Si vous aimez
les détails d'encodage, la section 4 vous ravira (c'est l'un des
points qui a suscité le plus de discussion à l'IETF).
Le type text/javascript
est enregistré
à l'IANA ; les types comme
application/javascript
sont marqués
comme dépassés. Même chose pour
text/ecmascript
. Le nom officiel de JavaScript
est ECMAscript, puisque normalisé à l'ECMA
mais personne n'utilise ce terme. (Il faut quand même noter que
JavaScript est un terme publicitaire mensonger puisqu'il avait été
choisi par le marketing pour profiter de la popularité - à
l'époque - de Java, un
langage avec lequel il n'a rien à voir.) Enfin, les types comme
text/x-javascript
qu'on voit parfois trainer
datent d'avant le RFC 6648, qui abandonnait
les identificateurs semi-privés commençant par
x-
.
La section 5 couvre les questions de sécurité. Elle est très longue car l'envoi de JavaScript à un programme qui l'exécutera (ce qui est très courant sur le Web) pose plein de problèmes de sécurité. Le RFC rappelle que l'exécution automatique d'un programme fourni par un tiers est évidemment intrinsèquement dangereuse, et doit se faire dans un bac à sable. Parmi les dangers (mais il y en a beaucoup d'autres !) le déni de service puisque JavaScript est un langage de Turing et permet donc, par exemple, des boucles infinies.
L'annexe B du RFC résume les changements depuis le RFC 4329 : le principal est bien sûr l'abandon de
application/javascript
au profit de
text/javascript
. Il y a aussi l'ajout de
quelques détails comme une faille de sécurité nouvelle, et le cas
des modules JavaScript.
Ah, un point de détail cité au détour d'un paragraphe du RFC : il
n'y a pas de norme pour les identificateurs de fragments dans un
URI
pointant vers une ressource de type
text/javascript
. Si
https://code.example/js/foreach.js#l23
pointe
vers du JavaScript, la signification du #l23
(l'identificateur de fragment) n'est pas spécifiée.
Date de publication du RFC : Mai 2022
Auteur(s) du RFC : A. Azimov (Qrator Labs & Yandex), E. Bogomazov (Qrator Labs), R. Bush (IIJ & Arrcus), K. Patel (Arrcus), K. Sriram (USA NIST)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 25 mai 2022
Vous savez sans doute que l'Internet repose sur le protocole de routage BGP et que ce protocole a été cité dans des accidents (voire des attaques) spectaculaires, par exemple lorsqu'un routeur BGP annonçait ou réannonçait des routes qu'il n'aurait pas dû annoncer (ce qu'on nomme une fuite de routes). Si BGP lui-même est pair-à-pair, en revanche, les relations entre les pairs ne sont pas forcément symétriques et des règles sont largement admises ; par exemple, on n'est pas censé annoncer à un transitaire des routes apprises d'un autre transitaire. Ce RFC étend BGP pour indiquer à l'ouverture de la session le type de relations qu'on a avec son pair BGP, ce qui permet de détecter plus facilement certaines fuites de routes.
La solution proposée s'appuie sur un modèle des relations entre opérateurs, le modèle « sans vallée » (valley-free, même si ce terme n'est pas utilisé dans le RFC). On le nomme aussi modèle Gao-Rexford. Ce modèle structure ces relations de telle façon à ce que le trafic aille forcément vers un opérateur aussi ou plus important (la « montagne »), avant de « redescendre » vers la destination ; le trafic ne descend pas dans une « vallée », sauf si elle est la destination finale. Le but est de permettre un routage optimum et d'éviter boucles et goulets d'étranglement, mais ce modèle a aussi une conséquence politico-économique, maintenir la domination des gros acteurs (typiquement les Tier-1). Les relations entre participants à une session BGP sont de type Client-Fournisseur ou Pair-Pair, et le trafic va toujours dans le sens du client vers le fournisseur, sauf pour les destinations finales (on n'utilise donc pas un pair pour du transit, c'est-à-dire pour joindre d'autres réseaux que ceux du pair).
En raison de ce modèle, un routeur BGP n'est pas censé annoncer à un transitaire des routes apprises d'un autre transitaire, ou annoncer à un pair des routes apprises d'un transitaire, ou bien apprises d'un autre pair (RFC 7908). Une fuite de routes est une annonce qui viole cette politique (qu'elle soit explicite ou implicite).
Traditionnellement, on empêche ces fuites par des règles dans la configuration du routeur. Par exemple, le client d'un transitaire va bloquer l'exportation par défaut, puis lister explicitement les routes qu'il veut annoncer. Et le transitaire va bloquer l'importation par défaut, et lister ensuite les routes qu'il acceptera de son client. Ce nouveau RFC ajoute un autre mécanisme, où la relation entre les deux partenaires BGP est explicitement marquée comme Client-Fournisseur ou Pair-Pair, facilitant la détection des fuites. Un concept qui était auparavant purement business, le type de relation entre acteurs BGP, passe ainsi dans le protocole.
Le RFC utilise donc ces termes, pour désigner la place d'un acteur BGP dans le processus de routage :
Une violation de ces règles est une fuite de routes (RFC 7908). Le RFC précise qu'il s'agit de relations « techniques », qu'elles n'impliquent pas forcément une relation business mais, en pratique, l'échange entre pairs est souvent gratuit et la relation Client-Fournisseur payante. Le RFC précise aussi qu'il peut exister des cas plus complexes, comme une relation Client-Fournisseur pour certains préfixes IP et Pair-Pair pour d'autres.
Bon, assez de politique et de business, place à la technique et au protocole. Le RFC définit une nouvelle capacité (RFC 5492) BGP, Role, code 9 et dont la valeur, stockée sur un octet, indique un des rôles listés ci-dessus (0 pour Fournisseur, 3 pour Client, 4 pour Pair, etc). D'autres rôles pourront être ajoutés dans le futur (politique « Examen par l'IETF », cf. RFC 8126). Ce rôle indique la place de l'AS et est configuré avec la session BGP (rappelez-vous qu'un AS peut être Client d'un côté et Fournisseur de l'autre). À l'ouverture de la connexion, les routeurs vérifient la compatibilité des rôles (si tous les deux annoncent qu'ils sont Fournisseur, c'est un problème, d'où un nouveau code d'erreur BGP, Role mismatch).
Une fois que les deux routeurs sont d'accord sur les rôles respectifs de leurs AS, les routes annoncées peuvent être marquées avec un nouvel attribut, OTC (Only To Customer, code 35), dont la valeur est un numéro d'AS. Une route ainsi marquée ne sera acceptée que si elle vient d'un Fournisseur, ou d'un pair dont le numéro d'AS coïncide. Autrement, elle sera considérée comme résultant d'une fuite.
Ah, et pour les cas compliqués dont on a parlé plus haut, comme les rôles différents pour chaque préfixe ? Dans ce cas-là, le mécanisme des rôles de ce RFC n'est pas adapté et ne doit pas être utilisé.
Ce système des rôles est apparemment mis en œuvre dans des patches (non encore intégrés ?) pour FRRouting et BIRD. Par exemple, pour FRRouting, on configurerait :
neighbor 2001:db8:999::1 local-role customer
Pour BIRD, cela serait :
configuration ... protocol bgp { ... local_role customer; }
Et la gestion des rôles devrait être bientôt
dans BGPkit. Et une version antérieure à la sortie du RFC
tourne sur les routeurs MikroTik (voir leur
documentation, local.role ebgp-customer
…).
Par contre, je ne sais pas pour Cisco ou
Juniper. Si quelqu'un a des informations…
Date de publication du RFC : Mars 2022
Auteur(s) du RFC : P. Faltstrom (Netnod)
Chemin des normes
Première rédaction de cet article le 24 avril 2022
La norme pour les noms de domaine en Unicode, dite « IDNA 2008 », prévoit une révision à chaque nouvelle version d'Unicode pour éventuellement s'adapter à des changements dus à ces nouvelles versions. Ce processus de révision a pas mal cafouillé (euphémisme), et ce RFC doit donc traiter d'un coup les versions 6 à 12 d'Unicode.
Le fond du problème est que l'ancienne norme sur les IDN (RFC 3490) était strictement liée à une version donnée d'Unicode et qu'il fallait donc une nouvelle norme pour chacune des versions annuelles d'Unicode. Vu le processus de publication d'une norme à l'IETF, ce n'était pas réaliste. La seconde norme IDN, « IDN 2 » ou « IDN 2008 » (bien qu'elle soit sortie en 2010) remplaçait les anciennes tables fixes de caractères autorisés ou interdits par un algorithme, à faire tourner à chaque sortie d'une version d'Unicode pour produire les tables listant les caractères qu'on peut accepter dans un nom de domaine internationalisé (le mécanisme exact, utilisant les propriétés des caractères listées dans la norme Unicode, figure dans le RFC 5892). En théorie, c'était très bien. En pratique, malgré les règles de stabilité d'Unicode, il se produisait parfois des problèmes. Comme le documente le RFC 8753, un caractère peut ainsi passer d'interdit à autorisé, ce qui n'est pas grave mais aussi dans certains cas d'autorisé à interdit ce qui est bien plus embêtant : que faire des noms déjà réservés qui utilisent ce caractère ? En général, il faut ajouter une exception manuelle, ce qui justifie un mécanisme de révision de la norme IDN, mis en place par ce RFC 8753. Ce nouveau RFC 9233 est le premier RFC de révision. Heureusement, cette fois, aucune exception manuelle n'a été nécessaire.
La précédente crise était due à Unicode version 6 qui avait créé trois incompatibilités (RFC 6452). Une seule était vraiment gênante, le caractère ᧚, qui, autorisé auparavant, était devenu interdit suite au changement de ses propriétés dans la norme Unicode. Le RFC 6452 avait documenté la décision de ne rien faire pour ce cas, ce caractère n'ayant apparemment jamais été utilisé. Unicode 7 a ajouté un autre problème, le ࢡ, qui était un cas différent (la possibilité de l'écrire de plusieurs façons), et la décision a été prise de faire désormais un examen formel de chaque nouvelle version d'Unicode. Mais cet examen a été souvent retardé et voilà pourquoi, alors qu'Unicode 13 est sorti (ainsi qu'Unicode 14 depuis), ce nouveau RFC ne traite que jusqu'à la version 12.
Passons maintenant à l'examen des changements apportés par les versions 7 à 12 d'Unicode, fait en section 3 du RFC :
En Unicode 11, 𑇉 qui passe d'interdit à autorisé, était un cas peu gênant. Le RFC prend donc la décision de ne pas ajouter d'exception pour ce caractère peu commun.
Voilà, arrivé ici, vous pensez peut-être que cela fait beaucoup de bruit pour rien puisque finalement les différentes versions d'Unicode n'ont pas créé de problème. Mais c'est justement pour s'assurer de cela que cet examen était nécessaire.
Pour compliquer davantage les choses, on notera qu'il existe encore sans doute (section 2.3 du RFC) des déploiements d'IDN qui en sont restés à la première version (celle du RFC 3490) voire qui sont un mélange des deux versions d'IDN, en ce qui concerne l'acceptation ou le refus des caractères.
En avril 2022, le travail pour Unicode 13 ou Unicode 14 n'a apparemment pas encore commencé…
Date de publication du RFC : Juillet 2022
Auteur(s) du RFC : D. Eastlake 3rd (Futurewei Technologies)
Chemin des normes
Première rédaction de cet article le 14 janvier 2025
Il existe tout un ensemble de normes pour assurer la sécurité de documents XML, par exemple les protéger contre la lecture non autorisée, ou bien permettre leur authentification. Ces normes dépendent d'algorithmes cryptographiques identifiés par un URI. Ce RFC met à jour la liste précédente de ces URI (qui était dans le RFC 6931) avec les nouveaux algorithmes et corrige quelques erreurs du précédent RFC.
Ces normes de sécurité de XML étaient à l'origine un travail conjoint de l'IETF et du W3C. C'était par exemple le cas des signatures XML du RFC 3275, du XML canonique des RFC 3076 ou RFC 3741. Elles sont désormais maintenues par le W3C qui a produit des versions plus récentes (par exemple pour les signatures XML, le XML canonique ou le chiffrement XML).
Dans un monde dynamique comme celui de la
cryptographie, où les progrès de la
cryptanalyse nécessitent des changements
d'algorithmes, les normes ne sont pas liées à un algorithme
particulier. Elles permettent l'agilité cryptographique (le
changement d'algorithme) et il faut donc pouvoir indiquer quel
algorithme est utilisé pour signer ou chiffrer un document
donné. Pour une norme W3C, on ne s'étonnera pas que l'indication
se fait par le biais d'un URI (RFC 3986). Les
nouveaux algorithmes commencent désormais par le préfixe
http://www.w3.org/2021/04/xmldsig-more#
(les
anciens algorithmes pouvant avoir d'autres préfixes). Ces nouveaux
algorithmes (avec 2021/04
dans leur
identificateur) sont relativement rares dans ce RFC : on n'invente
quand même pas un bon algorithme de cryptographie tous les jours et
la plupart des exemples dans cet article utilisent donc le vieux
préfixe. Un exemple récent ? EdDSA (RFC 8032) a l'URI
http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519
. Rappelez-vous
qu'il s'agit d'URI, pas forcément d'URL et que vous
n'obtiendrez donc pas forcément un résultat en pointant votre
navigateur Web vers
http://www.w3.org/2001/04/xmlenc#sha256
(cf. section 5.1).
Notons que notre RFC 9231 ne prend pas position sur la qualité cryptographique des algorithmes : il fournit un moyen de les désigner sans ambiguïté, c'est tout. Si on veut étudier cette qualité cryptographique, il faut lire d'autres documents (comme le RFC 6194 pour SHA-1).
Un exemple d'un ancien algorithme est
SHA-1 pour calculer les condensats
cryptographiques. Son URI est
http://www.w3.org/2000/09/xmldsig#sha1
. Sa
sécurité est aujourd'hui sérieusement battue en brèche (cf. RFC 6194). Autre exemple d'un algorithme qui était
déjà dans le RFC 6931, SHA-512,
identifié par http://www.w3.org/2001/04/xmlenc#sha512
.
Il existe aussi des identificateurs pour les MAC combinés
avec une condensation, par exemple
http://www.w3.org/2001/04/xmldsig-more#hmac-sha512
(RFC 6234).
Et pour les signatures avec un système à clé publique ?
L'identificateur indique l'algorithme de cryptographie asymétrique
et celui de condensation, par exemple
http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
(voir aussi le RFC 3447). Si on trouve RSA ennuyeux, il existe
aussi des identificateurs pour un algorithme à courbes
elliptiques (RFC 6090 mais notez
ses
errata), ECDSA, par exemple
http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512
.
Enfin, il y a les algorithmes de chiffrement symétrique. Par exemple,
Camellia (RFC 3713) sera identifié par
http://www.w3.org/2001/04/xmldsig-more#camellia256-cbc
. Le
plus récent SEED (RFC 4269) sera
http://www.w3.org/2007/05/xmldsig-more#seed128-cbc
.
Un exemple d'utilisation donné par notre RFC, pour de la cryptographie symétrique :
<EncryptionMethod Algorithm="http://www.w3.org/2021/04/xmldsig-more#chacha20"> <Nonce>0123456789abcdef01234567</Nonce> <Counter>fedcba09</Counter> </EncryptionMethod>
Voici pour la cryptographie. Mais les normes de sécurité XML
prévoient aussi une étape de canonicalisation avant chiffrement ou
signature, et là aussi, il y a plusieurs algorithmes, identifiés par
des URI comme
http://www.w3.org/2000/09/xmldsig#minimal
ou
http://www.w3.org/2006/12/xmlc14n11#WithComments
.
Quelle est la politique d'allocation dans le registre ? La
section 5 décrit celle du W3C (le préfixe
http://www.w3.org/2007/05/xmldsig-more#
est
figé, a priori, on n'y mettra pas de nouveaux algorithmes) et celle
de l'IETF : comme il est facile d'obtenir un URI (n'importe qui peut
en créer un), la seule question est celle de leur enregistrement. Il
se fera après un examen par un expert (voir le RFC 8126 pour les politiques d'allocation IETF) après
publication d'un texte décrivant le nouvel algorithme.
Quels changements depuis la version précédente de ce RFC, le RFC 6931 ? L'annexe A les liste. Les principaux, à mon avis, sont :