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 : Mai 2021
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 28 mai 2021
Ce RFC, qui fait partie de la première fournée sur QUIC, décrit les invariants du protocole QUIC, les choses qui ne changeront pas dans les futures versions de QUIC.
Comme tous les protocoles Internet, QUIC doit faire face à l'ossification, cette tendance à rendre les changements impossibles, en raison du nombre de composants du réseau qui refusent tout comportement qu'ils n'attendaient pas, même si c'était une évolution prévue ou en tout cas possible. Une des méthodes pour limiter cette ossification est la documentation d'invariants, des parties du protocole dont on promet qu'elles ne changeront pas dans les futures versions. Tout le reste peut bouger et les équipements comme les middleboxes peuvent donc s'appuyer sur ce RFC pour faire les choses proprement, en sachant ce qui durera longtemps, et ce qui est susceptible d'évoluer.
La section 15 du RFC 9000 explique le principe des versions de QUIC. QUIC est actuellement à la version 1. C'est la seule pour l'instant. Mais si une version 2 (puis 3, puis 4…) apparait un jour, il faudra négocier la version utilisée entre les deux parties (section 6 du RFC 9000). Ces nouvelles versions pourront améliorer le protocole, répondant à des phénomènes qui ne sont pas forcément prévisibles. Par exemple QUIC v1 utilise forcément TLS pour la sécurité (RFC 9001), mais les futures versions pourront peut-être utiliser un autre protocole cryptographique, plus sûr, ou plus rapide. L'expérience de nombreux protocoles IETF est qu'il est difficile de déployer une nouvelle version d'un protocole déjà en place, car il y a toujours des équipements dans le réseau qui faisaient des suppositions sur le protocole, qui ne sont plus vraies avec la nouvelle version (et, souvent même pas avec l'ancienne…) et qui pertuberont, voire couperont la communication. D'où cette idée de documenter les invariants, en indiquant donc clairement que tout ce qui n'est pas invariant… peut changer. Un exemple d'invariant est « A QUIC packet with a long header has the high bit of the first byte set to 1. All other bits in that byte are version specific. ». Actuellement (version 1), le deuxième bit vaut forcément 1 (RFC 9000, section 17.2), mais ce n'est pas un invariant : les autres versions feront peut-être différemmment.
Concevoir des invariants est tout un art. Trop d'invariants et QUIC ne peut plus évoluer. Pas assez d'invariants et ils ne servent plus à rien. Ainsi, il semble difficile de faire un répartiteur de charge qui marche avec toutes les futures versions de QUIC (pas assez d'invariants pour lui).
Et dans les paquets, qu'est-ce qui sera invariant ? (Petit rappel au passage, un datagramme UDP peut contenir plusieurs paquets QUIC.) Il y a deux sortes de paquets, les longs et les courts. (Plus rigoureusement, ceux à en-tête long et ceux à en-tête court.) On les distingue par le premier bit. Tous les autres bits du premier octet sont spécifiques d'une version particulière de QUIC, et ne sont donc pas invariants. Ce premier octet est suivi d'un numéro de version sur 32 bits (aujourd'hui, forcément 1, sauf en cas de négociation de version, non encore spécifiée), puis du connection ID de la destination (attention : longueur variable, dans les paquets longs, il est encodé sous forme longueur puis valeur, cela permettra d'avoir des identificateurs de connexion très longs dans le futur) puis, mais seulement pour les paquets longs, du connection ID de la source. Tout le reste du paquet dépend de la version de QUIC utilisée.
Notez que la longueur des paquets n'étant pas dans les invariants, on ne peut même pas trouver combien il y a de paquets QUIC dans un datagramme de manière indépendante de la version.
L'identificateur de connexion est une donnée opaque : la façon de le choisir pourra varier dans le futur.
Bien sûr, spécifier rigoureusement les invariants n'empêchera pas les middleboxes de tirer des fausses conclusions et, par exemple, de généraliser des comportements de la version 1 de QUIC à toutes les versions ultérieures (section 7 du RFC). L'annexe A donne une liste (sans doute incomplète) des suppositions qu'un observateur pourrait faire mais qui sont erronées. QUIC essaie de réduire ce qu'on peut observer, en chiffrant au maximum, afin de limiter ces suppositions erronées, mais il reste des choses visibles, qui ne sont pas forcément invariantes. Le RFC écrit ces suppositions de manière neutre, en notant qu'elles sont fausses. J'ai préféré rédiger en insistant sur leur fausseté. Donc, rappelez-vous que, notamment dans les futures versions de QUIC :
À noter que le numéro de version n'apparait que dans les paquets longs. Un programme qui observe des paquets QUIC de versions différentes ne peut donc analyser les paquets courts que s'il a mémorisé les numéros de versions correspondant à chaque connection ID de destination qu'il voit.
Autre point important : il n'y a pas d'invariant qui identifie un paquet QUIC. Pas de nombre magique ou de chose équivalente. Donc, aucun moyen d'être raisonnablement sûr que ce qui passe est du QUIC. C'est bien sûr volontaire, pour augmenter les chances que la neutralité soit respectée. Mais cela peut amener des middleboxes agressives à essayer de deviner si c'est bien du QUIC ou pas, en se trompant. (Le « QUIC bit » n'est évidemment pas une preuve suffisante, il peut s'agir d'un autre protocole où ce bit vaut 1.)
Date de publication du RFC : Mars 2021
Auteur(s) du RFC : P. Yang (Ant Group)
Pour information
Première rédaction de cet article le 11 mars 2021
La cryptographie est un outil essentiel pour la sécurité sur les réseaux numériques. N'avoir comme algorithmes de cryptographie que des algorithmes développés à l'étranger peut être jugé dangereux pour la sécurité nationale. Des pays comme la Russie et, bien sûr, les États-Unis, recommandent ou imposent des algorithmes « nationaux ». La Chine s'y met, avec les algorithmes de chiffrement ShangMi (« SM »), dont ce RFC décrit l'utilisation dans TLS.
Comme ces algorithmes sont obligatoires en Chine pour certaines applications (comme c'est le cas de l'algorithme russe Magma décrit dans le RFC 8891), il était nécessaire que TLS (RFC 8446) puisse les utiliser, indépendamment de l'opinion des cryptographes « occidentaux » à leur sujet. Ce RFC traite de deux algorithmes de chiffrement symétrique (« SM4 ») avec chiffrement intègre, une courbe elliptique (« curveSM2 «), un algorithme de condensation (« SM3 »), un algorithme de signature (« SM2 ») utilisant curveSM2 et SM3, et un d'échange de clés fondé sur ECDHE sur SM2. (Au passage, saviez-vous qu'il existe une courbe elliptique française officielle ?)
On notera que les Chinois n'ont pas poussé leurs algorithmes qu'à l'IETF, certains sont aussi normalisés à l'ISO (ISO/IEC 14888-3:2018, ISO/IEC 10118-3:2018 et ISO/IEC 18033-3:2010).
Le RFC ne décrit pas les algorithmes eux-mêmes, uniquement comment les utiliser dans le contexte de TLS 1.3 (RFC 8446). Si vous êtes curieux, les normes chinoises sont :
Les deux algorithmes de chiffrement symétrique sont désormais dans
le
registre IANA sous les noms de
TLS_SM4_GCM_SM3
et
TLS_SM4_CCM_SM3
. L'algorithme de signature,
sm2sig_sm3
est dans le
registre approprié. La courbe elliptique
curveSM2
a été ajoutée
à un autre registre.
Je ne connais pas de mise en œuvre de ces algorithmes dans les bibliothèques TLS habituelles. Si vous avez des informations… Mais Wireshark va bientôt savoir les afficher.
Ah, et si vous vous intéressez à l'Internet en Chine, je vous recommande le livre de Simone Pieranni, Red Mirror.
Date de publication du RFC : Mars 2021
Auteur(s) du RFC : L. Velvindron (cyberstorm.mu), S. Farrell (Trinity College Dublin)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF uta
Première rédaction de cet article le 24 mars 2021
Dans le cadre général de l'abandon des versions 1.0 et 1.1 du protocole TLS (cf. RFC 8996), ce RFC déclare que ces vieilles versions ne doivent plus être utilisées pour le courrier (mettant ainsi à jour le RFC 8314).
Ce RFC 8314 spécifiait l'usage de TLS pour la soumission et la récupération de courrier. Sa section 4.1 imposait une version minimale de TLS, la 1.1. Cette 1.1 étant officiellement abandonnée pour obsolescence dans le RFC 8996, la version minimale est maintenant la 1.2. C'est tout, ce RFC est simple et court.
Date de publication du RFC : Mars 2021
Auteur(s) du RFC : K. Moriarty (Dell EMC), S. Farrell (Trinity College Dublin)
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 24 mars 2021
Dernière mise à jour le 25 mars 2021
Ce RFC est très court, car il s'agit juste de formaliser une évidence : les versions 1.0 et 1.1 du protocole de cryptographie TLS ne devraient plus être utilisées, elles souffrent de diverses failles, notamment de sécurité. Les seules versions de TLS à utiliser sont la 1.2 (recommandée depuis 2008 !) et la 1.3 (publiée en 2018). Ainsi, une bibliothèque TLS pourra retirer tout le code correspondant à ces versions abandonnées, ce qui diminuera les risques (moins de code, moins de bogues).
Que reproche-t-on exactement à ces vieux protocoles (section 1 du RFC) ?
Désormais, la programmeuse ou le programmeur qui veut faire mincir son code en retirant TLS 1.0 et 1.1 peut, si des utilisateurs contestent, s'appuyer sur cette décision de l'IETF. Désormais, la règle est simple : le client ne doit pas proposer TLS 1.0 et 1.1, et s'il le fait le serveur ne doit pas l'accepter. Cela concerne de nombreux RFC, qui mentionnaient 1.0 et 1.1, et tous n'ont pas encore été mis à jour. Ainsi, le RFC 7562 est toujours d'actualité, simplement la mention qu'il fait de TLS 1.1 est réputée supprimée. De même, le RFC 7525, qui résume les bonnes pratiques d'utilisation de TLS doit désormais se lire en oubliant les quelques endroits où il cite encore TLS 1.1. D'autres RFC avaient déjà été abandonnés, comme par exemple le RFC 5101.
Donc, pour résumer les points pratiques de ce RFC (sections 4 et 5) :
ClientHello
, le serveur TLS
ne doit jamais l'accepter.ClientHello
,
le serveur TLS ne doit jamais l'accepter.Si vous êtes programmeu·r·se, virez le code de TLS 1.0 et 1.1 de vos logiciels. (OpenSSL a prévu de le faire en 2022.) Notez que certains protocoles récents comme Gemini ou DoH (RFC 8484) imposaient déjà TLS 1.2 au minimum.
Comme le note la section 7 du RFC, suivre les recommandations de
sécurité exposées ici va affecter l'interopérabilité : on ne pourra
plus communiquer avec les vieilles machines. J'ai à la maison une
vieille tablette pour laquelle le
constructeur ne propose pas de mise à jour logicielle et qui,
limitée à TLS 1.0, ne peut d'ores et déjà plus se connecter à
beaucoup de sites Web en HTTPS. L'obsolescence
programmée en raison de la sécurité… Plus grave, des
organisations peuvent être coincées avec une vieille version de TLS
sur des équipements, par exemple de contrôle industriel, qu'on ne
peut pas mettre à jour. (Lors des discussions à l'IETF sur ce RFC,
des personnes avaient suggéré d'attendre que le niveau d'utilisation
de TLS 1.0 et 1.1 tombe en dessous d'une certaine valeur, avant
d'abandonner officiellement ces protocoles. L'IETF a finalement
choisi une approche plus volontariste. Mais pensez aux
établissements comme les hôpitaux, avec tous les systèmes contrôlés
par des vieux PC pas mettables à jour.) Comme toujours en sécurité,
il n'y a pas de solution parfaite, uniquement des compromis. Le site de test TLS
montre ici un site Web
d'une banque qui continue à proposer TLS 1.0 et 1.1, ce qui baisse
sa note globale mais est peut-être justifié par le désir de ne pas
laisser tomber les clients qui ne peuvent pas facilement changer
leur logiciel : https://www.ssllabs.com/
Au contraire, voici ce qu'affiche un Firefox récent quand on essaie de se connecter à un vieux site Web qui n'accepte toujours pas TLS 1.2 :
À noter que DTLS 1.0 (RFC 4347) est également abandonné. Cela laisse DTLS 1.2, le 1.1 n'ayant jamais été normalisé, et le 1.3 n'étant pas prêt à l'époque (il a depuis été publié dans le RFC 9147).
Les RFC 2246 (TLS 1.0) et RFC 4346 (TLS 1.1) ont été officiellement reclassifiés comme n'ayant plus qu'un intérêt historique. Le RFC 7507 est également déclassé, le mécanisme qu'il décrit n'étant utile qu'avec TLS 1.0 et 1.1.
Date de publication du RFC : Février 2021
Auteur(s) du RFC : B.E. Carpenter (Univ. of Auckland), S. Farrell (Trinity College Dublin)
Expérimental
Première rédaction de cet article le 8 février 2021
L'épidémie de Covid-19 a remis en cause pas mal de choses dans nos sociétés, certaines fois très gravement. En moins dramatique, elle oblige l'IETF à adapter ses processus, étant donné qu'aucune réunion en présentiel n'a pu se tenir en 2020 et qu'on ne sait pas quand cela pourra reprendre. Or, la présence physique aux réunions était nécessaire pour se qualifier pour le NomCom, le comité de nomination. Ce RFC propose à titre expérimental de nouveaux critères de sélection. (Ces critères sont devenus officiels avec le RFC 9389.)
Ce NomCom a pour tâche, décrite dans le RFC 8713, de désigner des personnes nommées pour remplir certains rôles à l'IETF. Il est composé de volontaires qui, normalement, doivent avoir participé en présentiel à un minimum de réunions IETF. Aujourd'hui, comme vous le savez, les réunions physiques ne sont plus possibles (à l'heure où j'écris, la réunion de San Francisco est très incertaine). Ce RFC modifie donc les règles pour l'année 2021 (le RFC 8788 avait déjà traité le cas de 2020). Il est officiellement expérimental (cf. RFC 3933) ; si les vaccins tiennent leur promesse, l'idée pourra être abandonnée et on reviendra aux pratiques d'avant. Mais, à l'heure actuelle, personne ne peut faire de pronostics sérieux dans ce domaine… (En octobre 2021, l'« expérience » a été prolongée.) Peut-être faudra-t-il se résigner à une modification permanente du RFC 8713 (section 2 de notre RFC).
En attendant que le futur s'éclaircisse, les règles pour cette année sont donc modifiées (section 4). Au lieu du seul critère de présence physique aux réunions, on accepte désormais pour sièger au NomCom les gens qui ont participé aux réunions en ligne (qui nécessitent une inscription et un enregistrement de « présence »), ou bien ont été président d'un groupe de travail ou bien auteur d'au moins deux RFC dans les cinq dernières années. Comme pour le critère précédent de présence physique, le but (section 3) est de vérifier que les membres du NomCom connaissent bien l'IETF.
D'autres critères auraient été possibles, mais ont été rejetés (section 5), comme l'écriture d'Internet-Drafts ou l'envoi de messages sur les listes de diffusion de l'IETF. Notez que la qualité des contributions à l'IETF ne rentre pas en ligne de compte, car elle est subjective.
Pour déterminer la liste des critères, le RFC se fonde sur une analyse de données (annexe A). Les données de départ ne sont pas parfaites (si quelqu'un s'inscrit à une réunion sous le nom de John Smith et à la suivante en mettant J. A. Smith, il apparaitra comme deux personnes différentes) mais c'est mieux que rien. Les jolis diagrammes de Venn en art ASCII montrent que les nouveaux critères permettent en effet d'élargir l'ensemble des volontaires potentiels, et que les critères rejetés n'auraient de toute façon pas eu d'influence.
L'expérience a fait l'objet d'un rapport publié en septembre 2021.
Date de publication du RFC : Février 2021
Auteur(s) du RFC : Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 23 février 2021
Ce RFC normalise un nouvel algorithme pour la détection de paquets perdus dans TCP. Dans des cas comme celui où l'application n'a rien à envoyer (fin des données, ou attente d'une requête), RACK-TLP détecte les pertes plus rapidement, en utilisant des mesures fines du RTT, et en sollicitant des accusés de réception dans de nouveaux cas.
Pour comprendre cet algorithme et son intérêt, il faut revoir à quoi sert un protocole de transport comme TCP. Les données sont transmises sous forme de paquets (au format IP, dans le cas de l'Internet), paquets qui peuvent être perdus en route, par exemple si les files d'attente d'un routeur sont pleines. Le premier travail d'un protocole de transport est de réémettre les paquets perdus, de façon à ce que les applications reçoivent bien toutes les données (et dans l'ordre car, en prime, les paquets peuvent arriver dans le désordre, ce qu'on nomme un réordonnancement, qui peut être provoqué, par exemple, lorsque deux chemins sont utilisés pour les paquets). Un protocole de transport a donc besoin de détecter les pertes. Ce n'est pas si évident que cela. La méthode « on note quand on a émis un paquet et, si on n'a pas reçu d'accusé de réception au bout de N millisecondes, on le réémet » fonctionne, mais elle serait très inefficace. Notamment, le choix de N est difficile : trop court, et on déclarerait à tort des paquets comme perdus, réémettant pour rien, trop long et et ne détecterait la perte que trop tard, diminuant la capacité effective.
Le RFC original sur TCP, le RFC 793, expliquait déjà que N devait être calculé dynamiquement, en tenant compte du RTT attendu. En effet, si l'accusé de réception n'est pas arrivé au bout d'une durée égale au RTT, attendre davantage ne sert à rien. (Bien sûr, c'est plus compliqué que cela : le RTT peut être difficile à mesurer lorsque des paquets sont perdus, il varie dans le temps, puisque le réseau a pu changer, et il peut y avoir des temps de traitement qui s'ajoutent au RTT, il faut donc garder un peu de marge.) Armé de cette mesure dynamique du RTT, TCP peut calculer un RTO (Retransmission TimeOut, RFC 793, section 3.7) qui donne le temps d'attente. Le concept de RTO a été ensuite détaillé dans le RFC 6298 qui imposait un minimum d'une seconde (ce qui est beaucoup, par exemple à l'intérieur d'un même centre de données).
Mais d'autres difficultés surgissent ensuite. Par exemple, les accusés de réception de TCP indiquent le dernier octet reçu. Si on envoie trois paquets (plus rigoureusement trois segments, le terme utilisé par TCP) de cent octets, et qu'on reçoit un accusé de réception pour le centième octet indique que le premier paquet est arrivé mais ne dit rien du troisième. Peut-être est-il arrivé et que le second est perdu. Dans ce cas, la réémission du troisième paquet serait du gaspillage. Les accusés de réception cumulatifs de TCP ne permettent pas au récepteur de dire qu'il a reçu les premier et troisième paquets. Les SACK (Selective Acknowledgments) du RFC 2018 ont résolu ce problème. Dans l'exemple ci-dessus, SACK aurait permis au récepteur de dire « j'ai reçu les octets 0 - 100 et 200 - 300 [le second nombre de chaque bloc indique l'octet suivant le bloc] ».
Ensuite, on peut optimiser l'opération en n'attendant pas le RTO, dont on a vu qu'il était souvent trop long. L'idée de base est que, si le récepteur reçoit des données qui ne sont pas dans l'ordre attendu (le troisième paquet, alors qu'on n'a toujours pas vu le second), on renvoie un accusé de réception pour les données déjà reçues et déjà confirmées. Lorsque l'émetteur reçoit un accusé de réception qu'il a déjà vu (en fait, plusieurs), il comprend que des données manquent et il peut réémettre tout de suite, même si le RTO n'est pas écoulé. Ce mécanisme d'accusés de réception dupliqués (DUPACK, pour DUPlicate ACKnowledgment) a été décrit dans le RFC 5681, puis le RFC 6675. Une de ses faiblesses, que corrige notre RFC, est qu'il ne peut pas être utilisé à la fin d'une transmission, puisqu'il n'y a plus de données qui arrivent, empêchant le récepteur de détecter les pertes ou réordonnancements, et obligeant à attendre le RTO.
Et enfin, il faut se rappeler que le protocole de transport a une autre importante responsabilité : s'il doit s'assurer que toutes les données arrivent, et donc réémettre les paquets manquants, il doit aussi le faire en évitant la congestion (RFC 5681). Si des paquets se perdent, c'est peut-être que le réseau est saturé et réémettre trop brutalement pourrait encore aggraver la situation. Ceci dit, l'algorithme RACK-TLP décrit dans notre RFC ne traite que de la détection de pertes, les méthodes précédentes pour le contrôle de congestion restent valables. (C'est une des nouveautés de RACK-TLP : il sépare la détection de pertes du contrôle de congestion, pour lequel il existe plusieurs algorithmes comme le NewReno du RFC 6582 ou le CUBIC du RFC 8312. Ce découplage permet de faire évoluer séparement les algorithmes, et rend donc le système plus souple.)
Voici, désolé pour cette introduction un peu longue, on peut maintenant passer au sujet principal de ce RFC, RACK-TLP. Cet algorithme, ou plutôt ces deux algorithmes, vont être plus efficaces (détecter les pertes plus vite) que DUPACK, notamment à la fin d'une transmission, ou lorsqu'il y a des pertes des paquets retransmis, ou encore des réordonnancements fréquents des paquets (section 2.2 de notre RFC). RACK (Recent ACKnowledgment) va utiliser le RTT des accusés de réception pour détecter certaines pertes, alors que TLP (Tail Loss Probe) va émettre des paquets de données pour provoquer l'envoi d'accusés de réception par le récepteur.
La section 3 du RFC donne une vision générale des deux algorithmes. Commençons par RACK (Recent ACKnowledgment). Son but va être de détecter plus rapidement les pertes lorsque un paquet arrive au récepteur dans le désordre (paquet des octets 200 à 300 alors que le précédent était de 0 à 100, par exemple). Il va utiliser SACK (RFC 2018) pour cela, et RACK dépend donc du bon fonctionnement de SACK (section 4) et en outre, au lieu de ne regarder que les numéros de séquence contenus dans les ACK, comme le faisait DUPACK, il va également regarder le temps écoulé depuis l'émission d'un paquet. Les détails figurent en section 6 du RFC.
Quant à TLP (Tail Loss Probe), son rôle est de titiller le récepteur pour que celui-ci émette des accusés de récéption, même s'il n'en voyait pas la nécessité. Par exemple, si on arrive à la fin d'une session, l'émetteur a fini d'envoyer ses paquets, mais pas encore reçu les accusés de réception. Aucun envoi ne déclenchera donc DUPACK. Le principe de TLP est donc de solliciter un accusé de réception pour voir si on obtient un duplicata. RACK pourra alors utiliser cet accusé dupliqué. (Les deux algorithmes sont forcément utilisés ensemble, on n'utilise pas TLP sans RACK.) Les détails de TLP sont dans la section 7.
La résolution doit être meilleure qu'un quart du RTT. À l'intérieur d'un centre de données, cela implique de pouvoir compter les microsecondes, et, sur l'Internet public, les millisecondes.
La section 9 du RFC discute des avantages et inconvénients de RACK-TLP. Le gros avantage est que tout segment de données envoyé, même si c'est une retransmission, peut permettre de détecter des pertes. C'est surtout intéressant lorsqu'on est à la fin des données (et il n'y a donc plus rien à transmettre) ou lorsque des retransmissions sont perdues. Outre le cas de la fin du flot de données, les propriétés de RACK-TLP sont également utiles lorsque l'application garde la connexion ouverte mais n'a temporairement rien à transmettre (cas de sessions interactives, ou de protocoles requête/réponse comme EPP, où il ne se passe pas forcément quelque chose en permanence).
Mais comme la perfection n'existe pas en ce bas monde, RACK-TLP a aussi des inconvénients. Le principal est qu'il oblige à garder davantage d'état puisqu'il faut mémoriser l'heure de départ de chaque segment (contrairement à ce que proposait le RFC 6675), et avec une bonne résolution (un quart du RTT, demande le RFC). Une telle résolution n'est pas facile à obtenir dans un centre de données où les RTT sont très inférieurs à la milliseconde. Ceci dit, certaines mises en œuvre de TCP font déjà cela, même si, en théorie, elles pourraient se contenter de moins.
RACK-TLP coexiste avec d'autres algorithmes de détection de pertes, comme le classique RTO (RFC 6298) mais aussi avec ceux des RFC 7765, RFC 5682 et RFC 3522. De même, il coexiste avec divers algorithmes de contrôle de la congestion (RFC 5681, RFC 6937) puisqu'il se contente de détecter les pertes, il ne décide pas de comment on évite la congestion. En gros, RACK-TLP dit « ce segment est perdu » et la décision de réémission de celui-ci passera par l'algorithme de contrôle de la congestion, qui verra quand envoyer ces données à nouveau.
RACK-TLP est explicitement conçu pour TCP, mais il pourra aussi être utilisé dans le futur avec d'autres protocoles de transport, comme le futur QUIC.
RACK-TLP ne date pas d'aujourd'hui et il est déjà mis en œuvre dans Linux, FreeBSD et dans des systèmes d'exploitation moins connus comme Windows.
Date de publication du RFC : Juillet 2021
Auteur(s) du RFC : N. Jenkins, R. Stepanek (Fastmail)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF calext
Première rédaction de cet article le 20 juillet 2021
Beaucoup d'applications gèrent des agendas, avec des réunions à ne pas oublier, des évènements récurrents, des rendez-vous précis. Le format traditionnel d'échange de ces applications est iCalendar, normalisé dans le RFC 5545 (ou, dans sa déclinaison en JSON, jCal, dans le RFC 7265). Ce RFC propose une syntaxe JSON mais, surtout, un modèle de données très différent, JSCalendar. Le but est de remplacer iCalendar.
Les principes de base ? Simplicité (au moins dans les cas simples, car les calendriers sont des bêtes compliquées…), uniformité (autant que possible, une seule façon de représenter un évènement), tout en essayant de permettre des conversions depuis iCalendar (RFC 5545 et RFC 7986), donc en ayant un modèle de données compatible. JSCalendar, comme son nom l'indique, utilise JSON, plus exactement le sous-ensemble i-JSON, normalisé dans le RFC 7493.
Bon, mais pourquoi ne pas avoir gardé iCalendar ? Parce qu'il est trop complexe avec plusieurs formats de date, parce que ses règles de récurrence sont ambigües et difficiles à comprendre, parce que sa syntaxe est mal définie. jCal n'a pas ce dernier problème mais il garde tous les autres. On voit même des logiciels utiliser leur propre représentation en JSON des données iCalendar au lieu d'utiliser jCal. Bref, JSCalendar préfère repartir, sinon de zéro, du moins d'assez loin.
Voici un exemple très simple et très minimal de représentation d'un évènement en JSCalendar :
{ "@type": "Event", "uid": "a8df6573-0474-496d-8496-033ad45d7fea", "updated": "2020-01-02T18:23:04Z", "title": "Some event", "start": "2020-01-15T13:00:00", "timeZone": "America/New_York", "duration": "PT1H" }
Comme avec iCalendar, les données JSCalendar peuvent être échangées par courrier ou avec n'importe quel autre protocole de son choix comme JMAP ou WebDAV.
Avant d'attaquer ce format JSCalendar, rappelez-vous qu'il utilise JSON et que la terminologie est celle de JSON. Ainsi, on nomme « objet » ce qui, selon le langage utilisé, serait un dictionnaire ou un tableau associatif. Ensuite, outre les types de base de JSON, JSCalendar a des types supplémentaires (section 1), notamment :
Id
, un identificateur, pour que les
objets aient un nom simple et unique (c'est une chaîne de
caractères JSON). On peut par exemple utiliser un UUID (RFC 9562). Dans l'exemple ci-dessus, le membre
uid
de l'objet est un
Id
.UTCDateTime
, une date-et-heure (un
instant dans le temps) au format du RFC 3339
dans une chaîne de caractères JSON, est obligatoirement en
UTC. Un
tel type est intéressant pour les évènements internationaux comme
une vidéoconférence. Dans l'exemple
ci-dessus, le membre updated
est de ce
type.LocalDateTime
, une date-et-heure selon
le fuseau horaire local. Un tel type est utile pour les évènements
locaux, comme un pique-nique. Dans
l'exemple ci-dessus, qui concerne une réunion en présentiel,
start
est de ce type. On a besoin des deux
types, à la fois parce que les évènements en distanciel et en
présentiel n'ont pas les mêmes besoins, et aussi parce que le
décalage entre les deux peut varier. Les calendriers, c'est
compliqué et le RFC cite l'exemple de la
LocalDateTime
2020-10-04T02:30:00
qui n'existe pas à
Melbourne car le passage à
l'heure d'été fait qu'on saute de 2h à 3h,
mais qui peut apparaitre dans les calculs
(start
+ une durée) ou bien si les règles de
l'heure d'été changent.Duration
est une durée, exprimée comme
en iCalendar (PT1H
pour « une heure » dans
l'exemple ci-dessus, ce serait P1Y
pour une
année). Gag : une journée (P1D
) ne fait pas
forcément 24 heures, par exemple si on passe à l'heure d'été
pendant cette journée.SignedDuration
, une durée qui peut être
négative.TimeZoneId
, un identifiant pour un
fuseau horaire, pris dans la base de l'IANA, par
exemple Europe/Paris
.PatchObject
est un pointeur JSON (ces
pointeurs sont normalisés dans le RFC 6901)
qui permet de désigner un objet à modifier.Relation
est un objet, et plus un
simple type scalaire comme les précédents. Il permet d'indiquer
une relation entre deux objets, notamment vers un objet parent ou
enfant..Link
est également une relation et donc
un objet, mais vers le monde extérieur. Il a des propriétés comme
href
(dont la valeur est, comme vous vous en
doutez, un URI) ou cid
qui
identifie le contenu vers lequel on pointe, dans la syntaxe du
RFC 2392.Les types de JSCalendar figurent dans un registre IANA, qu'on peut remplir avec la procédure « Examen par un expert » du RFC 8126.
Bien, maintenant que nous avons tous nos types, construisons des objets. Il y en a de trois types (section 2) :
Event
(l'exemple ci-dessus, regardez
sa propriété @type
) est un évènement
ponctuel, par exemple une réunion professionnelle ou une
manifestation de rue. Il a une date-et-heure de départ, et une
durée.Task
est une tâche à accomplir, elle
peut avoir une date-et-heure limite et peut avoir une durée
estimée.Group
est un groupe d'évènements JSCalendar.JSCalendar n'a pas de règles de canonicalisation (normalisation)
générales, car cela dépend trop de considérations sémantiques que le
format ne peut pas connaitre. Par exemple, JSON permet des
tableaux, et JSCalendar utilise cette
possibilité mais, quand on veut décider si deux tableaux sont
équivalents, doit-on tenir compte de l'ordre des éléments
([1, 2] == [2, 1]
) ? Cela dépend de
l'application et JSCalendar ne fixe donc pas de règles pour ce
cas. (Un cas rigolo et encore pire est celui des valeurs qui sont
des URI
puisque la canonicalisation des URI dépend du plan -
scheme.)
Quelles sont les propriétés typiques des objets JSCalendar (section 4) ? On trouve notamment, communs aux trois types d'objets (évènement, tâche et groupe) :
@type
, le type d'objet (Event
dans l'exemple ci-dessus, un des trois types possibles).uid
(a8df6573-0474-496d-8496-033ad45d7fea
dans
l'exemple), l'identificateur de l'objet (il est recommandé que ce
soit un des UUID du RFC 9562).prodID
, un identificateur du logiciel
utilisé, un peu comme le User-Agent:
de
HTTP. Le
RFC suggère d'utiliser un FPI.updated
, une date-et-heure de type
UTCDateTime
.title
et
description
, des chaînes de caractères
utiles.locations
(notez le S) qui désigne les
lieux physiques de l'évènement. C'est compliqué. Chaque lieu est
un objet de type Location
qui a comme
propriétés possibles un type (tiré du registre
des types de lieux créé par le RFC 4589), et une localisation sous forme de
latitude et
longitude (RFC 5870).virtualLocations
, qui indique des
informations comme l'URI (via la propriété du même nom). Ainsi,
une conférence en ligne organisée par l'assocation Parinux via
BigBlueButton aura comme
virtualLocations
{"@type":
"VirtualLocation", "uri":
"https://bbb.parinux.org/b/ca--xgc-4r3-n8z", …}
.color
permet de suggérer au logiciel
une couleur à utiliser pour l'affichage, qui est une valeur
RGB en hexadécimal ou bien un nom de couleur
CSS.Et je suis loin d'avoir cité toutes les propriétés possibles, sans compter celles spécifiques à un type d'objet.
Pour le cas de locations
, le RFC fournit un
exemple de vol international (quand il était encore possible de prendre
l'avion) :
{ "...": "", "title": "Flight XY51 to Tokyo", "start": "2020-04-01T09:00:00", "timeZone": "Europe/Berlin", "duration": "PT10H30M", "locations": { "418d0b9b-b656-4b3c-909f-5b149ca779c9": { "@type": "Location", "rel": "start", "name": "Frankfurt Airport (FRA)" }, "c2c7ac67-dc13-411e-a7d4-0780fb61fb08": { "@type": "Location", "rel": "end", "name": "Narita International Airport (NRT)", "timeZone": "Asia/Tokyo" } } }
Notez les UUID pour identifier les lieux, et le changement de fuseau horaire entre le départ et l'arrivée. Si l'évènement a lieu à la fois en présentiel et en distanciel (ici, le concert est dans un lieu physique identifié par ses coordonnées géographiques, mais aussi diffusé en ligne), cela peut donner :
{ "...": "", "title": "Live from Music Bowl: The Band", "description": "Go see the biggest music event ever!", "locale": "en", "start": "2020-07-04T17:00:00", "timeZone": "America/New_York", "duration": "PT3H", "locations": { "c0503d30-8c50-4372-87b5-7657e8e0fedd": { "@type": "Location", "name": "The Music Bowl", "description": "Music Bowl, Central Park, New York", "coordinates": "geo:40.7829,-73.9654" } }, "virtualLocations": { "1": { "@type": "VirtualLocation", "name": "Free live Stream from Music Bowl", "uri": "https://stream.example.com/the_band_2020" } }, ...
Les fournisseurs de logiciel peuvent ajouter des propriétés
définies par eux. Dans ce cas, le RFC recommande fortement qu'ils
les nomment en les faisant précéder d'un nom de domaine identifiant le fournisseur. Si
celui-ci a example.com
et veut une propriété
toto
, il la nommera
example.com:toto
. C'est évidemment une solution
temporaire, les fournisseurs ont tout intérêt à enregistrer ces
propriétés pour qu'elles puissent servir à tout le monde. Le
mécanisme d'enregistrement de nouvelles propriétés est « Examen par
un expert » (RFC 8126) et les propriétés sont
dans un
registre IANA.
Passons maintenant à un aspect compliqué mais indispensable des
calendriers : les règles de récurrence, comme « Réunion de service
tous les premiers lundis du mois à 10 h, sauf jour férié ». Il est
important de maintenir ces évènements récurrents sous forme de
règles, et de ne pas de les instancier immédiatement, car
l'application des règles peut donner des résultats différents dans
le futur. JSCalendar permet une propriété
recurrenceRules
qui décrit ces règles de
récurrence et évidemment une
excludedRecurrenceRules
car s'il n'y a pas
d'exceptions, ce n'est pas drôle. Exprimer les règles n'est pas
facile. L'idée est de spécifier la récurrence sous forme d'une série
de règles, chacune indiquant une fréquence (annuelle, mensuelle, etc), à
chaque fois à partir de la propriété start
, le
calendrier utilisé (qui doit être un des calendriers disponibles
dans CLDR), la marche à suivre quand une règle
produit une date invalide dans ce calendrier (annuler l'évènement,
le mettre avant, le mettre après), et plein d'autres propriétés
optionnelles comme le jour du mois (« réunion tous les 6 du mois »),
de la semaine (« tous les mercredis »), etc. Il faut ensuite
plusieurs pages au RFC pour expliquer la façon subtile dont les
règles sont appliquées, et se combinent. Les exceptions de la
excludedRecurrenceRules
fonctionnent de la même
façon, et sont ensuite soustraites des dates-et-heures sélectionnées
par les règles.
Le RFC fournit cet exemple de récurrence : le premier
avril arrive tous les ans (et dure une journée, notez le
duration
) :
{ "...": "", "title": "April Fool's Day", "showWithoutTime": true, "start": "1900-04-01T00:00:00", "duration": "P1D", "recurrenceRules": [{ "@type": "RecurrenceRule", "frequency": "yearly" }] }
Alors qu'ici, on fait du yoga une demi-heure chaque jour à 7 h du matin :
{ "...": "", "title": "Yoga", "start": "2020-01-01T07:00:00", "duration": "PT30M", "recurrenceRules": [{ "@type": "RecurrenceRule", "frequency": "daily" }] }
Bon, ensuite, quelques détails pour aider le logiciel à classer
et présenter les évènements. Un évènement peut avoir des propriétés
comme priority
(s'il y a deux réunions en même
temps, laquelle choisir ?), freeBusyStatus
(est-ce que cet évènement fait que je doive être considéré comme
occupé ou est-il compatible avec autre chose ?),
privacy
(cet évènement peut-il être affiché
publiquement ?), participationStatus
(je
viendrai, je ne viendrai pas, je ne sais pas encore…), et plein
d'autres encore.
Il y a aussi des propriétés concernant d'autres sujets, par
exemple l'adaptation locale. Ainsi, la propriété
localizations
indique la ou les langues à utiliser (leur valeur est une
étiquette de langue du RFC 5646).
Toutes les propriétés vues jusqu'à présent étaient possibles pour tous les types d'objets JSCalendar (évènement, tâche et groupe). D'autres propriétés sont spécifiques à un ou deux types :
Event
) peuvent avoir
entre autres
un start
(un
LocalDateTime
qui indique le début de
l'évènement) et un duration
(la durée de
l'évènement).Task
) peuvent avoir un
start
mais aussi un due
qui indique la date où elles doivent être terminées, et un
percentComplete
(pour les chefs de
projet…).Group
) ont, par exemple,
entries
, qui indique les objets qui sont
membres du groupe.Les fichiers à ce format JSCalendar sont servis sur l'Internet
avec le type
application/jscalendar+json
.
Pour terminer, voyons un peu la sécurité de JSCalendar (section 7). Évidemment, toutes les informations stockées dans un calendrier sont sensibles : rares sont les personnes qui accepteraient de voir la totalité de leur agenda être publiée ! Celui-ci permet en effet de connaitre le graphe social (les gens qu'on connait), les lieux où passe la personne, ses habitudes et horaires, bref, que des choses personnelles. Toute application qui manipule des données JSCalendar doit donc soigneusement veiller à la confidentialité de ces données, et doit les protéger.
Le risque d'accès en lecture n'est pas le seul, la modification non autorisée de l'agenda serait également un problème, elle pourrait permettre, par exemple, de faire déplacer une personne en un lieu donné. D'autres conséquences d'une modification pourraient toucher la facturation (location d'une salle pendant une certaine durée) ou d'autres questions de sécurité (activer ou désactiver une alarme à certains moments). L'application qui manie du JSCalendar doit donc également empêcher ces changements non autorisés.
Notez que ces problèmes de sécurité ne concernent pas le format à proprement parler, mais les applications qui utilisent ce format. Rien dans JSCalendar, dans ce RFC, ne le rend particulièrement vulnérable ou au contraire protégé, tout est dans l'application.
Les règles de récurrence sont complexes et, comme tout programme, elles peuvent entrainer des conséquences imprévues, avec consommation de ressources informatiques associées. Un exemple aussi simple que la session de yoga quotidienne citée plus haut pourrait générer une infinité d'évènements, si elle était mise en œuvre par une répétition systématique, jusqu'à la fin des temps. Les programmes doivent donc faire attention lorsqu'ils évaluent les règles de récurrence.
L'un des buts d'un format standard d'évènements est évidemment l'échange de données. Il est donc normal et attendu qu'une application de gestion d'agenda reçoive des objets JSCalendar de l'extérieur, notamment via l'Internet. On voit souvent du iCalendar en pièce jointe d'un courrier, par exemple. Il ne faut pas faire une confiance aveugle à ces données venues d'on ne sait où, et ne pas tout intégrer dans le calendrier du propriétaire. L'authentification du courrier (par exemple avec DKIM, RFC 6376) aide un peu, mais n'est pas suffisante.
Les fuseaux horaires sont une source de confusion sans fin. Un utilisateur qui n'est pas attentif, lorsqu'on lui dit qu'un évènement a lieu à 10h30 (UTC-5) peut croire que c'est 10h30 de son fuseau horaire à lui. (Et encore, ici, j'ai indiqué le fuseau horaire de manière lisible, contrairement à ce que font la plupart des Étatsuniens, qui utilisent leurs sigles à eux comme PST, ou ce que font les Français qui n'indiquent pas le fuseau horaire, persuadés qu'ils sont que le monde entier est à l'heure de Paris.) Cette confusion peut être exploitée par des méchants qui utiliseraient les fuseaux horaires de manière délibérement peu claire pour tromper quelqu'un sur l'heure d'un évènement. (Dans la série Mad Men, les hommes qui ne supportent pas l'arrivée d'une femme dans le groupe lui donnent des informations trompeuses sur les heures de réunion, pour qu'elle manque ces évènements.)
Où trouve-t-on du JSCalendar à l'heure actuelle ? Mobilizon ne l'a pas encore (l'action « Ajouter à mon agenda » exporte du iCalendar). Fastmail annonce qu'ils gèrent JSCalendar (mais apparemment seulement dans les échanges JMAP, pas avec l'import/export normal). Cyrus l'a aussi (si vous avez des détails, ça m'intéresse). Pour le cas des récurrences, vous avez une intéressante mise en œuvre en Python. Attention, il y a aussi plein de logiciels qui s'appellent « jscalendar » (notamment des widgets JavaScript pour afficher un calendrier dans un formulaire Web) mais qui n'ont aucun rapport avec ce RFC.
Date de publication du RFC : Février 2021
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 10 février 2021
Le protocole de récupération d'informations auprès d'un registre RDAP peut parfois rapporter une grande quantité d'informations pour chaque objet récupéré (un nom de domaine, par exemple). Le RDAP originel ne permettait pas au serveur de ne renvoyer qu'une information partielle s'il trouvait que cela faisait trop. C'est désormais possible avec l'extension de ce RFC.
Le but est évidemment d'économiser les ressources du serveur, aussi bien que celles du client, en récupérant et en transférant moins de données. Avant notre RFC, la seule solution était de tout envoyer au client, même s'il n'en utiliserait pas la plus grande partie. (Si, au lieu de ne vouloir qu'une partie de chaque objet, on veut une partie des objets, il faut regarder le RFC 8977.)
Ce RFC étend donc le langage de requêtes de RDAP (normalisé dans
le RFC 9082), avec un paramètre
fieldSet
(section 2 de notre RFC) qui permet de
sélectionner une partie des champs de la réponse. Voici un exemple
d'URL pour
une requête de tous les noms de domaine de
.com
:
https://rdap.verisign.example/domains?name=*.com&fieldSet=afieldset
Et comment on connait les champs possibles ? Notre RFC introduit
un subsetting_metadata
qui indique l'ensemble
de champs courant, et les ensembles possibles, chacun identifié par
un nom, et comportant les liens (RFC 8288) à
utiliser. (Vous aurez une erreur HTTP 400 si vous êtes assez
vilain·e pour faire une requête RDAP avec un ensemble inexistant.)
Et pour savoir si le serveur RDAP que vous interrogez accepte cette
nouvelle extension permettant d'avoir des réponses partielles,
regardez si subsetting
apparait dans le tableau
rdapConformance
de la réponse du serveur (cette
extension est désormais dans le
registre IANA). Voici l'exemple que donne le RFC :
{ "rdapConformance": [ "rdap_level_0", "subsetting" ], ... "subsetting_metadata": { "currentFieldSet": "afieldset", "availableFieldSets": [ { "name": "anotherfieldset", "description": "Contains some fields", "default": false, "links": [ { "value": "https://example.com/rdap/domains?name=example*.com &fieldSet=afieldset", "rel": "alternate", "href": "https://example.com/rdap/domains?name=example*.com &fieldSet=anotherfieldset", "title": "Result Subset Link", "type": "application/rdap+json" } ] }, ... ] }, ... "domainSearchResults": [ ... ] }
Mais il y a encore plus simple que de regarder
subsetting_metadata
: notre RFC décrit, dans sa
section 4, trois ensembles de champs standards qui, espérons-le,
seront présents sur la plupart des serveurs RDAP :
id
, qui ne renvoie dans la réponse que
les identificateurs des objets (handle
pour
les contacts, unicodeName
pour les noms de
domaine, etc).brief
, un peu plus bavard, qui renvoie
l'information minimale (telle qu'estimée par le serveur) sur les
objets.full
, qui renvoie la réponse complète
(actuellement, avant le déploiement de ce RFC 8982,
c'est la valeur par défaut).
Un exemple de réponse avec l'ensemble id
, où on
n'a que le nom (ASCII) des domaines :
{ "rdapConformance": [ "rdap_level_0", "subsetting" ], ... "domainSearchResults": [ { "objectClassName": "domain", "ldhName": "example1.com", "links": [ { "value": "https://example.com/rdap/domain/example1.com", "rel": "self", "href": "https://example.com/rdap/domain/example1.com", "type": "application/rdap+json" } ] }, { "objectClassName": "domain", "ldhName": "example2.com", "links": [ { "value": "https://example.com/rdap/domain/example2.com", "rel": "self", "href": "https://example.com/rdap/domain/example2.com", "type": "application/rdap+json" } ] }, ... ] }
Quelles mises en œuvre sont disponibles ? Il en existe une
chez
.it
mais elle n'est
accessible sur leur serveur de test que si on est
authentifié.
Comme RDAP, contrairement à whois, permet l'authentification des clients (RFC 7481), on pourra imaginer des cas où l'ensemble complet de la réponse ne sera envoyé qu'à certains utilisateurs (section 8).
L'annexe A du RFC revient sur un choix sur lequel je suis passé rapidement : le fait que le client spécifie le nom d'un ensemble pré-défini de champs, plutôt que la liste des champs qui l'intéressent, ce qui serait plus souple. Mais cela aurait été compliqué pour les structures JSON profondes (il faudrait une syntaxe riche et peu lisible), et cela aurait compliqué les autorisations : que faire si un client demande deux champs et qu'il n'est autorisé que pour un seul ? Outre ces questions génériques de tout protocole utilisant JSON, il y avait des problèmes spécifiques à RDAP, comme le fait que le contenu sur une entité peut être réparti dans plusieurs structures JSON (ah, le jCard…) Des solutions existent, comme le langage de requêtes CQL, mais il a semblé trop coûteux pour un intérêt limité.
Date de publication du RFC : Février 2021
Auteur(s) du RFC : F. Gont (SI6 Networks), S. Krishnan (Kaloom), T. Narten, R. Draves (Microsoft Research)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 1 mars 2021
Une des particularités d'IPv6 est de disposer d'un mécanisme, l'autoconfiguration sans état qui permet à une machine de se fabriquer une adresse IP globale sans serveur DHCP. Autrefois, ce mécanisme créait souvent l'adresse IP à partir de l'adresse MAC de la machine et une même machine avait donc toujours le même identifiant (IID, Interface IDentifier, les 64 bits les plus à droite de l'adresse IPv6), même si elle changeait de réseau et donc de préfixe. Il était donc possible de « suivre à la trace » une machine, ce qui pouvait poser des problèmes de protection de la vie privée. Notre RFC, qui remplace le RFC 4941 fournit une solution, sous forme d'un mécanisme de création d'adresses temporaires, partiellement aléatoires (ou en tout cas imprévisibles). Les changements sont assez importants depuis le RFC 4941, qui traitait un peu les adresses IPv6 temporaires comme des citoyennes de deuxième classe. Désormais, elles sont au contraire le choix préféré.
L'autoconfiguration sans état (SLAAC, pour Stateless
Address Autoconfiguration), normalisée dans le RFC 4862, est souvent présentée comme un des gros
avantages d'IPv6. Sans nécessiter de serveur
central (contrairement à DHCP), ce mécanisme permet à chaque machine
d'obtenir une adresse globale (IPv4, via le
protocole du RFC 3927, ne permet que des
adresses purement locales) et unique. Cela se fait en concaténant un
préfixe (par exemple
2001:db8:32:aa12::
), annoncé par le
routeur, à un identifiant
d'interface (par exemple
213:e8ff:fe69:590d
, l'identifiant de mon PC
portable - les machines individuelles, non partagées, comme les
ordiphones sont particulièrement sensibles
puisque la connaissance de la machine implique celle de son maître),
typiquement dérivé de l'adresse MAC.
Partir de l'adresse MAC présente des avantages (quasiment toute
machine en a une, et, comme elle est relativement unique, l'unicité
de l'adresse IPv6 est obtenue facilement) mais aussi un
inconvénient : comme l'identifiant d'interface est toujours le même,
cela permet de reconnaître une machine, même lorsqu'elle change de
réseau. Dès qu'on voit une machine
XXXX:213:e8ff:fe69:590d
(où XXXX est le
préfixe), on sait que c'est mon PC. Un tiers qui écoute le réseau
peut ainsi suivre « à la trace » une machine, et c'est pareil pour
les
machines avec lesquelles on correspond sur l'Internet.
Or, on ne peut pas cacher son adresse IP, il faut l'exposer pour
communiquer. Le RFC 7721 analyse le problème
plus en détail (voir aussi le RFC 7707).
Les sections 1.2 et 2 de notre RFC décrivent la question à résoudre. Elles notent entre autre que le problème n'est pas aussi crucial que l'avaient prétendu certains. En effet, il existe bien d'autres façons, parfois plus faciles, de suivre une machine à la trace, par exemple par les fameux petits gâteaux de HTTP (RFC 6265) mais aussi par des moyens de plus haute technologie comme les métadonnées de la communication (taille des paquets, écart entre les paquets) ou les caractéristiques matérielles de l'ordinateur, que l'on peut observer sur le réseau. Parmi les autres méthodes, notons aussi que si on utilise les adresses temporaires de notre RFC 8981 mais qu'on configure sa machine pour mettre dynamiquement à jour un serveur DNS avec cette adresse, le nom de domaine suffirait alors à suivre l'utilisateur à la trace !
La section 2.2 commence à discuter des solutions possibles. DHCPv6 (RFC 3315, notamment la section 12) serait évidemment une solution, mais qui nécessite l'administration d'un serveur. L'idéal serait une solution qui permette, puisque IPv6 facilite l'existence de plusieurs adresses IP par machine, d'avoir une adresse stable pour les connexions entrantes et une adresse temporaire, non liée à l'adresse MAC, pour les connexions sortantes, celles qui « trahissent » la machine. C'est ce que propose notre RFC, en générant les adresses temporaires selon un mécanisme pseudo-aléatoire (ou en tout cas imprévisible à un observateur extérieur).
Enfin, la section 3 décrit le mécanisme de protection lui-même. Deux algorithmes sont proposés mais il faut bien noter qu'une machine est toujours libre d'en ajouter un autre, le mécanisme est unilatéral et ne nécessite pas que les machines avec qui on correspond le comprennent. Il suffit que l'algorithme respecte ces principes :
Le premier algorithme est détaillé dans la section 3.3.1 et nécessite l'usage d'une source aléatoire, selon le RFC 4086. On génère un identifiant d'interface avec cette source (attention, certains bits sont réservés, cf. RFC 7136), on vérifie qu'il ne fait pas partie des identifiants réservés du RFC 5453, puis on la préfixe avec le préfixe du réseau.
Second algorithme possible, en section 3.3.2, une génération à partir de l'algorithme du RFC 7217, ce qui permet d'utiliser le même algorithme pour ces deux catégories d'adresses, qui concernent des cas d'usage différents. Le principe est de passer les informations utilisées pour les adresses stables du RFC 7217 (dont un secret, pour éviter qu'un observateur ne puisse déterminer si deux adresses IP appartiennent à la même machine), en y ajoutant le temps (pour éviter la stabilité), à travers une PRF comme SHA-256. Plus besoin de générateur aléatoire dans ce cas.
Une fois l'adresse temporaire générée (les détails sont dans la section 3.4), et testée (DAD), il faut la renouveler de temps en temps (sections 3.5 et 3.6, cette dernière expliquant pourquoi renouveler une fois par jour est plus que suffisant). L'ancienne adresse peut rester active pour les connexions en cours mais plus pour de nouvelles connexions.
Notez que le RFC impose de fournir un moyen pour activer ou débrayer ces adresses temporaires, et que cela soit par préfixe IP, par exemple pour débrayer les adresses temporaires pour les adresses locales du RFC 4193.
Maintenant que l'algorithme est spécifié, la section 4 du RFC reprend de la hauteur et examine les conséquences de l'utilisation des adresses temporaires. C'est l'occasion de rappeler un principe de base de la sécurité : il n'y a pas de solution idéale, seulement des compromis. Si les adresses temporaires protègent davantage contre le « flicage », elles ont aussi des inconvénients comme de rendre le débogage des réseaux plus difficile. Par exemple, si on note un comportement bizarre associé à certaines adresses IP, il sera plus difficile de savoir s'il s'agit d'une seule machine ou de plusieurs. (Le RFC 7217 vise justement à traiter ce cas.)
Et puis les adresses temporaires de notre RFC ne concernent que l'identifiant d'interface, le préfixe IP reste, et il peut être révélateur, surtout s'il y a peu de machines dans le réseau. Si on veut vraiment éviter le traçage par adresse IP, il faut utiliser Tor ou une technique équivalente (cf. section 9).
L'utilisation des adresses temporaires peut également poser des problèmes avec certaines pratiques prétendument de sécurité comme le fait, pour un serveur, de refuser les connexions depuis une machine sans enregistrement DNS inverse (un nom correspondant à l'adresse, via un enregistrement PTR). Cette technique est assez ridicule mais néanmoins largement utilisée.
Un autre conflit se produira, note la section 8, si des mécanismes de validation des adresses IP source sont en place dans le réseau. Il peut en effet être difficile de distinguer une machine qui génère des adresses IP temporaires pour se protéger contre les indiscrets d'une machine qui se fabrique de fausses adresses IP source pour mener une attaque.
Quelles sont les différences entre le précédent RFC et celui-ci ? La section 5 du RFC résume les importants changements qu'il y a eu depuis le RFC 4941. Notamment :
Passons maintenant aux mises en œuvre. L'ancienne norme, le RFC 4941, est appliqué par presque tous les
systèmes d'exploitation, souvent de manière plus protectrice de la
vie privée, par exemple en étant activé par défaut. Les
particularités de notre nouveau RFC ne sont pas toujours d'ores et
déjà présentes. Ainsi, il existe des patches
pour
FreeBSD et pour
Linux mais pas forcément intégrés aux versions
officielles. Sur
Linux, le paramètre sysctl qui contrôle ce protocole est
net.ipv6.conf.XXX.use_tempaddr
où XXX est le
nom de l'interface réseau, par exemple eth0
. En
mettant dans le fichier /etc/sysctl.conf
:
# Adresses temporaires du RFC 8981 net.ipv6.conf.default.use_tempaddr = 2
On met en service les adresses temporaires, non « pistables » pour
toutes les interfaces (c'est le sens de la valeur
default
). Pour s'assurer que les réglages soient bien pris en
compte, il vaut mieux faire en sorte que le module
ipv6
soit chargé tout de suite (sur
Debian, le mettre dans
/etc/modules
) et que les interfaces fixes aient
leur propre réglage. Par exemple, sur Debian,
on peut mettre dans /etc/network/interfaces
:
iface eth2 inet dhcp pre-up sysctl -w net.ipv6.conf.eth2.use_tempaddr=2
Ou alors il faut nommer explicitement ses interfaces :
net.ipv6.conf.eth0.use_tempaddr = 2
Notez aussi
qu'ifconfig
n'affiche pas quelles
adresses sont les temporaires (mais ip addr
show
le fait).
Notons que l'adresse « pistable » est toujours présente mais
vient s'y ajouter une adresse temporaire choisie au hasard. Selon
les règles du RFC 6724, rappelées dans la
section 3.1 de notre RFC, c'est cette adresse temporaire qui sera
choisie, en théorie, pour les connexions sortantes (voir aussi le
RFC 5014 pour une API permettant de contrôler ce
choix). Avec Linux, il faut pour cela que
use_tempaddr
vale plus que un (sinon, l'adresse
temporaire est bien configurée mais pas utilisée par
défaut). ifconfig affichera donc :
wlan0 Link encap:Ethernet HWaddr 00:13:e8:69:59:0d ... inet6 addr: 2001:db8:32:aa12:615a:c7ba:73fb:e2b7/64 Scope:Global inet6 addr: 2001:db8:32:aa12:213:e8ff:fe69:590d/64 Scope:Global
puis, au démarrage suivant, l'adresse temporaire (la première ci-dessus) changera :
wlan0 Link encap:Ethernet HWaddr 00:13:e8:69:59:0d ... inet6 addr: 2001:db8:32:aa12:48a9:bf44:5167:463e/64 Scope:Global inet6 addr: 2001:db8:32:aa12:213:e8ff:fe69:590d/64 Scope:Global
Sur NetBSD, il n'y a qu'une seule variable
syscvtl pour toutes les interfaces. Il suffit donc de mettre dans
/etc/sysctl.conf
:
net.inet6.ip6.use_tempaddr=1
Pour que l'adresse temporaire soit utilisée par défaut, c'est
net.inet6.ip6.prefer_tempaddr
. Sur
FreeBSD, je n'ai pas essayé, mais je suppose
que les variables sysctl au nom bien parlant
net.inet6.ip6.use_tempaddr
et
net.inet6.ip6.prefer_tempaddr
sont là pour
cela.
Date de publication du RFC : Février 2021
Auteur(s) du RFC : J. Arkko, T. Hardie
Pour information
Première rédaction de cet article le 20 février 2021
L'atelier de l'IAB DEDR (Design Expectations vs. Deployment Reality) s'est tenu à Kirkkonummi en juin 2019 (oui, il faut du temps pour publier un RFC de compte-rendu). Son but était, par delà la constatation que le déploiement effectif des protocoles conçus par l'IETF ne suit pas toujours les attentes initiales, d'explorer pourquoi cela se passait comme cela et ce qu'on pouvait y changer.
Souvent, lors de la mise au point d'un nouveau protocole, les personnes qui y travaillent ont en tête un modèle de déploiement. Par exemple, pour le courrier électronique, l'idée dominante était qu'il y aurait souvent un serveur de messagerie par personne. On sait que ce n'est pas la situation actuelle. En raison de soucis de sécurité, de pressions économiques et/ou politiques, et d'autres facteurs, on a aujourd'hui un courrier nettement plus centralisé, avec un caractère oligopolistique marqué, dominé par une poignée d'acteurs qui dictent les règles. Cette centralisation n'est pas souhaitable (sauf pour les GAFA). Mais où est-ce que le dérapage a commencé ? Parfois, cela a été rapide, et parfois cela a pris beaucoup de temps.
Quelques exemples :
.us
qui était découpé
en entités géographiques de plus en plus spécifiques). Mais
l'espace des noms est aujourd'hui plus plat que prévu. On voit
même des organisations qui ont, mettons,
example.com
et qui, lançant un nouveau
produit, mettons foobar
, réservent
foobar.com
, voire
foobar-example.com
au lieu de tout simplement
créer foobar.example.com
(ce qui serait en
outre moins cher). Un autre exemple de centralisation est la
tendance à abandonner les résolveurs locaux (qui, il est vrai,
sont souvent mal gérés ou menteurs) pour des gros
résolveurs publics comme Google Public
DNS ou Quad9 (et cette
centralisation, contrairement à ce qui est souvent prétendu, n'a
rien à voir avec
DoH).L'IAB avait produit un RFC sur la question « qu'est-ce qui fait qu'un protocole a du succès », le RFC 5218. Dans un autre document, le RFC 8170, l'IAB étudiait la question des transitions (passage d'un protocole à un autre, ou d'une version d'un protocole à une autre). L'atelier de Kirkkonummi avait pour but de poursuivre la réflexion, en se focalisant sur les cas où les suppositions de base n'ont pas tenu face à la réalité.
L'agenda de l'atelier était structuré en cinq sujets, le passé (qu'avons-nous appris), les principes (quelles sont les forces à l'œuvre et comment les contrer ou les utiliser), la centralisation (coûts et bénéfices), la sécurité et le futur (fera-t-on mieux à l'avenir et savons-nous comment). 21 articles ont été reçus (ils sont en ligne), et 30 personnes ont participé.
Sur le passé, l'atelier a étudié des exemples de déploiement de protocoles, comme PKIX, DNSSEC, le NAT, l'IoT, etc. Souvent, ce qui a été effectivement déployé ne correspondait pas à ce qui était prévu. Par exemple, un protocole très demandé n'a pas eu le succès attendu (c'est le cas de DNSSEC : tout le monde réclame de la sécurité, mais quand il faut travailler pour la déployer, c'est autre chose) ou bien il n'a pas été déployé comme prévu. C'est d'autant plus vrai que l'IETF n'a pas de pouvoir : elle produit des normes et, ensuite, d'autres acteurs décident ou pas de déployer, et de la manière qu'ils veulent (le « marché » sacré). Le RFC cite quelques leçons de l'expérience passée :
Sur les principes, on a quand même quelques conclusions solides. Par exemple, il est clair qu'un nouveau protocole qui dépend de plusieurs autres services qui ne sont pas encore déployés aura davantage de mal à s'imposer qu'un protocole qui peut s'installer unilatéralement sans dépendre de personne. Et puis bien sûr, il y a l'inertie. Quand quelque chose marche suffisamment bien, il est difficile de la remplacer. (Le RFC donne l'exemple de BGP, notamment de sa sécurité.) Et cette inertie génère le phénomène bien reconnu de la prime au premier arrivant. Si un protocole fournit un nouveau service, un remplaçant aura le plus grand mal à s'imposer, même s'il est meilleur, face au tenant du titre. Le monde du virtuel est lourd et ne bouge pas facilement. Quand un protocole a eu un succès fou (terme défini et discuté dans le RFC 5218), le remplacer devient presque impossible. Autre principe analysé : l'IETF ne devrait de toute façon pas avoir d'autorité sur la façon dont ses protocoles sont déployés. Ce n'est pas son rôle et elle n'a pas de légitimité pour cela. Et, on l'a dit, les usages sont durs à prévoir. L'atelier a aussi constaté que certains modèles de déploiement, qui s'appliquent à beaucoup de cas, n'avaient pas été prévus et planifiés. C'est le cas de l'extrême centralisation, bien sûr, mais également le cas de « tout via le Web » qui fait que tout nouveau service sur l'Internet tend à être accessible uniquement à distance, avec les protocoles et les formats du Web. Cela tend à créer des silos fermés, même s'ils utilisent des protocoles ouverts. (C'est en réponse à ce problème qu'a été créée la licence Affero.)
Concernant le troisième sujet, la centralisation, l'atelier a constaté que la tendance à la centralisation n'est pas toujours claire dès le début. Le RFC cite l'exemple (très mauvais, à mon avis) de DoH. Autre constatation, la sécurité, et notamment le risque d'attaques par déni de services réparties est un puissant facteur de centralisation. Si vous voulez gérer un site Web sur un sujet controversé, avec des opposants puissants et prêts à tout, vous êtes quasiment obligé de faire appel à un hébergement de grande taille (face aux dDoS, la taille compte). Ceci dit, l'atelier a aussi identifié des forces qui peuvent aller en sens contraire à la centralisation. Une fédération peut mieux résister aux attaques (qui ne sont pas forcément techniques, cela peut être aussi la censure) qu'un gros silo centralisé, le chiffrement peut permettre même aux petits de limiter la surveillance exercée par les gros, les navigateurs Web peuvent adopter de meilleures pratiques pour limiter les données envoyées à la grosse plate-forme à laquelle on accède (toute cette liste du RFC est d'un optimisme souvent injustifié…), l'interopérabilité peut permettre davantage de concurrence (cf. le bon rapport du Conseil National du Numérique), et certaines tendance lourdes peuvent être combattues également par des moyens non-techniques (le RFC cite la régulation, ce qui n'est pas facilement accepté par les libertariens, même si ceux-ci ne sont pas plus nombreux à l'IETF qu'ailleurs).
Quatrième sujet, la sécurité. Traditionnellement, le modèle de menace de l'Internet (RFC 3552 mais aussi RFC 7258) est que tout ce qui est entre les deux machines qui communiquent peut être un ennemi. Cela implique notamment le choix du chiffrement de bout en bout. Toutefois, ce modèle de menace ne marche pas si c'est l'autre pair qui vous trahit. Ainsi, utiliser TLS quand vous parlez à Facebook vous protège contre des tiers mais pas contre Facebook lui-même. À l'heure où tant de communications en ligne sont médiées par des GAFA, c'est un point à prendre en considération. (Attention à ne pas jeter le bébé avec l'eau du bain ; le chiffrement de bout en bout reste nécessaire, mais n'est pas suffisant. Certains FAI remettent en cause le modèle de menace traditionnel pour combattre ou restreindre le chiffrement, en faisant comme si les GAFA étaient les seuls à faire de la surveillance.) Les participants à l'atelier sont plutôt tombés d'accord sur la nécessité de faire évoluer le modèle de menace mais il faudra veiller à ce que ne soit pas un prétexte pour relativiser l'importance du chiffrement, qui reste indispensable. À noter une autre limite d'un modèle de menace qui ne mentionnerait que les tiers situés sur le trajet : non seulement la partie distante (par exemple Facebook) peut être une menace, mais votre propre machine peut vous trahir, par exemple si vous n'utilisez pas du logiciel libre.
Enfin, cinquième et dernier sujet, le futur. L'IETF n'a pas de pouvoir contraignant sur les auteurs de logiciels, sur les opérateurs ou sur les utilisateurs (et heureusement). Elle ne peut agir que via ses normes. Par exemple, pour l'Internet des Objets, le RFC 8520 permet davantage d'ouverture dans la description des utilisations qu'un objet fera du réseau. Outre la production de bonnes normes, l'IETF peut être disponible quand on a besoin d'elle, par exemple comme réserve de connaissance et d'expertise.
La conclusion du RFC (section 5) indique que le problème est complexe, notamment en raison de la variété des parties prenantes : tout le monde n'est pas d'accord sur les problèmes, et encore moins sur les solutions. (Il n'existe pas de « communauté Internet », à part dans des discours politiciens.) D'autre part, certains des problèmes n'ont pas de solution évidente. Le RFC cite ainsi les dDoS, ou le spam. Cette absence de solution satisfaisante peut mener à déployer des « solutions » qui ont un rôle négatif. Le RFC note ainsi à juste titre que l'absence d'une solution de paiement en ligne correcte (anonyme, simple, bon marché, reposant sur des normes ouvertes, etc) pousse à faire dépendre la rémunération des créateurs de la publicité, avec toutes ses conséquences néfastes. Et l'Internet fait face à de nombreux autres défis stratégiques, comme la non-participation des utilisateurs aux questions qui les concernent, ou comme la délégation de décisions à des logiciels, par exemple le navigateur Web.
On l'a dit, la conclusion est que l'IETF doit se focaliser sur ce qu'elle sait faire et bien faire, la production de normes. Cela implique :
Un autre compte-rendu de cet atelier, mais très personnel, avait été fait par Geoff Huston.
Date de publication du RFC : Mars 2021
Auteur(s) du RFC : F. Gont (SI6 Networks), J. Zorz (6connect), R. Patterson (Sky UK)
Pour information
Réalisé dans le cadre du groupe de travail IETF v6ops
Première rédaction de cet article le 11 mars 2021
Au moment où je commençais à écrire cet article, mon FAI a trouvé drôle de changer le préfixe IPv6 attribué à mon réseau à la maison. Cela a parfaitement illustré le problème que décrit ce RFC : comment réagissent les machines IPv6 qui ont obtenu une adresse dynamique lors d'une rénumérotation brutale et sans avertissement ? En résumé : ça ne se passe pas toujours bien.
Posons le problème. Notre RFC se concentre sur le cas des machines qui ont obtenu une adresse par le système SLAAC (StateLess Address AutoConfiguration), normalisé dans le RFC 4862. Le routeur émet des messages RA (Router Advertisement) qui indiquent quel est le préfixe d'adresses IP utilisé sur le réseau local. Les machines vont alors prendre une adresse dans ce préfixe, tester qu'elle n'est pas déjà utilisée et c'est parti. Le routeur n'a pas besoin de mémoriser quelles adresses sont utilisées, d'où le terme de « sans état ». L'information distribuée par ces RA a une durée de vie, qui peut être de plusieurs jours, voire davantage.
Maintenant, envisageons un changement du préfixe, quelle que soit sa raison. Si ce changement est planifié, le routeur va accepter les deux préfixes, il va annoncer le nouveau mais l'ancien marchera encore, pendant la durée de vie qui était annoncée, et tout le monde sera heureux et communiquera. Mais si le changement de préfixe n'est pas planifié ? Par exemple, si le routeur obtient lui-même le préfixe dynamiquement (par exemple par le DHCP-PD du RFC 8415) puis qu'il redémarre et qu'il n'avait pas noté le préfixe précédent ? Il ne pourra alors pas continuer à router l'ancien préfixe, que les machines du réseau local utiliseront encore pendant un certain temps, à leur grand dam.
C'est ce qui s'est passé le 19 janvier 2021 (et les jours suivants), lorsque Free a subitement renuméroté les préfixes IPv6 d'un bon nombre de clients. Free n'utilise pas DHCP-PD, je suppose que les Freebox étaient configurées par un autre procédé, en tout cas, les utilisateurs n'ont pas été prévenus. Sur le réseau local, les machines avaient acquis les deux préfixes, l'ancien, conservé en mémoire, et le nouveau. Et l'ancien préfixe ne marchait plus (n'était pas routé), entrainant plein de problèmes. (Notons que l'adresse IPv4 avait également été changée mais ce n'est pas le sujet ici.) Il a fallu redémarrer toutes les machines, pour qu'elles oublient l'ancien préfixe. Une illustration parfaite du problème qui a motivé ce RFC : SLAAC avait été prévu pour des préfixes qui changent rarement et de manière planifiée (avec réduction préalable de la durée de vie, pour que la transition soit rapide). Les surprises, qu'elles soient dues à un problème technique ou simplement au manque de planification, ne sont pas prises en compte.
À part le mode Yolo de Free, dans quelles conditions aura-t-on de ces renumérotages brutaux, ces flash-renumbering events ? Un exemple est celui où le RA qui doit annoncer la fin de l'ancien préfixe (avec une durée de vie nulle) s'est perdu (le multicast n'est pas toujours fiable). Mais, de toute façon, envoyer ce RA d'avertissement suppose de savoir qu'un nouveau préfixe a remplacé l'ancien. Or, cette information n'est pas toujours disponible. Un exemple est celui où il n'y a même pas de RA d'avertissement, car le CPE obtient son préfixe IPv6 par DHCP-PD (Prefix Delegation, RFC 8415, section 6.3), avant de le redistribuer sur le réseau local en SLAAC. Si ce CPE redémarre, et n'a pas de mémoire permanente, il va peut-être obtenir un autre préfixe via DHCP-PD, ce qui fera un flash-renumbering event sur le réseau local. (Il peut aussi y avoir désynchronisation entre la durée de vie des RA faits via SLAAC et la durée du bail DHCP. En théorie, c'est interdit, mais certains CPE où les deux protocoles sont gérés par des modules logiciels différents font cette erreur.)
La Freebox n'utilise apparemment pas DHCP-PD mais le même problème d'ignorance du préfixe précédent peut survenir si la mise à jour des box est faite par un autre moyen de synchronisation. Bref, des tas de choses peuvent aller mal, particulièrement si l'opérateur réseau est négligent (ce qui a été le cas de Free) mais même parfois s'il essaie de bien faire. À partir de là, si certaines machines du réseau local continuent à utiliser l'ancien préfixe, leurs paquets seront probablement jetés par le routeur et aucune communication ne sera possible.
Le RFC 4861 suggère, dans sa section 6.2.1, pour les durées de validité des préfixes annoncés, des valeurs très élevées (une semaine pour la durée préférée et un mois pour la durée maximale). En cas de renumérotation brutale, c'est beaucoup trop (voir aussi la section 2.2), une machine pourrait rester déconnectée pendant une semaine, voire davantage. Divers trucs permettent d'améliorer un peu les choses, mais avec d'autres inconvénients. Il n'y a pas de méthode propre pour nettoyer les machines du réseau local de la mauvaise information. Bien sûr, le mieux serait qu'il n'y ait pas de renumérotation brutale mais ne nous faisons pas d'illusions : cela arrivera et il faut des solutions pour réparer.
La section 2 du RFC analyse en détail certains aspects du problème. Ainsi, est-ce qu'on ne pourrait pas tout simplement attribuer des préfixes IP fixes aux clients ? Cela simplifierait certainement beaucoup de choses. Mais une étude récente indique qu'un tiers des FAI britanniques utilisent des préfixes dynamiques pour IPv6. C'est certainement pénible pour les clients qui veulent, par exemple, héberger un serveur. Mais c'est un état des choses qu'il va falloir traiter. Même si ces FAI changeaient leurs pratiques, des problèmes subsisteraient, par exemple avec des routeurs à la maison qui sous-alloueraient des préfixes plus spécifiques à une partie de la maison et le feraient de façon dynamique. D'autant plus que l'idéal serait que les clients aient le choix, les adresses fixes pouvant (RFC 7721) poser des problèmes de vie privée (le RFC 4941 ne fait varier que la partie de l'adresse IP spécifique à la machine). Ainsi, la DPA allemande suggère de ne pas les utiliser.
Le fond du problème est évidemment que le routeur qui émet des RA n'a aucun moyen de supprimer les préfixes anciens s'il ne les connait pas. S'ils les connaissait, cela serait trivial, en les annonçant avec une durée de vie de zéro mais attention, lisez plus loin. Dans les cas de redémarrage d'un routeur qui n'a pas de mémoire permanente, ce qui est oublié est oublié et le routeur ne peut pas dire « ce préfixe n'existe plus ». Mais même si le routeur connait les anciens préfixes, il y a un piège avec le point e) de la section 5.5.3 du RFC 4862 : on ne peut pas réduire la durée de vie en dessous de deux heures. C'est déjà mieux qu'une semaine mais c'est encore trop long. Autre piège, même une fois qu'un préfixe n'est plus utilisé pour de nouvelles communications, il peut encore servir pour répondre aux anciennes, donc est encore considéré comme valide pour ce réseau, ce qui fait qu'il n'est pas possible de communiquer avec le nouveau titulaire de ce préfixe (s'il y en a un).Par exemple ce patch Linux viole le RFC 4862 pour permettre une meilleure robustesse en cas de renumérotation.
Autre sujet sur lequel il faudrait peut-être améliorer les protocoles, l'interaction entre DHCP et SLAAC, qui reste peu spécifiée. Au minimum, le logiciel dans le routeur qui reçoit les préfixes par DHCP-PD devrait transmettre au logiciel qui envoie les RA quelles sont les durées de vie à utiliser pour qu'elles soient cohérentes avec la durée du bail DHCP.
Bon, et qu'est-ce qu'on peut faire aujourd'hui pour atténuer les conséquences du problème ? Comme dit plus haut, on pourrait n'utiliser que des préfixes fixes, mais cela ne semble pas réaliste. On pourrait surtout changer les paramètres SLAAC pour des valeurs plus réalistes, en mettant 45 minutes comme durée de vie préférée et 90 minutes comme durée maximale de validité. De telles valeurs conviendraient mieux à la grande majorité des réseaux que les valeurs du RFC 4861. En l'absence de mécanisme pour invalider rapidement les anciens préfixes, de courtes durées de vie sont préférables. Dans certains cas (si on est sûrs qu'il n'y aura pas de rénumérotation brutale et/ou si des machines sont longtemps déconnectées du réseau), des valeurs plus longues peuvent être préférables.
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : M. Loffredo (IIT-CNR/Registro.it), M. Martinelli (IIT-CNR/Registro.it), S. Hollenbeck (Verisign Labs)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 24 janvier 2021
Le protocole RDAP, normalisé notamment dans les RFC 9082 et RFC 9083, permet de récupérer des informations structurées sur des trucs (oui, j'ai écrit « trucs ») enregistrés auprès d'un registre, par exemple des domaines auprès d'un registre de noms de domaine. Voici un RFC tout juste publié qui ajoute à RDAP la possibilité de trier les résultats et également de les afficher progressivement (paging). C'est évidemment surtout utile pour les requêtes de type « recherche », qui peuvent ramener beaucoup de résultats.
Avec des requêtes « exactes » (lookup dans le RFC 9082), le problème est moins grave. Ici, je cherche juste de l'information sur un nom de domaine et un seul :
% curl https://rdap.nic.bzh/domain/chouchen.bzh ... "events" : [ { "eventDate" : "2017-07-12T10:18:12Z", "eventAction" : "registration" }, { "eventDate" : "2020-07-09T09:49:06Z", "eventAction" : "last changed" }, ...
Mais si je me lançais dans une recherche plus ouverte
(search dit le RFC 9082),
avec par exemple la requête domains
(notez le S
à la fin, cf. RFC 9082, section 3.2.1), le
nombre de résultats pourrait être énorme. Par exemple, si je
demandais tous les domaines en
.bzh
avec
https://rdap.nic.bzh/rdap/domains?name=*.bzh
,
j'aurais une réponse d'une taille conséquente. (Et je ne vous dis pas
pour .com
…)
En pratique, cette requête ne fonctionnera pas car je ne connais aucun registre qui autorise les recherches RDAP aux utilisateurs anonymes. Ceux-ci ne peuvent faire que des requêtes exactes, à la fois pour épargner les ressources informatiques (cf. la section 7 du RFC sur la charge qu'impose les recherches), et pour éviter de distribuer trop d'informations à des inconnus pas toujours bien intentionnés. Pour effectuer ces recherches, il faut donc un compte et une autorisation. Autrement, vous récupérez un code 401, 403 ou bien carrément une liste vide.
Et si vous avez une telle autorisation, comment gérer une masse importante de résultats ? Avec le RDAP originel, vous récupérez la totalité des réponses, que vous devrez analyser, et ce sera à vous, client, de trier. (Si le serveur n'envoie qu'une partie des réponses, pour épargner le client et ses propres ressources, il n'a malheureusement aucun moyen de faire savoir au client qu'il a tronqué la liste.) C'est tout le but de notre RFC que de faire cela côté serveur. Des nouveaux paramètres dans la requête RDAP vont permettre de mieux contrôler les réponses, ce qui réduira les efforts du client RDAP, du serveur RDAP et du réseau, et permettra d'avoir des résultats plus pertinents.
La solution ? La section 2 de notre RFC décrit les nouveaux paramètres :
count
: le client demande à être
informé du nombre de trucs que contient la liste des réponses.sort
: le client demande à trier les
résultats.cursor
: ce paramètre permet d'indiquer
un endroit particulier de la liste (par exemple pour la récupérer
progressivement, par itérations successives).
Par exemple,
https://example.com/rdap/domains?name=example*.com&count=true
va récupérer dans .com
(si le registre de
.com
acceptait cette requête…) tous les noms de
domaine dont le nom commence par example
, et
indiquer leur nombre. Une réponse serait, par exemple :
"paging_metadata": { "totalCount": 43 }, "domainSearchResults": [ ... ]
(paging_metadata
est expliqué plus loin.)
Pour le paramètre sort
, le client peut
indiquer qu'il veut un tri, sur quel critère se fait le tri, et que
celui-ci doit être dans l'ordre croissant (a
pour ascending) ou décroissant
(d
pour descending). Ainsi,
https://example.com/rdap/domains?name=*.com&sort=name
demande tous les noms en .com
triés par
nom. https://example.com/rdap/domains?name=*.com&sort=registrationDate:d
demanderait tous les noms triés par date d'enregistrement, les plus
récents en premier.
L'ordre de tri dépend de la valeur JSON du résultat (comparaison
lexicographique
pour les chaînes de caractères et numérique pour les nombres), sauf
pour les adresses IP, qui
sont triées selon l'ordre des adresses et pour les dates qui sont
triées dans l'ordre chronologique. Ainsi, l'adresse
9.1.1.1
est inférieure à
10.1.1.1
(ce qui n'est pas le cas dans l'ordre
lexicographique). Le RFC fait remarquer que tous les SGBD
sérieux ont des fonctions pour traiter ce cas. Ainsi, dans
PostgreSQL, la comparaison des deux chaînes
de caractères donnera :
=> SELECT '10.1.1.1' < '9.1.1.1'; t
Alors que si les adresses IP sont mises dans une colonne ayant le
type correct (INET
), on a le bon résultat :
=> SELECT '10.1.1.1'::INET < '9.1.1.1'::INET; f
Après le sort=
, on trouve le nom de la
propriété sur laquelle on trie. C'est le nom d'un membre de l'objet
JSON de la réponse. Non, en fait, c'est plus compliqué que
cela. Certains membres de la réponse ne sont pas utilisables (comme
roles
, qui est multi-valué) et des informations
importantes (comme registrationDate
cité en
exemple plus haut) ne sont pas explicitement dans la réponse. Notre
RFC définit donc une liste de propriétés utilisables, et explique
comment on les calcule (par exempe,
registrationDate
peut se déduire des
events
). Plutôt que ces noms de propriétés, on
aurait tout pu faire en JSONpath ou JSON
Pointer (RFC 6901) mais ces deux syntaxes sont
complexes et longues
($.domainSearchResults[*].events[?(@.eventAction='registration')].eventDate
est le JSONPath pour registrationDate
). La
mention en JSONPath du critère de tri est donc facultative.
Et, bien sûr, si le client envoie un nom de propriété qui n'existe pas, il récupérera une erreur HTTP 400 avec une explication en JSON :
{ "errorCode": 400, "title": "Domain sorting property 'unknown' is not valid", "description": [ "Supported domain sorting properties are:" "'aproperty', 'anotherproperty'" ] }
Et le troisième paramètre, cursor
? Ce RFC
fournit deux méthodes pour indiquer où on en est dans la liste des
résultats, la pagination par décalage (offset
pagination) et celle par clé (keyset
pagination, qu'on trouve parfois
citée sous le nom ambigu de cursor
pagination, qui désigne plutôt une méthode avec état sur
le serveur). Ces deux méthodes ont en commun de ne
pas nécessiter d'état du côté du serveur. La
pagination par décalage consiste à fournir un décalage depuis le
début de la liste et un nombre d'éléments désiré, par exemple
« donne-moi 3 éléments, commençant au numéro 10 ». Elle est simple à
mettre en œuvre, par exemple avec SQL :
SELECT truc FROM Machins ORDER BY chose LIMIT 3 OFFSET 9;
Mais elle n'est pas forcément robuste si la base est modifiée pendant ce temps : passer d'une page à l'autre peut faire rater des données si une insertion a eu lieu entretemps (cela dépend aussi de si la base est relue à chaque requête paginée) et elle peut être lente (surtout avec RDAP où la construction des réponses prend du temps, alors que celles situées avant le début de la page seront jetées). L'autre méthode est la pagination par clé où on indique une caractéristique du dernier objet vu. Si les données sont triées, il est facile de récupérer les N objets suivants. Par exemple en SQL :
SELECT truc FROM Machins WHERE chose > [la valeur] ORDER BY chose LIMIT 3;
Un inconvénient de cette méthode est qu'il faut un champ (ou un
ensemble de champs) ayant un ordre (et pas de duplicata). RDAP rend
cela plus difficile, en agrégeant des informations provenant de
différentes tables (cf. l'annexe B du RFC). (Voir des descriptions
de cette pagination par clé dans « Paginating
Real-Time Data with Keyset Pagination » ou
« Twitter
Ads API », pour Twitter.)
RDAP permet les deux méthodes, chacune ayant ses avantages et ses
inconvénients. L'annexe B du RFC explique plus en détail ces
méthodes et les choix faits. (Sinon, en dehors de RDAP, un bon
article sur le choix d'une méthode de pagination, avec leur mise en
œuvre dans PostgreSQL est « Five
ways to paginate in Postgres, from the basic to the
exotic ».) Pour RDAP, un
https://example.com/rdap/domains?name=*.com&cursor=offset:9,limit:3
récupérerait une page de trois éléments, commençant au dixième, et
https://example.com/rdap/domains?name=*.com&cursor=key:foobar.com
trouverait les noms qui suivent foobar.com
.
Notez qu'en réalité, vous ne verrez pas directement le décalage, la
clé et la taille de la page dans l'URL : ils sont encodés pour
permettre d'utiliser des caractères quelconques (et aussi éviter que
le client ne les bricole, il est censé suivre les liens, pas
fabriquer les URL à la main). Les exemples du RFC utilisent
Base64 pour l'encodage, en notant qu'on peut
certainement faire mieux.
La capacité du serveur à mettre en œuvre le tri et la pagination
s'indiquent dans le tableau rdapConformance
avec les chaînes sorting
et
paging
(qui sont désormais dans le
registre IANA). Par exemple (pagination mais pas tri) :
"rdapConformance": [ "rdap_level_0", "paging" ]
Il est recommandé que le serveur documente ces possibilités dans
deux nouveaux éléments qui peuvent être présents dans une réponse,
sorting_metadata
et
paging_metadata
. Cela suit les principes
d'auto-découverte de HATEOAS. Dans la
description sorting_metadata
, on a
currentSort
qui indique le critère de tri
utilisé, et availableSorts
qui indique les
critères possibles. Chaque critère est indiqué avec un nom
(property
), le fait qu'il soit le critère par
défaut ou pas, éventuellement une expression JSONPath
désignant le champ de la réponse utilisé et enfin une série de liens
(RFC 8288) qui vont nous indiquer les URL à
utiliser. Pour paging_metadata
, on a
totalCount
qui indique le nombre d'objets
sélectionnés, pageSize
qui indique le nombre
récupérés à chaque itération, pageNumber
qui
dit à quelle page on en est et là encore les liens à suivre. Ce
pourrait, par exemple, être :
"sorting_metadata": { "currentSort": "name", "availableSorts": [ { "property": "registrationDate", "jsonPath": "$.domainSearchResults[*].events[?(@.eventAction==\"registration\")].eventDate", "default": false, "links": [ { "value": "https://example.com/rdap/domains?name=example*.com&sort=name", "rel": "alternate", "href": "https://example.com/rdap/domains?name=example*.com&sort=registrationDate", "title": "Result Ascending Sort Link", "type": "application/rdap+json" }, { "value": "https://example.com/rdap/domains?name=example*.com&sort=name", "rel": "alternate", "href": "https://example.com/rdap/domains?name=example*.com&sort=registrationDate:d", "title": "Result Descending Sort Link", "type": "application/rdap+json" } ] ...
Ici, sorting_metadata
nous indique que le tri
se fera sur la base du nom, mais nous donne les URL à utiliser pour
trier sur la date d'enregistrement. Quant à la pagination, voici un
exemple de réponse partielle, avec les liens permettant de récupérer
la suite :
"paging_metadata": { "totalCount": 73, "pageSize": 50, "pageNumber": 1, "links": [ { "value": "https://example.com/rdap/domains?name=example*.com", "rel": "next", "href": "https://example.com/rdap/domains?name=example*.com&cursor=wJlCDLIl6KTWypN7T6vc6nWEmEYe99Hjf1XY1xmqV-M=", "title": "Result Pagination Link", "type": "application/rdap+json" } ]
L'idée de permettre le contrôle des réponses via des nouveaux
paramètres (count
, sort
et
cursor
, présentés ci-dessus), vient entre autre
du protocole OData. Une autre solution aurait
été d'utiliser des en-têtes HTTP (RFC 7231). Mais
ceux-ci ne sont pas contrôlables depuis le
navigateur, ce qui aurait réduit le nombre de
clients possibles. Et puis cela rend plus difficile
l'auto-découverte des extensions, qui est plus pratique via des
URL, cette
auto-découverte étant en général considérée comme une excellente
pratique REST.
Il existe à l'heure actuelle une seule mise en œuvre de ce RFC
dans le RDAP non public de .it
.
La documentation est en ligne.
Notre RFC permet donc de ne récupérer qu'une partie des objets qui correspondent à la question posée. Si on veut plutôt récupérer une partie seulement de chaque objet, il faut utiliser le RFC 8982.
Date de publication du RFC : Février 2021
Auteur(s) du RFC : D. Wessels (Verisign), P. Barber (Verisign), M. Weinberg (Amazon), W. Kumari (Google), W. Hardaker (USC/ISI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 10 février 2021
Ce nouveau RFC normalise un mécanisme pour ajouter aux zones DNS un condensat qui permet de vérifier leur intégrité. Pourquoi, alors qu'on a TSIG, TLS et SSH pour les transporter, et PGP et DNSSEC pour les signer ? Lisez jusqu'au bout, vous allez voir.
Le nouveau type d'enregistrement DNS créé par ce RFC se nomme ZONEMD
et sa
valeur est un condensat de la zone. L'idée
est de permettre au destinataire d'une zone DNS de s'assurer qu'elle
n'a pas été modifiée en route. Elle est complémentaire des
différentes techniques de sécurité citées plus haut. Contrairement à
DNSSEC, elle protège une zone, pas un enregistrement.
Revenons un peu sur la terminologie. Une zone (RFC 8499, section 7) est un ensemble d'enregistrements servi par les mêmes serveurs. Ces serveurs reçoivent typiquement la zone depuis un maître, et utilisent souvent la technique AXFR (RFC 5936) pour cela. Ce transfert est souvent (mais pas toujours) protégé par le mécanisme TSIG (RFC 8945), qui permet de s'assurer que la zone est bien celle servie par le maître légitime. D'autres techniques de transfert peuvent être utilisées, avec leur propre mécanisme de sécurité. On peut par exemple utiliser rsync sur SSH. Les zones sont souvent stockées dans des fichiers de zone, du genre :
@ IN SOA ns4.bortzmeyer.org. hostmaster.bortzmeyer.org. ( 2018110902 7200 3600 604800 43200 ) IN NS ns4.bortzmeyer.org. IN NS ns2.bortzmeyer.org. IN NS ns1.bortzmeyer.org. IN NS puck.nether.net. IN MX 0 mail.bortzmeyer.org. IN TXT "v=spf1 mx -all" IN A 92.243.4.211 IN AAAA 2001:4b98:dc0:41:216:3eff:fe27:3d3f www IN CNAME ayla.bortzmeyer.org. sub IN NS toto.example.
Donc, les motivations de notre RFC sont d'abord le désir de pouvoir vérifier une zone après son transfert, indépendamment de la façon dont elle a été transférée. Une zone candidate évidente est la racine du DNS (cf. section 1.4.1), publiquement disponible, et que plusieurs résolveurs copient chez eux (RFC 8806). Ensuite, l'idée est de disposer d'un contrôle minimum (une somme de contrôle) pour détecter les modifications accidentelles (le rayon cosmique qui passe). Un exemple de modification accidentelle serait une troncation de la fin de la zone, d'habitude difficile à détecter puisque les fichiers de zone n'ont pas de marques de fin (contrairement à, par exemple, un fichier JSON). Notez que la « vraie » vérification nécessite DNSSEC. Si la zone n'est pas signée, le mécanisme décrit dans ce RFC ne fournit que la somme de contrôle : c'est suffisant contre les accidents, pas contre les attaques délibérées. Mais, de toute façon, tous les gens sérieux utilisent DNSSEC, non ?
Il existe de nombreuses techniques qui fournissent un service qui ressemble plus ou moins à un des deux services mentionnés ci-dessus. L'un des plus utilisés est certainement TSIG (RFC 8945). Beaucoup de transferts de zone (cf. RFC 5936) sont ainsi protégés par TSIG. Il a l'inconvénient de nécessiter un secret partagé entre serveur maître et serveur esclave. SIG(0) (RFC 2931) n'a pas cet inconvénient mais n'est quasiment jamais mis en œuvre. Toujours dans la catégories des protections du canal, on a TLS et l'IETF travaille sur un mécanisme de protection des transferts par TLS, mais on peut aussi imaginer d'utiliser simplement DoT (RFC 7858).
Ceci dit, toutes ces techniques de protection du canal ont le même défaut : une fois le transfert fait, elles ne servent plus à rien. Pas moyen de vérifier si le fichier est bien le fichier authentique. Ainsi, un serveur faisant autorité qui charge une zone à partir d'un fichier sur le disque ne peut pas vérifier que ce fichier n'a pas été modifié. Bien sûr, les protections fournies par le système de fichiers offrent certaines garanties, mais pas parfaites, par exemple si un programme a planté et laissé le fichier dans un état invalide (oui, ça s'est déjà produit). Bref, on préférerait une protection des données et pas seulement du canal.
Alors, pourquoi ne pas simplement utiliser DNSSEC,
qui fournit effectivement cette sécurité des données ? Le problème
est que dans une zone, seules les informations faisant autorité sont
signées. Les délégations (vers un sous-domaine, comme
sub.bortzmeyer.org
dans l'exemple plus haut) et
les colles (adresses IP des serveurs de noms qui sont dans la zone
qu'ils servent) ne sont pas signés. Pour une zone comme la racine,
où il n'y a quasiment que des délégations, ce serait un problème. Et
ce serait encore pire avec les zones qui utilisent NSEC3 (RFC 5155) avec opt-out puisqu'on
ne sait même plus si un nom non-signé existe ou pas. Donc, DNSSEC
est très bien, mais ne suffit pas pour notre cahier des charges.
On a d'autres outils pour la sécurité des données, le plus évident étant PGP (RFC 9580). D'ailleurs, ça tombe bien, la racine des noms de domaine est déjà distribuée avec une signature PGP :
% wget -q https://www.internic.net/domain/root.zone % wget -q https://www.internic.net/domain/root.zone.sig % gpg --verify root.zone.sig root.zone gpg: Signature made Wed Jan 13 08:22:50 2021 CET gpg: using DSA key 937BB869E3A238C5 gpg: Good signature from "Registry Administrator <nstld@verisign-grs.com>" [unknown] gpg: WARNING: This key is not certified with a trusted signature! gpg: There is no indication that the signature belongs to the owner. Primary key fingerprint: F0CB 1A32 6BDF 3F3E FA3A 01FA 937B B869 E3A2 38C5
Notez que Verisign utilise une signature détachée du fichier. Cela permet que le fichier reste un fichier de zone normal et chargeable, mais cela casse le lien entre la signature et le fichier. Bref, cela ne résout pas complètement le problème. (Les historiens et historiennes s'amuseront de noter qu'une autre solution, très proche de celle de notre RFC, figurait déjà dans le RFC 2065, mais qu'elle n'avait eu aucun succès.)
Bon, passons maintenant à la solution (section 1.3). On l'a dit,
le principe est d'avoir un enregistrement de type
ZONEMD
qui contient un
condensat de la zone. Il est généré par le
responsable de la zone et quiconque a téléchargé la zone, par
quelque moyen que ce soit, peut vérifier qu'il correspond bien à la
zone. En prime, si la zone est signée avec DNSSEC, on peut
vérifier que ce ZONEMD
est authentique.
Du fait que ce condensat couvre l'intégralité de la zone, il faut
le recalculer entièrement si la zone change, si peu que ce
soit. Cette solution ne convient donc pas aux grosses zones très
dynamiques (comme
.fr
). Dans le futur, le
ZONEMD
est suffisamment extensible pour que des
solutions à ce problème lui soient ajoutées. En attendant,
ZONEMD
convient mieux pour des zones comme la
racine (rappel, elle est disponible en ligne),
ou comme la zone d'une organisation. La
racine est un cas particulièrement
intéressant car elle est servie par un grand nombre de serveurs,
gérés par des organisations différentes. Sans compter les gens qui
la récupèrement localement (RFC 8806). Le
contrôle de son intégrité est donc crucial. Il y a une discussion en
ce moment au sein du RZERC pour proposer l'ajout
de ZONEMD
dans la racine.
Pour la zone d'une organisation, notre RFC rappelle qu'il est
recommandé d'avoir une diversité des serveurs de nom, afin d'éviter
les SPOF et que c'est donc une bonne idée
d'avoir des serveurs esclaves dans d'autres organisations. Comme
cette diversité peut entrainer des risques (le serveur esclave
est-il vraiment honnête ? le transfert s'est-il bien passé ?), là
aussi, la vérification de l'intégrité s'impose. Autre scénario
d'usage, les distributions de fichiers de zone, comme le fait
l'ICANN avec CZDS ou comme le fait le
registre du .ch
(téléchargez
ici). D'une manière générale, ce contrôle supplémentaire ne
peut pas faire de mal.
Plongeons maintenant dans les détails techniques avec la section
2 du RFC, qui explique le type d'enregistrement
ZONEMD
(code
63). Il se trouve forcément à l'apex
de la zone. Il comprend quatre champs :
Question présentation dans les fichiers de zone, ça ressemble à :
example.com. 86400 IN ZONEMD 2018031500 1 1 ( FEBE3D4CE2EC2FFA4BA99D46CD69D6D29711E55217057BEE 7EB1A7B641A47BA7FED2DD5B97AE499FAFA4F22C6BD647DE )
Ici, le numéro de série est 2018031500, le plan 1 (Simple) et l'algorithme de condensation SHA-384.
Il peut y avoir plusieurs ZONEMD
dans un
fichier, ils doivent avoir des couples {Plan, Algorithme}
différents. (Le but est de fournir plusieurs techniques de
vérification, et de permettre de passer en souplesse de l'une à
l'autre.)
Comment exactement se fait le calcul du condensat ? La section 3
le détaille. D'abord, on supprime de la zone les éventuels
ZONEMD
existants, et on place un
ZONEMD
bidon (il en faut un, en cas de
signature DNSSEC, pour éviter de casser la signature). Si on a
DNSSEC, on signe alors la zone (et il faudra re-faire le
RRSIG
du ZONEMD
après), on
canonicalise la zone (le condensat est calculé sur le format « sur
le câble » pas sur le format texte), et on calcule ensuite le
condensat sur la concaténation des enregistrements (à l'exclusion du
ZONEMD
bidon, qui avait été mis pour DNSSEC, et
de sa signature). On génère ensuite le vrai ZONEMD, puis, si on a
DNSSEC, on recalcule sa signature. Autant dire que vous ne le ferez
pas à la main ! L'endroit logique pour faire ce calcul du
ZONEMD
est dans le logiciel qui fait les
signatures DNSSEC. Notez que, contrairement à DNSSEC, tous les
enregistrements sont utilisés, y compris ceux qui ne font pas
autorité comme les délégations et les colles.
Un mot sur la canonicalisation. Tout calcul d'un condensat cryptographique doit se faire sur des données canonicalisées, c'est-à-dire mises sous une forme canonique, une forme unique et standardisée. Comme le changement d'un seul bit change complètement le condensat, il ne faut pas laisser des petites variations de détail (par exemple, pour le DNS, entre noms comprimés et non-comprimés) à l'imagination de chaque programmeur. Voilà pourquoi notre RFC spécifie rigoureusement une forme canonique (par exemple, les noms ne doivent pas être comprimés).
La vérification se fait de la même façon (section 4 du RFC). On
vérifie que le numéro de série est le même, on
canonicalise, on concatène, on condense et on vérifie qu'on trouve
bien ce qui était dans le ZONEMD
. (S'il y a
plusieurs ZONEMD
, le premier qui marche
suffit.) Le mieux est évidemment de tout valider avec DNSSEC, en
plus.
La section 6 de notre RFC revient sur la sécurité du
ZONEMD
, ce qu'il garantit et ce qu'il ne
garantit pas. D'abord, le point le plus important : sans DNSSEC,
ZONEMD
n'est qu'une somme de
contrôle, il garantit l'intégrité mais pas
l'authenticité, et il ne protège donc que contre les modifications
accidentelles (ce qui est déjà très bien !) Le bit flipping sera détecté mais pas
l'attaque délibérée et soignée. Ainsi, sans DNSSEC, un attaquant
n'aurait, par exemple, qu'à retirer l'enregistrement
ZONEMD
de la zone avant de faire ses
modifications (une attaque par repli).
D'autre part, les algorithmes de cryptographie vieillissent (RFC 7696)
et
ZONEMD
ne peut donc pas garantir de sécurité
sur le long terme. D'ailleurs, sur le long terme, on n'aurait
probablement plus les clés DNSSEC disponibles.
Plus drôle, ZONEMD
permet, comme toute
technique de sécurité, de nouveaux modes de déni de service, comme
l'a déjà expérimenté tout utilisateur de la cryptographie. Comme le
condensat est strictement binaire (il colle aux données, ou pas du
tout), la zone entière peut être considérée comme invalide si un
problème modifie ne serait-ce qu'un seul bit. Le souci de
l'intégrité de la zone et celui de sa disponibilité sont ici
en conflit, et le responsable de la zone doit choisir.
Si vous aimez les comparatifs de performance, la section 7 du RFC
discute de chiffres. Par exemple, une zone de 300 000
enregistrements a été ZONEMD
ifiée en deux
secondes et demi sur un serveur ordinaire. C'est encore trop lent
pour être utilisable sur des zones comme
.com
mais cela montre
que des grandes zones qui ne changent pas trop souvent peuvent être
protégées.
Et question mises en œuvre, ça se passe comment ? J'ai testé
l'excellente bibliothèque ldns. Le code avec
calcul de ZONEMD
devrait être publié dans une
version officielle en 2021 mais, en attendant,
c'est déjà dans git, en
. Une fois compilée, on peut fabriquer
le https://github.com/NLnetLabs/ldns
ZONEMD
(ici, avec la zone d'exemple montrée
au début de cet article). On génère une clé, puis on signe, avec
calcul de ZONEMD
:
% ./ldns-keygen -a ED25519 bortzmeyer.org Kbortzmeyer.org.+015+00990 % ./ldns-signzone -o bortzmeyer.org -z 1:1 bortzmeyer-org.zone Kbortzmeyer.org.+015+00990 % cat bortzmeyer-org.zone.signed ... bortzmeyer.org. 3600 IN ZONEMD 2018110902 1 1 b1af60c3c3e88502746cf831d2c64a5399c1e3c951136d79e63db2ea99898ba9f2c4f9b29e6208c09640aaa9deafe012 bortzmeyer.org. 3600 IN RRSIG ZONEMD 15 2 3600 20210215174229 20210118174229 990 bortzmeyer.org. FJ98//KDBF/iMl1bBkjGEBT3eeuH/rJDoGRDAoCBEocudLbA7F/38qwLpsPkr7oQcgbkLzhyHA7BXG/5fAwkAg==
(L'argument 1:1
signifie plan Simple et
algorithme SHA-384.) On peut ensuite vérifier la zone. Si elle a
un enregistrement ZONEMD
, il est inclus dans
la vérification :
% ./ldns-verify-zone bortzmeyer-org.zone.signed Zone is verified and complete
Si je modifie une délégation (qui n'est pas protégée par DNSSEC mais
qui l'est par ZONEMD
) :
% ./ldns-verify-zone bortzmeyer-org.zone.signed There were errors in the zone
Il y a aussi les dns-tools des gens
du .cl
. Pour
BIND, c'est en
cours de réflexion. Apparemment,
Unbound y travaille également. Voir aussi
l'exposé
de Verisign à la réunion OARC de juillet 2022. Des tests sont en cours
(septembre 2022).
Depuis, un bon article de Jan-Piet Mens a expliqué le fonctionnement pratique de ZONEMD, et les outils utilisables.
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : N. Kuhn (CNES), E. Lochin (ENAC)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF nwcrg
Première rédaction de cet article le 23 janvier 2021
Prenons de la hauteur (c'est le cas de le dire). Les communications via satellite posent des tas de problèmes techniques. L'envoi de chaque bit coûte cher, et dans certains cas d'usage, les pertes de paquets sont nombreuses, et on cherche donc à optimiser. Ce RFC explique comment on peut utiliser le network coding pour améliorer les performances de ces liaisons. Mais c'est encore un travail en cours.
Le network coding est un mécanisme d'encodage des données qui combine différentes données dans une même chaîne de bits, avant de les re-séparer à l'autre bout. Dans le cas le plus trivial on fait un XOR des données. Mais il y a d'autres moyens comme la convolution. Le network coding n'est pas un algorithme unique mais une classe de méthodes. Ces méthodes permettent de faire voyager davantage de données sur un même canal et/ou de diminuer le délai de transmission total, en ajoutant de la redondance. Le nom de network coding venait de l'encodage réalisé dans le réseau, pour tenir compte de sa topologie. Mais on peut utiliser des techniques du même genre de bout en bout (c'est le cas de FEC, dans le RFC 5052). (Je vous laisse en apprendre davantage sur Wikipédia). La question de l'encodage des données étant complexe, je vous renvoie également au RFC 8406 qui décrit le travail du groupe Network Coding de l'IRTF et définit les notions importantes.
Une idée de la latence dans une liaison satellite ? Le RFC observe que le RTT est typiquement de 0,7 secondes (deux passages par le satellite, la limite de la vitesse de la lumière, et le temps de traitement).
On peut se servir des satellites pour des tas de choses dans le domaine des télécommunications mais ce RFC se focalise sur l'accès à l'Internet par satellite, tel que normalisé par l'ETSI dans « Digital Video Broadcasting (DVB); Second Generation DVB Interactive Satellite System (DVB-RCS2); Part 2: Lower Layers for Satellite standard ». (Le RFC recommande également l'article de Ahmed, T., Dubois, E., Dupe, JB., Ferrus, R., Gelard, P., et N. Kuhn, « Software-defined satellite cloud RAN ».)
Le RFC décrit des scénarios où le network coding permet de gagner en capacité et en latence effective, en encodant plus efficacement les communications. Par exemple, en combinant le trafic de deux utilisateurs, on obtient parfois une quantité de données à transmettre qui est inférieure à celle de deux transmissions séparées, économisant ainsi de la capacité. Le RFC cite également l'exemple du multicast où, lorsqu'un des destinataires n'a pas reçu un paquet, on peut, pour éviter de faire attendre tout le monde, utiliser le network coding pour ré-envoyer les données en même temps que la suite du transfert. Là encore, la combinaison de toutes les données fait moins d'octets que lors de transmissions séparées. Cela pourrait s'adapter à des protocoles multicast comme NORM (RFC 5740) ou FLUTE (RFC 6726).
Le network coding permet également d'ajouter de la redondance aux données, sinon gratuitement, du moins à un « coût » raisonnable. Cela peut servir pour les cas de pertes de données comme le précédent. Un autre cas de perte est celui où la liaison satellite marche bien mais c'est tout près de l'utilisateur, par exemple dans son WiFi, que les paquets se perdent. Vu la latence très élevée des liaisons satellite, réémettre a des conséquences très désagréables sur la capacité effective (et sur le délai total). Une solution traditionnellement utilisée était le PEP (qui pose d'ailleurs souvent des problèmes de neutralité). Vous pouvez voir ici un exemple où le résultat est plutôt bon. Mais avec le chiffrement systématique, ces PEP deviennent moins efficaces. Cela redonne toute son importance au network coding.
Enfin, des pertes de paquets, avec leurs conséquences sur la latence car il faudra réémettre, se produisent également quand un équipement terminal passe d'une station de base à l'autre. Là encore, le network coding peut permettre d'éviter de bloquer le canal pendant la réémission.
Tout cela ne veut pas dire que le network coding peut être déployé immédiatement partout et va donner des résultats mirifiques. La section 4 du RFC décrit les défis qui restent à surmonter. Par exemple, les PEP (RFC 3135) ont toujours un rôle à jouer dans la communication satellitaire mais comment combiner leur rôle dans la lutte contre la congestion avec le network coding ? Faut-il réaliser cet encodage dans le PEP (RFC 9265) ? D'autre part, le network coding n'est pas complètement gratuit. Comme tout ajout de redondance, il va certes permettre de rattrapper certaines pertes de paquet, mais il occupe une partie du réseau. Et dans quelle couche ajouter cette fonction ? (Les couches hautes ont l'avantage de fenêtres - le nombre d'octets en transit - plus grandes, alors que les couches basses ne voient pas plus loin que le bout du paquet.)
Outre l'utilisation de satelittes pour la connectivité Internet des Terriens, il y a aussi une utilisation des communications spatiales pour échanger avec des vaisseaux lointains. La latence très élevée et l'indisponibilité fréquente de la liaison posent des défis particuliers. C'est ce qu'on nomme le DTN (Delay/Disruption Tolerant Network, décrit dans le RFC 4838). Le network coding peut aussi être utilisé ici, comme présenté dans l'article de Thai, T., Chaganti, V., Lochin, E., Lacan, J., Dubois, E., et P. Gelard, « Enabling E2E reliable communications with adaptive re-encoding over delay tolerant networks ».
Et, comme le rappelle la section 9 du RFC, il faut aussi tenir compte du chiffrement, qui est évidemment obligatoire puisque les liaisons avec les satellites peuvent trop facilement être écoutées.
Merci à Nicolas Kuhn et Emmanuel Lochin pour leur relecture attentive, sans laquelle il y aurait bien plus d'erreurs. (Je me suis aventuré très en dehors de mon domaine de compétence.) Les erreurs qui restent sont évidemment de mon fait.
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : M. Boucadair (Orange), T. Reddy (McAfee)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dots
Première rédaction de cet article le 13 janvier 2021
Le protocole DOTS, normalisé dans les RFC 8811, RFC 9132 et RFC 8783, sert à coordonner la réponse à une attaque par déni de service, entre la victime de l'attaque (le client DOTS) et un service d'atténuation de l'attaque (le serveur DOTS). Mais comment le client trouve-t-il son serveur ? Il peut y avoir une configuration manuelle, mais ce RFC propose aussi des moyens automatiques, basés sur un choix de plusieurs techniques, dont DHCP et NAPTR.
Attention, cela permet de trouver le serveur, mais pas le fournisseur du service d'atténuation. Car il faut un accord (souvent payant) avec ce fournisseur, et un échange de mécanismes d'authentification. Cette partie doit se faire manuellement. Le protocole de notre RFC prend ensuite le relais.
Notez aussi qu'il n'y a pas un seul moyen de découverte du serveur. La section 3 du RFC explique en effet que, vu la variété des cas d'utilisation de DOTS, on ne peut pas s'en tirer avec un seul mécanisme. Parfois le client a un CPE géré par le FAI, sur lequel on peut s'appuyer pour trouver le serveur DOTS, et parfois pas. Parfois, il faudra utiliser les résolveurs DNS d'un opérateur et parfois ce ne sera pas nécessaire. Parfois l'atténuateur est le FAI et parfois pas. Bref, il faut plusieurs solutions.
Voyons d'abord la procédure générale (section 4). Personnellement, je pense que le client DOTS doit donner la priorité aux configurations manuelles (DOTS est un système de sécurité, un strict contrôle de ce qui se passe est préférable). Mais le RFC ne décrit pas les choses ainsi. Il expose trois mécanismes, le premier, qualifié de configuration explicite, étant composé de deux techniques très différentes, la configuration manuelle ou bien DHCP. À noter au passage que la configuration manuelle peut indiquer le nom ou l'adresse IP mais, si elle indique l'adresse IP, le nom sera quand même obligatoire car il servira pour la vérification du certificat.
L'ordre des préférences entre ces mécanismes est imposé, pour que le résultat de la découverte soit prédictible. D'abord l'explicite (manuel ou DHCP, section 5), puis la résolution de service (section 6) puis la découverte de service (section 7).
Première technique automatique à utiliser, DHCP (section 5). Ce protocole va être utilisé pour récupérer le nom du serveur DOTS (le nom et pas seulement l'adresse IP car on en aura besoin pour authentifier la session TLS). Avec DHCPv6 (RFC 8415), l'option DHCP pour récupérer le nom est 141 en IPv6 et 147 pour IPv4. Une autre option permet de récupérer les adresses IP du serveur DOTS.
Deuxième technique, la résolution de service. Il faut partir d'un
nom, qui peut être configuré manuellement ou bien obtenu par
DHCP. Ce n'est pas le nom du serveur DOTS, contrairement au cas en
DHCP pur, mais celui du domaine dans lequel le client DOTS se
« trouve ». On va alors utiliser S-NAPTR (RFC 3958) sur ce
nom, pour obtenir les noms des serveurs. L'étiquette à utiliser est
DOTS
pour le service (enregistré
à l'IANA) et signal
(RFC 9132) ou data
(RFC 8783) pour le protocole (également
à l'IANA). Par exemple si le client DOTS est dans le domaine
example.net
, il va faire une requête DNS de type NAPTR. Si le
domaine comportait un enregistrement pour le service DOTS, il est
choisi, et on continue le processus (compliqué !) de NAPTR
ensuite. Si on cherche le serveur pour le protocole de
signalisation, on pourrait avoir successivement quatre requêtes
DNS :
example.net. IN NAPTR 100 10 "" DOTS:signal.udp "" signal.example.net. signal.example.net. IN NAPTR 100 10 "s" DOTS:signal.udp "" _dots-signal._udp.example.net. _dots-signal._udp.example.net. IN SRV 0 0 5000 a.example.net. a.example.net. IN AAAA 2001:db8::1
Troisième technique, la découverte de service (DNS-SD) du RFC 6763. On cherche alors un enregistrement de
type PTR dans _dots-signal.udp.example.net
. Il
nous donnera un nom pour lequel on fera une requête
SRV.
DOTS servant en cas d'attaque, il faut prévoir la possibilité que l'attaquant tente de perturber ou de détourner ce mécanisme de découverte du serveur. Par exemple, on sait que DHCP n'est pas spécialement sécurisé (euphémisme !). D'autre part, DOTS impose TLS, il faut donc un nom à vérifier dans le certificat (oui, on peut mettre des adresses IP dans les certificats mais c'est rare). Quant aux techniques reposant sur le DNS, le RFC conseille d'utiliser DNSSEC, ce qui semble la moindre des choses pour une technique de sécurité. Il suggère également de faire la résolution DNS via un canal sûr par exemple avec DoT (RFC 7858) ou DoH (RFC 8484).
Apparemment, il existe au moins une mise en œuvre de DOTS qui inclut les procédures de découverte de notre RFC, mais j'ignore laquelle.
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : G. Mirsky, X. Min (ZTE Corp), H. Nydell (Accedian Networks), R. Foote (Nokia), A. Masputra (Apple), E. Ruffini (OutSys)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ippm
Première rédaction de cet article le 22 janvier 2021
Le protocole STAMP, normalisé dans le RFC 8762, est un protocole pour piloter des mesures de performance vers un réflecteur qui renverra les paquets de test. Ce nouveau RFC étend STAMP pour ajouter un identificateur de session, et la possibilité d'ajouter des tas d'options sous forme de TLV.
Commençons par l'identificateur de session. Une session STAMP est normalement identifiée par le tuple classique {protocole (forcément UDP), adresse IP source, adresse IP destination, port source, port destination}. Ce n'est pas toujours pratique donc notre RFC ajoute un identificateur explicite, le SSID (STAMP Session Identifier). Il fait deux octets et est choisi par l'envoyeur. Mais où le mettre dans le paquet ? Le format original prévoyait qu'une partie du paquet STAMP soit composée de zéros, et c'est dans cette partie qu'on logera le SSID. Donc, si, après l'estimation de l'erreur de mesure, on trouve deux octets qui ne sont pas nuls, c'est le SSID.
La section 4 liste les TLV qui peuvent désormais être ajoutés aux paquets STAMP. Ces TLV sont situés à la fin du paquet STAMP. Ils commencent par une série de bits qui indiquent si le réflecteur connait le TLV en question, et si le réflecteur a détecté des problèmes par exemple un TLV à la syntaxe incorrecte (cf. le registre IANA). Les principaux TLV sont :
Si cela ne suffit pas, d'autres types de TLV pourront être créés dans le futur et mis dans le registre IANA. La politique d'enregistrement (cf. RFC 8126) est « examen par l'IETF » pour la première moitié de la plage disponible et « premier arrivé, premier servi » ensuite.
Date de publication du RFC : Décembre 2020
Auteur(s) du RFC : M. Slusarz (Open-Xchange)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF extra
Première rédaction de cet article le 19 décembre 2020
Le protocole IMAP d'accès aux boites aux lettres (RFC 3501) continue à évoluer et reçoit de nouvelles extensions. Celle normalisée dans ce RFC permet au client IMAP, le MUA, de ne récupérer qu'une partie du message, afin d'afficher un avant-gout de celui-ci à l'utilisateur humain, qui pourra ainsi mieux choisir s'il veut lire ce message ou pas.
Il y a déjà des clients de
messagerie qui présentent le début du message mais, sans
l'extension de ce RFC, cela nécessite de tout récupérer (un
FETCH BODYSTRUCTURE
pour savoir quelle partie
MIME récupérer, suivi d'un FETCH
BODY
, et sans possibilité de les exécuter en parallèle)
avant de ne sélectionner qu'une partie. Au contraire, avec
l'extension PREVIEW
(« avant-gout »), c'est le
serveur IMAP qui va sélectionner cet avant-gout. Avantages : une
présélection qui est identique sur tous les clients, moins de
travail pour le client et surtout moins de données
transmises. Avant, le client était forcé de récupérer beaucoup de
choses car il ne pouvait pas savoir à l'avance combien d'octets
récolter avant de générer l'avant-gout. Si le message était du
texte brut, OK, mais si c'était de
l'HTML, il
pouvait être nécessaire de ramasser beaucoup d'octets de formatage
et de gadgets avant d'en arriver au vrai contenu. (Ou bien, il
fallait procéder progressivement, récupérant une partie du message,
puis, si nécessaire, une autre, ce qui augmentait la latence.)
Donc, concrètement, comment ça se passe ? La section 3 de notre
RFC décrit l'extension en détail. Elle a la forme d'un attribut
PREVIEW
qui suit la commande
FETCH
(RFC 3501, section
6.4.5). Voici un exemple, la commande étant étiquetée
MYTAG01
:
Client : MYTAG01 FETCH 1 (PREVIEW) Serveur : * 1 FETCH (PREVIEW "Bonjour, voulez-vous gagner plein d'argent rapidement ?") MYTAG01 OK FETCH complete.
Idéalement, le serveur renvoie toujours le même avant-gout pour un message donné (mais le RFC ne l'impose pas car cela peut être difficile dans certains cas, par exemple en cas de mise à jour du logiciel du serveur, qui change l'algorithme de génération des avant-gouts).
La syntaxe formelle
de l'attribut PREVIEW
est
en section 6 du RFC.
Le format de l'avant-gout est forcément du texte brut, encodé en UTF-8, et ne doit pas avoir subi d'encodages baroques comme Quoted-Printable. Sa longueur est limitée à 256 caractères (caractères Unicode, pas octets, attention si vous programmez un client et que votre tampon est trop petit).
Le contenu de l'avant-gout est typiquement composé des premiers caractères du message. Cela implique qu'il peut contenir des informations privées et il ne doit donc être montré qu'aux clients qui sont autorisés à voir le message complet.
Parfois, le serveur ne peut pas générer un avant-gout, par exemple si le message est chiffré avec OpenPGP (RFC 4880) ou bien si le message est entièrement binaire, par exemple du PNG. Dans ces cas, le serveur est autorisé à renvoyer une chaîne de caractères vide.
Si le serveur génère un avant-gout lui-même (du genre « Image de
600x600 pixels, prise le 18 décembre 2020 », en utilisant les
métadonnées de l'image), il est recommandé
qu'il choisisse la langue indiquée par
l'extension LANGUAGE
(RFC 5255).
Comme l'avant-gout n'est pas forcément indispensable pour l'utilisateur, le RFC suggère (section 4) de le charger en arrière-plan, en affichant la liste des messages sans attendre tous ces avant-gouts.
Le serveur IMAP qui sait générer ces avant-gouts l'annonce via la
capacité PREVIEW
, qui est notée dans le
registre des capacités. Voici un exemple :
Client : MYTAG01 CAPABILITY Serveur : * CAPABILITY IMAP4rev1 PREVIEW MYTAG01 OK Capability command completed. Client : MYTAG02 FETCH 1 (RFC822.SIZE PREVIEW) Serveur : * 1 FETCH (RFC822.SIZE 5647 PREVIEW {200} Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur aliquam turpis et ante dictum, et pulvinar dui congue. ligula nullam ) MYTAG02 OK FETCH complete.
Attention si vous mettez en œuvre cette extension, elle nécessite davantage de travail du serveur, donc un client méchant pourrait surcharger ledit serveur. Veillez bien à authentifier les clients, pour retrouver le méchant (section 7 du RFC).
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : A. Decimo (IRIF, University of Paris-Diderot), D. Schinazi (Google LLC), J. Chroboczek (IRIF, University of Paris-Diderot)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF babel
Première rédaction de cet article le 12 janvier 2021
Le protocole de routage Babel, normalisé dans le RFC 8966, n'offre par défaut aucune sécurité. Notamment, il n'y a aucun moyen d'authentifier un routeur voisin, ni de s'assurer que les messages suivants viennent bien de lui. Sans même parler de la confidentialité de ces messages. Si on veut ces services, ce RFC fournit un moyen : sécuriser les paquets Babel avec DTLS.
La sécurité est évidemment souhaitable pour un protocole de routage. Sans elle, par exemple, une méchante machine pourrait détourner le trafic. Cela ne veut pas dire qu'on va toujours employer une solution de sécurité. Babel (RFC 8966) est conçu entre autres pour des réseaux peu ou pas gérés, par exemple un événement temporaire où chacun apporte son PC et où la connectivité se fait de manière ad hoc. Dans un tel contexte, déployer de la sécurité ne serait pas facile. Mais, si on y tient, Babel fournit désormais deux mécanismes de sécurité (cf. RFC 8966, section 6), l'un, plutôt simple et ne fournissant pas de confidentialité, est décrit dans le RFC 8967, l'autre, bien plus complet, figure dans notre RFC et repose sur DTLS (qui est normalisé dans le RFC 9147). DTLS, version datagramme de TLS, fournit authentification, intégrité et confidentialité.
L'adaptation de DTLS à Babel est décrite dans la section 2 du
RFC. Parmi les problèmes, le fait que DTLS soit très asymétrique
(client-serveur) alors que Babel est pair-à-pair, et le fait que
DTLS ne marche qu'en unicast alors que Babel peut
utiliser unicast ou
multicast. Donc, chaque routeur Babel doit être
un serveur DTLS, écoutant sur le port 6699
(qui n'est pas le 6696 du Babel non sécurisé). Le routeur Babel
continue à écouter sur le port non chiffré, et, lorsqu'il entend un
nouveau routeur s'annoncer en multicast, il va
tenter de communiquer en DTLS avec lui. Puisque DTLS, comme TLS, est
client-serveur, il faut décider de qui sera le client et qui sera le
serveur : le routeur de plus faible adresse sera le client (par
exemple, fe80::1:2
est inférieure à
fe80::2:1
. Le serveur différencie les sessions
DTLS par le port source ou par les connection
identifiers du RFC 9146. Comme avec
le Babel non chiffré, les adresses IPv6 utilisées doivent être des
adresses locales au lien. Client et serveur doivent s'authentifier,
en suivant les méthodes TLS habituelles (chacun doit donc avoir un
certificat).
Une fois que la session DTLS est en marche, les paquets Babel
unicast passent sur cette session et sont donc
intégralement (du premier au dernier octet) protégés par le
chiffrement. Pour que la sécurité fournie par DTLS soit ne soit pas
compromise, le routeur ne doit plus envoyer à son voisin de paquets
sur le port non chiffré, à part des paquets
Hello
qui sont transmis en
multicast (pour lequel DTLS ne marche pas) et qui
permettent de découvrir d'éventuels nouveaux voisins (la règle
exacte est un peu plus compliquée, regardez le RFC). On ne peut donc
plus envoyer d'information en multicast (à part
les Hello
). En réception, un nœud Babel
sécurisé doit ignorer les paquets non protégés par DTLS, sauf
l'information de type Hello
.
Une stricte sécurité nécessite qu'un routeur Babel sécurisé
n'accepte plus de voisins qui ne gèrent pas DTLS, et ignore donc
tous leurs paquets sauf ceux qui contiennent un
TLV Hello
. Autrement,
les communications non sécurisées ficheraient en l'air la
confidentialité de l'information. Si on veut
une sécurité plus relaxée, un nœud peut faire tourner les deux versions
du protocole, sécurisée et non sécurisée, mais
alors, exige le RFC, uniquement sur des interfaces réseau différentes.
Comme toutes les fois où on rajoute des données, on risque de dépasser la MTU. La section 3 du RFC nous rappelle qu'il faut éviter d'envoyer des paquets qui seront fragmentés et donc qu'il faut éviter de fabriquer des paquets qui, après chiffrement, seraient plus gros que la MTU.
Rien n'est parfait en ce bas monde et l'ajout de DTLS, s'il résout quelques problèmes de sécurité, ne fait pas tout, et même parfois ajoute des ennuis. La section 5 du RFC rappelle ainsi qu'un méchant pourrait tenter des négociations DTLS nombreuses avec une machine afin de la faire travailler pour rien (une variante de Slowloris). Limiter le nombre de connexions par client n'aiderait pas puisqu'il pourrait toujours mentir sur son adresse IP.
Même en faisant tourner Babel sur DTLS, les messages de type
Hello
peuvent toujours être envoyés sans
protection, et donc manipulés à loisir. Un routeur Babel doit donc
toujours être capable d'utiliser des Hello
unicast et protégés.
Et puis bien sûr, limitation classique de TLS, authentifier un routeur voisin et sécuriser la communication avec lui ne signifie pas que ce voisin est honnête ; il peut toujours mentir, par exemple en annonçant des routes vers des préfixes qu'il ne sait en fait pas joindre.
À ma connaissance, il n'existe pas encore de mise en œuvre de ce RFC.
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : C. Dô, W. Kołodziejak, J. Chroboczek (IRIF, University of Paris-Diderot)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF babel
Première rédaction de cet article le 12 janvier 2021
Le protocole de routage Babel, normalisé dans le RFC 8966, avait à l'origine zéro sécurité. N'importe quelle machine pouvait se dire routeur et annoncer ce qu'elle voulait. Le RFC 7298 avait introduit un mécanisme d'authentification. Ce nouveau RFC le remplace avec un nouveau mécanisme. Chaque paquet est authentifié par HMAC, et les routeurs doivent donc partager une clé secrète.
Comme, par défaut, Babel croit aveuglément ce que ses voisins lui racontent, un méchant peut facilement, par exemple, détourner du trafic (en annonçant une route de plus forte préférence). Le problème est bien connu mais n'a pas de solution simple. Par exemple, Babel permet à la fois de l'unicast et du multicast, le second étant plus difficile à protéger. Une solution de sécurité, DTLS, spécifiée pour Babel dans le RFC 8968, résout le problème en ne faisant que de l'unicast. Notre RFC choisit une autre solution, qui marche pour l'unicast et le multicast. Chaque paquet est authentifié par un MAC attaché au paquet, calculé entre autres à partir d'une clé partagée.
La solution décrite dans ce RFC implique que tous les routeurs connectés au réseau partagent la clé secrète, et que tous ces routeurs soient eux-mêmes de confiance (l'authentification n'implique pas l'honnêteté du routeur authentifié, point très souvent oublié quand on parle d'authentification…) En pratique, c'est un objectif difficile à atteindre, il nécessite un réseau relativement bien géré. Pour un rassemblement temporaire où tout le monde partage sa connectivité, faire circuler ce mot de passe partagé sera difficile.
Ce mécanisme a par contre l'avantage de ne pas nécessiter que les horloges soient correctes ou synchronisées, et ne nécessite pas de stockage de données permanent (ce qui serait contraignant pour, par exemple, certains objets connectés).
Pour que tout marche bien et qu'on soit heureux et en sécurité, ce mécanisme d'authentification compte sur deux pré-requis :
En échange de ces garanties, le mécanisme de ce RFC garantit l'intégrité des paquets et le rejet des rejeux (dans certaines conditions, voir le RFC).
La section 2 du RFC résume très bien le protocole : quand un routeur Babel envoie un paquet sur une des interfaces où la protection MAC a été activée, il calcule le MAC et l'ajoute à la fin du paquet. Quand il reçoit un paquet sur une des interfaces où la protection MAC a été activée, il calcule le MAC et, s'il ne correspond pas à ce qu'il trouve à la fin du paquet, le paquet est jeté. Simple, non ? Mais c'est en fait un peu plus compliqué. Pour protéger contre les attaques par rejeu, la machine qui émet doit maintenir un compteur des paquets envoyés, le PC (Packet Counter). Il est inclus dans les paquets envoyés et protégé par le MAC.
Ce PC ne protège pas dans tous les cas. Par exemple, si un routeur Babel vient de démarrer, et n'a pas de stockage permanent, il ne connait pas les PC de ses voisins et ne sait donc pas à quoi s'attendre. Dans ce cas, il doit ignorer le paquet et mettre l'émetteur au défi de répondre à un numnique qu'il envoie. Le voisin répond en incluant le numnique et son nouveau PC, prouvant ainsi qu'il ne s'agit pas d'un rejeu.
Petite difficulté, en l'absence de stockage permanent, le PC peut revenir en arrière et un PC être réutilisé. Outre le PC, il faut donc un autre nombre, l'index. Celui-ci n'est, comme le numnique utilisé dans les défis, jamais réutilisé. En pratique, un générateur aléatoire est une solution raisonnable pour fabriquer numniques et index.
La section 3 du RFC décrit les structures de données qu'il faut utiliser pour mettre en œuvre ce protocole. La table des interfaces (RFC 8966, section 3.2.3), doit être enrichie avec une indication de l'activation de la protection MAC sur l'interface, et la liste des clés à utiliser (Babel permet d'avoir plusieurs clés, notamment pour permettre le remplacement d'une clé, et le récepteur doit donc les tester toutes). Il faut également ajouter à la table des interfaces l'index et le PC décrits plus haut.
Quant à la table des (routeurs) voisins (RFC 8966, section 3.2.4), il faut y ajouter l'index et le PC de chaque voisin, et le numnique.
Enfin la section 4 détaille le fonctionnement. Comment calculer le MAC (avec un pseudo-en-tête), où mettre le TLV qui indique le PC, et celui qui contient la ou les MAC (dans la remorque, c'est-à-dire la partie du paquet après la longueur explicite), etc. La section 6 fournit quant à elle le format exact des TLV utilisés : le MAC TLV qui stocke le MAC, le PC TLV qui indique le compteur, et les deux TLV qui permettent de lancer un défi et d'obtenir une réponse, pour se resynchroniser en cas d'oubli du compteur. Ils ont été ajoutés au registre IANA.
Les gens de l'opérationnel aimeront la section 5, qui décrit comment déployer initialement cette option, et comment effectuer un changement de clé. Pour le déploiement initial, il faut configurer les machines dans un mode où elles signent mais ne vérifient pas les paquets entrants (il faut donc que les implémentations aient un tel mode). Cela permet de déployer progressivement. Une fois tous les routeurs ainsi configurés, on peut activer le mode normal, avec signature et vérification. Pour le remplacement de clés, on ajoute d'abord la nouvelle clé aux routeurs, qui signent donc avec les deux clés, l'ancienne et la nouvelle, puis, une fois que tous les routeurs ont la nouvelle clé, on retire l'ancienne.
Un petit bilan de la sécurité de ce mécanisme, en section 7 : d'abord un rappel qu'il est simple et qu'il ne fournit que le minimum, l'authentification et l'intégrité des paquets. Si on veut d'avantage, il faut passer à DTLS, avec le RFC 8968. Ensuite, certaines valeurs utilisées doivent être générées sans que l'attaquant puisse les deviner, ce qui nécessite, par exemple, un générateur de nombres aléatoires sérieux. D'autant plus que la taille limitée de certaines valeurs peut permettre des attaques à la force brute. Si, par exemple, les clés dérivent d'une phrase de passe, il faut une bonne phrase de passe, plus une fonction de dérivation qui gêne ces attaques, comme PBKDF2 (RFC 2898), bcrypt ou scrypt (RFC 7914).
Et, comme toujours, il y a la lancinante menace des attaques par déni de service, par exemple en envoyant des paquets délibérement conçus pour faire faire des calculs cryptographiques inutiles aux victimes. C'est pour limiter leurs conséquences que, par exemple, Babel impose de limiter le rythme d'envoi des défis, pour éviter de s'épuiser à défier un attaquant.
Je n'ai pas vu quelles mises en œuvre de Babel avaient ce mécanisme. il ne semble pas dans la version officielle de babeld, en tout cas, même si il y a du code dans une branche séparée. Et ce n'est pas encore dans une version officielle de BIRD mais le code est déjà écrit.
Le RFC ne décrit pas les changements depuis le RFC 7298 car il s'agit en fait d'un protocole différent, et incompatible (ce ne sont pas les mêmes TLV, par exemple).
Merci à Juliusz Chroboczek pour sa relecture.
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : J. Chroboczek (IRIF, University of Paris-Diderot), D. Schinazi (Google LLC)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF babel
Première rédaction de cet article le 12 janvier 2021
Le travail sur les protocoles de routage ne désarme pas, motivé à la fois par les avancées de la science et par les nouvelles demandes (par exemple pour les réseaux ad hoc). Ainsi Babel est un protocole de routage, de la famille des protocoles à vecteur de distance, qui vise notammment à réduire drastiquement les probabilités de boucle. Il avait été décrit originellement dans le RFC 6126 ; ce nouveau RFC ne change pas fondamentalement le protocole Babel (quoique certains changements ne seront pas compris par les vieilles versions) mais il a désormais le statut de norme, et des solutions de sécurité plus riches. Ce manque de sécurité était la principale critique adressée à Babel.
Il y a deux familles de protocole de routage, ceux à vecteur de distance comme l'ancêtre RIP et ceux à états des liens comme OSPF (RFC 2328). Ce dernier est aujourd'hui bien plus utilisé que RIP, et à juste titre. Mais les problèmes de RIP n'ont pas forcément la même ampleur chez tous les membres de sa famille, et les protocoles à vecteurs de distance n'ont pas dit leur dernier mot.
Babel s'inspire de protocoles de routage plus récents comme DSDV. Il vise à être utilisable, à la fois sur les réseaux classiques, où le routage se fait sur la base du préfixe IP et sur les réseaux ad hoc, où il n'y a typiquement pas de regroupement par préfixe, où le routage se fait sur des adresses IP « à plat » (on peut dire que, dans un réseau ad hoc, chaque nœud est un routeur).
L'un des principaux inconvénients du bon vieux protocole RIP est sa capacité à former des boucles lorsque le réseau change de topologie. Ainsi, si un lien entre les routeurs A et B casse, A va envoyer les paquets à un autre routeur C, qui va probablement les renvoyer à A et ainsi de suite (le champ « TTL » pour IPv4 et « Hop limit » dans IPv6 a précisement pour but d'éviter qu'un paquet ne tourne sans fin). Babel, lui, évitera les boucles la plupart du temps mais, en revanche, il ne trouvera pas immédiatement la route optimale entre deux points. La section 1.1 du RFC spécifie plus rigoureusement les propriétés de Babel.
Babel peut fonctionner avec différentes métriques pour indiquer
les coûts de telle ou telle route, le protocole lui-même étant
indépendant de la métrique utilisée (cf. annexe A du RFC pour des
conseils sur les choix). D'ailleurs, je vous recommande la lecture
de l'Internet-Draft
draft-chroboczek-babel-doesnt-care
,
pour mieux comprendre la philosophie de Babel.
Autre particularité de Babel, les associations entre deux machines pourront se faire même si elles utilisent des paramètres différents (par exemple pour la valeur de l'intervalle de temps entre deux « Hello » ; cf. l'annexe B pour une discussion du choix de ces paramètres, les compromis que ce choix implique entre intensité du trafic et détection rapide des changements, et les valeurs recommandées pour ces paramètres). Le RFC annonce ainsi que Babel est particulièrement adapté aux environnements « sans-fil » où certaines machines, devant économiser leur batterie, devront choisir des intervalles plus grands, ou bien aux environnements non gérés, où chaque machine est configurée indépendamment.
Je l'ai dit, rien n'est parfait en ce bas monde, et Babel a des limites, décrites en section 1.2. D'abord, Babel envoie périodiquement toutes les informations dont il dispose, ce qui, dans un réseau stable, mène à un trafic total plus important que, par exemple, OSPF (qui n'envoie que les changements). Ensuite, Babel a des mécanismes d'attente lorsqu'un préfixe disparait, qui s'appliquent aux préfixes plus généraux. Ainsi, lorsque deux préfixes deviennent agrégés, l'agrégat n'est pas joignable immédiatement.
Comment Babel atteint-il ses merveilleux objectifs ? La section 2 détaille les principes de base du protocole, la 3 l'échange de paquets et la 4 l'encodage d'iceux. Commençons par les principes. Babel est fondé sur le bon vieil algorithme de Bellman-Ford, tout comme RIP. Tout lien entre deux points A et B a un coût (qui n'est pas forcément un coût monétaire, c'est un nombre qui a la signification qu'on veut, cf. section 3.5.2). Le coût est additif (la somme des coûts d'un chemin complet faisant la métrique du chemin, section 2.1 et annexe A), ce qui veut dire que Métrique(A -> C) - pour une route passant par B >= Coût(A -> B) + Coût(B -> C). L'algorithme va essayer de calculer la route ayant la métrique le plus faible.
Un nœud Babel garde trace de ses voisins nœuds en
envoyant périodiquement des messages Hello
et
en les prévenant qu'ils ont été entendus par des messages
IHU
(I Heard You). Le
contenu des messages Hello
et
IHU
permet de déterminer le coût.
Pour chaque source (d'un préfixe, pas d'un paquet), le nœud garde trace de la métrique vers cette source (lorsqu'un paquet tentera d'atteindre le préfixe annoncé) et du routeur suivant (next hop). Au début, évidemment la métrique est infinie et le routeur suivant indéterminé. Le nœud envoie à ses voisins les routes qu'il connait. Si celle-ci est meilleure que celle que connait le voisin, ce dernier l'adopte (si la distance était infinie - route inconnue, toute route sera meilleure).
L'algorithme « naïf » ci-dessus est ensuite amélioré de plusieurs façons : envoi immédiat de nouvelles routes (sans attendre l'émission périodique), mémorisation, non seulement de la meilleure route mais aussi de routes alternatives, pour pouvoir réagir plus vite en cas de coupure, etc.
La section 2.3 rappelle un problème archi-connu de l'algorithme de Bellman-Ford : la facilité avec laquelle des boucles se forment. Dans le cas d'un réseau simple comme celui-ci A annonce une route de métrique 1 vers S, B utilise donc A comme routeur suivant, avec une métrique de 2. Si le lien entre S (S = source de l'annonce) et A casse comme B continue à publier une route de métrique 2 vers S, A se met à envoyer les paquets à B. Mais B les renvoie à A, créant ainsi une boucle. Les annonces ultérieures ne résolvent pas le problème : A annonce une route de métrique 3, passant par B, B l'enregistre et annonce une route de métrique 4 passant par A, etc. RIP résout le problème en ayant une limite arbitraire à la métrique, limite qui finit par être atteinte et stoppe la boucle (méthode dite du « comptage à l'infini »).
Cette méthode oblige à avoir une limite très basse pour la métrique. Babel a une autre approche : les mises à jour ne sont pas forcément acceptées, Babel teste pour voir si elles créent une boucle (section 2.4). Toute annonce est donc examinée au regard d'une condition, dite « de faisabilité ». Plusieurs conditions sont possibles. Par exemple, BGP utilise la condition « Mon propre numéro d'AS n'apparaît pas dans l'annonce. ». (Cela n'empêche pas les micro-boucles, boucles de courte durée en cas de coupure, cf. RFC 5715.) Une autre condition, utilisée par DSDV et AODV (RFC 3561), repose sur l'observation qu'une boucle ne se forme que lorsqu'une annonce a une métrique moins bonne que la métrique de la route qui a été retirée. En n'acceptant que les annonces qui améliorent la métrique, on peut donc éviter les boucles. Babel utilise une règle un peu plus complexe, empruntée à EIGRP, qui tient compte de l'histoire des annonces faites par le routeur.
Comme il n'y a pas de miracles en routage, cette idée de ne pas accepter n'importe quelle annonce de route a une contrepartie : la famine. Celle-ci peut se produire lorsqu'il existe une route mais qu'aucun routeur ne l'accepte (section 2.5). EIGRP résout le problème en « rédémarrant » tout le réseau (resynchronisation globale des routeurs). Babel, lui, emprunte à DSDV une solution moins radicale, en numérotant les annonces, de manière strictement croissante, lorsqu'un routeur détecte un changement dans ses liens. Une route pourra alors être acceptée si elle est plus récente (si elle a un numéro de séquence plus élevé), et un routeur Babel peut demander explicitement aux autres routeurs d'incrémenter ce nombre, pour accélérer la convergence. Ce numéro n'est par contre pas utilisé pour sélectionner la meilleure route (seule la métrique compte pour cela), uniquement pour voir si une annonce est récente.
À noter que tout se complique s'il existe plusieurs routeurs qui annoncent originellement la même route (section 2.7 ; un exemple typique est la route par défaut, annoncée par tous les routeurs ayant une connexion extérieure). Babel gère ce problème en associant à chaque préfixe l'identité du routeur qui s'est annoncé comme origine et considère par la suite ces annonces comme distinctes, même si le préfixe est le même. Conséquence : Babel ne peut plus garantir qu'il n'y aura pas de boucle (Babel essaie de construire un graphe acyclique mais l'union de plusieurs graphes acycliques n'est pas forcément acyclique). Par contre, il pourra détecter ces boucles a posteriori et les éliminer plus rapidement qu'avec du comptage vers l'infini.
Notez aussi que chaque routeur Babel est libre de rejeter les
routes qui lui semblent déraisonnables, comme
127.0.0.1/32
, sans affecter le fonctionnement
du protocole (le détail de cette question du filtrage des routes est
dans l'annexe C.)
Voilà pour les principes. Et le protocole ? La section 3 le décrit. Chaque routeur a une identité sur huit octets (le plus simple est de prendre l'adresse MAC d'une des interfaces). Les messages sont envoyés dans des paquets UDP et encodés en TLV. Le paquet peut être adressé à une destination unicast ou bien multicast. Les TLV peuvent contenir des sous-TLV dans leur partie Valeur.
Un routeur Babel doit se souvenir d'un certain nombre de choses (section 3.2), notamment :
Les préfixes annoncés sont sans rapport avec la version du protocole IP utilisée pour transporter l'annonce. Un préfixe IPv4 peut donc être envoyé en IPv6. Le RFC recommande de faire tourner Babel sur IPv6, même si le réseau est en partie en IPv4.
Les messages Babel ne bénéficient pas d'une garantie de délivrance (c'est de l'UDP, après tout), mais un routeur Babel peut demander à ses voisins d'accuser réception (section 3.3). La décision de le demander ou pas découle de la politique locale de chaque routeur. Si un routeur ne demande pas d'accusé de réception, l'envoi périodique des routes permettra de s'assurer que, au bout d'un certain temps, tous les routeurs auront toute l'information. Les accusés de réception peuvent toutefois être utiles en cas de mises à jour urgentes dont on veut être sûr qu'elles ont été reçues.
Comment un nœud Babel trouve t-il ses voisins ? La section 3.4 décrit ce mécanisme. Les voisins sont détectés par les messages Hello qu'ils émettent. Les messages IHU (I Heard You) envoyés en sens inverse permettent notamment de s'assurer que le lien est bien bidirectionnel.
Les détails de la maintenance de la table de routage figurent en section 3.5. Chaque mise à jour envoyée par un nœud Babel est un quintuplet {préfixe IP, longueur du préfixe, ID du routeur, numéro de séquence, métrique}. Chacune de ces mises à jour est évaluée en regard des conditions de faisabilité : une distance de faisabilité est un doublet {numéro de séquence, métrique} et ces distances sont ordonnées en comparant d'abord le numéro de séquence (numéro plus élevée => distance de faisabilité meilleure) et ensuite la métrique (où le critère est inverse). Une mise à jour n'est acceptée que si sa distance de faisabilité est meilleure.
Si la table des routes contient plusieurs routes vers un préfixe donné, laquelle choisir et donc réannoncer aux voisins (section 3.6) ? La politique de sélection n'est pas partie intégrante de Babel. Plusieurs mises en œuvre de ce protocole pourraient faire des choix différents. Les seules contraintes à cette politique sont qu'il ne faut jamais réannoncer les routes avec une métrique infinie (ce sont les retraits, lorsqu'une route n'est plus accessible), ou les routes infaisables (selon le critère de faisabilité cité plus haut). Si les différents routeurs ont des politiques différentes, cela peut mener à des oscillations (routes changeant en permanence) mais il n'existe pas à l'heure actuelle de critères scientifiques pour choisir une bonne politique. On pourrait imaginer que le routeur ne garde que la route avec la métrique le plus faible, ou bien qu'il privilégie la stabilité en gardant la première route sélectionnée, ou encore qu'il prenne en compte des critères comme la stabilité du routeur voisin dans le temps. En attendant les recherches sur ce point, la stratégie conseillée est de privilégier la route de plus faible métrique, en ajoutant un petit délai pour éviter de changer trop souvent. Notez que la méthode de calcul des métriques n'est pas imposée par Babel : tant que cette méthode obéit à certains critères, notamment de monotonie, elle peut être utilisée.
Une fois le routeur décidé, il doit envoyer les mises à jour à ses voisins (section 3.7). Ces mises à jour sont transportées dans des paquets multicast (mais peuvent l'être en unicast). Les changements récents sont transmis immédiatement, mais un nœud Babel transmet de toute façon la totalité de ses routes à intervalles réguliers. Petite optimisation : les mises à jour ne sont pas transmises sur l'interface réseau d'où la route venait, mais uniquement si on est sûr que ladite interface mène à un réseau symétrique (un Ethernet filaire est symétrique mais un lien WiFi ad hoc ne l'est pas forcément).
Un routeur Babel peut toujours demander explicitement des annonces de routes à un voisin (section 3.8). Il peut aussi demander une incrémentation du numéro de séquence, au cas où il n'existe plus aucune route pour un préfixe donné (problème de la famine, section 3.8.2.1).
La section 4 spécifie l'encodage des messages Babel sur le
réseau. C'est un paquet UDP, envoyé à une adresse
multicast (ff02::1:6
ou
224.0.0.111
) ou bien
unicast, avec un TTL de 1
(puisque les messages Babel n'ont jamais besoin d'être routés), et
un port source et
destination de
6696. En IPv6, les adresses IP de
source et de destination unicast sont locales au
lien et en IPv4 des adresses du réseau
local.
Les données envoyées dans le message sont typées et la section
4.1 liste les types possibles, par exemple
interval, un entier de 16 bits qui sert à
représenter des durées en centisecondes (rappelez-vous que, dans
Babel, un routeur informe ses voisins de ses paramètres temporels,
par exemple de la fréquence à laquelle il envoie des
Hello
). Plus complexe est le type
address, puisque Babel permet d'encoder les
adresses par différents moyens (par exemple, pour une adresse IPv6
locale au lien, le préfixe
fe80::/64
peut être omis). Quant à l'ID du
routeur, cet identifiant est stocké sur huit octets.
Ensuite, ces données sont mises dans des TLV, eux-même placés derrière
l'en-tête Babel, qui indique un nombre magique (42...) pour
identifier un paquet Babel, un numéro de version (aujourd'hui 2) et
la longueur du message. (La fonction
babel_print_v2
dans le code de
tcpdump est un bon moyen de découvrir les
différents types et leur rôle.) Le message est suivi d'une remorque
qui n'est pas comptée pour le calcul de la longueur du message, et
qui sert notamment pour l'authentification
(cf. RFC 8967). La remorque, une
nouveauté qui n'existait pas explicitement dans le RFC 6126, est elle-même composée de TLV. Chaque TLV, comme son
nom l'indique, comprend un type (entier sur huit bits), une longueur
et une valeur, le corps, qui peut comprendre
plusieurs champs (dépendant du type). Parmi les types existants :
Hello
. Le corps
contient notamment le numéro de séquence actuel du routeur. Le type
5 désigne une réponse au Hello
, le IHU, et
ajoute des informations comme le coût de la liaison entre les deux
routeurs.Update
(type 8) contient notamment un préfixe
(avec sa longueur), un numéro de séquence, et une métrique.Les types de TLV sont stockés dans un registre IANA. On peut en ajouter à condition de fournir une spécification écrite (« Spécification nécessaire », cf. RFC 8126). Il y a aussi un registre des sous-TLV.
Quelle est la sécurité de Babel ? La section 6 dit franchement qu'elle est, par défaut, à peu près inexistante. Un méchant peut annoncer les préfixes qu'il veut, avec une bonne métrique pour être sûr d'être sélectionné, afin d'attirer tout le trafic.
En IPv6, une protection modérée est fournie par le fait que les adresses source et destination sont locales au lien. Comme les routeurs IPv6 ne sont pas censés faire suivre les paquets ayant ces adresses, cela garantit que le paquet vient bien du réseau local. Une raison de plus d'utiliser IPv6.
Ce manque de sécurité dans le Babel original du RFC 6126 avait suscité beaucoup de discussions à l'IETF lors de la mise de Babel sur le chemin des normes (voir par exemple cet examen de la direction de la sécurité). Normalement, l'IETF exige de tout protocole qu'il soit raisonnablement sécurisé (la règle figure dans le RFC 3365). Certaines personnes s'étaient donc vigoureusement opposées à la normalisation officielle de Babel tant qu'il n'avait pas de solution de sécurité disponible. D'autres faisaient remarquer que Babel était quand même déployable pour des réseaux fermés, « entre copains », même sans sécurité, mais les critiques pointaient le fait que tôt ou tard, tout réseau fermé risque de se retrouver ouvert. D'un autre côté, sécuriser des réseaux « ad hoc », par exemple un lot de machines mobiles qui se retrouvent ensemble pour un événement temporaire, est un problème non encore résolu.
Un grand changement de notre RFC est de permettre la signature des messages. Deux mécanismes existent, décrits dans les RFC 8967 (signature HMAC, pour authentifier, la solution la plus légère) et RFC 8968 (DTLS, qui fournit en plus la confidentialité). (Notons que, en matière de routage, la signature ne résout pas tout : c'est une chose d'authentifier un voisin, une autre de vérifier qu'il est autorisé à annoncer ce préfixe.)
J'ai parlé plus haut de la détermination des coûts des liens. L'annexe A du RFC contient des détails intéressants sur cette détermination. Ainsi, contrairement aux liens fixes, les liens radio ont en général une qualité variable, surtout en plein air. Déterminer cette qualité est indispensable pour router sur des liens radio, mais pas facile. L'algorithme ETX (décrit dans l'article de De Couto, D., Aguayo, D., Bicket, J., et R. Morris, « A high-throughput path metric for multi-hop wireless networks ») est recommandé pour cela.
L'annexe D est consacrée aux mécanismes d'extension du protocole, et reprend largement le RFC 7557, qu'elle remplace. Babel prévoyait des mécanismes d'extension en réservant certaines valeurs et en précisant le comportement d'un routeur Babel lors de la réception de valeurs inconnues. Ainsi :
Quelles sont donc les possibilités d'extensions propres ? Cela commence par une nouvelle version du protocole (l'actuelle version est la 2), qui utiliserait des numéros 3, puis 4... Cela ne doit être utilisé que si la nouvelle version est incompatible avec les précédentes et ne peut pas interopérer sur le même réseau.
Moins radicale, une extension de la version 2 peut introduire de nouveaux TLV (qui seront ignorés par les mises en œuvre anciennes de la version 2). Ces nouveaux TLV doivent suivre le format de la section 4.3. De la même façon, on peut introduire de nouveaux sous-TLV (des TLV contenus dans d'autres TLV, décrits en section 4.4).
Si on veut ajouter des données dans un TLV existant, en
s'assurant qu'il restera correctement analysé par les anciennes
mises en œuvre, il faut jouer sur la différence entre la taille
explicite (explicit size) et la taille effective
(natural size) du TLV. La taille explicite est
celle qui est indiqué dans le champ Length
spécifié dans la section 4.3. La taille effective est celle déduite
d'une analyse des données (plus ou moins compliquée selon le type de
TLV). Comme les implémentations de Babel doivent ignorer les données
situées après la taille explicite, on peut s'en servir pour ajouter
des données. Elles doivent être encodées sous forme de sous-TLV,
chacun ayant type, longueur et valeur (leur format exact est décrit
en section 4.4).
Enfin, après le dernier TLV (Babel est transporté sur UDP, qui indique une longueur explicite), on peut encore ajouter des données, une « remorque ». C'est ce que fait le RFC 8966.
Bon, mais alors quel mécanisme d'extension choisir ? La section 4 fournit des pistes aux développeurs. Le choix de faire une nouvelle version est un choix drastique. Il ne devrait être fait que si la nouvelle version est réellement incompatible avec la précédente.
Un nouveau TLV, ou bien un nouveau sous-TLV d'un TLV existant est
la solution à la plupart des problèmes d'extension. Par exemple, si
on veut mettre de l'information supplémentaire aux mises à jour de
routes (TLV Update
), on peut créer un nouveau
TLV « Update
enrichi » ou bien un sous-TLV de
Update
qui contiendra l'information
supplémentaire. Attention, les conséquences de l'un ou l'autre choix
ne seront pas les mêmes. Un TLV « Update
enrichi » serait totalement ignoré par un Babel ancien, alors qu'un
TLV Update
avec un sous-TLV
d'« enrichissement » verrait la mise à jour des routes acceptée,
seule l'information supplémentaire serait perdue.
Il existe désormais, pour permettre le développement d'extensions, un registre IANA des types de TLV et un des sous-TLV (section 5 du RFC) et plusieurs extensions s'en servent déjà.
Enfin, l'annexe F du RFC résume les changements depuis le premier RFC, le RFC 6126, qui documentait la version 2 de Babel. On reste en version 2 car le protocole de notre RFC reste essentiellement compatible avec le précédent. Il y a toutefois trois fonctions de ce protocole qui pourraient créer des problèmes sur un réseau où certaines machines sont restées au RFC 6126 :
Hello
en
unicast sont une
nouveauté. L'ancien RFC ne les mentionnait pas. Cela peut
entrainer une mauvaise interprétation des numéros de séquence (qui
sont distincts en unicast et
multicast).Hello
peuvent désormais
avoir un intervalle entre deux messages qui est nul, ce qui
n'existait pas avant.
Bref, si on veut déployer le Babel de ce RFC dans un réseau où il
reste de vieilles mises en œuvre, il faut prendre garde à ne pas
utiliser ces trois fonctions. Si on préfère mettre à jour les vieux
programmes, sans toutefois y intégrer tout ce que contient notre
RFC, il faut au minimum ignorer (ou bien gérer correctement) les
Hello
en unicast, ou bien
avec un intervalle nul, et il faut ignorer un TLV qui contient un
sous-TLV obligatoire mais inconnu.
Il y a d'autres changements depuis le RFC 6126 mais qui ne sont pas de nature à affecter l'interopérabilité ; voyez le RFC pour les détails.
Vous pourrez trouver plus d'informations sur Babel en lisant le RFC, ou bien sur la page Web officielle. Si vous voulez approfondir la question des protocoles de routage, une excellente comparaison a été faite par David Murray, Michael Dixon et Terry Koziniec dans « An Experimental Comparison of Routing Protocols in Multi Hop Ad Hoc Networks » où ils comparent Babel (qui l'emporte largement), OLSR (RFC 7181) et Batman (ce dernier est dans le noyau Linux officiel). Notez aussi que l'IETF a un protocole standard pour ce problème, RPL, décrit dans le RFC 6550. Si vous aimez les vidéos, l'auteur de Babel explique le protocole en anglais.
Qu'en est-il des mises en œuvre de ce protocole ? Il existe une
implémentation
d'exemple, babeld, assez éprouvée
pour être disponible en paquetage dans plusieurs systèmes, comme babeld dans
Debian ou dans
OpenWrt, plateforme très souvent utilisée
pour des routeurs libres (cf. https://openwrt.org/docs/guide-user/services/babeld
). babeld
ne met pas en œuvre les solutions de sécurité des RFC 8966 ou RFC 8967. Une
autre implémentation se trouve dans Bird. Si
vous voulez écrire votre implémentation, l'annexe E contient
plusieurs conseils utiles, accompagnés de calculs, par exemple sur
la consommation mémoire et réseau. Le RFC proclame que Babel est un
protocole relativement simple et, par exemple, l'implémentation de
référence contient environ 12 500 lignes de C.
Néanmoins, cela peut être trop, une fois compilé, pour des objets (le RFC cite les fours à micro-ondes...) et l'annexe E décrit donc des sous-ensembles raisonnables de Babel. Par exemple, une mise en œuvre passive pourrait apprendre des routes, sans rien annoncer. Plus utile, une mise en œuvre « parasite » n'annonce que la route vers elle-même et ne retransmet pas les routes apprises. Ne routant les paquets, elle ne risquerait pas de créer des boucles et pourrait donc omettre certaines données, comme la liste des sources. (Le RFC liste par contre ce que la mise en œuvre parasite doit faire.)
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : J. Chroboczek (IRIF, University of Paris-Diderot)
Pour information
Réalisé dans le cadre du groupe de travail IETF babel
Première rédaction de cet article le 12 janvier 2021
Comme tout bon protocole, le protocole de routage Babel, normalisé dans le RFC 8966 ne fait pas de miracles et ne marche pas dans tous les cas. Il est donc utile de savoir quels sont les cas où Babel est efficace et utile. Ce RFC s'applique à identifier et documenter les créneaux pour Babel.
C'est qu'il en existe beaucoup de protocoles de routage internes à un AS. Le RFC cite OSPF (RFC 5340) et IS-IS (RFC 1195), qui ont la faveur des grands réseaux gérés par des professionnels, mais je trouve que Babel se situe plutôt du côté de protocoles prévus pour des réseaux moins organisés, comme Batman ou RPL (RFC 6550). Je vous laisse lire le RFC 8966 pour voir comment fonctionne Babel. Disons juste que c'est un protocole à vecteur de distances (comme RIP - RFC 2453 - mais sans ses inconvénients) et qui considère la faisabilité de chaque route potentielle avant de l'ajouter, supprimant ainsi le risque de boucles. En refusant des routes qui pourraient peut-être créer une boucle, le risque est la famine, l'absence de route pour une destination, risque dont Babel se protège avec un mécanisme de numéro de séquence dans les annonces.
La section 2 de notre RFC explicite les caractéristiques de Babel, les bonnes et les mauvaises. D'abord, la simplicité. Babel est conceptuellement simple, ce qui facilite les analyses du protocole, et aussi sa mise en œuvre. Comme le note le RFC, Babel peut être expliqué en un micro-siècle. Et question programmation, le RFC cite une mise en œuvre de Babel réalisée en deux nuits (le RFC ne dit pas ce que le ou la programmeur·e faisait de jour…)
Ensuite, la résistance. Babel ne dépend que de quelques propriétés simples du réseau et peut donc fonctionner dans un large éventail de situations. Babel demande juste que les métriques soient strictement monotones croissantes (emprunter le chemin A puis le B, doit forcément coûter strictement plus cher que de juste prendre le chemin A, autrement des boucles pourraient se former) et distributives à gauche (cf. « Metarouting » de Griffin et Sobrinho). En revanche, Babel n'exige pas un transport fiable (les paquets ont le droit de se perdre, ou de doubler un autre paquet) et n'exige pas que la communication soit transitive (dans les liens radio, il peut arriver que A puisse joindre B et B puisse parler à C, sans que pour autant A puisse communiquer directement avec C). Des protocoles comme OSPF (RFC 5340) ou IS-IS sont bien plus exigeants de ce point de vue.
Babel est extensible : comme détaillé dans
l'annexe C du RFC 8966, le protocole
Babel peut être étendu. Cela a été fait, par exemple, pour permettre
du routage en fonction de la source (RFC 9079)
ou bien du routage en fonction du RTT (RFC 9616). Il y a aussi des
extensions qui n'ont pas (encore ?) été déployées comme du routage
en fonction des fréquences radio (draft-chroboczek-babel-diversity-routing
)
ou en fonction du ToS (draft-chouasne-babel-tos-specific
).
Mais Babel a aussi des défauts, notamment :
Babel a connu plusieurs déploiements dans le monde réel (section 3). Certains de ces déploiements étaient dans des réseaux mixtes, mêlant des parties filaires, routant sur le préfixe, et des parties radio, avec du routage mesh sur les adresses IP. Babel a aussi été déployé dans des overlays, en utilisant l'extension tenant compte du RTT, citée plus haut. Il a aussi été utilisé dans des réseaux purement mesh. Voir à ce sujet les articles « An experimental comparison of routing protocols in multi hop ad hoc networks » et « Real-world performance of current proactive multi-hop mesh protocols ». Ces réseaux utilisent traditionnellement plutôt des protocoles comme OLSR (RFC 7181). Enfin, Babel a été déployé dans des petits réseaux non gérés (pas d'administrateur système).
Et la sécurité, pour finir (section 5). Comme tous les protocoles à vecteur de distance, Babel reçoit l'information de ses voisins. Il faut donc leur faire confiance, un voisin menteur peut annoncer ce qu'il veut. En prime, si le réseau sous-jacent n'est pas lui-même sécurisé (par exemple si c'est du WiFi sans WPA), il n'y a même pas besoin d'être routeur voisin, toute machine peut tripoter les paquets.
Pour empêcher cela, il y a deux solutions cryptographiques, dans les RFC 8967 (la solution la plus simple) et RFC 8968 (plus complexe mais plus sûre et qui fournit en prime de la confidentialité). La solution avec le MAC, normalisée dans le RFC 8967, est celle recommandée car elle convient mieux au caractère de la plupart des réseaux utilisant Babel. (Le RFC n'en parle apparamment pas, mais notez qu'authentifier le voisin ne résout pas complètement le problème. On peut être authentifié et mentir.)
Enfin, si on n'utilise pas de solution de confidentialité, un observateur peut déduire des messages Babel la position d'un routeur WiFi qui se déplacerait, ce qui peut être considéré comme un problème.
Sinon, pour creuser cette question de l'applicabilité de Babel, vous pouvez lire le texte, très vivant, « Babel does not care ».
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : C. Huitema (Private Octopus)
Pour information
Première rédaction de cet article le 23 janvier 2021
On raconte souvent que la publication d'un RFC prend longtemps, très longtemps, trop longtemps, et on dit parfois que cela est dû à une bureaucratie excessive dans le processus de publication. Peut-on objectiver cette opinion ? Dans ce RFC, l'auteur étudie le trajet de plusieurs RFC (pris au hasard) publiés en 2018 et regarde où étaient les goulets d'étranglement, et ce qui a pris du temps. Les RFC étudiés ont mis en moyenne trois ans et quatre mois à être publiés, depuis le premier document écrit jusqu'à la publication finale. (Mais il y a de grandes variations.) La majorité du temps est passée dans le groupe de travail, ce ne sont donc pas forcément les étapes « bureaucratiques » qui retardent.
Heureusement pour cette étude, le DataTracker enregistre les différentes étapes de la vie d'un document et permet donc au chercheur de travailler sur des données solides. Mais attention, si les données sont fiables, leur interprétation est toujours délicate. Et, si une métrique devient un objectif, elle cesse d'être une bonne métrique. Si l'IETF ne cherchait qu'à raccourci le délai de publication, ce serait simple, il suffirait d'approuver sans discussion et immédiatement toutes les propositions, même mauvaises. (Ce qu'on nomme le rubber stamping en anglais.)
La section 2 expose la méthodologie suivie. D'abord, déterminer les dates importantes dans la vie d'un RFC. Tous les RFC ne suivent pas le même parcours, loin de là. Le cas typique est celui d'un document individuel, qui est ensuite adopté par un groupe de travail, approuvé par l'IESG, puis publié par le RFC Editor. Dans cette analyse, trois périodes sont prises en compte :
Ainsi, pour le dernier RFC publié au moment où j'écris cet article (et qui n'est donc pas pris en compte dans l'analyse, qui couvre des RFC de 2018), pour ce RFC 9003, on voit sur le DataTracker qu'il a été documenté pour la première fois en décembre 2017, adopté par un groupe de travail en avril 2018, soumis à l'IESG en avril 2020, a eu un IETF Last Call en juin 2020, a été approuvé par l'IESG en novembre 2020 et enfin publié en RFC début janvier 2021. Mais beaucoup d'autres RFC suivent un autre parcours. Pour la voie indépendante (décrite dans le RFC 4846), l'analyse de ce RFC 8963 considère l'approbation par l'ISE (Independent Submission Editor) comme terminant la première période et l'envoi au RFC Editor comme terminant la deuxième.
Les vingt RFC qui sont utilisés dans l'étude ont été pris au hasard parmi ceux publiés en 2018. Le RFC reconnait que c'est un échantillon bien petit mais c'est parce que les données disponibles ne fournissaient pas toutes les informations nécessaires et qu'il fallait étudier « manuellement » chaque RFC, ce qui limitait forcément leur nombre. Je ne vais pas lister ici tous les RFC étudiés, juste en citer quelques uns que je trouve intéressant à un titre ou l'autre :
Bien, après cette sélection d'un échantillon de RFC, qu'observe-t-on (section 4 de notre RFC) ? Le délai moyen entre le premier Internet Draft et la publication en RFC est de trois ans et trois mois. Mais les variations sont telles que cette moyenne n'a guère de sens (c'est souvent le cas avec la moyenne). Le plus malheureux est le RFC 8492, six ans et demi au total, mais ce cas n'est pas forcément généralisable. Pour les RFC de l'IETF, où le délai moyen est presque le même, le temps passé dans le groupe de travail est de deux ans et neuf mois, à l'IESG de trois à quatre mois et à peu près pareil chez le RFC Editor. Bref, 80 % du temps se passe en « vrai » travail, dans le groupe, les passages plus « bureaucratiques » n'occupent que 20 % du temps et ne sont donc pas les principaux responsables des délais.
L'étude regarde aussi des échantillons de RFC publiés en 2008 et en 1998 (en utilisant cette fois, à juste titre, des valeurs médianes) et note que le délai était à peu près le même en 2008 mais qu'il était trois à quatre fois plus court en 1998.
Et la partie purement éditoriale, chez le RFC Editor ? On a vu qu'elle prenait de trois à quatre mois. C'est quatre en moyenne mais si on exclut l'extrême RFC 8492, c'est plutôt trois mois, ce que je trouve plutôt court pour relire et préparer des documents longs et complexes, où une coquille peut être très gênante (ce ne sont pas des romans !) Quelles sont les caractéristiques des RFC que l'on pourrait corréler à ce délai chez le RFC Editor ? L'étude montre une corrélation entre le délai et la longueur du document, ce qui n'est pas surprenant. Même chose pour la corrélation entre le nombre de changements faits depuis l'approbation par l'IESG et la publication. (Une des tâches du RFC Editor est de faire en sorte que les RFC soient en bon anglais alors que beaucoup d'auteurs ne sont pas anglophones.) Plus étonnant, le nombre d'auteurs ne diminue pas le délai, au contraire, il l'augmente. Un nouveau cas de « quand on ajoute des gens au projet, le projet n'avance pas plus vite » ? Il ne faut pas oublier qu'il existe une zone grise entre « changement purement éditorial », où le RFC Editor devrait décider, et « changement qui a des conséquences techniques » et où l'approbation des auteurs est cruciale. Il n'est pas toujours facile de s'assurer de la nature d'un changement proposé par le RFC Editor. Et, si le changement a des conséquences techniques, il faut mettre les auteurs d'accord entre eux, ce qui est long (ils peuvent être dans des fuseaux horaires différents, avec des obligations professionnelles différentes) et peut soulever des désaccords de fond.
Et pour la voie indépendante ? Trois des RFC de l'échantillon étaient sur cette voie. Ils ont été publiés plus vite que sur la voie IETF, ce qui est logique puisqu'ils ne nécessitent pas le consensus de l'IETF (voir le RFC 8789). En revanche, le fait que la fonction d'ISE (Independant Stream Editor, cf. RFC 8730) soit une tâche assurée par un seul volontaire peut entrainer des retards ce qui a été le cas, ironiquement, de ce RFC 8963.
L'étude se penche aussi sur le nombre de citations dont bénéficie chaque RFC (section 5) car elle peut donner une idée sommaire de leur impact. Elle a utilisé l'API de Semantic Scholar. Par exemple, pour le RFC 8483, publié l'année de l'étude mais pas inclus dans l'échantillon :
% curl -s https://api.semanticscholar.org/v1/paper/10.17487/rfc8483\?include_unknown_references=true \ jq .
On voit qu'il est cité une fois, précisément par ce RFC. En revanche, le RFC 8446, sur TLS 1.3 est cité pas moins de 773 fois :
% curl -s https://api.semanticscholar.org/v1/paper/10.17487/rfc8446\?include_unknown_references=true | \ jq .citations\|length 773
C'est de très loin le plus « populaire » dans le corpus (l'échantillon étudié). C'est bien sûr en partie en raison de l'importance critique de TLS, mais c'est aussi parce que TLS 1.3 a été étudié par des chercheurs et fait l'objet d'une validation formelle, un sujet qui génère beaucoup d'articles dans le monde académique. C'est aussi le cas du RFC 8312, sur Cubic, car la congestion est également un sujet de recherche reconnu. Notez que le passage du temps est impitoyable : les RFC des échantillons de 2008 et 1998 ne sont quasiment plus cités (sauf le RFC 2267, qui résiste). Est-ce que le nombre de citations est corrélé à l'ampleur du déploiement ? Dans le cas des RFC 8446 et RFC 8312, on pourrait le croire, mais le RFC 8441 a également un très vaste déploiement, et quasiment aucune citation. À l'inverse, un RFC qui a un nombre de citations record, le RFC 5326, n'a quasiment pas eu de déploiement, mais a excité beaucoup de chercheurs académiques (et même médiatique, vu son sujet cool), ce qui explique le nombre de citations. Bref, le monde de la recherche et celui des réseaux n'ont pas des jugements identiques.
On avait noté que le RFC 2267 était beaucoup cité. Or, il n'est plus d'actualité, ayant officiellement été remplacé par le RFC 2827. Mais les citations privilégient la première occurrence, et ne reflètent donc pas l'état actuel de la normalisation.
Bon, on a vu que les citations dans le monde de la recherche ne reflétaient pas réellement l'« importance » d'un RFC. Et le nombre d'occurrences dans un moteur de recherche, c'est mieux ? L'auteur a donc compté le nombre de références aux RFC dans Google et Bing. Il y a quelques pièges. Par exemple, en cherchant « RFC8441 », Bing trouve beaucoup de références à ce RFC 8441, qui parle de WebSockets, mais c'est parce qu'il compte des numéros de téléphone se terminant par 8441, ou des adresses postales « 8441 Main Street ». Google n'a pas cette amusante bogue. Ceci dit, il est noyé par le nombre très élevé de miroirs des RFC, puisque l'IETF permet et encourage ces copies multiples (contrairement aux organisations de normalisation fermées comme l'AFNOR). Ceci dit, les RFC 8441 et RFC 8471 sont plus souvent cités, notamment car ils apparaissent fréquemment dans les commentaires du code source d'un logiciel qui les met en œuvre. Le « test Google » est donc assez pertinent pour évaluer le déploiement effectif.
Quelles conclusions en tirer (section 6 du RFC) ? D'abord, si on trouve le délai de publication trop long, il faut se focaliser sur le temps passé dans le groupe de travail, car c'est clairement le principal contributeur au délai. [En même temps, c'est logique, c'est là où a lieu le « vrai » travail.] Malheureusement, c'est là où le DataTracker contient le moins d'information alors que les phases « bureaucratiques » comme le temps passé à l'IESG bénéficient d'une traçabilité plus détaillée. Ainsi, on ne voit pas les derniers appels (Last Calls) internes aux groupes de travail. Bien sûr, on pourrait rajouter ces informations mais attention, cela ferait une charge de travail supplémentaire pour les présidents des groupes de travail (des volontaires). Récolter des données pour faire des études, c'est bien, mais cela ne doit pas mener à encore plus de bureaucratie.
Du fait que l'étude ne porte que sur des RFC effectivement publiés, elle pourrait être accusée de sensibilité au biais du survivant. Peut-être que les RFC qui n'ont pas été publiés auraient aussi des choses à nous dire ? L'abandon d'un projet de normalisation est fréquent (pensons par exemple à DBOUND ou RPZ) et parfaitement normal et souhaitable ; toutes les idées ne sont pas bonnes et le travail de tri est une bonne chose. Mais, en n'étudiant que les RFC publiés, on ne peut pas savoir si le tri n'a pas été trop violent et si de bonnes idées n'en ont pas été victimes. L'IETF ne documente pas les abandons et une étude de drafts sans successeur pourrait être intéressante.
Pour terminer, je me suis amusé à regarder ce qu'il en était des trois RFC que j'ai écrit :
Date de publication du RFC : Novembre 2020
Auteur(s) du RFC : M. Allman (ICSI)
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 24 novembre 2020
Vous savez certainement que l'Internet fait circuler les données sous forme de paquets indépendants, et qu'un paquet peut toujours être perdu, par exemple parce qu'un rayon cosmique agressif est passé à ce moment-là ou, moins spectaculaire, parce que les files d'un routeur étaient pleines et qu'il a dû se résigner à laisser tomber le paquet. Des protocoles existent donc pour gérer ces pertes de paquets, ce qui implique de les détecter. Et comment sait-on qu'un paquet a été perdu ? C'est plus complexe que ça n'en a l'air, et ce RFC tente d'établir un cadre générique pour la détection de pertes.
Ne tournons pas autour du pot : la seule façon fiable de savoir
si un paquet a été perdu, c'est d'attendre qu'il arrive (ce que le
RFC nomme, dans son titre, time-based loss
detection) et, s'il n'arrive pas, de le déclarer
perdu. Plus précisément, pour l'émetteur (car le récepteur ne sait
pas forcément qu'on lui a envoyé un paquet), on émet un paquet et on
attend une confirmation qu'il a été reçu (avec TCP, cette
confirmation sera le ACK
, avec le DNS sur UDP, ce sera la réponse DNS). Si la
confirmation n'a pas été reçue, s'il y a timeout,
c'est qu'un paquet (la demande, ou bien l'accusé de réception) n'est
pas arrivé. Mais attendre combien de temps ? Si on attend peu de
temps (mettons 100 millisecondes), on risque de considérer le paquet
comme perdu, alors que le voyage était simplement un peu long (en
100 ms, vous ne pouvez même pas faire un aller-retour entre la
France et les
Philippines, ne serait-ce qu'à cause de la
limite de la vitesse de la lumière). Et si on attend longtemps
(mettons 5 secondes), on ne pourra pas réagir rapidement aux pertes,
et la latence perçue par l'utilisateur
sera insupportable (la sensation de « vitesse » pour l'utilisateur
dépend davantage de la latence que de la capacité). Il faut donc faire un compromis
entre réactivité et justesse.
Il existe d'autres méthodes que l'attente pour détecter des pertes, par exemple TCP et SCTP utilisent aussi les accusés de réception sélectifs (RFC 2018, RFC 9260 dans sa section 3.3.4 et RFC 6675) mais aucune de ces autres méthodes ne détecte toutes les pertes, et la détection par absence de réponse reste donc indispensable.
L'Internet, vous le savez, est un ensemble compliqué de réseaux, reliés par des câbles très variés (et quelques liens radio), avec des débits bien différents et changeant d'un moment à l'autre. Tout chemin d'une machine à l'autre va avoir un certain nombre de propriétés (qui varient dans le temps) telles que la latence ou la capacité. Et le taux de perte de paquets, qui nous intéresse ici. (Voir aussi le RFC 7680.)
Notre RFC suppose que la perte de paquets est une indication de congestion (RFC 5681). Ce n'est pas vrai à 100 %, surtout sur les liens radio, où des paquets peuvent être détruits par des perturbations électro-magnétiques sans qu'il y ait congestion, mais c'est quand même proche de la vérité.
Et, au fait, pourquoi détecter la perte de paquets ? Pourquoi ne pas tout simplement ignorer le problème ? Deux raisons :
Résultat, beaucoup de protocoles ont un mécanisme de détection de pertes : TCP (RFC 6298), bien sûr, mais aussi SCTP (RFC 9260), SIP (RFC 3261), etc.
Le RFC cite souvent l'article de Allman, M. et Paxson V, « On Estimating End-to-End Network Path Properties » donc vous avez le droit d'interrompre votre lecture ici pour lire cet article avant de continuer.
Reprenons, avec la section 2 du RFC, qui explique les buts et non-buts de ce RFC :
Nous arrivons maintenant à la section 4 du RFC, qui liste les exigences auxquelles doivent obéir les mécanismes de détection de pertes. (En pratique, les mécanismes existants collent déjà à ces exigences mais elles n'avaient pas été formalisées.) Un petit retour sur la notion de latence, d'abord. On veut savoir combien de temps attendre avant de déclarer un paquet perdu. Cette durée se nomme RTO (Retransmission TimeOut). Les latences dans l'Internet étant extrêmement variables, il serait intéressant de faire dépendre le RTO de la latence. Mais quelle latence ? Le temps d'aller-retour entre deux machines est le temps qu'il faut à un paquet IP pour aller de la machine A à la machine B plus le temps qu'il faut à un paquet IP pour aller de B à A. On ne peut pas mesurer directement ce temps, car le temps de traitement dans la machine B n'est pas forcément connu. Dans le cas de l'ICMP Echo utilisé par ping, ce temps est considéré comme négligeable, ce qui est assez correct si l'amer est une machine Unix dont le noyau traite l'ICMP Echo. Cela l'est moins si l'amer est un routeur qui traite les réponses ICMP à sa plus basse priorité. Et cela l'est encore moins si un client DNS essaie d'estimer la latence vers un résolveur. Si le résolveur avait la réponse dans sa mémoire, le temps de traitement est faible. S'il devait demander aux serveurs faisant autorité, ce temps peut être bien supérieur à la latence. Le RFC distingue donc RTT (Round-Trip Time), la vraie latence, et FT (Feedback Time) qui est ce qu'affichent ping, dig et autres outils. La machine qui veut savoir combien de temps attendre une réaction de la machine en face, avant de déclarer qu'un paquet est perdu, a tout intérêt à avoir une idée du FT.
Certaines des exigences du RFC sont quantitatives. Ainsi, tant qu'on n'a pas mesuré le FT (au début de la session), le RFC requiert que le RTO soit d'au moins une seconde, pour ne pas surcharger le réseau avec des réémissions, et parce que la perte étant interprétée comme un signe de congestion, un RTO trop faible amènerait à réduire la quantité de données qu'on peut envoyer, diminuant ainsi la capacité effective. Sans compter le problème de l'ambiguïté. Si on a réémis un paquet, et qu'une réponse revient, était-elle pour le paquet initial ou pour la réémission ? Dans le doute, il ne faut pas utiliser le temps mesuré pour changer son estimation du RTO. Et c'est une des raisons pour lesquelles il faut prendre son temps pour les premières mesures. Ah, au fait, pourquoi une seconde et pas 0,75 ou 1,25 ? Cela vient d'une analyse quantitative des RTT typiques de l'Internet, exposée dans l'annexe A du RFC 6298.
Après plusieurs mesures, on connait mieux le FT et on peut abaisser le RTO. Il n'y a pas de durée minimale, donc on peut s'approcher de zéro tant qu'on veut.
Le RFC dit aussi que le RTO (Retransmission TimeOut, le délai d'attente) doit se mesurer à partir de plusieurs observations du FT, pour éviter une mesure faussée par un cas extrême. Et il faut refaire ces observations souvent car le réseau change ; les vieilles mesures n'ont pas d'intérêt. C'est ce que fait TCP avec son smoothed RTT, utilisant la moyenne mobile exponentielle (RFC 6298).
L'Internet étant ce qu'il est, il est recommandé de s'assurer qu'un méchant ne puisse pas facilement fausser cette mesure en injectant des faux paquets. Pour TCP, cette assurance est donnée par le caractère imprévisible du numéro de séquence initial, si l'attaquant n'est pas sur le chemin (RFC 5961). Si on veut une protection contre un attaquant situé sur le chemin, il faut de la cryptographie.
Le RFC recommande de refaire l'évaluation du RTO au moins une fois par RTT et, de préference, aussi souvent que des données sont échangées. TCP le fait une fois par RTT et, si on utilise le RFC 7323, à chaque accusé de réception.
Le RFC rappelle qu'en l'absence d'indication du contraire, une perte de paquets doit être considérée comme un indicateur de congestion, et qu'il faut donc ralentir (RFC 5681, pour le cas de TCP).
Et, dernière exigence, en cas de perte de paquets, le RTO doit croitre exponentiellement, pour s'ajuster rapidement à la charge du réseau. On pourra réduire le RTO lorsqu'on aura la preuve que les paquets passent et qu'on reçoit des accusés de réception et/ou des réponses. Dans tous les cas, le RFC limite le RTO à 60 secondes, ce qui est déjà énorme (je n'ai jamais vu une réponse revenir après une durée aussi longue).
Enfin, la section 5 discute des exigences posées et de leurs limites. La tension entre le désir de réactivité (un RTO faible) et celui de mesures correctes (un RTO plus important, pour être raisonnablement sûr de ne pas conclure à tort qu'il y a eu une perte) est une tension fondamentale : on n'aura jamais de solution parfaite, juste des compromis. Il existe des techniques qui permettent de mieux détecter les pertes (l'algorithme Eifel - RFC 3522, le F-RTO du RFC 5682, DSACK - RFC 2883 et RFC 3708…) mais elles ne sont pas complètes et l'attente bornée par le RTO reste donc nécessaire en dernier recours.
Le noyau Linux a mis en œuvre plusieurs techniques non normalisées (par exemple des modifications du RFC 6298) sans que cela ne crée apparemment de problèmes. D'une manière générale, les algorithmes de détection de pertes de TCP, SCTP ou QUIC sont largement compatibles avec les exigences de ce RFC.
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : M. Nottingham
Pour information
Première rédaction de cet article le 30 janvier 2021
Enregistrer une clé
privée (ou tout autre secret) dans un dépôt public (par
exemple sur GitHub) est un gag courant. La
nature des VCS fait qu'il est souvent difficile de
retirer cette clé. Pour limiter un peu les dégâts, ce RFC enregistre
un nouveau plan d'URI, secret-token:
, qui
permettra de marquer ces secrets, autorisant par exemple le VCS à
rejeter leur enregistrement.
Ce RFC se focalise sur les secrets qui sont « au porteur » (bearer tokens) c'est-à-dire que leur seule connaissance suffit à les utiliser ; aucune autre vérification n'est faite. Ce peut être un mot de passe, une clé d'API, etc. Voici un exemple avec une clé GitLab (je vous rassure, je l'ai révoquée depuis) :
La révélation de ces secrets via un enregistrement accidentel,
par exemple dans un dépôt logiciel public, est un grand
classique. Lisez par exemple le témoignage « I
Published My AWS Secret Key to GitHub », ou une
aventure similaire, « Exposing
your AWS access keys on Github can be extremely costly. A personal
experience. », un avertissement
de Github à ses utilisateurs, ou enfin une étude
détaillée publiée à NDSS. Un git add
de
trop (ou bien un secret mis dans le code source) et, au prochain
commit, le secret se
retrouve dans le dépôt, puis publié au premier git
push
.
L'idée de ce RFC est de marquer clairement ces secrets pour que, par exemple, des audits du dépôt les repèrent plus facilement. Ou pour que les systèmes d'intégration continue puissent les rejeter automatiquement.
Le plan d'URI est simple (section 2 du RFC) :
la chaîne secret-token:
suivie du secret. Un
exemple serait
secret-token:E92FB7EB-D882-47A4-A265-A0B6135DC842%20foo
(notez l'échappement du caractère
d'espacement). Par exemple avec les secrets au porteur
pour HTTP
du RFC 6750, cela donnerait un envoi au serveur :
GET /authenticated/stuff HTTP/1.1 Host: www.example.com Authorization: Bearer secret-token:E92FB7EB-D882-47A4-A265-A0B6135DC842%20foo
Le plan secret-token:
est désormais dans le
registre IANA.
Je n'ai pas encore trouvé d'organisation qui distribue ces secrets au porteur en utilisant ce plan d'URI mais il semble que GitHub soit tenté.
Date de publication du RFC : Décembre 2020
Auteur(s) du RFC : T. Hardie
Première rédaction de cet article le 3 décembre 2020
Le domaine uri.arpa
a été créé par le RFC 3405 pour stocker dans le DNS des règles gouvernant la
résolution d'URI via des enregistrements NAPTR. Ce
nouveau RFC
introduit un très léger changement des procédures d'enregistrement
dans ce domaine uri.arpa
.
À titre d'exemple de ces règles, vous pouvez regarder la règle pour les URN.
En effet, le RFC 3405 parlait d'un IETF tree qui avait été créé par le RFC 2717 (dans sa section 2.2) mais supprimé par le RFC 4395. Notre RFC 8958 prend simplement acte de cette suppression et retire les mentions IETF tree du RFC 3405.
Date de publication du RFC : Décembre 2020
Auteur(s) du RFC : C. Loibl (next layer Telekom GmbH), R. Raszuk (NTT Network Innovations), S. Hares (Huawei)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 1 janvier 2021
Il existe un mécanisme de diffusion des règles de filtrage IP, FlowSpec, normalisé dans le RFC 8955. Ce mécanisme permet d'utiliser BGP pour envoyer à tous les routeurs d'un AS les mêmes règles, ce qui est très pratique, par exemple pour bloquer une attaque sur tous les routeurs d'entrée du domaine. Le FlowSpec original ne gérait qu'IPv4, voici son adaptation à IPv6.
En fait, il n'y avait pas grand'chose à faire. Après tout, IPv6 n'est pas un nouveau protocole, juste une nouvelle version du même protocole IP. Des concepts cruciaux, comme la définition d'un préfixe en préfixe/longueur, ou comme la règle du préfixe le plus spécifique restent les mêmes. Ce RFC est donc assez simple. Il ne reprend pas l'essentiel du RFC 8955, il ajoute juste ce qui est spécifique à IPv6. (À l'IETF, il avait été proposé de fusionner les deux documents mais, finalement, il y a un RFC générique + IPv4, le RFC 8955, et un spécifique à IPv6, notre RFC 8956.)
Petit rappel sur FlowSpec : les règles de filtrage sont transportées en BGP, les deux routeurs doivent annoncer la capacité « multi-protocole » (les capacités sont définies dans le RFC 5492) définie dans le RFC 4760, la famille de l'adresse (AFI pour Address Family Identifier) est 2 (qui indique IPv6) et le SAFI (Subsequent Address Family Identifier) est le même qu'en IPv4, 133.
Le gros de notre RFC (section 3) est l'énumération des différents
types d'identifiant d'un trafic à filtrer. Ce sont les mêmes
qu'en IPv4 mais avec quelques adaptations. Ainsi, le type 1,
« préfixe de destination » permet par exemple de dire à ses routeurs
« jette à la poubelle tout ce qui est destiné à
2001:db:96b:1::/64
». Son encodage ressemble
beaucoup à celui en IPv4 mais avec un truc en plus, le décalage
(offset) depuis le début de l'adresse. Avec un
décalage de 0, on est dans le classique préfixe/longueur mais on
peut aussi ignorer des bits au début de l'adresse avec un décalage
non-nul.
Le type 3, « protocole de niveau supérieur » est l'un de ceux dont la définition IPv6 est la plus différente de celle d'IPv4. En effet, IPv6 a les en-têtes d'extension (RFC 8200, section 4), une spécificité de cette version. Le champ « Next header » d'IPv6 (RFC 8200, section 3) n'a pas la même sémantique que le champ « Protocole » d'IPv4 puisqu'il peut y avoir plusieurs en-têtes avant celui qui indique le protocole de couche 4 (qui sera typiquement TCP ou UDP). Le choix de FlowSpec est que le type 3 désigne le dernier champ « Next header », celui qui identifie le protocole de transport, ce qui est logique, c'est bien là-dessus qu'on veut filtrer, en général. Rappelez-vous au passage que d'analyser les en-têtes d'extension IPv6 pour arriver au protocole de transport n'est hélas pas trivial. Et il y a d'autres pièges, expliqués dans les RFC 7112 et RFC 8883.
Autre type d'identifiant qui est différent en IPv6, le type 7, ICMP. Si le protocole est très proche, les types et les codes ne sont en effet pas les mêmes. Ainsi, l'un des types les plus célèbres, Echo Request, utilisé par ping, vaut 8 en IPv4 et 138 en IPv6.
Les autres types se comportent en IPv6 exactement comme en IPv4. Par exemple, si vous voulez dire à vos routeurs de filtrer sur le port, les types 4, 5 et 6, dédiés à cet usage, ont le même encodage et la même sémantique qu'en IPv4 (ce qui est logique, puisque le port est un concept de couche 4).
FlowSpec est aujourd'hui largement mis en œuvre dans les logiciels de routage, y compris en IPv6. Vous pouvez consulter à ce sujet le rapport de mise en œuvre. Si vous voulez jouer avec FlowSpec, le code Python d'exemple de l'annexe A est disponible en ligne.
Date de publication du RFC : Décembre 2020
Auteur(s) du RFC : C. Loibl (next layer Telekom GmbH), S. Hares (Huawei), R. Raszuk (Bloomberg LP), D. McPherson (Verisign), M. Bacher (T-Mobile Austria)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 1 janvier 2021
Lorsqu'on a un grand réseau compliqué, diffuser à tous les routeurs des règles de filtrage, par exemple pour faire face à une attaque par déni de service, peut être complexe. Il existe des logiciels permettant de gérer ses routeurs collectivement mais ne serait-il pas plus simple de réutiliser pour cette tâche les protocoles existants et notamment BGP ? Après tout, des règles de filtrage sont une forme de route. On profiterait ainsi des configurations existantes et de l'expérience disponible. C'est ce que se sont dit les auteurs de ce RFC. « FlowSpec » (nom officieux de cette technique) consiste à diffuser des règles de traitement du trafic en BGP, notamment à des fins de filtrage. Ce RFC remplace le RFC FlowSpec original, le RFC 5575, le texte ayant sérieusement changé (mais le protocole est presque le même).
Les routeurs modernes disposent en effet de nombreuses capacités de traitement du trafic. Outre leur tâche de base de faire suivre les paquets, ils peuvent les classifier, limiter leur débit, jeter certains paquets, etc. La décision peut être prise en fonction de critères tels que les adresses IP source et destination ou les ports source et destination. Un flot (flow) est donc défini comme un tuple rassemblant les critères d'acceptation d'un paquet IP. Notre RFC 8955 encode ces critères dans un attribut NLRI (Network Layer Reachability Information est décrit dans la section 4.3 du RFC 4271) BGP, de manière à ce qu'ils puissent être transportés par BGP jusqu'à tous les routeurs concernés. Sans FlowSpec, il aurait fallu qu'un humain ou un programme se connecte sur tous les routeurs et y rentre l'ACL concernée.
Pour reconnaitre les paquets FlowSpec, ils sont marqués avec le SAFI (concept introduit dans le RFC 4760) 133 pour les règles IPv4 (et IPv6 grâce au RFC 8956) et 134 pour celles des VPN.
La section 4 du RFC donne l'encodage du NLRI. Un message
UPDATE
de BGP est utilisé, avec les attributs
MP_REACH_NLRI
et
MP_UNREACH_NLRI
du RFC 4760 et un NLRI FlowSpec. Celui-ci compte plusieurs
couples {type, valeur} où l'interprétation de la valeur dépend du
type (le type est codé sur un octet). Voici les principaux types :
10.0.1.0/24
sera encodé (noté en
hexadécimal)
01 18 0a 00 01
(type 1, longueur 24 - 18 en
hexa - puis le préfixe dont vous noterez que seuls les trois
premiers octets sont indiqués, le dernier n'étant pas pertinent
ici).RST
.Tous les types de composants sont enregistrés à l'IANA.
Une fois qu'on a cet arsenal, à quoi peut-on l'utiliser ? La section 5 détaille le cas du filtrage. Autrefois, les règles de filtrage étaient assez statiques (je me souviens de l'époque où tous les réseaux en France avaient une ACL, installée manuellement, pour filtrer le réseau de l'EPITA). Aujourd'hui, avec les nombreuses DoS qui vont et viennent, il faut un mécanisme bien plus dynamique. La première solution apparue a été de publier via le protocole de routage des préfixes de destination à refuser. Cela permet même à un opérateur de laisser un de ses clients contrôler le filtrage, en envoyant en BGP à l'opérateur les préfixes, marqués d'une communauté qui va déclencher le filtrage (à ma connaissance, aucun opérateur n'a utilisé cette possibilité, en raison du risque qu'une erreur du client ne se propage). De toute façon, c'est très limité en cas de DoS (par exemple, on souhaite plus souvent filtrer sur la source que sur la destination). Au contraire, le mécanisme FlowSpec de ce RFC donne bien plus de critères de filtrage.
Cela peut d'ailleurs s'avérer dangereux : une annonce FlowSpec trop générale et on bloque du trafic légitime. C'est particulièrement vrai si un opérateur accepte du FlowSpec de ses clients : il ne faut pas permettre à un client de filtrer les autres. D'où la procédure suggérée par la section 6, qui demande de n'accepter les NLRI FlowSpec que s'ils contiennent un préfixe de destination, et que ce préfixe de destination est routé vers le même client qui envoie le NLRI (notez que cette règle a été assouplie par le RFC 9117). Ainsi, un client ne peut pas déclencher le filtrage d'un autre puisqu'il ne peut influencer que le filtrage des paquets qui lui sont destinés.
Au fait, en section 5, on a juste vu comment indiquer les
critères de classification du trafic qu'on voulait filtrer. Mais
comment indiquer le traitement qu'on veut voir appliqué aux paquets
ainsi classés ? (Ce n'est pas forcément les jeter : on peut vouloir
être plus subtil.) FlowSpec utilise les communautés étendues du RFC 4360. La valeur sans doute la plus importante
est 0x8006, traffic-rate
, qui permet de
spécifier un débit maximal pour les paquets qui correspondent aux
critères mis dans le NLRI. Le débit est en octets/seconde. En
mettant zéro, on demande à ce que tous les paquets classés soient
jetés. (Mais on peut aussi indiquer des paquets/seconde, c'est une
nouveauté de ce RFC.) Les autres valeurs possibles permettent des
actions comme de modifier les bits DSCP du trafic classé.
Comme toutes les armes, celle-ci peut être dangereuse pour celui qui la manipule. La section 12 est donc la bienvenue, pour avertir de ces risques. Par exemple, comme indiqué plus haut, si on permet aux messages FlowSpec de franchir les frontières entre AS, un AS maladroit ou méchant risque de déclencher un filtrage chez son voisin. D'où l'importance de la validation, n'accepter des règles FlowSpec que pour les préfixes de l'AS qui annonce ces règles.
Ensuite, comme tous les systèmes de commande des routeurs à distance, FlowSpec permet de déclencher un filtrage sur tous les routeurs qui l'accepteront. Si ce filtrage est subtil (par exemple, filtrer tous les paquets plus grands que 900 octets), les problèmes qui en résultent seront difficiles à diagnostiquer.
FlowSpec a joué un rôle important dans la panne Level 3 / CenturyLink d'août 2020. Et, avant cela, dans la panne CloudFlare du 3 mars 2013, où des critères incorrects (une taille de paquet supérieure au maximum permis par IP) avaient été envoyés à tous les routeurs. Ce n'est pas une bogue de FlowSpec : tout mécanisme de diffusion automatique de l'information à N machines différentes a le même problème potentiel. Si l'information était fausse, le mécanisme de diffusion transmet l'erreur à tous... (Dans le monde des serveurs Unix, le même problème peut se produire avec des logiciels comme Chef ou Puppet. Lisez un cas rigolo avec Ansible.) Comme le prévient notre RFC : « When automated systems are used, care should be taken to ensure the correctness of the automated system. » Toutefois, contrairement à ce que laisse entendre le RFC, il n'y a pas que les processus automatiques qui injectent des erreurs : les humains le font aussi.
Si vous voulez en apprendre plus sur FlowSpec :
Si vous vous intéressez à l'utilisation de BGP lors d'attaques par déni de service, vous pouvez aussi consulter les RFC 3882 et RFC 5635.
Les changements depuis la norme originale, le RFC 5575, sont résumés dans l'annexe B. Parmi les principaux :
FlowSpec est utilisé depuis dix ans et de nombreuses mises en œuvre existent (cf. la liste). Sur les Juniper, on peut consulter leur documentation en ligne.
Date de publication du RFC : Décembre 2020
Auteur(s) du RFC : K. Moriarty (Dell Technologies)
Pour information
Première rédaction de cet article le 17 décembre 2020
Voici le compte-rendu de la deuxième édition de l'atelier CARIS (Coordinating Attack Response at Internet Scale), un atelier de l'ISOC consacré à la défense de l'Internet contre les différentes attaques possibles, par exemple les dDoS. Cet atelier s'est tenu à Cambridge en mars 2019. Par rapport au premier CARIS, documenté dans le RFC 8073, on note l'accent mis sur les conséquences du chiffrement, désormais largement répandu.
Les problèmes de sécurité sur l'Internet sont bien connus. C'est tous les jours qu'on entend parler d'une attaque plus ou moins réussie contre des infrastructures du réseau. Ainsi, Google a été victime d'une grosse attaque en 2017 (mais qui n'a été révélée que des années après). Mais, pour l'instant, nous n'avons pas de solution miracle. L'idée de base des ateliers CARIS est de rassembler aussi bien des opérateurs de réseau, qui sont « sur le front » tous les jours, que des chercheurs, des fournisseurs de solutions de défense, et des CSIRT, pour voir ensemble ce qu'on pouvait améliorer. Pour participer, il fallait avoir soumis un article, et seules les personnes dont un article était accepté pouvait venir, garantissant un bon niveau de qualité aux débats, et permettant de limiter le nombre de participants, afin d'éviter que l'atelier ne soit juste une juxtaposition de discours.
La section 2 du RFC présente les quatorze papiers qui ont été acceptés. On les trouve en ligne.
Le but de l'atelier était d'identifier les points sur lesquels des progrès pourraient être faits. Par exemple, tout le monde est d'accord pour dire qu'on manque de professionnel·le·s compétent·e·s en cybersécurité mais il ne faut pas espérer de miracles : selon une étude, il manque trois millions de personnes dans ce domaine et il n'y a simplement aucune chance qu'on puisse les trouver à court terme. Plus réaliste, l'atelier s'est focalisé sur le déploiement du chiffrement (TLS 1.3, normalisé dans le RFC 8446, le futur - à l'époque - QUIC, et pourquoi pas le TCPcrypt du RFC 8548), déploiement qui peut parfois gêner la détection de problèmes, et sur les mécanismes de détection et de prévention. Une importance particulière était donnée au passage à l'échelle (on ne peut plus traiter chaque attaque individuellement et manuellement, il y en a trop).
Bon, maintenant, les conclusions de l'atelier (section 4). Première session, sur l'adoption des normes. C'est une banalité à l'IETF que de constater que ce n'est pas parce qu'on a normalisé une technique de sécurité qu'elle va être déployée. Beaucoup de gens aiment râler contre l'insécurité de l'Internet mais, dès qu'il s'agit de dépenser de l'argent ou du temps pour déployer les solutions de sécurité, il y a moins d'enthousiasme. (J'écris cet article au moment de la publication de la faille de sécurité SadDNS. Cela fait plus de dix ans qu'on a une solution opérationnelle contre la famille d'attaques dont SadDNS fait partie, DNSSEC et, pourtant, DNSSEC n'est toujours pas déployé sur la plupart des domaines.) Commençons par un point optimiste : certaines des technologies de sécurité de l'IETF ont été largement déployées, comme SSL (RFC 6101), remplacé il y a quinze ans par TLS (RFC 8446). L'impulsion initiale venait clairement du secteur du commerce électronique, qui voulait protéger les numéros des cartes de crédit. Lié à TLS, X.509 (RFC 5280) est aussi un succès. Cette fois, l'impulsion initiale est plutôt venue des États. (X.509 doit être une des très rares normes UIT survivantes sur l'Internet.)
Moins directement lié à la sécurité, SNMP (RFC 3410) est aussi un succès, même s'il est en cours de remplacement par les techniques autour de YANG comme RESTCONF. Toujours pour la gestion de réseaux, IPfix (RFC 7011) est également un succès, largement mis en œuvre sur beaucoup d'équipements réseau.
Par contre, il y a des semi-échecs et des échecs. Le format de
description d'incidents de sécurité IODEF
(RFC 7970) ne semble pas très répandu. (Il a
un concurrent en dehors de l'IETF, STIX - Structured Threat Information eXpression, qui
ne semble pas mieux réussir.) IODEF est utilisé par des CSIRT
mais souffre de son niveau de détail (beaucoup d'opérationnels
veulent des synthèses, pas des données brutes) et, comme toutes les
techniques d'échange d'information sur les questions de sécurité,
souffre également des problèmes de confiance qui grippent la
circulation de l'information. Autre technique de sécurité
excellente mais peu adoptée, DANE (RFC 7671). Malgré de nombreux efforts de promotion (comme
, le blog que
vous lisez a une note de 93 %, car la configuration TLS est
tolérante) et même avec une reconnaissance légale partielle en
Allemagne, DANE reste très minoritaire.https://internet.nl
Un autre cas fameux de non-succès, même s'il n'est pas directement lié à la sécurité, est IPv6 (RFC 8200).
Deuxième session, les protocoles nouveaux. L'atelier s'est penché sur le format MUD (Manufacturer Usage Description, RFC 8520) qui pourrait aider à boucher une petite partie des trous de sécurité de l'Internet des objets. Il a également travaillé l'échange de données et les problèmes de confiance qu'il pose. Comme à CARIS 1, plusieurs participants ont noté que cet échange de données reste gouverné par des relations personnelles. La confiance ne passe pas facilement à l'échelle. L'échange porte souvent sur des IOC et un standard possible a émergé, MISP.
Une fois le problème détecté, il reste à coordonner la réaction, puisque l'attaque peut toucher plusieurs parties. C'est encore un domaine qui ne passe guère à l'échelle. L'Internet n'a pas de mécanisme (technique mais surtout humain) pour coordonner des centaines de victimes différentes. Des tas d'obstacles à la coordination ont été mentionnés, des outils trop difficiles à utiliser en passant par les obstacles frontaliers à l'échange, les obligations légales qui peuvent interdire l'échange de données, et bien sûr le problème récurrent de la confiance. Vous vous en doutez, pas plus qu'au premier atelier, il n'y aura eu de solution parfaite découverte pendant les sessions.
La session sur la surveillance a vu plusieurs discussions intéressantes. Ce fut le cas par exemple du problème de la réputation des adresses IP. Ces adresses sont souvent des IOC et on se les échange souvent, ce qui soulève des questions liées à la vie privée. (Un des papiers de l'atelier est « Measured Approaches to IPv6 Address Anonymization and Identity Association », de David Plonka et Arthur Berger , qui explique la difficulté de l'« anonymisation » des adresses IP si on veut qu'elles restent utiles pour les opérationnels.) L'exploitation correcte de ces adresses IP nécessite de connaitre les plans d'adressage utilisés (si une adresse IPv6 se comporte mal, faut-il bloquer tout le préfixe /64 ? Tout le /48 ?). Il n'y a pas de ressources publiquement disponibles à ce sujet, qui permettrait de connaitre, pour une adresse IP donnée, l'étendue du préfixe englobant. (Je ne parle évidemment pas du routage, pour lequel ces bases existents, mais de la responsabilité.) Une des suggestions était d'étendre les bases des RIR. Une autre était de créer une nouvelle base. Le problème est toujours le même : comment obtenir que ces bases soient peuplées, et correctement peuplées ?
Une des questions amusantes lorsqu'on essaie de déboguer un problème de communication entre deux applications est de savoir quoi faire si la communication est chiffrée. Il n'est évidemment pas question de réclamer une porte dérobée pour court-circuiter le chiffrement, cela créerait une énorme faille de sécurité. Mais alors comment faire pour savoir ce qui se dit ? On a besoin de la coopération de l'application. Mais toutes les applications ne permettent pas facilement de journaliser les informations importantes et, quand elles le font, ce n'est pas dans un format cohérent. D'où une suggestion lors de l'atelier de voir s'il ne serait pas envisageable de mettre cette fonction dans les compilateurs, pour avoir un mécanisme de journalisation partout disponibles.
Pendant qu'on parle de chiffrement, une autre question est celle de l'identification d'une machine ou d'un protocole par le fingerprinting, c'est-à-dire en observant des informations non chiffrées (taille des paquets, temps de réponse, variations permises par le protocole, etc). Le fingerprinting pose évidemment des gros risques pour la vie privée et beaucoup de travaux sur des protocoles récents (comme QUIC) visaient à limiter son efficacité.
Pour résumer l'atelier (section 6 du RFC), plusieurs projets ont été lancés pour travailler sur des points soulevés dans l'atelier. À suivre, donc.
Date de publication du RFC : Novembre 2020
Auteur(s) du RFC : K. Larose (Agilicus), D. Dolson, H. Liu (Google)
Pour information
Réalisé dans le cadre du groupe de travail IETF capport
Première rédaction de cet article le 1 décembre 2020
Les portails captifs sont ces insupportables pages Web vers lesquels certains réseaux d'accès en WiFi vous détournent pour vous faire laisser des données personnelles avant d'avoir accès à l'Internet. Très répandus dans les hôtels et aéroports, ils utilisent pour ce détournement des techniques qui sont souvent celles des attaquants, et ils posent donc de graves problèmes de sécurité, sans parler de l'utilisabilité. Le groupe de travail CAPPORT de l'IETF travaille à définir des solutions techniques qui rendent ces portails captifs un peu moins pénibles. Ce RFC décrit l'architecture générale du système, d'autres RFC décrivant les solutions partielles.
Vu de l'utilisateurice, la connexion à
l'Internet sur pas mal de
hotspots se fait ainsi :
on accroche le réseau WiFi, on se rend à une page Web
quelconque et, au lieu de la page demandée, on est détourné vers une
page des gérants du hotspot, qui vous montre de
la publicité, exige des données personnelles (et parfois une
authentification) et l'acceptation de
conditions d'utilisation léonines, avant de continuer. Si la page
que vous vouliez visiter était en HTTPS, ce qui est le cas le
plus courant aujourd'hui, vous aurez probablement en outre un
avertissement de sécurité. Certains systèmes
d'exploitation, comme Android, ou
certains navigateurs,
comme Firefox,
détectent automatiquement la captivité, suspendent leurs autres
activités, et vous proposent d'aller directement à une page du
portail. Mais ça reste pénible, par exemple parce que c'est
difficile à automatiser. (Un exemple d'automatisation en
shell avec curl que j'ai récupéré
en ligne est
. Un autre en Go et utilisant Chrome
est captive-browser.)hotel-wifi.sh
Techniquement, la mise en œuvre du concept de portail captif se fait en général actuellement en détournant le trafic HTTP (via un transparent proxy) ou avec un résolveur DNS menteur, qui donne l'adresse IP du portail en réponse à toute requête. L'annexe A du RFC résume les mises en œuvre actuelles de portails captifs, et les méthodes utilisées pour pouvoir se connecter le moins mal possible. La machine qui veut savoir si elle est derrière un portail captif va typiquement faire une requête HTTP vers un URL connu et contrôlé par le fournisseur de l'application ou du système d'explication. Cet URL est appelé le canari. (Au lieu ou en plus de HTTP, on peut faire un test DNS sur un nom de domaine où on connait le résultat, au cas où le portail captif fonctionne avec un résolveur menteur. Attention, DNS et HTTP peuvent donner des résultats différents, détournement DNS mais pas HTTP, ou bien le contraire.) Notez que toutes les techniques avec un canari peuvent mener à des faux négatifs si le canari est connu et que le gardien derrière le portail captif le laisse passer, via une règle spécifique.
Notre RFC choisit d'être positif et donne une liste des exigences auxquelles essaie de répondre les solutions développées dans le groupe de travail. Ces exigences peuvent aussi être lues comme une liste de tous les inconvénients des portails captifs actuels. Un échantillon de ces exigences :
detectportal.firefox.com
de Firefox.L'architecture décrite ici repose sur :
Plus concrètement, le RFC liste les composants du problème et donc des solutions. D'abord, la machine qui se connecte (user equipment, dans le RFC). Pour l'instant, la description se limite aux machines qui ont un navigateur Web, même si elles sont loin d'être les seules, ou même les plus nombreuses. Pour que tout marche comme il faut, cette machine doit pouvoir récupérer l'URI de l'API du portail captif (par exemple, elle doit avoir un client DHCP), doit pouvoir gérer le fait que certaines de ses interfaces réseaux sont derrière un portail captif et pas les autres (RFC 7556), doit pouvoir notifier l'utilisateur qu'il y a un tel portail (et attention au hameçonnage !), et ce serait bien qu'il y a ait un moyen de dire aux applications « on est encore en captivité, n'essayez pas de vous connecter tout de suite ». Un exemple de cette dernière demande est fournie par Android où l'utilisation du réseau par défaut ne passe de la 4G au WiFi qu'une fois qu'Android a pu tester que l'accès WiFi n'était pas en captivité. Enfin, si la machine gère l'API des portails captifs, elle doit évidemment suivre toutes les règles de sécurité, notamment la validation du certificat.
Ensuite, deuxième composant, le système de distribution d'informations dans le réseau (provisioning service), DHCP ou RA. C'est lui qui va devoir envoyer l'URI de l'API à la machine qui se connecte, comme normalisé dans le RFC 8910.
Troisième composant, le serveur derrière l'API. Il permet de se
passer des « canaris », des tests du genre « est-ce que j'arrive à
me connecter à
https://detectportal.example
? », il y a juste
à interroger l'API. Cette API (RFC 8908) doit
permettre au minimum de connaitre l'état de la connexion (en
captivité ou pas), et d'apprendre l'URI que l'humain devra visiter
pour sortir de captivité (« j'ai lu les 200 pages de textes
juridiques des conditions d'utilisation, les 100 pages du code de
conduite et je les accepte inconditionnellement »). Elle doit
utiliser HTTPS.
Enfin, il y a le composant « gardien » (enforcement) qui s'assure qu'une machine en captivité ne puisse effectivement pas sortir ou en tout cas, ne puisse pas aller en dehors des services autorisés (qui doivent évidemment inclure le portail où on accepte les conditions). Il est typiquement placé dans le premier routeur. L'architecture présentée dans ce RFC ne change pas ce composant (contrairement aux trois premiers, qui devront être créés ou modifiés).
Parmi ces différents composants qui interagissent, l'un d'eux mérite une section entière, la section 3, dédiée à la machine qui se connecte (user equipment) et notamment à la question de son identité. Car il faut pouvoir identifier cette machine, et que les autres composants soient d'accord sur cette identification. Par exemple, une fois que le portail captif aura accepté la connexion, il faudra que le système « gardien » laisse désormais passer les paquets. Pour cette identification, plusieurs identificateurs sont possibles, soit présents explicitement dans les paquets, soit déduits d'autres informations. On peut imaginer, par exemple, utiliser l'adresse MAC ou l'adresse IP. Ou bien, pour les identificateurs qui ne sont pas dans le paquet, l'interface physique de connexion. Le RFC liste leurs propriétés souhaitables :
Notons que l'adresse IP a l'avantage d'être un identificateur qui n'est pas local au premier segment de réseau, ce qui peut permettre, par exemple, d'avoir un gardien qui soit situé un peu plus loin.
Armé de tous ces éléments, il est possible de présenter le traitement complet (section 4). Donc, dans l'ordre des évènements, dans le monde futur où ces différents RFC auront été déployés :
Si tout le monde joue le jeu, et que les techniques conformes à l'architecture décrite dans ce RFC sont déployées, les portails captifs, quoique toujours pénibles, devraient devenir plus gérables. Hélas, il est probable que beaucoup de déploiements ne soient jamais changé, notamment dans les environnements où le client est… captif (hôtels, aéroports) et où il n'y a donc aucune motivation commerciale pour améliorer les choses. Les logiciels vont donc devoir continuer à utiliser des heuristiques de type canari pendant une très longue période, car ils ne pourront pas compter sur des portails captifs propres.
Tout cela laisse ouvert quelques problèmes de sécurité, que la section 6 étudie. D'abord, fondamentalement, il faut faire confiance au réseau d'accès. Via DHCP et RA, il peut vous conduire n'importe où. (Le RFC 7556 discute de cette difficulté à authentifier le réseau d'accès.) Des protections comme l'authentification du serveur en TLS limitent un peu les dégâts mais ne résolvent pas tout. Ceci dit, c'est pour cela que notre RFC impose l'utilisation de HTTPS pour accéder à l'API. Mais, d'une manière générale, le réseau d'accès, et le portail captif qu'il utilise, a tous les pouvoirs. Il peut par exemple vous bloquer l'accès à tout ou partie de l'Internet, même après que vous ayez accepté les conditions d'utilisation.
Il y a aussi des questions de vie privée. L'identificateur de la machine qui se connecte peut être détourné de son usage pour servir à surveiller un utilisateur, par exemple. Les bases de données utilisées par les différents composants de cette architecture de portail captif doivent donc être considérées comme contenant des données personnelles. L'utilisateur peut changer ces identificateurs (pour l'adresse MAC, avec un logiciel comme macchanger) mais cela peut casser son accès Internet, si le gardien utilise justement cet identificateur.
Date de publication du RFC : Novembre 2020
Auteur(s) du RFC : S. Litkowski, S. Agrawal, K. Ananthamurthy (Cisco), K. Patel (Arrcus)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF bess
Première rédaction de cet article le 20 novembre 2020
Le protocole de routage BGP annonce des préfixes qu'on sait joindre, avec l'adresse IP du premier routeur à qui envoyer les paquets pour ce préfixe. (Ce routeur est appelé le next hop.) BGP a une extension, BGP multi-protocoles (RFC 4760) où les préfixes annoncés (NLRI pour Network Layer Reachability Information) ne sont plus forcément de la même famille que les adresses utilisées dans la session BGP. On peut donc annoncer des préfixes IPv6 sur une session BGP établie en IPv4 et réciproquement. Notre RFC, qui succède au RFC 5549 avec quelques petits changements, étend encore cette possibilité en permettant que le next hop ait une adresse de version différente de celle du préfixe.
Normalement, BGP multi-protocoles (RFC 4760) impose la version (IPv4 ou IPv6) du next hop via l'AFI (Address Family Identifier) et le SAFI (Subsequent Address Family Identifier) indiqués dans l'annonce (cf. la liste actuelle des AFI possible et celle des SAFI). Ainsi, un AFI de 1 (IPv4) couplé avec un SAFI valant 1 (unicast), lors de l'annonce d'un préfixe IPv4, impose que l'adresse du routeur suivant soit en IPv4. Désormais, cette règle est plus libérale, le routeur suivant peut avoir une adresse IPv6. Cela peut faciliter, par exemple, la vie des opérateurs qui, en interne, connectent des ilots IPv4 au-dessus d'un cœur de réseau IPv6 (cf. RFC 4925). Notez que cela ne règle que la question de l'annonce BGP. Il reste encore à router un préfixe IPv4 via une adresse IPv6 mais ce n'est plus l'affaire de BGP.
Il y avait déjà des exceptions à la règle comme quoi le préfixe
et l'adresse du routeur suivant étaient de la même famille. Ainsi,
le RFC 6074 permettait cela pour le couple AFI 25
(L2VPN) / SAFI 65
(VPLS). Mais le couple AFI 2 / SAFI 1 (IPv6 /
unicast) ne permet pas de telles exceptions (RFC 2545). Une astuce (RFC 4798 et RFC 4659) permet de s'en
tirer en encodant l'adresse IPv4 du next hop dans
une adresse IPv6. (Oui, ::192.0.2.66
est une
adresse IPv4 encodée dans les seize octets d'IPv6, cf. RFC 4291, section 2.5.5.2.) Quant au cas inverse
(AFI IPv4, routeur suivant en IPv6), elle fait l'objet de notre RFC.
Lorsque l'adresse du next hop n'est pas de la famille du préfixe, il faut trouver la famille, ce qui peut se faire par la taille de l'adresse du next hop (quatre octets, c'est de l'IPv4, seize octets, de l'IPv6). C'est ce que propose le RFC 4684.
L'extension permettant la liberté d'avoir des next hop dans une famille différente du préfixe est spécifiée complètement en section 4. Elle liste les couples AFI / SAFI pour lesquels on est autorisé à avoir un next hop IPv6 alors que le préfixe est en IPv4. Le routeur BGP qui reçoit ces annonces doit utiliser la longueur de l'adresse pour trouver tout seul si le next hop est IPv4 ou IPv6 (la méthode des RFC 4684 et RFC 6074).
L'utilisation de cette liberté nécessite de l'annoncer à son pair BGP, pour ne pas surprendre des routeurs BGP anciens. Cela se fait avec les capacités du RFC 5492. La capacité se nomme Extended Next Hop Encoding et a le code 5. Cette capacité est restreinte à certains couples AFI / SAFI, listés dans l'annonce de la capacité. Par exemple, le routeur qui veut annoncer une adresse IPv6 comme next hop pour de l'unicast IPv4 va indiquer dans le champ Valeur de la capacité 1 / 1 (le couple AFI /SAFI) et le next hop AFI 2.
La section 2 du RFC résume les changements depuis le RFC 5549, que notre RFC remplace. L'encodage de l'adresse du next hop change dans le cas des VPN, et, pour les VPN MPLS, extension de l'exception au multicast. Bref, rien de bien crucial.
Date de publication du RFC : Décembre 2020
Auteur(s) du RFC : C. Bormann (Universität Bremen
TZI), P. Hoffman (ICANN)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cbor
Première rédaction de cet article le 5 décembre 2020
Il existait un zillion de formats binaires d'échange de données ? Et bien ça en fera un zillion plus un. CBOR (Concise Binary Object Representation) est un format qui utilise un modèle de données très proche de celui de JSON, mais est encodé en binaire, avec comme but principal d'être simple à encoder et décoder, même par des machines ayant peu de ressources matérielles. Normalisé à l'origine dans le RFC 7049, il est désormais spécifié dans ce nouveau RFC. Si le texte de la norme a changé, le format reste le même.
Parmi les autres formats binaires courants, on connait ASN.1 (plus exactement BER ou DER, utilisés dans plusieurs protocoles IETF), EBML ou MessagePack mais ils avaient des cahiers des charges assez différents (l'annexe E du RFC contient une comparaison). CBOR se distingue d'abord par sa référence à JSON (RFC 8259), dont le modèle de données sert de point de départ à CBOR, puis par le choix de faciliter le travail des logiciels qui devront créer ou lire du CBOR. CBOR doit pouvoir tourner sur des machines très limitées (« classe 1 », en suivant la terminologie du RFC 7228). Par contre, la taille des données encodées n'est qu'une considération secondaire (section 1.1 du RFC pour une liste prioritisée des objectifs de CBOR). Quant au lien avec JSON, l'idée est d'avoir des modèles de données suffisamment proches pour qu'écrire des convertisseurs CBOR->JSON et JSON->CBOR soit assez facile, et pour que les protocoles qui utilisent actuellement JSON puissent être adaptés à CBOR sans douleur excessive. CBOR se veut sans schéma, ou, plus exactement, sans schéma obligatoire. Et le but est que les fichiers CBOR restent utilisables pendant des dizaines d'années, ce qui impose d'être simple et bien documenté.
La spécification complète de CBOR est en section 3 de ce RFC. Chaque élément contenu dans le flot de données commence par un octet dont les trois bits de plus fort poids indiquent le type majeur. Les cinq bits suivants donnent des détails. Ce mécanisme permet de programmeur un décodeur CBOR avec une table de seulement 256 entrées (l'annexe B fournit cette table et l'annexe C un décodeur en pseudo-code très proche de C). Pour un entier, si la valeur que codent ces cinq bits suivants est inférieure à 24, elle est utilisée telle quelle. Sinon, cela veut dire que les détails sont sur plusieurs octets et qu'il faut lire les suivants (la valeur des cinq bits codant la longueur à lire). Selon le type majeur, les données qui suivent le premier octet sont une valeur (c'est le cas des entiers, par exemple) ou bien un doublet {longueur, valeur} (les chaînes de caractères, par exemple). L'annexe A de notre RFC contient de nombreux exemples de valeurs CBOR avec leur encodage.
Quels sont les types majeurs possibles ? Si les trois premiers
bits sont à zéro, le type majeur, 0, est un entier non signé. Si les
cinq bits suivants sont inférieurs à 24, c'est la valeur de cet
entier. S'ils sont égaux à 24, c'est que l'entier se trouve dans
l'octet suivant l'octet initial, s'ils sont égaux à 25, que l'entier
se trouve dans les deux octets suivants, et ainsi de suite (31 est
réservé pour les tailles indéterminées, décrites plus
loin). L'entier 10 se représentera donc 00001010, l'entier 42 sera
00011000 00101010, etc. Presque pareil pour un type majeur de 1,
sauf que l'entier sera alors signé, et négatif. La valeur sera -1
moins la valeur encodée. Ainsi, -3 sera 00100010. Vous voulez
vérifier ? L'excellent terrain de jeu http://cbor.me
vous le permet, essayez par exemple http://cbor.me?diag=42
.
Le type majeur 2 sera une chaîne d'octets (principal ajout par
rapport au modèle de données de JSON). La longueur est codée
d'abord, en suivant la même règle que pour les entiers. Puis
viennent les données. Le type 3 indique une chaîne de caractères et
non plus d'octets. Ce sont forcément des caractères
Unicode, encodés en
UTF-8 (RFC 3629). Le
champ longueur (codé comme un entier) indique le nombre d'octets de
l'encodage UTF-8, pas le nombre de caractères (pour connaître ce
dernier, il faut un décodeur UTF-8). Vous voulez des exemples ?
Connectez-vous à http://www.cbor.me/?diag=%22lait%22
et vous voyez que la chaîne « lait » est représentée par
646c616974 : 64 = 01100100, type majeur 3 puis une longueur de
4. Les codes ASCII suivent (rappelez-vous qu'ASCII est
un sous-ensemble d'UTF-8). Avec des caractères non-ASCII comme http://www.cbor.me/?diag=%22caf%C3%A9%22
, on aurait
65636166c3a9 (même type majeur, longueur 5
octets, puis les caractères, avec c3a9 qui code
le é en UTF-8).
Le type majeur 4 indique un tableau. Rappelez-vous que CBOR utilise un modèle de données qui est très proche de celui de JSON. Les structures de données possibles sont donc les tableaux et les objets (que CBOR appelle les maps). Un tableau est encodé comme une chaîne d'octets, longueur (suivant les règles des entiers) puis les éléments du tableau, à la queue leu leu. La longueur est cette fois le nombre d'éléments, pas le nombre d'octets. Les éléments d'un tableau ne sont pas forcément tous du même type. Les tableaux (et d'autres types, cf. section 3.2) peuvent aussi être représentés sans indiquer explicitement la longueur ; le tableau est alors terminé par un élément spécial, le break code. Par défaut, un encodeur CBOR est libre de choisir la forme à longueur définie ou celle à longueur indéfinie, et le décodeur doit donc s'attendre à rencontrer les deux.
Le type majeur 5 indique une map (ce qu'on appelle objet en JSON et dictionnaire ou hash dans d'autres langages). Chaque élément d'une map est un doublet {clé, valeur}. L'encodage est le même que pour les tableaux, la longueur étant le nombre de doublets. Chaque doublet est encodé en mettant la clé, puis la valeur. Donc, le premier scalaire est la clé de la première entrée de la map, le deuxième la valeur de la première entrée, le troisième la clé de la deuxième entrée, etc.
Les clés doivent être uniques (une question problématique en JSON où les descriptions existantes de ce format ne sont ni claires ni cohérentes sur ce point).
Je passe sur le type majeur 6, voyez plus loin le paragraphe sur les étiquettes. Le type majeur 7 sert à coder les flottants (encodés ensuite en IEEE 754) et aussi d'autres types scalaires et le break code utilisé dans le paragraphe suivant. Les autres types scalaires, nommés « valeurs simples » (simple values) sont des valeurs spéciales comme 20 pour le booléen Faux, 21 pour le Vrai, et 22 pour le néant. Elles sont stockées dans un registre IANA.
Dans la description ci-dessus, les types vectoriels (tableaux,
chaînes, maps) commencent par la longueur du
vecteur. Pour un encodeur CBOR, cela veut dire qu'il faut connaître
cette longueur avant même d'écrire le premier élément. Cela peut
être contraignant, par exemple si on encode au fil de l'eau
(streaming) des données en cours de
production. CBOR permet donc d'avoir des longueurs
indéterminées. Pour cela, on met 31 comme « longueur » et cette
valeur spéciale indique que la longueur n'est pas encore connue. Le
flot des éléments devra donc avoir une fin explicite cette fois, le
break code. Celui-ci est représenté par un
élément de type majeur 7 et de détails 31, donc tous les bits de
l'octet à 1. Par exemple, http://cbor.me/?diag=%28_%20%22lait%22%29
nous montre que la
chaîne « lait » ainsi codée (le _ indique
qu'on veut un codage en longueur indéterminée) sera
7f646c616974ff. 7f est le type majeur 3, chaîne de caractères, avec
la longueur 31, indiquant qu'elle est indéterminée. Puis suit la
chaîne elle-même (les chaînes indéterminées en CBOR sont faites par
concaténation de châines de longueur déterminée), puis le
break code ff.
La même technique peut être utilisée pour les chaînes d'octets et de caractères, afin de ne pas avoir à spécifier leur longueur au début. Cette possibilité de listes de longueur indéterminée a été ajoutée pour faciliter la vie du streaming.
Revenons au type majeur 6. Il indique une étiquette (tag), qui sert à préciser la sémantique de l'élément qui suit (cf. section 3.4). Un exemple typique est pour indiquer qu'une chaîne de caractères est un fait une donnée structurée, par exemple une date ou un numéro de téléphone. Un décodeur n'a pas besoin de comprendre les étiquettes, il peut parfaitement les ignorer. Les valeurs possibles pour les étiquettes sont stockées dans un registre IANA, et on voit que beaucoup ont déjà été enregistrées, en plus de celles de ce RFC (par exemple par le RFC 8746 ou par le RFC 9132).
Quelques valeurs d'étiquette intéressantes ? La valeur 0 indique une date au format du RFC 3339 (une chaîne de caractères). La valeur 1 étiquette au contraire un entier, et indique une date comme un nombre de secondes depuis le 1er janvier 1970. Les valeurs négatives sont autorisées mais leur sémantique n'est pas normalisée (UTC n'est pas défini pour les dates avant l'epoch, surtout quand le calendrier a changé).
Les valeurs 2 et 3 étiquettent une chaîne d'octets et indiquent qu'on recommande de l'interpréter comme un grand entier (dont la valeur n'aurait pas tenu dans les types majeurs 0 ou 1). Les décodeurs qui ne gèrent pas les étiquettes se contenteront de passer à l'application cette chaîne d'octets, les autres passeront un grand entier.
Autre cas rigolos, les nombres décimaux non entiers. Certains ne peuvent pas être représentés de manière exacte sous forme d'un flottant. On peut alors les représenter par un couple [exposant, mantisse]. Par exemple, 273,15 est le couple [-2, 27315] (l'exposant est en base 10). On peut donc l'encoder en CBOR sous forme d'un tableau de deux éléments, et ajouter l'étiquette de valeur 4 pour préciser qu'on voulait un nombre unique.
D'autres étiquettes précisent le contenu d'une chaîne de caractères : l'étiquette 32 indique que la chaîne est un URI, la 34 que la chaîne est du Base64 (RFC 4648) et la 36 que cela va être un message MIME (RFC 2045). Comme l'interprétation des étiquettes est optionnelle, un décodeur CBOR qui n'a pas envie de s'embêter peut juste renvoyer à l'application cette chaîne.
Une astuce amusante pour finir les étiquettes, et la spécification du format : l'étiquette 55799 signifie juste que ce qui suit est du CBOR, sans modifier sa sémantique. Encodée, elle sera représentée par 0xd9d9f7 (type majeur 6 sur trois bits, puis détails 25 qui indiquent que le nombre est sur deux octets puis le nombre lui-même, d9f7 en hexa). Ce nombre 0xd9d9f7 peut donc servir de nombre magique. Si on le trouve au début d'un fichier, c'est probablement du CBOR (il ne peut jamais apparaître au début d'un fichier JSON, donc ce nombre est particulièrement utile quand on veut distinguer tout de suite si on a affaire à du CBOR ou à du JSON).
Maintenant que le format est défini rigoureusement, passons à son utilisation. CBOR est conçu pour des environnements où il ne sera souvent pas possible de négocier les détails du format entre les deux parties. Un décodeur CBOR générique peut décoder sans connaître le schéma utilisé en face. Mais, en pratique, lorsqu'un protocole utilise CBOR pour la communication, il est autorisé (section 5 du RFC) à mettre des restrictions, ou des informations supplémentaires, afin de faciliter la mise en œuvre de CBOR dans des environnements très contraints en ressources. Ainsi, on a parfaitement le droit de faire un décodeur CBOR qui ne gérera pas les nombres flottants, si un protocole donné n'en a pas besoin.
Un cas délicat est celui des maps (section 5.6). CBOR ne place guère de restrictions sur le type des clés et un protocole ou format qui utilise CBOR voudra souvent être plus restrictif. Par exemple, si on veut absolument être compatible avec JSON, restreindre les clés à des chaînes en UTF-8 est souhaitable. Si on tient à utiliser d'autres types pour les clés (voire des types différents pour les clés d'une même map !), il faut se demander comment on les traduira lorsqu'on enverra ces maps à une application. Par exemple, en JavaScript, la clé formée de l'entier 1 est indistinguable de celle formée de la chaîne de caractères "1". Une application en JavaScript ne pourra donc pas se servir d'une map qui aurait de telles clés, de types variés.
On a vu que certains éléments CBOR pouvaient être encodés de différentes manières, par exemple un tableau peut être représenté par {longueur, valeurs} ou bien par {valeurs, break code}. Cela facilite la tâche des encodeurs mais peut compliquer celle des décodeurs, surtout sur les machines contraintes en ressources, et cela peut rendre certaines opérations, comme la comparaison de deux fichiers, délicates. Existe-t-il une forme canonique de CBOR ? Pas à proprement parler mais la section 4.1 décrit la notion de sérialisation favorite, des décodeurs étant autorisés à ne connaitre qu'une sérialisation possible. Notamment, cela implique pour l'encodeur de :
Tous les encodeurs CBOR qui suivent ces règles produiront, pour un même jeu de données, le même encodage.
Plus stricte est la notion de sérialisation déterministe de la section 4.2. Là encore, chacun est libre de la définir comme il veut (il n'y a pas de forme canonique officielle de CBOR, rappelez-vous) mais elle ajoute des règles minimales à la sérialisation favorite :
Autre question pratique importante, le comportement en cas d'erreurs. Que doit faire un décodeur CBOR si deux clés sont identiques dans une map, ce qui est normalement interdit en CBOR ? Ou si un champ longueur indique qu'on va avoir un tableau de 5 éléments mais qu'on n'en rencontre que 4 avant la fin du fichier ? Ou si une chaîne de caractères, derrière son type majeur 3, n'est pas de l'UTF-8 correct ? D'abord, un point de terminologie important : un fichier CBOR est bien formé si sa syntaxe est bien celle de CBOR, il est valide s'il est bien formé et que les différents éléments sont conformes à leur sémantique (par exemple, la date après une étiquette de valeur 0 doit être au format du RFC 3339). Si le document n'est pas bien formé, ce n'est même pas du CBOR et doit être rejeté par un décodeur. S'il n'est pas valide, il peut quand même être utile, par exemple si l'application fait ses propres contrôles. Les sections 5.2 et 5.3 décrivent la question. CBOR n'est pas pédant : un décodeur a le droit d'ignorer certaines erreurs, de remplacer les valeurs par ce qui lui semble approprié. CBOR penche nettement du côté « être indulgent avec les données reçues » ; il faut dire qu'une application qui utilise CBOR peut toujours le renforcer en ajoutant l'obligation de rejeter ces données erronées. Un décodeur strict peut donc s'arrêter à la première erreur. Ainsi, un pare-feu qui analyse du CBOR à la recherche de contenu malveillant a tout intérêt à rejeter les données CBOR incorrectes (puisqu'il ne sait pas trop comment elles seront interprétées par la vraie application). Bref, la norme CBOR ne spécifie pas de traitement d'erreur unique. Je vous recommande la lecture de l'annexe C, qui donne en pseudo-code un décodeur CBOR minimum qui ne vérifie pas la validité, uniquement le fait que le fichier est bien formé, et l'annexe F, qui revient sur cette notion de « bien formé » et donne des exemples.
Comme CBOR a un modèle de données proche de celui de JSON, on aura souvent envie d'utiliser CBOR comme encodage efficace de JSON. Comment convertir du CBOR en JSON et vice-versa sans trop de surprises ? La section 6 du RFC se penche sur ce problème. Depuis CBOR vers JSON, il est recommandé de produire du I-JSON (RFC 7493). Les traductions suivantes sont suggérées :
En sens inverse, de JSON vers CBOR, c'est plus simple, puisque JSON n'a pas de constructions qui seraient absentes de CBOR.
Pour les amateurs de futurisme, la section 7 discute des éventuelles évolutions de CBOR. Pour les faciliter, CBOR a réservé de la place dans certains espaces. Ainsi, le type majeur 7 permettra d'encoder encore quelques valeurs simples (cela nécessitera un RFC sur le chemin des normes, cf. RFC 8126 et la section 9.1 de notre RFC). Et on peut ajouter d'autres valeurs d'étiquettes (selon des règles qui dépendent de la valeur numérique : les valeurs les plus faibles nécessiteront une procédure plus complexe, cf. section 9.2).
CBOR est un format binaire. Cela veut dire, entre autres, qu'il n'est pas évident de montrer des valeurs CBOR dans, mettons, une documentation, contrairement à JSON. La section 8 décrit donc un format texte (volontairement non spécifié en détail) qui permettra de mettre des valeurs CBOR dans du texte. Nulle grammaire formelle pour ce format de diagnostic : il est prévu pour l'utilisation par un humain, pas par un analyseur syntaxique. Ce format ressemble à JSON avec quelques extensions pour les nouveautés de CBOR. Par exemple, les étiquettes sont représentées par un nombre suivi d'une valeur entre parenthèses. Ainsi, la date (une chaîne de caractères étiquetée par la valeur 0) sera notée :
0("2013-10-12T11:34:00Z")
Une map de deux éléments sera notée comme en JSON :
{"Fun": true, "Amt": -2}
Même chose pour les tableaux. Ici, avec étiquette sur deux chaînes de caractères :
[32("http://cbor.io/"), 34("SW5zw6lyZXogaWNpIHVuIMWTdWYgZGUgUMOicXVlcw==")]
L'annexe G du RFC 8610 ajoute quelques extensions utiles à ce format de diagnostic.
Lors de l'envoi de données encodées en CBOR, le type MIME à
utiliser sera application/cbor
. Comme l'idée est d'avoir des formats
définis en utilisant la syntaxe CBOR et des règles sémantiques
spécifiques, on verra aussi sans doute des types MIME utilisant la
notation plus du RFC 6839, par exemple
application/monformat+cbor
.
Voici par exemple un petit service Web qui envoie la date
courante en CBOR (avec deux étiquettes différentes, celle pour les
dates au format lisible et celle pour les dates en nombre de
secondes, et en prime celles du RFC 8943). Il a été réalisé avec la bibliothèque
flunn. Il utilise le type MIME
application/cbor
:
% curl -s https://www.bortzmeyer.org/apps/date-in-cbor | read-cbor - ... Tag 0 String of length 20: 2020-11-16T15:02:24Z Tag 1 Unsigned integer 1605538944 ...
(Le programme read-cbor
est présenté plus loin.)
Un petit mot sur la sécurité (section 10) : il est bien connu qu'un analyseur mal écrit est un gros risque de sécurité et d'innombrables attaques ont déjà été réalisées en envoyant à la victime un fichier délibérement incorrect, conçu pour déclencher une faille de l'analyseur. Ainsi, en CBOR, un décodeur qui lirait une longueur, puis chercherait le nombre d'éléments indiqué, sans vérifier qu'il est arrivé au bout du fichier, pourrait déclencher un débordement de tampon. Les auteurs de décodeurs CBOR sont donc priés de programmer de manière défensive, voire paranoïaque : ne faites pas confiance au contenu venu de l'extérieur.
Autre problème de sécurité, le risque d'une attaque par
déni de
service. Un attaquant taquin peut envoyer un fichier
CBOR où la longueur d'un tableau est un très grand nombre, dans
l'espoir qu'un analyseur naïf va juste faire
malloc(length)
sans se demander si cela ne
consommera pas toute la mémoire.
Enfin, comme indiqué plus haut à propos du traitement d'erreur, comme CBOR ne spécifie pas de règles standard pour la gestion des données erronées, un attaquant peut exploiter cette propriété pour faire passer des données « dangereuses » en les encodant de telle façon que l'IDS n'y voit que du feu. Prenons par exemple cette map :
{"CodeToExecute": "OK", "CodeToExecute": "DANGER"}
Imaginons qu'une application lise ensuite la donnée indexée par
CodeToExecute
. Si, en cas de clés dupliquées,
elle lit la dernière valeur, elle exécutera le code dangereux. Si un
IDS lit la première valeur, il ne se sera pas inquiété. Voilà une
bonne raison de rejeter du CBOR invalide (les clés dupliquées sont
interdites) : il peut être interprété de plusieurs façons. Notez
quand même que ce problème des clés dupliquées, déjà présent en
JSON, a suscité des discussions passionnées à l'IETF, entre ceux
qui réclamaient une interdiction stricte et absolue et ceux qui
voulaient laisser davantage de latitude aux décodeurs. (La section
5.6 est une bonne lecture ici.)
Pour les amateurs d'alternatives, l'annexe E du RFC compare CBOR à des formats analogues. Attention, la comparaison se fait à la lumière du cahier des charges de CBOR, qui n'était pas forcément le cahier des charges de ces formats. Ainsi, ASN.1 (ou plutôt ses sérialisations comme BER ou DER, PER étant nettement moins courant puisqu'il nécessite de connaître le schéma des données) est utilisé par plusieurs protocoles IETF (comme LDAP) mais le décoder est une entreprise compliquée.
MessagePack est beaucoup plus proche de CBOR, dans ses objectifs et ses résultats, et a même été le point de départ du projet CBOR. Mais il souffre de l'absence d'extensibilité propre. Plusieurs propositions d'extensions sont restées bloquées à cause de cela.
BSON (connu surtout via son utilisation dans MongoDB) a le même problème. En outre, il est conçu pour le stockage d'objets JSON dans une base de données, pas pour la transmission sur le réseau (ce qui explique certains de ses choix). Enfin, MSDTP, spécifié dans le RFC 713, n'a jamais été réellement utilisé.
Rappelez-vous que CBOR prioritise la simplicité de l'encodeur et du décodeur plutôt que la taille des données encodées. Néanmoins, un tableau en annexe E.5 compare les tailles d'un même objet encodé avec tous ces protocoles : BSON est de loin le plus bavard (BER est le second), MessagePack et CBOR les plus compacts.
Une liste des implémentations est publiée en https://cbor.io/
. Au moins quatre existent, en Python,
Ruby, JavaScript et
Java. J'avais moi-même
écrit un décodeur CBOR très limité (pour un besoin ponctuel) en Go. Il est disponible ici et son seul rôle est
d'afficher le CBOR sous forme arborescente, pour aider à déboguer un
producteur de CBOR. Cela donne quelque chose du genre :
% ./read-cbor test.cbor Array of 3 items String of length 5: C-DNS Map of 4 items Unsigned integer 0 => Unsigned integer 0 Unsigned integer 1 => Unsigned integer 5 Unsigned integer 4 => String of length 70: Experimental dnstap client, IETF 99 hackathon, data from unbound 1.6.4 Unsigned integer 5 => String of length 5: godin Array of indefinite number of items Map of 3 items Unsigned integer 0 => Map of 1 items Unsigned integer 1 => Array of 2 items Unsigned integer 1500204267 Unsigned integer 0 Unsigned integer 2 => Map of indefinite number of items Unsigned integer 0 => Array of 2 items Byte string of length 16 Byte string of length 16 ...
L'annexe G de notre RFC résume les changements depuis le RFC 7049. Le format reste le même, les fichiers CBOR d'avant sont toujours du CBOR. Il y a eu dans ce nouveau RFC des corrections d'erreurs (comme un premier exemple erroné, un autre, et encore un), un durcissement des conditions d'enregistrement des nouvelles étiquettes pour les valeurs les plus basses, une description plus détaillée du modèle de données (la section 2 est une nouveauté de notre RFC), un approfondissement des questions de représentation des nombres, etc. Notre RFC 8949 est également plus rigoureux sur les questions de sérialisation préférée et déterministe. L'étiquette 35, qui annonçait qu'on allait rencontrer une expression rationnelle a été retirée (le RFC note qu'il existe plusieurs normes pour ces expressions et que l'étiquette n'est pas définie de manière assez rigoureuse pour trancher).
Date de publication du RFC : Décembre 2020
Auteur(s) du RFC : CJ. Bernardos (UC3M), A. Mourad (InterDigital)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dhc
Première rédaction de cet article le 2 décembre 2020
Les adresses MAC sur 48
bits, normalisées par l'IEEE, ont un bit qui indique si
l'adresse découle d'un plan mondial et est donc globalement unique,
ou bien si elle a été allouée via une méthode locale. Plus
récemment, l'IEEE a découpé l'espace local en quatre quadrants, pouvant avoir
des poltiques différentes. Depuis qu'on peut allouer ces adresses
MAC locales par DHCP (RFC 8947), il serait intéressant de choisir son
quadrant. C'est ce que permet la nouvelle option DHCP
QUAD
normalisée dans ce nouveau RFC.
Tout·e étudiant·e en réseaux informatiques a appris le découpage des adresses MAC de 48 bits via le bit U/L (Universal/Local). Les adresses avec ce bit à 1 sont gérées localement (et donc pas forcément uniques au niveau mondial). En 2017, l'IEEE a ajouté un nouveau concept, le SLAP (Structured Local Address Plan). L'espace des adresses locales est découpé en quadrants, identifiés par deux bits (troisième et quatrième position dans l'adresse) :
De son côté, l'IETF a, dans le RFC 8947, normalisé une option de DHCPv6 qui permet d'obtenir des adresses MAC par DHCP.
Or, dans certains cas, une machine pourrait vouloir choisir le quadrant dans lequel son adresse MAC se situe. Le RFC cite l'exemple d'objets connectés qui voudraient une adresse dans le quadrant ELI (Extended Local Identifier) pour avoir l'identifiant du fabricant au début de l'adresse, sans pour autant que le fabricant ne soit obligé d'allouer à la fabrication une adresse unique à chaque objet. Par contre, des systèmes qui changeraient leur adresse MAC pour éviter la traçabilité préféreraient sans doute un adresse dans le quadrant AAI (Administratively Assigned Identifier). L'annexe A du RFC est très intéressante de ce point de vue, décrivant plusieurs scénarios et les raisons du choix de tel ou tel quadrant.
Notre nouveau RFC ajoute donc une option au protocole DHCP
que normalisait le RFC 8415. Elle se nomme
QUAD
et repose sur l'option
LLADDR
du RFC 8947. Le
client met cette option QUAD
dans la requête
DHCP, pour indiquer son quadrant favori. Le serveur ne l'utilise pas
dans la réponse. Soit il est d'accord pour allouer une adresse MAC
dans ce quadrant et il le fait, soit il propose une autre
adresse. (Rappelez-vous qu'en DHCP, le client propose, et le serveur
décide.) Le client regarde l'adresse renvoyée et sait ainsi si sa
préférence pour un quadrant particulier a été satisfaite ou pas.
La section 4 décrit les détails de l'option
QUAD
. Elle permet d'exprimer une liste de
quadrants, avec des préférences associées. L'option est désormais
enregistrée
à l'IANA (code 140).
Pour l'instant, je ne connais pas de mise en œuvre de ce RFC que ce soit côté client ou serveur.
Date de publication du RFC : Décembre 2020
Auteur(s) du RFC : B. Volz (Cisco), T. Mrugalski (ISC), CJ. Bernardos (UC3M)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dhc
Première rédaction de cet article le 2 décembre 2020
L'utilisation de DHCP pour attribuer des adresses IP est bien connue. Ce nouveau RFC spécifie comment utiliser DHCP pour attribuer des adresses MAC.
Dans quels cas est-ce utile ? La principale motivation vient des environnements virtualisés où on crée des quantités industrielles de machines virtuelles. C'est le cas chez les gros hébergeurs, par exemple, ou bien dans les organisations qui ont d'immenses fermes de machines. Si les adresses MAC de ces machines sont attribuées au hasard, le risque de collision est important, d'autant plus qu'il n'y a pas de protocole de détection de ces collisions. (Sur le risque de collision, notamment en raison du paradoxe de l'anniversaire, voir le RFC 4429, annexe A.1.) Bien sûr, les adresses MAC n'ont pas besoin d'être uniques au niveau mondial, mais la taille de certains réseaux L2 fait que la collision est un réel problème. Un autre scénario est celui des objets connectés (RFC 7228), où leur nombre pourrait menacer l'espace d'adressage OUI. L'idée est donc de récupérer ces adresses MAC auprès d'un serveur DHCP, ce qui évitera les collisions.
En effet, le protocole DHCP, normalisé pour sa version IPv6 dans le RFC 8415, ne sert pas qu'à attribuer des adresses IP. On peut s'en servir pour n'importe quel type de ressources. Et il offre toutes les fonctions nécessaires pour gérer ces ressources, et de nombreuses mises en œuvre déjà bien testées. Notre RFC va donc s'appuyer dessus pour les adresses MAC de 48 bits (EUI-48) d'IEEE 802 (et peut-être dans le futur pour d'autres types d'adresses).
Dans ces adresses de 48 bits, un bit nous intéresse particulièrement, le U/L. Comme son nom l'indique (U = universal, L = local), il indique si l'adresse suit le mécanisme d'allocation de l'IEEE (et est donc a priori unique au niveau mondial) ou bien si l'adresse est gérée selon des règles locales. Les adresses allouées via DHCP seront en général des adresses à gestion locale. Étant purement locales (aucune garantie quant à leur unicité), elles ne doivent pas être utilisées pour fabriquer le DUID - DHCP Unique Identifier - de DHCP (cf. RFC 8415, section 11). Notez aussi que la norme IEEE 802c découpe l'espace local en quatre parties (détails dans l'annexe A du RFC ou, pour les courageuses et les courageux, dans la norme 802c). Pendant qu'on parle de l'IEEE, leur norme 802 1CQ prévoit un mécanisme d'affectation des adresses MAC, qui peut être un concurrent de celui de ce RFC. Le RFC 8948 fournit un moyen de choisir l'un de ces quadrants.
Un peu de terminologie est nécessaire pour suivre ce RFC (section 3). Un bloc d'adresses est une suite consécutive d'adresses MAC. IA_LL désigne une Identity Association for Link-Layer Address et c'est une option DHCP (code 138) qui va beaucoup servir. Autre option DHCP, LLADDR (code 139) qui est l'option qui va servir à transporter les adresses MAC.
La section 4 du RFC présente les principes de déploiement de la solution. Il y a notamment deux scénarios envisagés :
Bon, sinon, le fonctionnement de l'allocation d'adresses MAC
marche à peu près comme celui de l'allocation d'adresses IP. Le
client DHCP envoie un message Solicit
incluant
une option IA_LL qui contient elle-même une option LLADDR qui
indique le type d'adresse souhaitée et peut aussi inclure une
suggestion, si le client a une idée de l'adresse qu'il voudrait. Le
serveur répond avec Advertise
contenant
l'adresse ou le bloc d'adresses alloué (qui ne sont pas forcément
ceux suggérés par le client, comme toujours avec DHCP). Si
nécessaire, il y aura ensuite l'échange habituel
Request
puis Reply
. Bref,
du DHCPv6 classique. Le client devra renouveler l'allocation au
bout du temps indiqué dans le bail (le serveur peut toujours donner
l'adresse sans limite de temps, cf. RFC 8415,
section 7.7). Le client peut explicitement abandonner l'adresse,
avec un message Release
. On l'a dit, ça se
passe comme pour les adresses IP.
Les fanas du placement exact des bits liront la section 10, où
est décrit l'encodage de l'option IA_LL et de la « sous-option »
LLADDR. C'est là qu'on trouvera l'indication des blocs d'adresses
MAC, encodés par la première adresse puis la taille du bloc
(-1). Ainsi, 02:04:06:08:0a
/ 3 indique un bloc
qui va de 02:04:06:08:0a
à
02:04:06:08:0d
. Pendant qu'on parle de bits,
notez que les bits indiquant diverses caractéristiques de l'adresse
MAC figurent dans le premier octet, et que la transmission se fait
en commençant par le bit le moins significatif (IEEE 802 est
petit-boutien pour les bits). Ainsi,
l'adresse citée plus haut, 02:04:06:08:0a
a un
premier octet qui vaut 2, soit 00000010 en binaire, ce qui sera
transmis comme 01000000. Le premier bit est le M, qui indique ici
qu'il s'agit d'une adresse
unicast, le second est
U/L, indiquant ici que c'est bien une adresse locale, les deux bits
suivants sont une nouveauté de IEEE
802c et indiquent le quadrant des adresses (cf. annexe A du
RFC, puis le RFC 8948).
Quelques conseils pour les administrateurs des serveurs DHCP qui feront cette allocation d'adresses MAC figurent en section 11 du RFC. Par exemple, il ne faut allouer que des adresses locales (bit U/L à 1).
Les deux nouvelles options, IA_LL et LLADDR ont été mises dans le registre IANA.
Pour finir, l'annexe A du RFC résume la norme IEEE 802c. Dans la norme IEEE 802 originale, il y avait, comme indiqué plus haut, un bit U/L qui disait si l'adresse était gérée selon des règles locales (et n'était donc pas forcément unique au niveau mondial). 802c ajoute à ces adresses locales la notion de quadrant, découpant l'espace local en quatre. Après le bit M (unicast ou groupe) et le bit U/L (local ou pas). deux bits indiquent dans quel quadrant se trouve l'adresse :
02:04:06:08:0a
et suivantes sont dans ce
quadrant),Un mécanisme de sélection du quadrant est normalisé dans le RFC 8948.
Pour l'instant, je ne connais pas de mise en œuvre de ce RFC que ce soit côté client ou serveur.
Date de publication du RFC : Novembre 2020
Auteur(s) du RFC : F. Dupont (ISC), S. Morris (ISC), P. Vixie (Farsight), D. Eastlake 3rd (Futurewei), O. Gudmundsson (Cloudflare), B. Wellington (Akamai)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 1 décembre 2020
Le DNS a des vulnérabilités à plusieurs endroits, notamment des risques d'usurpation, qui permettent de glisser une réponse mensongère à la place de la bonne. Il existe plusieurs solutions pour ces problèmes, se différenciant notamment par leur degré de complexité et leur facilité à être déployées. TSIG (Transaction SIGnature), normalisé dans ce RFC (qui remplace le RFC 2845), est une solution de vérification de l'intégrité du canal, permettant à deux machines parlant DNS de s'assurer de l'identité de l'interlocuteur. TSIG est surtout utilisé entre serveurs DNS maîtres et esclaves, pour sécuriser les transferts de zone (aujourd'hui, presque tous les transferts entre serveurs faisant autorité sont protégés par TSIG).
TSIG repose sur l'existence d'une clé secrète, partagée entre les deux serveurs, qui sert à générer un HMAC permettant de s'assurer que le dialogue DNS a bien lieu avec la machine attendue, et que les données n'ont pas été modifiées en route. L'obligation de partager une clé secrète le rend difficilement utilisable, en pratique, pour communiquer avec un grand nombre de clients. Mais, entre deux serveurs faisant autorité pour la même zone, ce n'est pas un problème (section 1.1 du RFC). On peut gérer ces secrets partagés manuellement. Même chose entre un serveur maître et un client qui met à jour les données par dynamic update (RFC 2136).
Bien sûr, si tout le monde utilisait DNSSEC partout, le problème de sécuriser le transfert de zones entre serveurs faisant autorité serait moins grave (mais attention, DNSSEC ne signe pas les délégations et les colles, cf. la section 10.2 de notre RFC). En attendant un futur lointain « tout-DNSSEC », TSIG fournit une solution simple et légère pour éviter qu'un méchant ne se glisse dans la conversation entre maître et esclave (par exemple grâce à une attaque BGP) et ne donne à l'esclave de fausses informations. (Il existe aussi des solutions non-DNS comme de transférer la zone avec rsync au dessus de SSH. Notez d'autre part que TSIG peut servir à d'autres choses que le transfert de zones, comme l'exemple des mises à jour dynamiques cité plus haut. Par contre, pour sécuriser la communication entre le client final et le résolveur, il vaut mieux utiliser DoT - RFC 7858 - ou DoH - RFC 8484.)
Comment fonctionne TSIG ? Les sections 4 et 5 décrivent le protocole. Le principe est de calculer, avec la clé secrète, un HMAC des données transmises, et de mettre ce HMAC (cette « signature ») dans un pseudo-enregistrement TSIG qui sera joint aux données, dans la section additionnelle. À la réception, l'enregistrement TSIG (obligatoirement le dernier de la section additionnelle) sera extrait, le HMAC calculé et vérifié.
Pour éviter des attaques par rejeu, les données sur lesquelles portent le HMAC incluent l'heure. C'est une des causes les plus fréquentes de problèmes avec TSIG : les deux machines doivent avoir des horloges très proches (section 10, qui recommande une tolérance - champ Fudge - de cinq minutes).
Le format exact des enregistrements TSIG est décrit en section 4. L'enregistrement est donc le dernier, et est mis dans la section additionnelle (voir des exemples plus loin avec dig et tshark). Le type de TSIG est 250 et, évidemment, ces pseudo-enregistrements ne doivent pas être gardés dans les caches. Plusieurs algorithmes de HMAC sont possibles. Un est obligatoire et recommandé, celui fondé sur SHA-256 (que j'utilise dans les exemples par la suite). On peut toujours utiliser SHA-1 et MD5 (avant que vous ne râliez : oui, SHA-1 et MD5 ont de gros problèmes mais cela n'affecte pas forcément leur utilisation en HMAC, cf. RFC 6151). Un registre IANA contient les algorithmes actuellement possibles (cf. aussi section 6 du RFC).
Le nom dans l'enregistrement TSIG est le nom de la clé (une
raison pour bien le choisir, il inclut typiquement le nom des deux
machines qui communiquent), la classe est
ANY
, le TTL nul et les données contiennent
le nom de l'algorithme utilisé, le moment de la signature, et bien
sûr la signature elle-même.
Le fait que la clé soit secrète implique des pratiques de
sécurité sérieuses, qui font l'objet de la section 8. Par
exemple, l'outil de génération de clés de BIND crée des fichiers en
mode 0600
, ce qui veut dire lisibles uniquement
par leur créateur, ce qui est la moindre des choses. Encore faut-il
assurer la sécurité de la machine qui stocke ces fichiers. (Voir
aussi le RFC 2104).
Voyons maintenant des exemples concrets où
polly
(192.168.2.27
) est
un serveur DNS maître pour la zone example.test
et jadis
(192.168.2.29
)
un serveur esclave. Pour utiliser TSIG afin d'authentifier un
transfert de zone entre deux machines, commençons avec BIND. Il faut
d'abord générer une clé. Le programme
tsig-keygen
, livré avec BIND, génère de
nombreux types de clés (d'où ses nombreuses options). Pour TSIG, on
veut du SHA-256 (mais tsig-keygen
connait
d'autres algorithmes) :
% tsig-keygen -a HMAC-SHA256 polly-jadis key "polly-jadis" { algorithm hmac-sha256; secret "LnDomTT+IRf0daLYqxkstFpTpmFfcOvyxtRaq2VhHSI="; };
(Avant BIND 9.13, le programme à utiliser était
dnssec-keygen
, qui ne fait désormais plus que
du DNSSEC.) On a donné à la clé le nom des
deux machines entre lesquelles se fera la communication (le nom est
affiché dans le journal lors d'un transfert réussi, et à plusieurs
autres endroits, donc il vaut mieux le choisir long et descriptif),
plus un chiffre pour distinguer d'éventuelles autres clés. La clé
est partagée entre ces deux machines. On met alors la clé dans la
configuration de BIND, sur les deux machines
(tsig-keygen
produit directement de la
configuration BIND). Naturellement, il faut transférer la clé de
manière sécurisée entre les deux machines, pas par courrier
électronique ordinaire, par exemple. Sur le serveur maître, on
précise que seuls ceux qui connaissent la clé peuvent transférer la
zone :
zone "example.test" { type master; file "/etc/bind/example.test"; allow-transfer { key polly-jadis; }; };
Les autres clients seront rejetés :
Apr 6 17:45:15 polly daemon.err named[27960]: client @0x159c250 192.168.2.29#51962 (example.test): zone transfer 'example.test/AXFR/IN' denied
Mais, si on connait la clé, le transfert est possible (ici, un test avec dig) :
% dig -y hmac-sha256:polly-jadis:LnDomTT+IRf0daLYqxkstFpTpmFfcOvyxtRaq2VhHSI= @polly.sources.org AXFR example.test ; <<>> DiG 9.16.1-Debian <<>> -y hmac-sha256 @polly.sources.org AXFR example.test ; (1 server found) ;; global options: +cmd example.test. 86400 IN SOA polly.sources.org. hostmaster.sources.org. 2020040600 7200 3600 604800 43200 example.test. 86400 IN NS polly.sources.org. example.test. 86400 IN SOA polly.sources.org. hostmaster.sources.org. 2020040600 7200 3600 604800 43200 polly-jadis. 0 ANY TSIG hmac-sha256. 1586195193 300 32 pJciUWLOkg1lc6J+msC+dzYotVSOr8PSXgE6fFU3UwE= 25875 NOERROR 0 ;; Query time: 10 msec ;; SERVER: 192.168.2.27#53(192.168.2.27) ;; WHEN: Mon Apr 06 17:46:33 UTC 2020 ;; XFR size: 3 records (messages 1, bytes 267)
On voit que dig affiche fidèlement tous les enregistrements, y
compris le pseudo-enregistrement de type TSIG qui contient la
signature. Un point de sécurité, toutefois (merci à Jan-Piet Mens
pour sa vigilance) : avec ce -y
, quiconque a un
compte sur la même machine Unix peut voir la clé avec
ps. Il peut être préférable de la mettre dans un
fichier et de dire à dig d'utiliser ce fichier (dig -k
polly-jadis.key …
).
Maintenant que le test marche, configurons le serveur BIND esclave :
// To avoid copy-and-paste errors, you can also redirect tsig-keygen's // output to a file and then include it in named.conf with 'include // polly-jadis.key'. key "polly-jadis" { algorithm hmac-sha256; secret "LnDomTT+IRf0daLYqxkstFpTpmFfcOvyxtRaq2VhHSI="; }; zone "example.test" { type slave; masters {192.168.2.27;}; file "example.test"; }; server 192.168.2.27 { keys { polly-jadis; }; };
et c'est tout : l'esclave va désormais utiliser TSIG pour les transferts de zone. Le programme tshark va d'ailleurs nous afficher les enregistrements TSIG :
Queries example.test: type AXFR, class IN Name: example.test [Name Length: 12] [Label Count: 2] Type: AXFR (transfer of an entire zone) (252) Class: IN (0x0001) Additional records polly-jadis: type TSIG, class ANY Name: polly-jadis Type: TSIG (Transaction Signature) (250) Class: ANY (0x00ff) Time to live: 0 Data length: 61 Algorithm Name: hmac-sha256 Time Signed: Apr 6, 2020 19:51:36.000000000 CEST Fudge: 300 MAC Size: 32 MAC [Expert Info (Warning/Undecoded): No dissector for algorithm:hmac-sha256] [No dissector for algorithm:hmac-sha256] [Severity level: Warning] [Group: Undecoded] Original Id: 37862 Error: No error (0) Other Len: 0
Et si l'esclave est un nsd et pas un BIND ? C'est le même principe. On configure l'esclave :
key: name: polly-jadis algorithm: hmac-sha256 secret: "LnDomTT+IRf0daLYqxkstFpTpmFfcOvyxtRaq2VhHSI=" zone: name: "example.test" zonefile: "example.test" allow-notify: 192.168.2.27 NOKEY request-xfr: 192.168.2.27 polly-jadis
Et le prochain nsd-control transfer example.test
(ou bien la
réception de la notification) déclenchera un transfert, qui sera
ainsi enregistré :
Apr 07 06:46:48 jadis nsd[30577]: notify for example.test. from 192.168.2.27 serial 2020040700 Apr 07 06:46:48 jadis nsd[30577]: [2020-04-07 06:46:48.831] nsd[30577]: info: notify for example.test. from 192.168.2.27 serial 2020040700 Apr 07 06:46:48 jadis nsd[30565]: xfrd: zone example.test written received XFR packet from 192.168.2.27 with serial 2020040700 to disk Apr 07 06:46:48 jadis nsd[30565]: [2020-04-07 06:46:48.837] nsd[30565]: info: xfrd: zone example.test written received XFR packet from 192.168.2.27 with serial 2020040700 to disk
Pour générer des clés sans utiliser BIND, on peut consulter mon autre article sur TSIG.
On l'a vu, TSIG nécessite des horloges synchronisées. Une des
erreurs les plus fréquentes est d'oublier ce point. Si une machine
n'est pas à l'heure, on va trouver dans le journal des échecs TSIG
comme (section 5.2.3 du RFC), renvoyés avec le code de retour DNS
NOTAUTH
(code 9) et un code
BADTIME
dans l'enregistrement TSIG joint :
Mar 24 22:14:53 ludwig named[30611]: client 192.168.2.7#65495: \ request has invalid signature: TSIG golgoth-ludwig-1: tsig verify failure (BADTIME)
Ce nouveau RFC ne change pas le protocole du RFC 2845. Un ancien client peut interopérer avec un nouveau serveur, et réciproquement. Seule change vraiment la procédure de vérification, qui est unilatérale. Une des motivations pour la production de ce nouveau RFC venait de failles de sécurité de 2016-2017 (CVE-2017-3142, CVE-2017-3143 et CVE-2017-11104). Certes, elle concernaient des mises en œuvre, pas vraiment le protocole, mais elles ont poussé à produire un RFC plus complet et plus clair.
Le débat à l'IETF au début était « est-ce qu'on en profite pour réparer tout ce qui ne va pas dans le RFC 2845, ou bien est-ce qu'on se limite à réparer ce qui est clairement cassé ? » Il a été tranché en faveur d'une solution conservatrice : le protocole reste le même, il ne s'agit pas d'un « TSIG v2 ».
Date de publication du RFC : Novembre 2020
Auteur(s) du RFC : M. Jones (Microsoft), A. Nadalin (Independent), J. Richter (pdv Financial Software GmbH)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cbor
Première rédaction de cet article le 21 novembre 2020
Le format de données CBOR, normalisé dans le RFC 8949, possède un certain nombre de types de données de base, et un mécanisme d'extensions, les étiquettes (tags). Ce RFC spécifie deux nouvelles étiquettes, pour indiquer des dates.
Le RFC 8949 déclarait déjà deux types pour les estampilles temporelles, l'étiquette 0 pour une chaîne de caractères dont le contenu est une estampille au format du RFC 3339 (avec la date et l'heure), et l'étiquette 1 pour une estampille sous la forme d'un nombre de secondes depuis l'epoch. Dans ce nouveau RFC sont ajoutées deux étiquettes, 100 pour un entier qui va stocker le nombre de jours depuis l'epoch, et 1004 pour une date seule (sans heure) au format du RFC 3339. L'epoch est celle de la norme Posix 1 / IEEE Standard 1003.1, le 1 janvier 1970. Dans les deux cas, comme on ne stocke pas l'heure, des considérations comme le fuseau horaire ou les secondes intercalaires sont inutiles. Quant au calendrier utilisé, c'est le grégorien.
Dans ce calendrier, John Lennon (je reprends l'exemple du RFC…) est né le 9 octobre 1940 et mort le 8 décembre 1980. (Les dates utilisées dans ce RFC n'incluent pas l'heure.) Pour la première étiquette, 100, qui indique le nombre de jours depuis l'epoch, l'auteur d'I Am the Walrus est né le -10676. C'est un nombre négatif puisque l'epoch utilisée est le 1 janvier 1970, après sa naissance. Lennon est mort le 3994. Pour le cas de la deuxième étiquette, 1004, il est né le 1940-10-09 et mort le 1980-12-08, suivant le format du RFC 3339. Le jour (lundi, mardi, mercredi…) est explicitement non mentionné, si on en a besoin, il faut le recalculer.
Les deux formats, en nombre de jours depuis l'epoch, et en RFC 3339 ont l'avantage que les comparaisons de date sont triviales, une simple comparaison d'entiers dans le premier cas, de chaînes de caractères dans le suivant, suffit.
Petit piège des dates indiquées sans l'heure, un même événement peut survenir à deux dates différentes selon le fuseau horaire. Ainsi, une vidéoconférence qui a lieu, à Tokyo, le 12 octobre à 10h00 sera considérée par les habitants d'Honolulu comme se tenant le 11 octobre à 15h00.
Les deux étiquettes ont été enregistrées à l'IANA. La valeur 100 pour la première a été choisie car 100 est le code ASCII de 'd' (pour date).
Si vous voulez un fichier CBOR utilisant ces deux étiquettes,
vous pouvez appeler le service
https://www.bortzmeyer.org/apps/date-in-cbor
qui vous renvoie un tableau avec les quatre façons de servir une
date en CBOR, les deux standards du RFC 8949,
et les deux de notre RFC. Ici, on utilise le programmme read-cbor pour afficher
plus joliment :
% wget -q -O - https://www.bortzmeyer.org/apps/date-in-cbor | ./read-cbor - Array of 5 items ... Tag 0 String of length 20: 2020-11-21T06:44:33Z Tag 1 Unsigned integer 1605941073 Tag 100 Unsigned integer 18587 Tag 1004 String of length 10: 2020-11-21
Date de publication du RFC : Février 2021
Auteur(s) du RFC : I. Grigorik, Y. Weiss (Google)
Expérimental
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 9 février 2021
Aux débuts du Web,
il allait de soi que le contenu renvoyé à un client était identique
quel que soit le client. On demandait un article scientifique, le
même article était donné à tout le monde. La séparation du contenu
et de la présentation que permet HTML faisait en sorte que ce contenu
s'adaptait automatiquement aux différents clients et notamment à
leur taille. Mais petit à petit l'habitude s'est prise d'envoyer un
contenu différent selon le client. C'était parfois pour de bonnes
raisons (s'adapter à la langue de
l'utilisateur) et parfois pour des mauvaises (incompréhension de la
séparation du contenu et de la présentation, ignorance du Web par
des commerciaux qui voulaient contrôler l'apparence exacte de la
page). Cette adaptation au client peut se faire en tenant compte des
en-têtes envoyés par le
navigateur dans sa requête, notamment
l'affreux User-Agent:
, très indiscret et en
général mensonger. Mais la tendance actuelle est de ne pas utiliser
systématiquement ces en-têtes, très dangereux pour la vie
privée et parfois inutiles : si le serveur HTTP n'adapte pas le
contenu en fonction des en-têtes, pourquoi les envoyer ? Ce nouveau
RFC propose une
solution : un en-tête Accept-CH:
envoyé par le
serveur qui indique ce que le serveur va faire
des en-têtes d'indication envoyés par le client (client
hints) dans ses futures requêtes. Il s'agit d'un projet
Google, déjà mis en œuvre dans
Chrome mais, pour l'instant, seulement avec
le statut « Expérimental ».
Le Web a toujours été prévu pour être accédé par des machines très différentes, en terme de taille d'écran, de logiciels utilisés, de capacités de traitement. Sans compter les préférences propres à l'utilisateur, par exemple sa langue. C'est ainsi que, par exemple, les lignes de texte ne sont pas définies dans le source en HTML, elles seront calculées dynamiquement par le navigateur qui, lui, connait la largeur de la fenêtre. Mais il a été difficile de faire comprendre cela à des marketeux habitués de la page imprimée et qui insistaient pour contrôler tout au pixel près.
Bref, il y a longtemps que des gens qui ne connaissent pas le Web
font n'importe quoi, par exemple en regardant l'en-tête
User-Agent:
(RFC 7231,
section 5.5.3) et en ajustant le contenu Web à cet en-tête, ce qui
nécessite une base de données de tous les
User-Agent:
possibles puisque chaque nouvelle
version d'un navigateur peut changer les choses. (D'où ces
User-Agent:
ridicules et mensongers qu'on voit
aujourd'hui, où chaque navigateur met le nom de tous les autres, par
exemple Edge annonce Mozilla/5.0
(Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/68.0.2704.79 Safari/537.36 Edge/18.014
.) Ces
techniques sont évidemment très mauvaises : elles compliquent les
serveurs, elles ne permettent pas de gérer le cas d'un nouveau
navigateur (sauf à ce qu'il annonce les noms de ces prédécesseurs
dans User-Agent:
, ce qui se fait couramment),
elles ne permettent pas facilement à l'utilisateur ou à son
navigateur de savoir quels critères ont été utilisés par le serveur
pour adapter le contenu. Et elles sont très indiscrètes, exposant
par défaut bien plus d'informations que ce qui est nécessaire au
serveur pour adapter son contenu. C'est ainsi que des
services comme Panopticlick ou
Am I
unique? peuvent fonctionner, démontrant la
possibilité d'identifier un utilisateur sans
biscuits, avec juste du
fingerprinting passif.
Un site Web peut aussi ajuster son contenu en fonction d'un utilisateur particulier, suivi, par exemple, par les biscuits du RFC 6265, ou via des paramètres dans l'URL mais cela complique les choses, par exemple en obligeant à mettre des informations dans l'URL de chaque ressource du site Web.
Une meilleure solution, au moins en théorie, est la négociation de contenu
HTTP (RFC 7231, section 3.4.1) :
le client utilise alors des en-têtes bien définis (comme
Accept-Language:
pour la
langue) auxquels le serveur peut répondre par
un contenu spécifique. La négociation de contenu pose
toutefois un problème, note le RFC, c'est que les en-têtes sont
systématiquement envoyés par le client, qui ne sait pas si le
serveur en fera quelque chose. Cela peut notamment avoir des
conséquences en terme de vie
privée, un client pouvant être identifié par le jeu
complet des en-têtes qu'il envoie, même en l'absence de biscuits de
traçage. (En outre, pour le cas particulier de la langue, la
négociation de contenu n'aide guère car elle ne prend pas en compte
des questions comme la
notion de version originale.)
Au contraire, estime le RFC (avec pas mal d'optimisme), le mécanisme décrit ici peut potentiellement être davantage protecteur, en obligeant le serveur à annoncer quelles informations il va utiliser et en n'envoyant les informations permettant l'ajustement du contenu que lorsqu'elles sont explicitement demandées.
En quoi consiste ce mécanisme, justement ? La section 2 du RFC le
décrit. Un en-tête CH (Client Hint, indication
par le client) est un en-tête HTTP qui va envoyer des données
permettant au serveur de modifier le contenu. Les clients HTTP (les
navigateurs Web, par exemple) envoient les en-tête CH en fonction de
leur propre configuration, et des demandes du serveur, via l'en-tête
Accept-CH:
. Le projet de spécification
« Client
Hints Infrastructure » discute de telles politiques
et donne un jeu minimal d'indications qui n'identifient pas trop le
client. Notre RFC ajoute que, par défaut, le client HTTP ne devrait
pas envoyer de données autres que celles contenues dans ce jeu
minimal. On verra bien si les navigateurs Web suivront cette
recommandation. En tout cas, le RFC insiste bien sur le risque
d'identification (fingerprinting), avec toute
technique où le client exprime des préférences. Notez qu'à l'heure
actuelle, les indications client ne sont pas encore spécifiées, mais
il existe des
propositions.
Si le client envoie des en-têtes CH (Client
Hint), le serveur peut alors ajuster son contenu. Comme
celui-ci dépend des en-têtes CH, le serveur doit penser à ajouter un
en-tête Vary:
(RFC 7231,
section 7.1.4). Le serveur doit ignorer les indications qu'il ne
comprend pas (ce qui permettra d'en déployer des nouvelles sans tout
casser).
Comment est-ce que le serveur HTTP indique qu'il accepte le
système des indications client (client hints) ?
La section 3 de notre RFC décrit l'en-tête
Accept-CH:
(Accept Client
Hints) qui sert à indiquer que le serveur connait bien ce
système. Il figure désormais dans le
registre des en-têtes. Accept-CH:
est un
en-tête structuré (RFC 9651)
et sa valeur est une liste des indications qu'il accepte. Ainsi :
Accept-CH: CH-Example, CH-Example-again
indique que le serveur HTTP connait le système décrit dans notre RFC
et qu'il utilise deux indications client,
CH-Example
et
CH-Example-again
. (À l'heure actuelle, il
n'y a pas encore d'indications client normalisées, donc le RFC et
cet article utilisent des exemples bidons.)
Voici un exemple plus complet. Le serveur veut savoir quel logiciel utilise le client et sur quel système d'exploitation. Il envoie :
Accept-CH: Sec-CH-UA-Full-Version, Sec-CH-UA-Platform
(UA = User Agent, le client HTTP, il s'agit ici
de propositions
d'indications client ; Sec-
et
CH-
sont expliqués plus loin.) Le navigateur va
alors, dans ses requêtes suivantes, et s'il est d'accord, envoyer :
Sec-CH-UA: "Examplary Browser"; v="73" Sec-CH-UA-Full-Version: "73.3R8.2H.1" Sec-CH-UA-Platform: "Windows"
(Notez que cet exemple particulier suppose que
Sec-CH-UA:
est envoyé systématiquement, même
non demandé, point qui est encore en discussion.)
Ce système des indications client pose évidemment des problèmes
de sécurité, analysés dans la section 4. D'abord, si les indications
client ont été conçues pour mieux respecter la vie privée, elles ne
sont pas non plus une solution magique. Un serveur malveillant peut
indiquer une liste très longue dans son
Accept-CH:
espérant que les clients naïfs lui
enverront plein d'informations. (Notons que les chercheurs en
sécurité pourront analyser les réponses des serveurs HTTP et
compiler des listes de ceux qui abusent, contrairement au système
actuel où le serveur peut capter l'information de manière purement
passive.) Le client prudent ne doit envoyer au serveur que ce qui
est déjà accessible audit serveur par d'autres moyens (API
JavaScript par exemple). Et il doit tenir
compte des critères suivants :
À noter que des indications apparemment utiles, comme la langue, peuvent être dangereuses ; s'il s'agit d'une langue rare, elle peut aider à l'identification d'un utilisateur, s'il s'agit de la langue d'une minorité persécutée, elle peut être une information sensible.
Le RFC recommande (reste à savoir si cela sera suivi) que les
clients HTTP devraient lier le Accept-CH:
à une
origine (RFC 6454) et n'envoyer donc les informations qu'à
l'origine qui les demande. Ils devraient également être
configurables, permettant à l'utilisateur d'envoyer plus ou moins
d'informations selon son degré de méfiance. (Le RFC n'en parle pas,
mais j'ajoute que la valeur par défaut - très bavarde ou au
contraire très discrète - est un choix crucial, car peu
d'utilisateurs changeront ce réglage.) Et le RFC demande que tout
logiciel client permette également la discrétion totale, en
n'envoyant aucune indication client. (André Sintzoff me fait
remarquer à juste titre que « l'absence d'information est déjà une
information ». Signaler qu'on ne veut pas être suivi à la trace peut
vous signaler comme élement suspect, à surveiller.)
La liste des indications à envoyer pour chaque origine doit évidemment être remise à zéro lorsqu'on effectue des opérations de nettoyage, comme de supprimer les biscuits.
La même section 4 donne des conseils aux auteurs de futures
indications client. On a vu qu'à l'heure actuelle aucune n'était
encore normalisée mais des projets existent
déjà, notamment pour remplacer
User-Agent:
. Un des choix cruciaux est de
décider si les indications peuvent être générées par une application
(typiquement du code JavaScript exécuté par
le navigateur Web) ou bien seulement par le client. Les secondes
sont évidemment plus sûres et c'est pour cela que leur nom est
préfixée par Sec-
(pour
Secure). C'est une idée qui vient de la spécification
Fetch.
Toujours côté nommage des indications client, le RFC recommande
que le nom soit préfixé de CH-
pour
Client Hints, ce qui peut permettre de les
distinguer facilement.
À l'heure actuelle, Chromium met déjà en œuvre ce système des indications client.
Date de publication du RFC : Février 2021
Auteur(s) du RFC : M. Nottingham (Fastly), P-H. Kamp (The Varnish Cache Project)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 9 février 2021
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 (depuis
remplacé par le RFC 9651) 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 existants ne seront pas
changés, pour préserver la compatibilité.
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 7231, section 8.3.1, 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 spécifiée en ABNF (RFC 5234) mais le RFC fournit aussi des algorithmes d'analyse et, malheureusement, exige que, en cas de désaccord entre les algorithmes et la grammaire, ce soit les algorithmes qui l'emportent.
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 8941. 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) :
ListOfListsOfStrings-Example: ("foo" "bar"), ("baz"), ("bat" "one"), ()
Le cas des listes vides avait occupé une bonne partie des discussions à l'IETF (le vide, ça remplit…) Ainsi, un en-tête structuré dans 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, 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
doivent hélas être en ASCII (RFC 20). Si on veut envoyer de l'Unicode, il faudra utiliser
le type « contenu binaire » en précisant l'encodage (sans doute
UTF-8). 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 programmeu·r·se·s 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 programmeu·r·se·s, l'annexe B du RFC donne quelques conseils pour les auteur·e·s 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 n'y a dans le registre
des en-têtes qu'un seul en-tête structuré, le
Accept-CH:
du RFC 8942. Sa valeur est une liste d'identificateurs. Mais
plusieurs autres en-têtes structurés sont en préparation.
Si vous voulez un exposé synthétique sur ces en-têtes structuré, 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.6/site-packages/http_sfv-0.9.1-py3.6.egg/http_sfv/util.py", line 57, in parse 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.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.parse(b"en=\"Applepie\", fr=\"Tarte aux pommes\"") >>> print(my_dict) en="Applepie", fr="Tarte aux pommes" >>> print(my_dict["fr"]) "Tarte aux pommes"
Date de publication du RFC : Octobre 2020
Auteur(s) du RFC : C. Cremers (CISPA Helmholtz Center for Information Security), L. Garratt (Cisco Meraki), S. Smyshlyaev (CryptoPro), N. Sullivan, C. Wood (Cloudflare)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF cfrg
Première rédaction de cet article le 17 octobre 2020
Tout le monde sait désormais que la génération de nombres aléatoires (ou, plus rigoureusement, de nombres imprévisibles, pseudo-aléatoires) est un composant crucial des solutions de sécurité à base de cryptographie, comme TLS. Les failles des générateurs de nombres pseudo-aléatoires, ou bien les attaques exploitant une faiblesse de ces générateurs sont les problèmes les plus fréquents en cryptographie. Idéalement, il faut utiliser un générateur sans défauts. Mais, parfois, on n'a pas le choix et on doit se contenter d'un générateur moins satisfaisant. Ce nouveau RFC décrit un mécanisme qui permet d'améliorer la sécurité de ces générateurs imparfaits, en les nourrissant avec des clés privées.
Un générateur de nombres pseudo-aléatoires cryptographique (CSPRNG, pour Cryptographically-Strong PseudoRandom Number Generator) produit une séquence de bits qui est indistinguable d'une séquence aléatoire. Un attaquant qui observe la séquence ne peut pas prédire les bits suivants.
On les nomme générateur pseudo-aléatoires car un générateur réellement aléatoire devrait être fondé sur un phénomène physique aléatoire. Un exemple classique d'un tel phénomène, dont la théorie nous dit qu'il est réellement aléatoire, est la désintégration radioactive. Des exemples plus pratiques sont par exemple des diodes forcées de produire un bruit blanc (comme dans le Cryptech). (Ou alors un chat marchant sur le clavier ?) Mais, en pratique, les ordinateurs se servent de générateurs pseudo-aléatoires, ce qui suffit s'ils sont imprévisibles à un observateur extérieur.
Beaucoup de solutions de sécurité,
même en l'absence de cryptographie, dépendent de cette propriété
d'imprévisibilité. Ainsi, TCP, pour protéger contre un attaquant aveugle
(situé en dehors du chemin du paquet), a besoin de numéros de
séquence initiaux qui soient imprévisibles (RFC 5961). Et la cryptographie consomme
énormément de ces nombres pseudo-aléatoires. Ainsi, TLS (RFC 8446) se sert de tels nombres à de nombreux endroits, par
exemple les numniques utilisés dans le
ClientHello
, sans compter les nombres
pseudo-aléatoires utilisés lors de la génération des clés de
session. La plupart des autres protocoles de
chiffrement dépendent de tels nombres que
l'attaquant ne peut pas prévoir.
Réaliser un générateur pseudo-aléatoire sur une machine aussi déterministe qu'un ordinateur n'est pas facile, en l'absence de source quantique comme un composant radioactif. (Cf. RFC 4086.) La solution adoptée la plus simple est d'utiliser une fonction qui calcule une séquence de nombres pseudo-aléatoires d'une manière que, même en observant la séquence, on ne puisse pas deviner le nombre suivant (la mathématique fournit plusieurs fonctions pour cela). Comme l'algorithme est en général connu, et que l'ordinateur est déterministe, s'il connait le premier nombre, la graine, un observateur pourrait calculer toute la séquence. La graine ne doit donc pas être connue des observateurs extérieurs. Elle est par exemple calculée à partir d'éléments qu'un tel observateur ne connait pas, comme des mouvements physiques à l'intérieur de l'ordinateur, ou bien provoqués par l'utilisateur.
De tels générateurs pseudo-aléatoires sont très difficiles à réaliser correctement et, en cryptographie, il y a eu bien plus de failles de sécurité dues à un générateur pseudo-aléatoire que dues aux failles des algorithmes de cryptographie eux-mêmes. L'exemple le plus fameux était la bogue Debian, où une erreur de programmation avait réduit drastiquement le nombre de graines possibles (l'entropie). Un autre exemple amusant est le cas du générateur cubain qui oubliait le chiffre 9.
Mais le générateur pseudo-aléatoire peut aussi être affaibli délibérement, pour faciliter la surveillance, comme l'avait fait le NIST, sur ordre de la NSA, en normalisant le générateur Dual-EC avec une faille connue.
Obtenir une graine de qualité est très difficile, surtout sur des appareils ayant peu de pièces mobiles, donc peu de mouvements physiques qui pourraient fournir des données aléatoires. Un truc avait déjà été proposé : combiner la graine avec la clé privée utilisée pour la communication (par exemple en prenant un condensat de la concaténation de la graine avec la clé privée, pour faire une graine de meilleure qualité), puisque la clé privée est secrète. L'idée vient du projet Naxos. (Cela suppose bien sûr que la clé privée n'ait pas été créée par la source qu'on essaie d'améliorer. Elle peut par exemple avoir été produite sur un autre appareil, ayant davantage d'entropie.) Le problème de cette approche avec la clé privée, est que les clés privées sont parfois enfermées dans un HSM, qui ne les laisse pas sortir.
La solution proposée par notre RFC est de ne pas utiliser directement la clé secrète, mais une signature de la graine. C'est une valeur qui est imprévisible par un observateur, puisqu'elle dépend de la clé privée, que l'attaquant ne connait pas. Ainsi, on peut obtenir une séquence de nombres pseudo-aléatoires que l'observateur ne pourra pas distinguer d'une séquence réellement aléatoire, même si la graine initiale n'était pas terrible.
L'algorithme exact est formulé dans la section 3 du RFC. Le Nième nombre pseudo-aléatoire est l'expansion (RFC 5869, section 2.3) de l'extraction (RFC 5869, section 2.2) d'un condensat de la signature d'une valeur qui ne change pas à chaque itération de la séquence, ce qui fait que cet algorithme reste rapide. (J'ai simplifié, il y a d'autres paramètres, notamment la valeur « normale » de la séquence, initiée à partir de la graine.) La signature doit évidemment être secrète (sinon, on retombe dans le générateur pseudo-aléatoire d'avant ce RFC). La fonction de signature doit être déterministe (exemple dans le RFC 6979). La « valeur qui ne change pas à chaque itération » peut, par exemple (section 4 du RFC) être l'adresse MAC de la machine mais attention aux cas de collisions (entre VM, par exemple). Une autre valeur constante est utilisée par l'algorithme et peut, par exemple, être un compteur, ou bien l'heure de démarrage. Ces deux valeurs n'ont pas besoin d'être secrètes.
Une analyse de sécurité de ce mécanisme a été faite dans l'article « Limiting the impact of unreliable randomness in deployed security protocols ». Parmi les points à surveiller (section 9), la nécessité de bien garder secrète la signature, ce qui n'est pas toujours évident notamment en cas d'attaque par canal auxiliaire.
À propos de générateurs pseudo-aléatoires, je vous recommande cet article très détaillé en français sur la mise en œuvre de ces générateurs sur Linux.
Merci à Kim Minh Kaplan pour une bonne relecture.
Date de publication du RFC : Octobre 2020
Auteur(s) du RFC : S. Dickinson (Sinodun IT), B. Overeinder (NLnet Labs), R. van Rijswijk-Deij (NLnet Labs), A. Mankin (Salesforce)
Réalisé dans le cadre du groupe de travail IETF dprive
Première rédaction de cet article le 25 octobre 2020
Vous gérez un résolveur DNS qui promet de protéger la vie privée des utilisateurs et utilisatrices ? Alors, vous serez certainement intéressé par ce nouveau RFC qui rassemble les bonnes pratiques en matière de gestion d'un tel résolveur, et explique comment documenter la politique du résolveur, les choix effectués. On y trouve une bonne analyse des questions de sécurité, une discussion de ce qui doit être dans une politique, une analyse comparée des politiques, et même un exemple de politique.
Depuis la publication du RFC 7626, les
risques pour la vie privée
posés par l'utilisation du DNS sont bien connus. Par exemple, un de ces
risques est que le trafic DNS entre une machine terminale et le
résolveur est en clair et peut
donc trivialement être écouté, ce qui est d'autant plus gênant que
ce trafic inclut toutes les informations (il n'y a pas de
minimisation possible de la question, par exemple). Un autre risque
est que le gérant du résolveur voit forcément tout le trafic, même
si on chiffre le trafic
entre la machine terminale et lui. Il peut abuser de cette confiance
qu'on lui fait. Une des solutions possibles face à ces risques est
d'utiliser, non pas le résolveur annoncé par le réseau d'accès à
l'Internet mais un résolveur extérieur, à qui vous faites confiance,
et avec qui vous communiquez de manière chiffrée. De tels résolveurs
existent. Certains sont publics (accessibles à tous), gérés par des
GAFA comme Cloudflare
(avec son résolveur 1.1.1.1
) ou gérés par des
associations ou bien des individus (vous trouverez une liste
partielle à la fin
du README de ce logiciel). D'autres de ces résolveurs sont
réservés à tel ou tel groupe ou organisation.
Le problème pour l'utilisateur est celui du choix : lequel prendre ? Pour cela, il faut déjà que ceux et celles qui gèrent le résolveur aient documenté leurs pratiques et qu'on leur fasse confiance pour respecter leurs promesses. C'est l'un des buts de ce RFC : fournir un cadre général de description des pratiques d'un résolveur « vie privée », pour faciliter la tâche des rédacteurs et rédactrices de ces documents, dans l'esprit du RFC 6841, qui faisait la même chose pour DNSSEC. Notre RFC n'impose pas une politique particulière, il décrit juste les choix possibles, et ce qu'il ne faut pas oublier de mettre dans son RPS, son Recursive operator Privacy Statement.
Optimiste, notre RFC estime que des promesses formelles de strict préservation de la vie privée des utilisateurs seraient même un avantage pour les résolveurs ayant de tels engagements, leur apportant davantage d'utilisateurs. L'expérience du Web avec le succès des GAFA, entreprises capitalistes captatrices de données personnelles, même lorsqu'une alternative libre et respectueuse de la vie privée existe, me fait douter de ce pronostic.
Notez que la question du choix d'un résolveur est une question complexe. Le RFC cite par exemple le fait qu'avoir un résolveur stable, utilisé pour toutes les connexions d'une machine mobile, peut permettre à ce résolveur de vous suivre, lorsque vous changez de réseau d'accès.
La section 2 décrit le domaine d'applicabilité de ce document : il vise les gérants de résolveurs DNS, pas les utilisateurs finaux, ni les gérants de serveurs faisant autorité. Suivant les principes des RFC 6973 et RFC 7626, il va se pencher sur ce qui arrive aux données en transit sur l'Internet, aux données au repos sur un des serveurs (journaux, par exemple) et aux données transmises pour effectuer le travail (requêtes envoyées par un résolveur aux serveurs faisant autorité, lorsque la réponse n'est pas déjà dans la mémoire du résolveur).
La section 5 est le cœur de notre RFC, elle décrit les
recommandations concrètes. Pour illustrer ces recommandations, je
dirai à chaque fois comment elles ont été mises en œuvre sur le
résolveur sécurisé que je gère,
dot.bortzmeyer.fr
&
https://doh.bortzmeyer.fr/
. Non pas qu'il soit
le meilleur, le plus rapide, le plus sûr ou quoi que ce soit
d'autre. Mais parce qu'il met en œuvre les recommandations de ce
RFC, et que je sais qu'il respecte sa politique
affichée. Ces notes sur mon résolveur apparaitront entre
crochets.
D'abord, en transit, les communications faites en clair peuvent être écoutées par un surveillant passif et, pire, modifiées par un attaquant actif (section 5.1 du RFC). La première recommandation va de soi, il faut chiffrer. Un résolveur DNS public qui n'aurait pas de chiffrement (comme ceux actuellement proposés par certaines associations) n'a guère d'intérêt du point de vue de la vie privée. Pour ce chiffrement, deux techniques normalisées, DoT (DNS sur TLS, RFC 7858 et RFC 8310) et DoH (DNS sur HTTPS, RFC 8484). [Mon résolveur les déploie toutes les deux.] Il existe aussi un DNS sur DTLS (RFC 8094) mais qui n'a eu aucun succès, et des techniques non normalisées comme DNSCrypt, ou une forme ou l'autre de VPN vers le résolveur. Le RFC note que le chiffrement protège le canal, pas les données, et qu'il ne dispense donc pas de DNSSEC (cf. section 5.1.4). [Évidemment mon résolveur valide avec DNSSEC.] Un avantage des canaux sécurisés créés avec DoT ou DoH est qu'il y a beaucoup moins de risque que DNSSEC soit bloqué (cf. RFC 8027).
[Beaucoup de techniciens ont tendance à limiter la protection de la vie privée au chiffrement. C'est un exemple de fascination pour une technique complexe, au détriment d'autres mesures moins geek, qui sont présentées plus loin. Le chiffrement est nécessaire mais certainement pas suffisant.]
Chiffrer n'est pas très utile si on n'a pas authentifié celui avec qui on communique. Un attaquant actif peut toujours tenter de se faire passer pour le serveur qu'on essaie de joindre, par exemple par des attaques BGP ou tout simplement en injectant dans son réseau les routes nécessaires (comme le cas turc). Il faut donc authentifier le résolveur. DoT ne présentait guère de solutions satisfaisantes à l'origine, mais ça s'est amélioré avec le RFC 8310. Un résolveur DoT doit donc présenter un certificat qui permette son authentification ou bien publier sa clé publique (SPKI Subject Public Key Info, mais son utilisation est aujourd'hui déconseillée, car elle rend difficile le changement de clé). Le certificat peut contenir un nom ou une adresse IP. S'il contient un nom, il faut que le client connaisse ce nom, pour l'authentifier (un résolveur DNS traditionnel n'était connu que par son adresse IP). Ainsi, le certificat du résolveur Quad9 contient nom(s) et adresse(s) IP :
% openssl s_client -showcerts -connect 9.9.9.9:853 | openssl x509 -text ... Subject: C = US, ST = California, L = Berkeley, O = Quad9, CN = *.quad9.net ... X509v3 Subject Alternative Name: DNS:*.quad9.net, DNS:quad9.net, IP Address:9.9.9.9, IP Address:9.9.9.10, IP Address:9.9.9.11, IP Address:9.9.9.12, IP Address:9.9.9.13, IP Address:9.9.9.14, IP Address:9.9.9.15, IP Address:149.112.112.9, IP Address:149.112.112.10, IP Address:149.112.112.11, IP Address:149.112.112.12, IP Address:149.112.112.13, IP Address:149.112.112.14, IP Address:149.112.112.15, IP Address:149.112.112.112, IP Address:2620:FE:0:0:0:0:0:9, IP Address:2620:FE:0:0:0:0:0:10, IP Address:2620:FE:0:0:0:0:0:11, IP Address:2620:FE:0:0:0:0:0:12, IP Address:2620:FE:0:0:0:0:0:13, IP Address:2620:FE:0:0:0:0:0:14, IP Address:2620:FE:0:0:0:0:0:15, IP Address:2620:FE:0:0:0:0:0:FE, IP Address:2620:FE:0:0:0:0:FE:9, IP Address:2620:FE:0:0:0:0:FE:10, IP Address:2620:FE:0:0:0:0:FE:11, IP Address:2620:FE:0:0:0:0:FE:12, IP Address:2620:FE:0:0:0:0:FE:13, IP Address:2620:FE:0:0:0:0:FE:14, IP Address:2620:FE:0:0:0:0:FE:15 ...
[Mon résolveur, lui, utilise Let's Encrypt,
qui ne permet pas encore (cf. RFC 8738) de mettre
une adresse IP dans le certificat. Il n'y a donc que le nom. On peut
aussi l'authentifier avec sa clé publique (SPKI), qui vaut
eHAFsxc9HJW8QlJB6kDlR0tkTwD97X/TXYc1AzFkTFY=
.]
Puisqu'on parle de certificats : le RFC note à juste
titre que les opérateurs de serveurs DNS ne sont pas forcément
experts en la matière, sauf à demander de l'aide à leurs collègues
HTTP qui
gèrent ces certificats depuis longtemps. Le RFC recommande donc
d'automatiser (par exemple avec ACME, cf. RFC 8555),
et de superviser les
certificats (c'est le B A BA, mais combien d'administrateurs
système ne le font toujours pas ?).
Le RFC a également des recommandations techniques sur les protocoles utilisés. En effet :
Il est donc conseillé de suivre les recommandations TLS du RFC 7525, de n'utiliser que des versions récentes de TLS. Un rappel, d'ailleurs : les services comme SSLabs.com peuvent parfaitement tester votre résolveur DoH. [J'ai une bonne note.]
Il est également conseillé de faire du remplissage (RFC 7830 et RFC 8467 ou bien, pour DoH, avec le remplissage HTTP/2), et de ne pas imposer des fonctions qui sont dangereuses pour la vie privée comme la reprise de session TLS (RFC 5077), ou comme les cookies (DNS, RFC 7873 ou HTTP, RFC 6265). [Le logiciel que j'utilise pour mon résolveur, dnsdist, semble faire du remplissage DNS, mais je n'ai pas testé.]
Question non plus de sécurité mais de performance, le RFC recommande de gérer les requêtes en parallèle, y compris de pouvoir donner des réponses dans le désordre (RFC 7766) et de maintenir les sessions TLS ouvertes (RFC 7766 et RFC 7828, voire RFC 8490). [Le logiciel utilisé sur mon résolveur, dnsdist, ne sait pas encore générer des réponses dans un ordre différent de celui d'arrivée.]
Le résolveur DNS est un composant crucial de l'utilisation de l'Internet. S'il est en panne, c'est comme si tout l'Internet est en panne. La disponibilité du résolveur est donc une question cruciale. Si les pannes sont trop fréquentes, les utilisateurs risquent de se rabattre sur des solutions qui ne respectent pas leur vie privée, voire sur des solutions non sécurisées. Le risque est d'autant plus élevé qu'il y a des attaques par déni de service visant les résolveurs DNS (cf. cet article de l'AFNIC). Le RFC recommande donc de tout faire pour assurer cette disponibilité. [Mon propre résolveur, étant un projet personnel, et installé sur un VPS ordinaire, n'est certainement pas bien placé de ce point de vue.]
Bien sûr, il faut fournir aux utilisateurs des services qui protègent la vie privée les mêmes options qu'aux autres visiteurs : résolveur non menteur, validation DNSSEC, etc. (Il existe des services où les choix sont exclusifs et où, par exemple, choisir un résolveur non menteur prive de DNSSEC.)
La protection de la vie privée ne plait pas à tout le monde. Les partisans de la surveillance ont défendu leur point de vue dans le RFC 8404 en réclamant de la visibilité sur le trafic, justement ce que le chiffrement veut empêcher. Ce RFC 8932 essaie de ménager la chèvre et le chou en signalant aux opérateurs de résolveurs chiffrants qu'ils ont toujours accès au trafic en clair, en prenant les données après leur déchiffrement, sur le résolveur. C'est ce que permet, par exemple, la technologie dnstap. Après tout, TLS ne protège qu'en transit, une fois arrivé sur le résolveur, les données sont en clair. [Mon résolveur ne copie pas le trafic en clair, qui n'est jamais examiné. Le logiciel utilisé est capable de faire du dnstap, mais a été compilé sans cette option.] Notez que ce RFC 8932 reprend hélas sans nuances les affirmations du RFC 8404 comme quoi il est légitime d'avoir accès aux données de trafic.
Une technique courante pour mettre en œuvre un résolveur avec TLS est de prendre un résolveur ordinaire, comme BIND, et de le placer derrière un relais qui terminera la session TLS avant de faire suivre au vrai résolveur, typiquement situé sur la même machine pour des raisons de sécurité. Des logiciels comme stunnel, haproxy ou nginx permettent de faire cela facilement, et fournissent des fonctions utiles, par exemple de limitation du trafic. Mais cela ne va pas sans limites. Notamment, avec cette méthode, le vrai résolveur ne voit pas l'adresse IP du client, mais celle du relais. Cela interdit d'utiliser des solutions de sécurité comme les ACL ou comme RRL (Response Rate Limiting). [Mon résolveur utilise un relais, mais avec un logiciel spécialisé dans le DNS, dnsdist. dnsdist peut résoudre le problème de la mauvaise adresse IP en utilisant le PROXY protocol mais je n'utilise pas cette possibilité.]
Nous avons vu jusqu'à présent le cas des données en transit, entre le client et le résolveur DNS. La réponse principale aux problèmes de vie privée était le chiffrement. Passons maintenant au cas des données « au repos », stockées sur le résolveur, par exemple dans des journaux, ou dans des pcap (ou équivalent) qui contiennent le trafic enregistré (section 5.2). Évidemment, pour qu'un service puisse se prétendre « protecteur de la vie privée », il faut qu'une telle rétention de données soit très limitée, notamment dans le temps (c'est un des principes du RGPD, par exemple). Si on garde de telles données pour des raisons légitimes (statistiques, par exemple), il est recommandé de les agréger afin de fournir un peu d'anonymisation. (La lecture recommandée sur ce point est le RFC 6973.) Pour des problèmes à court terme (analyser et comprendre une attaque par déni de service en cours, par exemple), le mieux est de ne pas stocker les données sur un support pérenne, mais de les garder uniquement en mémoire. [Mon résolveur n'enregistre rien sur disque. Des statistiques fortement agrégées sont possibles mais, à l'heure actuelle, ce n'est pas fait.]
Si le chiffrement est le principal outil technique dont on dispose pour protéger les données lorsqu'elles se déplacent d'une machine à l'autre, il est moins utile lorsque des données sont enregistrées sur le serveur. Ici, l'outil technique essentiel est la minimisation des données. C'est le deuxième pilier d'une vraie protection de la vie privée, avec le chiffrement, mais elle est très souvent oubliée, bien que des textes juridiques comme le RGPD insistent sur son importance. Mais attention, minimiser des données n'est pas facile. On a fait de gros progrès ces dernières années en matière de ré-identification de personnes à partir de données qui semblaient minimisées. Comme le note le RFC, il n'y a pas de solution générale, largement acceptée, qui permette de minimiser les données tout en gardant leur utilité. C'est pour cela que quand un décideur sérieux et sûr de lui affirme bien fort « Ne vous inquiétez pas de cette base de données, tout est anonymisé », vous pouvez être raisonnablement sûr qu'il est soit malhonnête, soit incompétent ; réellement anonymiser est très difficile.
Cela fait pourtant quinze ou vingt ans qu'il y a des recherches actives en « anonymisation », motivées entre autre par la volonté de partager des données à des fins de recherches scientifiques. Mais le problème résiste. Et les discussions sont difficiles, car les discoureurs utilisent souvent une terminologie floue, voire incorrecte (par mauvaise foi, ou par ignorance). Notre RFC rappelle donc des points de terminologie (déjà abordés dans le RFC 6973) :
Quand un politicien ou un autre Monsieur Sérieux parle d'anonymisation, il confond presque toujours avec la simple pseudonymisation. Mais la distinction entre les deux n'est pas toujours binaire.
Dans les données d'un résolveur DNS, l'une des principales
sources d'information sur l'identité de l'utilisateur est l'adresse
IP source (cf. RFC 7626, section 2.2). De
telles adresses sont
clairement des données personnelles et il serait donc
intéressant de les « anonymiser ». De nombreuses techniques, de
qualité très variable, existent pour cela, et le RFC les présente
dans son annexe B, que je décris à la fin de cet article. Aucune de
ces techniques ne s'impose comme solution idéale marchant dans tous
les cas. (À part, évidemment, supprimer complètement l'adresse IP.)
Notez qu'il existe d'autres sources d'information sur le client que
son adresse IP, comme la question posée (s'il demande
security.debian.org
, c'est une machine
Debian) ou comme le
fingerprinting des caractéristiques techniques de
sa session. Le problème est évidemment pire avec DoH, en raison des
innombrables en-têtes HTTP très révélateurs que les navigateurs
s'obstinent à envoyer (User-Agent:
, par
exemple). Dans certains cas, on voit même des équipements comme la
box ajouter
des informations précises sur le client.
Ces données stockées sur le résolveur peuvent parfois rester uniquement dans l'organisation qui gère le résolveur, mais elles sont parfois partagées avec d'autres. (Méfiez-vous des petites lettres : quand on vous promet que « nous ne vendrons jamais vos données », cela ne veut pas dire qu'elles ne seront pas partagées, juste que le partageur ne recevra pas directement d'argent pour ce partage.) Le partage peut être utile pour la science (envoi des données à des chercheurs qui feront ensuite d'intéressants articles sur le DNS), mais il est dangereux pour la vie privée. Comme exemple d'organisation qui partage avec les universités, voir SURFnet et leur politique de partage. [Mon résolveur ne partage avec personne.]
Après les données qui circulent entre le client et le résolveur, puis le cas des données stockées sur le résolveur, voyons le cas des données envoyées par le résolveur, pour obtenir la réponse à ses questions (section 5.3 du RFC). Bien sûr, cela serait très bien si le résolveur chiffrait ses communications avec les serveurs faisant autorité, mais il n'existe actuellement pas de norme pour cela (des propositions ont été faites, et des tests menés, mais sans résultat pour l'instant).
Et, de toute façon, cela ne protègerait que contre un tiers, pas
contre les serveurs faisant autorité qui enregistrent les questions
posées. Face à ce risque, la principale recommandation du RFC
est d'utiliser la QNAME minimisation (RFC 9156) pour réduire ces données, en application
du principe général « ne pas envoyer plus que ce qui est
nécessaire ». [Mon résolveur DoT/DoH fait appel à un résolveur qui fait
cela.] Seconde recommandation, respecter les consignes ECS
(RFC 7871) : si le client demande à ce que les
données ECS soient réduites ou supprimées, il faut lui
obéir. [Mon résolveur débraye ECS avec la directive dnsdist
useClientSubnet=false
.] (Notez
que, par défaut, cela permet toujours au résolveur de transmettre
aux serveurs faisant autorité l'adresse de son client…)
Deux autres façons, pour le résolveur, de limiter les données qu'il envoie aux serveurs faisant autorité :
[Sur mon résolveur DoH/DoT, le vrai résolveur qui tourne derrière,
un Unbound, a les options
harden-below-nxdomain
et
aggressive-nsec
, qui mettent à peu près en
œuvre les RFC 8020 et RFC 8198.] On peut aussi imaginer un résolveur qui envoie des
requêtes bidon, pour brouiller les informations connues du serveur
faisant autorité, ou, moins méchamment, qui demande en avance (avant
l'expiration du TTL) des données
qui sont dans sa mémoire, sans attendre une requête explicite d'un
de ses clients, pour casser la corrélation entre un client et une
requête. Pour l'instant, il n'existe pas de recommandations
consensuelles sur ces techniques. Enfin, un résolveur peut aussi
faire suivre toutes ses requêtes à un résolveur plus gros
(forwarder), au-dessus d'un lien sécurisé. Cela a
l'avantage que le gros résolveur, ayant une mémoire plus importante,
enverra moins de données aux serveurs faisant autorité, et que sa
base de clients plus large rendra plus difficile la
corrélation. Évidemment, dans ce cas, le danger peut venir du gros
résolveur à qui on fait tout suivre, et le choix final n'est donc
pas évident du tout. [Mon résolveur ne fait pas suivre à un gros
résolveur public, aucun ne me semblant satisfaisant. Comme il a peu
de clients, cela veut dire que les données envoyées aux serveurs
faisant autorité peuvent être un problème, question vie privée.]
Une fois que cette section 5 du RFC a exposé tous les risques et
les solutions possibles, la section 6 parle de la RPS,
Recursive operator Privacy Statement, la
déclaration officielle des gérants d'un résolveur à ses clients,
exposant ce qu'ils ou elles font, et ce qu'elles ou ils ne font pas
avec les données. (Au début du développement de ce RFC, la RPS se
nommait DROP - DNS Recursive Operator Privacy
statement, ce qui était plus drôle.) Notre RFC recommande
très fortement que les opérateurs d'un résolveur DNS publient une
RPS, l'exposition de leur politique. Le but étant de permettre aux
utilisateurs humains de choisir en toute connaissance de cause un
résolveur ayant une politique qu'ils acceptent. Évidemment, il y a
des limites à cette idée. D'abord, les utilisateurs ne lisent pas
forcément les conditions d'utilisation / codes de conduite et autres
textes longs et incompréhensibles. (Le RFC propose des
recommandations sur la rédaction des RPS, justement pour diminuer ce
problème, en facilitant les comparaisons.) [Mon résolveur a une RPS
publique, accessible en
. Rédigée bien
avant la publication de ce RFC, elle y est pourtant relativement
conforme.]https://doh.bortzmeyer.fr/policy
Le RFC propose un plan pour la RPS. Rien n'est évidemment obligatoire dans ce plan, mais c'est une utile liste pour vérifier qu'on n'a rien oublié. Le but est de faire une RPS utilisable, et lisible, et le plan ne couvre pas donc d'éventuelles questions financières (si le service est payant), et ne prétend pas être un document juridique rigoureux, puisqu'elle doit pouvoir être lue et comprise.
Parmi les points à considérer quand on écrit une RPS :
L'opérateur du résolveur qui veut bien faire doit également communiquer à ses utilisateurs :
Un autre problème avec ces RPS est évidemment le risque de
mensonge. « Votre vie privée est importante », « Nous nous engageons
à ne jamais transmettre vos données à qui que ce soit », il serait
très naïf de prendre ces déclarations pour argent comptant. Le RFC
est réaliste et sa section 6.2 du RFC parle des mécanismes qui
peuvent permettre, dans le cas idéal, d'augmenter légèrement les
chances que la politique affichée soit respectée. Par exemple, on
peut produire des transparency reports où on
documente ce qu'on a été forcés de faire (« en raison d'une
injonction judiciaire, j'ai dû ajouter une règle pour bloquer
machin.example
»). Des vérifications techniques
peuvent être faites de l'extérieur, comme
l'uptime, le remplissage
ou la minimisation des requêtes. De tels outils existent pour la
qualité TLS des serveurs, comme SSLlabs.com, déjà
cité, ou Internet.nl. [Opinion
personnelle : beaucoup de tests d'Internet.nl ne sont pas pertinents
pour DoH.] Plus fort, on peut recourir à des audits externes, qui augmentent normalement
la confiance qu'on accorde à la RPS, puisqu'un auditeur indépendant
aura vérifié sa mise en œuvre. [Mon avis est que c'est d'une
fiabilité limitée, puisque l'auditeur est choisi et payé par
l'organisation qui se fait auditer… Et puis, même si vous êtes
root sur les machines de l'organisation
auditée, ce qui n'arrive jamais, s'assurer que, par exemple, les
données sont bien détruites au bout du temps prescrit est
non-trivial.] Cloudflare est ainsi audité
(par KPMG) et vous pouvez lire le
premier rapport (très court et sans aucun détail sur le
processus de vérification).
L'annexe A du RFC liste les documents, notamment les RFC, qui sont pertinents pour les opérateurs de résolveurs promettant le respect de la vie privée. On y trouve les normes techniques sur les solutions améliorant la protection de l'intimité, mais aussi celles décrivant les solutions qui peuvent diminuer la vie privée, comme ECS (RFC 7871), les cookies DNS (RFC 7873), la reprise de sessions TLS (RFC 5077, un format pour stocker des données sur le trafic DNS (RFC 8618, mais il y a aussi le traditionnel pcap), le passive DNS (RFC 8499), etc.
L'annexe C du RFC cite une intéressante comparaison, faite en 2019, de diverses RPS (Recursive operator Privacy Statement, les politiques des gérants de résolveurs). Comme ces RPS sont très différentes par la forme, une comparaison est difficile. Certaines font plusieurs pages, ce qui les rend longues à analyser. Aujourd'hui, en l'absence de cadres et d'outils pour éplucher ces RPS, une comparaison sérieuse par les utilisateurs n'est pas réaliste. Quelques exemples réels : la RPS de mon résolveur (la seule en français), celle de PowerDNS, celle de Quad9, celle de Cloudflare… Mozilla a développé, pour son programme TRR (Trusted Recursive Resolve), une liste de critères que les résolveurs qui veulent rejoindre le programme doivent respecter. Une sorte de méta-RPS. [Apparemment, le seul truc qui me manque est le transparency report annuel.]
L'annexe D du RFC contient un exemple de RPS. Elle ne prétend pas être parfaite, et il ne faudrait surtout pas la copier/coller directement dans une vraie RPS mais elle peut servir de source d'inspiration. Notez également qu'elle est écrite en anglais, ce qui ne conviendra pas forcément à un service non-étatsunien. Parmi les points que j'ai notés dans cette RPS (rappelez-vous qu'il s'agit juste d'un exemple et vous n'avez pas à l'utiliser telle quelle) :
On a dit plus haut que l'« anonymisation » d'adresses IP était un art très difficile. L'annexe B de notre RFC fait le point sur les techniques existantes, leurs avantages et inconvénients. C'est une lecture très recommandée pour ceux et celles qui veulent réellement « anonymiser », pas juste en parler. Un tableau résume ces techniques, leurs forces et leurs faiblesses. Le problème est de dégrader les données suffisamment pour qu'on ne puisse plus identifier l'utilisateur, tout en gardant assez d'informations pour réaliser des analyses. À bien des égards, c'est la quadrature du cercle.
Pour mettre de l'ordre dans les techniques d'« anonymisation », le RFC commence par lister les propriétés possibles des techniques (cf. RFC 6235). Il y a entre autres :
2001:db8:1:cafe:fafa:42:1:b53
devient
2001:db8:1:cafe::
, qui est toujours une
adresse IP). Par contre, condenser ne garde
pas le format (le condensat SHA-256 de
cette adresse deviendrait
98a09452f58ffeba29e7ca06978b3d65e104308a7a7f48b0399d6e33c391f663
).Pour atteindre nos objectifs, on a beaucoup de solutions (pas forcément incompatibles) :
L'annexe B rentre ensuite dans les détails en examinant plusieurs techniques documentées (rappelez-vous que les responsables des données sont souvent très vagues, se contentant d'agiter les mains en disant « c'est anonymisé avec des techniques très avancées »). Par exemple, Google Analytics permet aux webmestres de demander à généraliser les adresses IP en mettant à zéro une partie des bits. Comme le note le RFC, cette méthode est très contestable, car elle garde bien trop de bits (24 en IPv4 et 48 en IPv6).
Plusieurs des techniques listées ont une mise en œuvre dans du logiciel publiquement accessible, mais je n'ai pas toujours réussi à tester. Pour dnswasher, il faut apparemment compiler tout PowerDNS. Une fois que c'est fait, dnswasher nous dit ce qu'il sait faire :
% ./dnswasher -h Syntax: dnswasher INFILE1 [INFILE2..] OUTFILE Allowed options: -h [ --help ] produce help message --version show version number -k [ --key ] arg base64 encoded 128 bit key for ipcipher -p [ --passphrase ] arg passphrase for ipcipher (will be used to derive key) -d [ --decrypt ] decrypt IP addresses with ipcipher
dnswasher remplace chaque adresse IP dans un fichier pcap par une autre adresse, attribuée dans l'ordre. C'est de la pseudonymisation (chaque adresse correspond à un pseudonyme et un seul), le trafic d'une adresse suffit en général à l'identifier, par ses centres d'intérêt. Traitons un pcap de requêtes DNS avec ce logiciel :
% ./dnswasher ~/tmp/dns.pcap ~/tmp/anon.pcap Saw 18 correct packets, 1 runts, 0 oversize, 0 unknown encaps % tcpdump -n -r ~/tmp/anon.pcap 15:28:49.674322 IP 1.0.0.0.41041 > 213.251.128.136.53: 51257 [1au] SOA? toto.fr. (36)
L'adresse 1.0.0.0
n'est pas la vraie, c'est le
résultat du remplacement fait par dnswasher. En revanche,
213.251.128.136
est la vraie adresse. dnswasher
ne remplace pas les adresses quand le port
est 53, considérant que les serveurs n'ont pas de vie privée,
contrairement aux clients.
Dans l'exemple ci-dessus, on n'avait pas utilisé de clé de
chiffrement, et dnswasher remplace les adresses IP avec
1.0.0.0
, 1.0.0.1
, etc. En
rajoutant l'option -p toto
où toto est notre clé
(simpliste) :
15:28:49.674322 IP 248.71.56.220.41041 > 213.251.128.136.53: 51257 [1au] SOA? toto.fr. (36)
L'adresse est remplacée par une adresse imprévisible, ici
248.71.56.220
. Cela
marche aussi en IPv6 :
15:28:49.672925 IP6 b4a:7e80:52fe:4003:6116:fcb8:4e5a:b334.52346 > 2001:41d0:209::1.53: 37568 [1au] SOA? toto.fr. (36)
(Une adresse comme
b4a:7e80:52fe:4003:6116:fcb8:4e5a:b334.52346
ne
figure pas encore dans les plages d'adresses attribuées.)
TCPdpriv, lui, préserve le préfixe des adresses IP. À noter qu'il n'est pas déterministe : les valeurs de remplacement seront différentes à chaque exécution.
On peut aussi chiffrer l'adresse IP, en utilisant une clé qu'on garde secrète. Cela a l'avantage qu'un attaquant ne peut pas simplement faire tourner l'algorithme sur toutes les adresses IP possibles, comme il le peut si on utilise une simple condensation. C'est ce que fait Crypto-PAn.
Comme chaque logiciel a sa propre façon de remplacer les adresses IP, cela peut rendre difficile l'échange de données et le travail en commun. D'où le projet ipcipher. Ce n'est pas du code, mais une spécification (avec des pointeurs vers des logiciels qui mettent en œuvre cette spécification). Au passage, je vous recommande cet article à ce sujet. La spécification ipcipher peut être mise en œuvre, par exemple, avec le programme ipcrypt, qui traite des fichiers texte au format CSV :
% cat test2.csv foo,127.0.0.1 bar,9.9.9.9 baz,204.62.14.153 sha,9.9.9.9 % ./ipcrypt test2.csv 1 e foo,118.234.188.6 bar,142.118.72.81 baz,235.237.54.9 sha,142.118.72.81
Notez le déterminisme : 9.9.9.9
est toujours
remplacé par la même adresse IP.
Enfin, des structures de données rigolotes peuvent fournir d'autres services. C'est ainsi que les filtres de Bloom peuvent permettre de savoir si une requête a été faite, mais sans pouvoir trouver la liste des requêtes. Un exemple d'application aux données DNS est l'article « Privacy-Conscious Threat Intelligence Using DNSBLOOM ».
Date de publication du RFC : Novembre 2020
Auteur(s) du RFC : U. Carion (Segment)
Expérimental
Première rédaction de cet article le 7 novembre 2020
Il existe plusieurs langages pour décrire la structure d'un document JSON. Aucun ne fait l'objet d'un clair consensus. Sans compter les nombreux programmeurs qui ne veulent pas entendre parler d'un schéma formel. Ce nouveau RFC décrit un de ces langages, JTD, JSON Type Definition. Une de ses particularités est que le schéma est lui-même écrit en JSON. Son cahier des charges est de faciliter la génération automatique de code à partir du schéma. JTD est plus limité que certains langages de schéma, afin de faciliter l'écriture d'outils JTD.
On l'a dit, il n'existe pas d'accord dans le monde JSON en faveur d'un langage de schéma particulier. La culture de ce monde JSON est même souvent opposée au principe d'un schéma. Beaucoup de programmeurs qui utilisent JSON préfèrent l'agilité, au sens « on envoie ce qu'on veut et le client se débrouille pour le comprendre ». Les mêmes désaccords existent à l'IETF, et c'est pour cela que ce RFC n'est pas sur le chemin des normes, mais a juste l'état « Expérimental ».
JSON est normalisé dans le RFC 8259. D'innombrables fichiers de données sont disponibles au format JSON, et de très nombreuses API prennent du JSON en entrée et en rendent en sortie. La description des structures de ces requêtes et réponses est typiquement faite en langage informel. C'est par exemple le cas de beaucoup de RFC qui normalisent un format utilisant JSON comme le RFC 9083, les RFC 8620 et RFC 8621, le RFC 7033, etc. Une des raisons pour lesquelles il est difficile de remplacer ces descriptions en langue naturelle par un schéma formel (comme on le fait couramment pour XML, par exemple avec Relax NG) est qu'il n'y a pas d'accord sur le cahier des charges du langage de schéma. JTD (JSON Type Definition) a des exigences bien précises (section 1 du RFC). Avant de comparer JTD à ses concurrents (cf. par exemple l'annexe B), il faut bien comprendre ces exigences, qui influent évidemment sur le langage :
Ainsi, JTD a des entiers sur 8, 16 et 32
bits, qu'un générateur de code peut traduire directement en (le RFC
utilise des exemples C++)
int8_t
, int16_t
, etc, mais
pas d'entiers de 64 bits, pourtant admis en JSON mais peu portables
(cf. annexe A.1). JTD permet de décrire les propriétés d'un objet
(« objet », en JSON, désigne un dictionnaire)
qu'on peut traduire en struct
ou
std::map
C++.
Les fans de théorie des langages et de langages formels noteront que JTD n'est pas lui-même spécifié en JTD. Le choix ayant été fait d'un format simple, JTD n'a pas le pouvoir de se décrire lui-même et c'est pour cela que la description de JTD est faite en CDDL (Concise Data Definition Language, RFC 8610).
La syntaxe exacte est spécifiée en section 2, une fois que vous avez (re)lu le RFC 8610. Ainsi, la description de base, en CDDL, d'un membre d'un objet JSON est :
properties = (with-properties // with-optional-properties) with-properties = ( properties: { * tstr => { schema }}, ? optionalProperties: { * tstr => { schema }}, ? additionalProperties: bool, shared, )
Ce qui veut dire en langage naturel que le schéma JTD peut avoir un
membre properties
, lui-même ayant des membres
composés d'un nom et d'un schéma. Le schéma peut être, entre autres,
un type, ce qui est le cas dans l'exemple ci-dessous. Voici un
schéma JTD trivial, en JSON comme il se doit :
{ "properties": { "name": { "type": "string" }, "ok": { "type": "boolean", "nullable": true }, "level": { "type": "int32" } } }
Ce schéma accepte le document JSON :
{ "name": "Foobar", "ok": false, "level": 1 }
Ou bien ce document :
{ "name": "Durand", "ok": null, "level": 42 }
(L'élément nullable peut valoir
null
; si on veut pouvoir omettre complètement
un membre, il faut le déclarer dans
optionalProperties
, pas properties
.) Par contre, cet autre document n'est pas
valide :
{ "name": "Zig", "ok": true, "level": 0, "extra": true }
Car il y a un membre de trop, extra
. Par
défaut, JTD ne le permet pas mais un schéma peut comporter
additionalProperties: true
ce qui les
autorisera.
Ce document JSON ne sera pas accepté non plus :
{ "name": "Invalid", "ok": true }
Car la propriété level
ne peut pas être absente.
Un exemple plus détaillé, et pour un cas réel, figure dans l'annexe C du RFC, en utilisant le langage du RFC 7071.
JTD ne permet pas de vrai mécanisme d'extension mais on peut
toujours ajouter un membre metadata
dont la
valeur est un objet JSON quelconque, et qui sert à définir des
« extensions » non portables.
Jouons d'ailleurs un peu avec une mise en œuvre de JTD. Vous en trouverez plusieurs ici, pour divers langages de programmation. Essayons avec celui en Python. D'abord, installer le paquetage :
% git clone https://github.com/jsontypedef/json-typedef-python.git % cd json-typedef-python % python setup.py build % python setup.py install --user
(Oui, on aurait pu utiliser pip install jtd
à
la place.)
Le paquetage n'est pas livré avec un script exécutable, on en crée
un en suivant la
documentation. Il est simple :
#!/usr/bin/env python3 import sys import json import jtd if len(sys.argv) != 3: raise Exception("Usage: %s schema json-file" % sys.argv[0]) textSchema = open(sys.argv[1], 'r').read() textJsonData = open(sys.argv[2], 'r').read() schema = jtd.Schema.from_dict(json.loads(textSchema)) jsonData = json.loads(textJsonData) result = jtd.validate(schema=schema, instance=jsonData) print(result)
Si le fichier JSON correspond au schéma, il affichera un tableau vide, sinon un tableau contenant la liste des erreurs :
% ./jtd.py myschema.json mydoc1.json []
(myschema.json
contient le schéma d'exemple
plus haut et mydoc1.json
est le premier exemple
JSON.) Si, par contre, le fichier JSON est invalide :
% ./jtd.py myschema.json mydoc3.json [ValidationError(instance_path=['extra'], schema_path=[])]
(mydoc3.json
était l'exemple avec le membre
supplémentaire, extra
.)
Une particularité de JTD est de normaliser le mécanisme de
signalement d'erreurs. Les erreurs doivent être formatées en JSON
(évidemment…) avec un membre instancePath
qui
est un pointeur JSON (RFC 6901) indiquant la partie invalide du document, et un
membre schemaPath
, également un pointeur, qui
indique la partie du schéma qui invalidait cette partie du document
(cf. le message d'erreur ci-dessus, peu convivial mais
normalisé).
JTD est spécifié en CDDL donc on peut tester ses schémas avec les outils CDDL, ici un outil en Ruby :
% gem install cddl --user
Ensuite, on peut valider ses schémas :
% cddl jtd.cddl validate myschema.json %
Si le schéma a une erreur (ici, j'ai utilisé le type
char
, qui n'existe pas) :
% cddl jtd.cddl validate wrongschema.json CDDL validation failure (nil for {"properties"=>{"name"=>{"type"=>"char"}, "ok"=>{"type"=>"boolean", "nullable"=>true}, "level"=>{"type"=>"int32"}}}): ["char", [:text, "timestamp"], nil] ["char", [:text, "timestamp"], null]
(Oui, les messages d'erreur de l'outil cddl
sont horribles.)
Et avec l'exemple de l'annexe C, le reputon du RFC 7071 :
% cddl jtd.cddl validate reputon.json %
C'est parfait, le schéma du RFC est correct, validons le fichier JSON tiré de la section 6.4 du RFC 7071 :
% ./jtd.py reputon.json r1.json []
Si jamais il y a une erreur (ici, on a enlevé le membre
rating
) :
% ./jtd.py reputon.json r1.json [ValidationError(instance_path=['reputons', '0'], schema_path=['properties', 'reputons', 'elements', 'properties', 'rating'])]
Une intéressante annexe B fait une comparaison de JTD avec CDDL. Par exemple, le schéma CDDL :
root = "PENDING" / "DONE" / "CANCELED"
accepterait les mêmes documents que le schéma JTD :
{ "enum": ["PENDING", "DONE", "CANCELED"]}
Et celui-ci, en CDDL (où le point d'interrogation indique un terme facultatif) :
root = { a: bool, b: number, ? c: tstr, ? d: tdate }
reviendrait à ce schéma JTD :
{ "properties": { "a": { "type": "boolean" }, "b": { "type": "float32" } }, "optionalProperties": { "c": { "type": "string" }, "d": { "type": "timestamp" } } }
Merci à Ulysse Carion pour sa relecture.
Date de publication du RFC : Octobre 2020
Auteur(s) du RFC : L. Colitti, J. Linkova (Google), M. Richardson (Sandelman), T. Mrugalski (ISC)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dhc
Première rédaction de cet article le 17 octobre 2020
Si une machine a IPv6 et est ravie de n'utiliser que ce protocole, pas la peine pour un serveur DHCP de lui envoyer une des rares adresses IPv4 restantes. Ce nouveau RFC décrit une option de DHCP-IPv4 qui permet au client de dire au serveur « je n'ai pas vraiment besoin d'IPv4 donc, s'il y a IPv6 sur ce réseau, tu peux garder les adresses IPv4 pour les nécessiteux ».
Idéalement, l'administrateur réseaux qui configure un nouveau réseau voudrait le faire en IPv6 seulement, ce qui simplifierait son travail. Mais, en pratique, c'est difficile (le RFC 6586 décrit une telle configuration). En effet, beaucoup de machines ne sont pas encore entrées dans le XXIe siècle, et ne gèrent qu'IPv4 ou, plus exactement, ont besoin d'IPv4 pour au moins certaines fonctions (comme des versions de Windows qui avaient IPv6 mais ne pouvaient parler à leur résolveur DNS qu'au-dessus d'IPv4). Il faut donc se résigner à gérer IPv4 pour ces machines anciennes. Mais, vu la pénurie d'adresses IPv4, il est également souhaitable de ne pas allouer d'adresses IPv4 aux machines qui n'en ont pas besoin.
La solution la plus courante à l'heure actuelle est de mettre les machines antédiluviennes et les machines modernes dans des réseaux séparés (VLAN ou SSID différents, par exemple, comme c'est fait aux réunions IETF ou RIPE), le réseau pour les ancêtres étant le seul à avoir un serveur DHCPv4 (RFC 2131). Cela a plusieurs inconvénients :
L'idéal serait de n'avoir qu'un réseau, accueillant aussi bien des machines IPv6 que des machines IPv4, sans que les machines IPv6 n'obtiennent d'adresse IPv4, dont elles n'ont pas besoin. On ne peut pas demander aux utilisateurs de débrayer complètement IPv4, car les machines mobiles peuvent en avoir besoin sur des réseaux purement IPv4 (ce qui existe encore). La machine IPv6 ne sachant pas, a priori, si le réseau où elle se connecte est seulement IPv4, seulement IPv6 ou accepte les deux, elle va donc, logiquement, demander une adresse IPv4 en DHCP, et bloquer ainsi une précieuse adresse IPv4. Retarder cette demande pour voir si IPv6 ne suffirait pas se traduirait par un délai peu acceptable lorsque le réseau n'a pas IPv6.
Notre RFC résout ce problème en créant une nouvelle option pour les requêtes et réponses DHCP v4 : dans la requête, cette option indique que le client n'a pas absolument besoin d'une adresse IPv4, qu'il peut se passer de ce vieux protocole, si le réseau a de l'IPv6 bien configuré et, dans la réponse, cette option indique que le réseau fonctionne bien en « IPv6 seulement ». Et comment fait-on si on veut joindre un service IPv4 depuis un tel réseau ? Il existe des techniques pour cela, la plus répandue étant le NAT64 du RFC 6146, et la réponse du serveur DHCP indique également qu'une de ces techniques est présente.
La section 1.2 précise quelques termes importants pour comprendre les choix à effectuer pour le client et pour le serveur DHCP :
La section 2 du RFC résume ensuite les bonnes raisons qu'il y a pour signaler au réseau, via l'option DHCP, qu'on se débrouille très bien avec IPv6 seul :
La section 3 du RFC présente l'option elle-même. Elle contient le code 108 (IPv6-only Preferred), enregistré à l'IANA (cf. section 5), la taille de l'option (toujours quatre, mais précisée car c'est l'encodage habituel des options DHCP, cf. RFC 2132) et la valeur, qui est, dans la réponse, le nombre de secondes que le client peut mémoriser cette information, avant de redemander au serveur DHCP.
Le logiciel client DHCP doit donc offrir un moyen à son administrateur pour activer « je me débrouille très bien en IPv6 », et cela doit être par interface réseau. Dans ce cas, le client DHCP doit envoyer l'option décrite dans notre RFC. La décision d'activer le mode « IPv6 seul » peut aussi être prise automatiquement par le système d'exploitation, par exemple parce qu'il détecte que 464XLAT (RFC 6877) fonctionne. De même, des objets connectés qui ne parlent qu'à des services accessibles en IPv6 pourraient être livrés avec l'option « IPv6 seul » déjà activée.
Outre le côté « je suis un bon citoyen, je ne gaspille pas des ressources rares » qu'il y a à ne pas demander d'adresse IPv4, cette option permet de couper un protocole réseau inutile, diminuant la surface d'attaque de la machine.
Côté serveur DHCP, il faut un moyen de configurer, pour chaque réseau, si on accepte l'option « IPv6 me suffit ». Si elle est activée, le serveur, lorsqu'il recevra cette option dans une requête (et uniquement dans ce cas), la mettra dans sa réponse, et n'attribuera pas d'adresse IPv4. On voit donc que les vieux clients DHCP, qui ne connaissent pas cette option et ne l'inclueront donc pas dans leurs requêtes, ne verront pas de changement, ils continueront à avoir des adresses IPv4 comme avant (s'il en reste…).
A priori (section 4 du RFC), l'administrateurice du serveur DHCP ne va pas activer cette option si son réseau ne fournit pas un mécanisme permettant aux machines purement IPv6 de joindre des services purement IPv4 (par exemple le mécanisme NAT64 du RFC 6146). En effet, on ne peut pas s'attendre, à court terme, à ce que tous les services Internet soient accessibles en IPv6. Activer l'option « IPv6 seul » sans avoir un mécanisme de traduction comme NAT64 n'est réaliste que sur des réseaux non-connectés à l'Internet public.
Un petit mot sur la sécurité, juste pour rappeler que DHCP n'est pas vraiment sécurisé et que l'option « v6 seul » a pu être mise, retirée ou modifiée suite aux actions d'un attaquant. Cela n'a rien de spécifique à cette option, c'est un problème général de DHCP, contre lequel il faut déployer des protections comme le DHCP snooping.
Au moment de la sortie du RFC, je ne connaissais pas encore de mise en œuvre de cette option. Mais elle n'est pas trop dure à ajouter, elle n'a rien de très nouveau ou d'extraordinaire et, en 2022, Android et iOS l'envoyaient, comme le montre une étude faite pendant une réunion RIPE.
Un dernier mot : le RFC 2563 prévoyait une option pour couper l'auto-configuration des adresses IPv4. Elle peut être utilisée en même temps que l'option de notre RFC, qu'elle complète assez bien.
Date de publication du RFC : Octobre 2020
Auteur(s) du RFC : M. Welzl, S. Gjessing (University of Oslo)
Pour information
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 22 octobre 2020
Ce nouveau RFC s'incrit dans le travail en cours à l'IETF pour formaliser un peu plus les services que la couche transport offre aux applications. Le RFC 8095 décrivait tous les services possibles. Ce nouveau RFC 8923 liste les services minimums, ceux à offrir dans presque tous les cas.
Comme le note le RFC 8095, les applications tournant sur la famille de protocoles TCP/IP ne peuvent en général pas aujourd'hui exprimer les services attendus de la couche transport. Elles doivent choisir un protocole de transport (comme TCP ou UDP), même si ce protocole ne convient pas tout à fait. Toute l'idée derrière le travail du groupe TAPS de l'IETF est de permettre au contraire aux applications d'indiquer des services (« je veux un envoi de données fiable et dans l'ordre, avec confidentialité et intégrité ») et que le système d'exploitation choisisse alors la solution la plus adaptée (ici, cela pourrait être TCP ou SCTP avec TLS par dessus, ou bien le futur QUIC). De la même façon qu'un langage de programmation de haut niveau utilise des concepts plus abstraits qu'un langage de bas niveau, on pourrait ainsi avoir une interface de programmation réseau de plus haut niveau que les traditionnelles prises normalisées par Posix. Ce RFC ne normalise pas une telle API, il décrit seulement les services attendus. La liste des services possibles est dans le RFC 8095, et les protocoles qui les fournissent sont documentés dans le RFC 8303 et RFC 8304. Le cœur de notre nouveau RFC 8923 est la section 6, qui liste les services minimum attendus (je vous en dévoile l'essentiel tout de suite : établissement de connexion, fin de connexion, envoi de données, réception de données).
Ce RFC ne spécifie pas une API précise,
cela sera éventuellement fait dans un autre document,
draft-ietf-taps-interface
.
À noter que les services minimums exposés ici peuvent être mis en œuvre sur TCP, et, dans certains cas, sur UDP. TCP offre un ensemble de services qui est quasiment un sur-ensemble de celui d'UDP, donc, sur le seul critère des services, on pourrait n'utiliser que TCP. (Exercice : quelle(s) exception(s) trouvez-vous ?) Comme il s'agit de services de la couche transport aux applications, l'implémentation peut être faite d'un seul côté (il n'est pas nécessaire que les deux partenaires qui communiquent le fassent).
Et, si vous lisez le RFC, faites attention à la section 2 sur la terminologie, section qui reprend le RFC 8303 : une fonction (Transport Feature) est une fonction particulière que le protocole de transport va effectuer, par exemple la confidentialité, la fiabilité de la distribution des données, le découpage des données en messages, etc, un service (Transport Service) est un ensemble cohérent de fonctions, demandé par l'application, et un protocole (Transport Protocol) est une réalisation concrète d'un ou plusieurs services.
Comment définir un jeu minimal de services (section 3 du RFC) ? D'abord, il faut définir les services « sémantiques » (functional features) que l'application doit connaitre. Par exemple, l'application doit savoir si le service « distribution des données dans l'ordre » est disponible ou pas. Et il y a les services qui sont plutôt des optimisations comme l'activation ou la désactivation de DSCP ou comme l'algorithme de Nagle. Si l'application ignore ces services, ce n'est pas trop grave, elle fonctionnera quand même, même si c'est avec des performances sous-optimales.
La méthode utilisée par les auteurs du RFC pour construire la liste des services minimums est donc :
Ainsi (section 4 du RFC), le service « établir une connexion » peut être mis en œuvre sur TCP de manière triviale, mais aussi sur UDP, en refaisant un équivalent de la triple poignée de mains. Le service « envoyer un message » peut être mis en œuvre sur UDP et TCP (TCP, en prime, assurera sa distribution à l'autre bout, mais ce n'est pas nécessaire). En revanche, le service « envoyer un message avec garantie qu'il soit distribué ou qu'on soit notifié », s'il est simple à faire en TCP, ne l'est pas en UDP (il faudrait refaire tout TCP au dessus d'UDP).
La section 5 du RFC discute de divers problèmes quand on essaie de définir un ensemble minimal de services. Par exemple, TCP ne met pas de délimiteur dans le flot d'octets qui circulent. Contrairement à UDP, on ne peut pas « recevoir un message », seulement recevoir des données. Certains protocoles applicatifs (comme DNS ou EPP) ajoutent une longueur devant chaque message qu'ils envoient, pour avoir une sémantique de message et plus de flot d'octets. TCP n'est donc pas un strict sur-ensemble d'UDP.
Pour contourner cette limitation, notre RFC définit (section 5.1) la notion de « flot d'octets découpé en messages par l'application » (Application-Framed Bytestream). Dans un tel flot, le protocole applicatif indique les frontières de message, par exemple en précédant chaque message d'une longueur, comme le fait le DNS.
Autre problème amusant, certains services peuvent être d'assez bas niveau par rapport aux demandes des applications. Ainsi, le RFC 8303 identifie des services comme « couper Nagle », « configurer DSCP » ou encore « utiliser LEDBAT ». Il serait sans doute plus simple, pour un protocole applicatif, d'avoir une configuration de plus haut niveau. Par exemple, « latence minimale » désactiverait Nagle et mettrait les bits qui vont bien en DSCP.
Nous arrivons finalement au résultat principal de ce RFC, la section 6, qui contient l'ensemble minimal de services. Chaque service est configurable via un ensemble de paramètres. Il est implémentable uniquement avec TCP, et d'un seul côté de la connexion. Dans certains cas, TCP fournira plus que ce qui est demandé, mais ce n'est pas un problème. Je ne vais pas lister tout cet ensemble minimal ici, juste énumérer quelques-uns de ses membres :
Pour l'envoi de données, les paramètres sont, entre autres :
Pour la réception de données, le service est juste la réception d'octets, un protocole applicatif qui veut des messages doit utiliser les « flots d'octets découpés par l'application » décrits en section 5.1.
On notera que la sécurité est un service, également, ou plutôt un ensemble de services (section 8 du RFC). Dans les protocoles IETF, la sécurité est souvent fournie par une couche intermédiaire entre la couche de transport et la couche application à proprement parler. C'est ainsi que fonctionne TLS, par exemple. Mais la tendance va peut-être aller vers l'intégration, avec QUIC.
Date de publication du RFC : Octobre 2020
Auteur(s) du RFC : T. Enghardt (TU Berlin), T. Pauly (Apple), C. Perkins (University of Glasgow), K. Rose (Akamai Technologies), C.A. Wood (Cloudflare)
Pour information
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 22 octobre 2020
Quels sont les services de sécurité fournis par les protocoles de transport existants ? Ce nouveau RFC fait le point et examine ces services de sécurité pour un certain nombre de ces protocoles, comme TLS, QUIC, WireGuard, etc. Cela intéressera tous les gens qui travaillent sur la sécurité de l'Internet.
Ce RFC est issu du groupe de travail IETF TAPS, dont le projet est d'abstraire les services que rend la couche Transport pour permettre davantage d'indépendance entre les applications et les différents protocoles de transport. Vous pouvez en apprendre plus dans les RFC 8095 et RFC 8303.
Parmi les services que fournit la couche Transport aux applications, il y a la sécurité. Ainsi, TCP (si le RFC 5961 a bien été mis en œuvre et si l'attaquant n'est pas sur le chemin) protège assez bien contre les usurpations d'adresses IP. Notez que le RFC utilise le terme de « protocole de transport » pour tout ce qui est en dessous de l'application, et utilisé par elle. Ainsi, TLS est vu comme un protocole de transport, du point de vue de l'application, il fonctionne de la même façon (ouvrir une connexion, envoyer des données, lire des réponses, fermer la connexion), avec en prime les services permis par la cryptographie, comme la confidentialité. TLS est donc vu, à juste titre, comme un protocole d'infrastructure, et qui fait donc partie de ceux étudiés dans le cadre du projet TAPS.
La section 1 du RFC explique d'ailleurs quels protocoles ont été intégrés dans l'étude et pourquoi. L'idée était d'étudier des cas assez différents les uns des autres. Des protocoles très répandus comme SSH (RFC 4251), GRE (avec ses extensions de sécurité du RFC 2890) ou L2TP (RFC 5641) ont ainsi été écartés, pas parce qu'ils sont mauvais mais parce qu'ils fournissent à peu près les mêmes services que des protocoles étudiés (par exemple SSH vs. TLS) et ne nécessitaient donc pas d'étude séparée. Plus animée, au sein du groupe de travail TAPS, avait été la discussion sur des protocoles non-IETF comme WireGuard ou MinimaLT. Ils ne sont pas normalisés (et, souvent, même pas décrits dans une spécification stable), mais ils sont utilisés dans l'Internet, et présentent des particularités suffisamment importantes pour qu'ils soient étudiés ici. En revanche, les protocoles qui ne fournissent que de l'authentification, comme AO (RFC 5925) n'ont pas été pris en compte.
Comme c'est le cas en général dans le projet TAPS, le but est de découvrir et de documenter ce que les protocoles de transport ont en commun, pour faciliter leur choix et leur utilisation par l'application. Par contre, contrairement à d'autres cas d'usage de TAPS, il n'est pas prévu de permettre le choix automatique d'un protocole de sécurité. Pour les autres services, un tel choix automatique se justifie (RFC 8095). Si une application demande juste le service « transport d'octets fiable, j'ai des fichiers à envoyer », lui fournir TCP ou SCTP revient au même, et l'application se moque probablement du protocole choisi. Mais la sécurité est plus compliquée, avec davantage de pièges et de différences subtiles, et il est donc sans doute préférable que l'application choisisse explicitement le protocole.
La section 2 du RFC permet de réviser la terminologie, je vous renvoie à mon article sur le RFC 8303 pour la différence entre fonction (Transport Feature) et service (Transport Service).
Et la section 3 s'attaque aux protocoles eux-mêmes. Elle les classe selon qu'ils protègent n'importe quelle application, une application spécifique, l'application et le transport, ou bien carrément tout le paquet IP. Le RFC note qu'aucune classification n'est parfaite. Notamment, plusieurs protocoles sont séparés en deux parties, un protocole de connexion, permettant par exemple l'échange des clés de session, protocole utilisé essentiellement, voire uniquement, à l'ouverture et à la fermeture de la session, et le protocole de chiffrement qui traite les données transmises. Ces deux protocoles sont plus ou moins intégrés, de la fusion complète (tcpcrypt, RFC 8548) à la séparation complète (IPsec, où ESP, décrit dans le RFC 4303, ne fait que chiffrer, les clés pouvant être fournies par IKE, spécifié dans le RFC 7296, ou par une autre méthode). Par exemple, TLS décrit ces deux protocoles séparement (RFC 8446) mais on les considérait comme liés… jusqu'à ce que QUIC décide de n'utiliser qu'un seul des deux.
Bon, assez d'avertissements, passons à la première catégorie, les protocoles qui fournissent une protection générique aux applications. Ils ne protègent donc pas contre des attaques affectant la couche 4 comme les faux paquets RST. Le plus connu de ces protocoles est évidemment TLS (RFC 8446). Mais il y a aussi DTLS (RFC 9147).
Ensuite, il y a les protocoles spécifiques à une application. C'est par exemple le cas de SRTP (RFC 3711), extension sécurisée de RTP qui s'appuie sur DTLS.
Puis il y a la catégorie des protocoles qui protègent le transport ce qui, par exemple, protège contre les faux RST (ReSeT), et empêche un observateur de voir certains aspects de la connexion. C'est le cas notamment de QUIC, normalisé depuis. QUIC utilise TLS pour obtenir les clés, qui sont ensuite utilisées par un protocole sans lien avec TLS, et qui chiffre y compris la couche Transport. Notons que le prédécesseur de QUIC, développé par Google sous le même nom (le RFC nomme cet ancien protocole gQUIC pour le distinguer du QUIC normalisé), n'utilisait pas TLS.
Toujours dans la catégorie des protocoles qui protègent les couches Transport et Application, tcpcrypt (RFC 8548). Il fait du chiffrement « opportuniste » (au sens de « sans authentification ») et est donc vulnérable aux attaques actives. Mais il a l'avantage de chiffrer sans participation de l'application, contrairement à TLS. Bon, en pratique, il n'a connu quasiment aucun déploiement.
Il y a aussi MinimaLT, qui intègre l'équivalent de TCP et l'équivalent de TLS (comme le fait QUIC).
Le RFC mentionne également CurveCP, qui fusionne également transport et chiffrement. Comme QUIC ou MinimaLT, il ne permet pas de connexions non-sécurisées.
Et enfin il y a les protocoles qui protègent tout, même IP. Le plus connu est évidemment IPsec (RFC 4303 et aussi RFC 7296, pour l'échange de clés, qui est un protocole complètement séparé). Mais il y a aussi WireGuard qui est nettement moins riche (pas d'agilité cryptographique, par exemple, ni même de négociation des paramètres cryptographiques ; ce n'est pas un problème pour des tunnels statiques mais c'est ennuyeux pour un usage plus général sur l'Internet). Et il y a OpenVPN, qui est sans doute le plus répandu chez les utilisateurs ordinaires, pour sa simplicité de mise en œuvre. On trouve par exemple OpenVPN dans tous des systèmes comme OpenWrt et donc dans tous les routeurs qui l'utilisent.
Une fois cette liste de protocoles « intéressants » établie, notre RFC va se mettre à les classer, selon divers critères. Le premier (section 4 du RFC) : est-ce que le protocole dépend d'un transport sous-jacent, qui fournit des propriétés de fiabilité et d'ordre des données (si oui, ce transport sera en général TCP) ? C'est le cas de TLS, bien sûr, mais aussi, d'une certaine façon, de tcpcrypt (qui dépend de TCP et surtout de son option ENO, normalisée dans le RFC 8547). Tous les autres protocoles peuvent fonctionner directement sur IP mais, en général, ils s'appuient plutôt sur UDP, pour réussir à passer les différentes middleboxes qui bloquent les protocoles de transport « alternatifs ».
Et l'interface avec les applications ? Comment ces différents protocoles se présentent-ils aux applications qui les utilisent ? Il y a ici beaucoup à dire (le RFC fournit un tableau synthétique de quelles possibilités et quels choix chaque protocole fournit aux applications). L'analyse est découpée en plusieurs parties (les services liés à l'établissement de la connexion, ceux accessibles pendant la session et ceux liés à la terminaison de la connexion), chaque partie listant plusieurs services. Pour chaque service, le RFC dresse la liste des différents protocoles qui le fournissent. Ainsi, la partie sur l'établissement de connexion indique entre autres le service « Extensions au protocole de base accessibles à l'application ». Ce service est fourni par TLS (via ALPN, RFC 7301) ou QUIC mais pas par IPsec ou CurveCP. De même le service « Délégation de l'authentification » peut être fourni par IPsec (par exemple via EAP, RFC 3748) ou tcpcrypt mais pas par les autres.
La section 7 rappelle que ce RFC ne fait qu'analyser les protocoles existants, il ne propose pas de changement. D'autre part, cette section note que certaines attaques ont été laissées de côté (comme celle par canal secondaire ou comme l'analyse de trafic).
La section 8 sur la vie privée rappelle ces faiblesses ; même si l'un des buts principaux du chiffrement est d'assurer la confidentialité, la plupart des protocoles présentés laissent encore fuiter une certaine quantité d'information, par exemple les certificats en clair de TLS (avant la version 1.3).
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : D. Franke (Akamai), D. Sibold, K. Teichel (PTB), M. Dansarie, R. Sundblad (Netnod)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ntp
Première rédaction de cet article le 1 octobre 2020
Ce nouveau RFC spécifie un mécanisme de sécurité pour le protocole de synchronisation d'horloges NTP. Une heure incorrecte peut empêcher, par exemple, de valider des certificats cryptographiques. Ou fausser les informations enregistrées dans les journaux. Ce mécanisme de sécurité NTS (Network Time Security) permettra de sécuriser le mode client-serveur de NTP, en utilisant TLS pour négocier les clés cryptographiques qui serviront à chiffrer le trafic NTP ultérieur..
La sécurité n'était pas jugée trop importante au début de NTP : après tout, l'heure qu'il est n'est pas un secret. Et il n'y avait pas d'application cruciale dépendant de l'heure. Aujourd'hui, les choses sont différentes. NTP est un protocole critique pour la sécurité de l'Internet. NTP a quand même un mécanisme de sécurité, fondé sur des clés secrètes partagées entre les deux pairs qui communiquent. Comme tout mécanisme à clé partagée, il ne passe pas à l'échelle, d'où le développement du mécanisme « Autokey » dans le RFC 5906. Ce mécanisme n'a pas été un grand succès et un RFC a été écrit pour préciser le cahier des charges d'une meilleure solution, le RFC 7384. La sécurité de l'Internet repose de plus en plus que l'exactitude de l'horloge de la machine. Plusieurs systèmes de sécurité, comme DNSSEC ou X.509 doivent vérifier des dates et doivent donc pouvoir se fier à l'horloge de la machine. Même criticité de l'horloge quand on veut coordonner une smart grid, ou tout autre processus industriel, analyser des journaux après une attaque, assurer la traçabilité d'opérations financières soumises à régulation, etc. Or, comme l'explique bien le RFC 7384, les protocoles de synchronisation d'horloge existants, comme NTP sont peu ou pas sécurisés. (On consultera également avec profit le RFC 8633 qui parle entre autre de sécurité.)
Sécuriser la synchronisation d'horloge est donc crucial aujourd'hui. Cela peut se faire par un protocole extérieur au service de synchronisation, comme IPsec, ou bien par des mesures dans le service de synchronisation. Les deux services les plus populaires sont NTP (RFC 5905) et PTP. Aucun des deux n'offre actuellement toutes les fonctions de sécurité souhaitées par le RFC 7384. Mais les protocoles externes ont un inconvénient : ils peuvent affecter le service de synchronisation. Par exemple, si l'envoi d'un paquet NTP est retardé car IKE cherche des clés, les mesures temporelles vont être faussées. Le paquet arrivera intact mais n'aura pas la même utilité.
Les menaces contre les protocoles de synchronisation d'horloge sont décrites dans le RFC 7384. Les objectifs de notre RFC sont d'y répondre, notamment :
Rappelons que NTP dispose de plusieurs modes de fonctionnement
(RFC 5905, section 3). Le plus connu et sans
doute le plus utilisé est le mode client-serveur (si vous faites sur
Unix un ntpdate
ntp.nic.fr
, c'est ce mode que vous utilisez). Mais il y
aussi un mode symétrique, un mode à diffusion et le « mode » de contrôle
(dont beaucoup de fonctions ne sont pas normalisées). Le mode
symétrique est sans doute celui qui a le plus d'exigences de
sécurité (les modes symétriques et de contrôle de NTP nécessitent
notamment une protection réciproque contre le
rejeu, qui nécessite de maintenir un état de
chaque côté) mais le mode client-serveur est plus répandu, et pose
des difficultés particulières (un serveur peut avoir beaucoup de
clients, et ne pas souhaiter maintenir un état par client). Notre
RFC ne couvre que le mode client-serveur. Pour ce mode, la solution
décrite dans notre RFC 8915 est d'utiliser TLS afin que
le client authentifie le serveur, puis d'utiliser cette session TLS
pour générer la clé qui servira à sécuriser du NTP classique, sur
UDP. Les serveurs
NTP devront donc désormais également écouter en TCP, ce qu'ils ne
font pas en général pas actuellement. La partie TLS de NTS
(Network Time Security, normalisé dans ce RFC) se
nomme NTS-KE (pour Network Time Security - Key
Exchange). La solution à deux protocoles peut sembler
compliquée mais elle permet d'avoir les avantages de TLS (protocole
éprouvé) et d'UDP (protocole rapide).
Une fois qu'on a la clé (et d'autres informations utiles, dont les cookies), on ferme la session TLS, et on va utiliser la cryptographie pour sécuriser les paquets. Les clés sont dérivées à partir de la session TLS, suivant l'algorithme du RFC 5705. Quatre nouveaux champs NTP sont utilisés pour les paquets suivants (ils sont présentés en section 5), non TLS. Dans ces paquets se trouve un cookie qui va permettre au serveur de vérifier le client, et de récupérer la bonne clé. Les cookies envoyés du client vers le serveur et en sens inverse sont changés à chaque fois, pour éviter toute traçabilité, car ils ne sont pas dans la partie chiffrée du paquet. Notez que le format des cookies n'est pas spécifié par ce RFC (bien qu'un format soit suggéré en section 6). Pour le client, le cookie est opaque : on le traite comme une chaîne de bits, sans structure interne.
On a dit que NTS (Network Time Security) utilisait TLS. En fait, il se restreint à un profil spécifique de TLS, décrit en section 3. Comme NTS est un protocole récent, il n'y a pas besoin d'interagir avec les vieilles versions de TLS et notre RFC impose donc TLS 1.3 (RFC 8446) au minimum, ALPN (RFC 7301), les bonnes pratiques TLS du RFC 7525, et les règles des RFC 5280 et RFC 6125 pour l'authentification du serveur.
Passons ensuite au détail du protocole NTS-KE (Network
Time Security - Key Establishment) qui va permettre, en
utilisant TLS, d'obtenir les clés qui serviront au
chiffrement symétrique ultérieur. Il est
spécifié dans la section 4 du RFC. Le serveur doit écouter sur le
port ntske
(4460 par défaut). Le client utilise
ALPN (RFC 7301) pour
annoncer qu'il veut l'application ntske/1
. On établit la session TLS, on échange des
messages et on raccroche (l'idée est
de ne pas obliger le serveur à garder une session TLS ouverte pour
chacun de ses clients, qui peuvent être nombreux).
Le format de ces messages est composé d'un champ indiquant le type du message, de la longueur du message, des données du message, et d'un bit indiquant la criticité du type de message. Si ce bit est à 1 et que le récepteur ne connait pas ce type, il raccroche. Les types de message actuels sont notamment :
D'autres types de message pourront venir dans le futur, cf. le registre.
Une fois la négociation faite avec le protocole NTS-KE, tournant sur TLS, on peut passer au NTP normal, avec le serveur indiqué. Comme indiqué plus haut, quatre champs supplémentaires ont été ajoutés à NTP pour gérer la sécurité. Ils sont présentés dans la section 5 de notre RFC. Les clés cryptographiques sont obtenues par la fonction de dérivation (HKDF) du RFC 8446, section 7.5 (voir aussi le RFC 5705). Les clés du client vers le serveur sont différentes de celles du serveur vers le client (ce sont les clés c2s et s2c dans l'exemple avec ntsclient montré plus loin).
Les paquets NTP échangés ensuite, et sécurisés avec NTS, comporteront l'en-tête NTP classique (RFC 5905, section 7.3), qui ne sera pas chiffré (mais sera authentifié), et le paquet original chiffré dans un champ d'extension (cf. RFC 5905, section 7.5, et RFC 7822). Les quatre nouveaux champs seront :
On a vu que le format des cookies n'était pas imposé. Cela n'affecte pas l'interopérabilité puisque, de toute façon, le client n'est pas censé comprendre les cookies qu'il reçoit, il doit juste les renvoyer tels quels. La section 6 décrit quand même un format suggéré. Elle rappelle que les cookies de NTS sont utilisés à peu près comme les cookies de session de TLS (RFC 5077). Comme le serveur doit pouvoir reconnaitre les bons cookies des mauvais, et comme il est préférable qu'il ne conserve pas un état différent par client, le format suggéré permet au serveur de fabriquer des cookies qu'il pourra reconnaitre, sans qu'un attaquant n'arrive à en fabriquer. Le serveur part pour cela d'une clé secrète, changée de temps en temps. Pour le cas un peu plus compliqué où un ou plusieurs serveurs NTP assureraient le service avec des cookies distribués par un serveur NTS-KE séparé, le RFC recommande que les clés suivantes soient dérivées de la première, par un cliquet et la fonction de dérivation HKDF du RFC 5869.
Quelles sont les mises en œuvre de NTS à l'heure actuelle ? La
principale est dans chrony (mais dans le
dépôt git seulement, la version 3.5 n'a pas
encore NTS). chrony est
écrit en C et comprend un
client et un serveur. NTS est compilé par défaut (cela peut être
débrayé avec l'option --disable-nts
du script
./configure
), si et seulement si les
bibliothèques sur lesquelles s'appuie chrony (comme la bibliothèque
cryptographique nettle) ont tout ce qu'il
faut. Ainsi, sur une Ubuntu stable,
./configure
n'active pas l'option NTS alors que
ça marche sur une Debian instable (sur cette
plate-forme, pensez à installer les paquetages
bison
et asciidoctor
,
./configure
ne vérifie pas leur présence). Cela
se voit dans cette ligne émise par ./configure
(le +NTS
) :
% ./configure ... Checking for SIV in nettle : No Checking for SIV in gnutls : Yes Features : +CMDMON +NTP +REFCLOCK +RTC -PRIVDROP -SCFILTER -SIGND +ASYNCDNS +NTS +READLINE +SECHASH +IPV6 -DEBUG
Autre serveur ayant NTS, NTPsec. (Développement sur Github.) Écrit en
C. C'est ce code qui est
utilisé pour deux serveurs NTS publics,
ntp1.glypnod.com:123
et
ntp2.glypnod.com:123
(exemples plus loin).
Il y a également une
mise en œuvre de NTS en FPGA (sur Github). La même organisation gère deux
serveurs NTP publics, nts.sth1.ntp.se:4460
et
nts.sth2.ntp.se:4460
. Elle a publié une
bonne synthèse de ses travaux et un article
plus détaillé.
Dernier serveur public ayant NTS, celui de
Cloudflare, time.cloudflare.com
. Il
utilise sans doute nts-rust, écrit par
Cloudflare (en Rust).
Les autres mises en œuvre de NTS semblent assez expérimentales, plutôt de la preuve de concept pas très maintenue. Il y a :
https://gitlab.com/MLanger/nts/
, écrit en
C++.https://github.com/Netnod/nts-poc-python
, écrit
en Python.https://gitlab.com/hacklunch/ntsclient
, écrit
en Go, mais client
seulement, voir l'exemple plus loin.Voici un exemple avec ntsclient et les serveurs publics mentionnés plus haut. Vérifiez que vous avez un compilateur Go puis :
% git clone https://gitlab.com/hacklunch/ntsclient.git % make % ./ntsclient --server=ntp1.glypnod.com:123 -n Network time on ntp1.glypnod.com:123 2020-07-14 09:01:08.684729607 +0000 UTC. Local clock off by -73.844479ms.
(Le serveur devrait migrer bientôt vers le port 4460.)
Si on veut voir plus de détails et toute la machinerie NTS (le
type
de message 4 est AEAD, le type 5 le
cookie, l'algorithme
15 est AEAD_AES_SIV_CMAC_256
) :
% ./ntsclient --server=ntp1.glypnod.com:123 -n --debug Conf: &main.Config{Server:"ntp1.glypnod.com:123", CACert:"", Interval:1000} Connecting to KE server ntp1.glypnod.com:123 Record type 1 Critical set Record type 4 Record type 5 Record type 5 Record type 5 Record type 5 Record type 5 Record type 5 Record type 5 Record type 5 Record type 0 Critical set NTSKE exchange yielded: c2s: ece2b86a7e86611e6431313b1e45b02a8665f732ad9813087f7fc773bd7f2ff9 s2c: 818effb93856caaf17e296656a900a9b17229e2f79e69f43f9834d3c08194c06 server: ntp1.glypnod.com port: 123 algo: 15 8 cookies: [puis les cookies]
Notez que les messages de type 5 ont été répétés huit fois, car, conformément aux recommandations du RFC, le serveur envoie huit cookies. Notez aussi que si vous voulez analyser avec Wireshark, il va par défaut interpréter ce trafic sur le port 123 comme étant du NTP et donc afficher n'importe quoi. Il faut lui dire explicitement de l'interpréter comme du TLS (Decode as...). On voit le trafic NTS-KE en TCP puis du trafic NTP plus classique en UDP.
Enfin, la section 8 du RFC détaille quelques points de sécurité qui avaient pu être traités un peu rapidement auparavant. D'abord, le risque de dDoS. NTS, décrit dans notre RFC, apporte une nouveauté dans le monde NTP, la cryptographie asymétrique. Nécessaire pour l'authentification du serveur, elle est bien plus lente que la symétrique et, donc, potentiellement, un botnet pourrait écrouler le serveur sous la charge, en le forçant à faire beaucoup d'opérations de cryptographie asymétrique. Pour se protéger, NTS sépare conceptuellement l'authentification (faite par NTS-KE) et le service NTP à proprement parler. Ils peuvent même être assurés par des serveurs différents, limitant ainsi le risque qu'une attaque ne perturbe le service NTP.
Lorsqu'on parle de NTP et d'attaques par déni de service, on pense aussi à l'utilisation de NTP dans les attaques par réflexion et amplification. Notez qu'elles utilisent en général des fonctions non-standard des serveurs NTP. Le protocole lui-même n'a pas forcément de défauts. En tout cas, NTS a été conçu pour ne pas ajouter de nouvelles possibilités d'amplification. Tous les champs envoyés par le serveur ont une taille qui est au maximum celle des champs correspondants dans la requête du client. C'est par exemple pour cela que le client doit envoyer un champ de demande de cookie, rempli avec des zéros, pour chaque cookie supplémentaire qu'il réclame. Cela peut sembler inutile, mais c'est pour décourager toute tentative d'amplification.
Cette section 8 discute également la vérification du
certificat du serveur. Bon, on suit les
règles des RFC 5280 et RFC 6125, d'accord. Mais cela laisse un problème amusant : la
date d'expiration du certificat. Regardons celui des serveurs
publics cités plus haut, avec gnutls-cli
:
% gnutls-cli ntp1.glypnod.com:123 Processed 126 CA certificate(s). Resolving 'ntp1.glypnod.com:123'... Connecting to '104.131.155.175:123'... - Certificate type: X.509 - Got a certificate list of 2 certificates. - Certificate[0] info: - subject `CN=ntp1.glypnod.com', issuer `CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US', serial 0x04f305691067ef030d19eb53bbb392588d07, RSA key 2048 bits, signed using RSA-SHA256, activated `2020-05-20 02:12:34 UTC', expires `2020-08-18 02:12:34 UTC', pin-sha256="lLj5QsLH8M8PjLSWe6SNlXv4fxVAyI6Uep99RWskvOU=" Public Key ID: sha1:726426063ea3c388ebcc23f913b41a15d4ef38b0 sha256:94b8f942c2c7f0cf0f8cb4967ba48d957bf87f1540c88e947a9f7d456b24bce5 Public Key PIN: pin-sha256:lLj5QsLH8M8PjLSWe6SNlXv4fxVAyI6Uep99RWskvOU= - Certificate[1] info: - subject `CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US', issuer `CN=DST Root CA X3,O=Digital Signature Trust Co.', serial 0x0a0141420000015385736a0b85eca708, RSA key 2048 bits, signed using RSA-SHA256, activated `2016-03-17 16:40:46 UTC', expires `2021-03-17 16:40:46 UTC', pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=" - Status: The certificate is trusted. - Description: (TLS1.3-X.509)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM) - Options: - Handshake was completed
On a un classique certificat Let's Encrypt, qui expire le 18 août 2020. Cette date figure dans les certificats pour éviter qu'un malveillant qui aurait mis la main sur la clé privée correspondant à un certificat puisse profiter de son forfait éternellement. Même si la révocation du certificat ne marche pas, le malveillant n'aura qu'un temps limité pour utiliser le certificat. Sauf que la vérification que cette date d'expiration n'est pas atteinte dépend de l'horloge de la machine. Et que le but de NTP est justement de mettre cette horloge à l'heure… On a donc un problème d'œuf et de poule : faire du NTP sécurisé nécessite NTS, or utiliser NTS nécessite de vérifier un certificat, mais vérifier un certificat nécessite une horloge à l'heure, donc que NTP fonctionne. Il n'y a pas de solution idéale à ce problème. Notre RFC suggère quelques trucs utiles, comme :
ntp_adjtime
, le résultat de
cette fonction (dans ce cas, une valeur autre que
TIME_ERROR
) permet de savoir si l'horloge est
correcte,Comme toujours avec la cryptographie, lorsqu'il existe une version non sécurisée d'un protocole (ici, le NTP traditionnel), il y a le risque d'une attaque par repli, qui consiste à faire croire à une des deux parties que l'autre partie ne sait pas faire de la sécurité (ici, du NTS). Ce NTP stripping est possible si, par exemple, le client NTP se rabat en NTP classique en cas de problème de connexion au serveur NTS-KE. Le RFC recommande donc que le repli ne se fasse pas par défaut, mais uniquement si le logiciel a été explicitement configuré pour prendre ce risque.
Enfin, si on veut faire de la synchronisation d'horloges
sécurisée, un des problèmes les plus difficiles est l'attaque par
retard (section 11.5 du RFC). Le MAC empêche un attaquant actif de
modifier les messages mais il peut les retarder, donnant ainsi au
client une fausse idée de l'heure qu'il est réellement (RFC 7384, section 3.2.6 et Mizrahi, T.,
« A game theoretic analysis of delay attacks against time
synchronization protocols », dans les Proceedings
of Precision Clock Synchronization for Measurement Control and
Communication à ISPCS 2012). Ce dernier article n'est pas
en ligne mais, heureusement, il y a Sci-Hub
(DOI 10.1109/ISPCS.2012.6336612
).
Les contre-mesures possibles ? Utiliser plusieurs serveurs, ce que fait déjà NTP. Faire attention à ce qu'ils soient joignables par des chemins différents (l'attaquant devra alors contrôler plusieurs chemins). Tout chiffrer avec IPsec, ce qui n'empêche pas l'attaque mais rend plus difficile l'identification des seuls paquets de synchronisation.
Revenons au mode symétrique de NTP, qui n'est pas traité par notre RFC. Vous vous demandez peut-être comment le sécuriser. Lors des discussions à l'IETF sur NTS, il avait été envisagé d'encapsuler tous les paquets dans DTLS mais cette option n'a finalement pas été retenue. Donc, pour le symétrique, la méthode recommandée est d'utiliser le MAC des RFC 5905 (section 7.3) et RFC 8573. Et le mode à diffusion ? Il n'y a pas pour l'instant de solution à ce difficile problème.
Et la vie privée (section 9) ? Il y a d'abord la question de la non-traçabilité. On ne veut pas qu'un observateur puisse savoir que deux requêtes NTP sont dues au même client. D'où l'idée d'envoyer plusieurs cookies (et de les renouveler), puisque, autrement, ces identificateurs envoyés en clair trahiraient le client, même s'il a changé d'adresse IP.
Et la confidentialité ? Le chiffrement de l'essentiel du paquet NTP fournit ce service, les en-têtes qui restent en clair, comme le cookie, ne posent pas de problèmes de confidentialité particuliers.
Un petit mot maintenant sur l'historique de ce RFC. Le projet
initial était plus ambitieux
(cf. l'Internet-Draft
draft-ietf-ntp-network-time-security
). Il
s'agissait de développer un mécanisme abstrait, commun à NTP et
PTP/IEEE 1588 (alors que ce RFC 8915 est spécifique à NTP). Un autre groupe de travail
IETF TICTOC continue
de son côté mais ne fait plus de sécurité.
Si vous voulez des bonnes lectures sur NTS, autres que le RFC, il y a un article sur la sécurité NTP à la conférence ISPCS 17, « New Security Mechanisms for Network Time Synchronization Protocols ») (un des auteurs de l'article est un des auteurs du RFC). C'est un exposé général des techniques de sécurité donc il inclut PTP mais la partie NTP est presque à jour, à part la suggestion de DTLS pour les autres modes, qui n'a finalement pas été retenue). Les supports du même article à la conférence sont sur Slideshare. Et cet article est résumé dans l'IETF journal de novembre 2017, volume 13 issue 2. Il y a aussi l'article de l'ISOC, qui résume bien les enjeux.
Date de publication du RFC : Octobre 2020
Auteur(s) du RFC : W. Kumari (Google), E. Hunt
(ISC), R. Arends (ICANN), W. Hardaker
(USC/ISI), D. Lawrence (Oracle +
Dyn)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 24 octobre 2020
Un problème classique du DNS est qu'il n'y a pas assez de choix pour le
code de retour renvoyé par un serveur DNS. Contrairement à la richesse des codes de retour HTTP, le
DNS est très limité. Ainsi, le code de retour
SERVFAIL
(SERver FAILure)
sert à peu près à tout, et indique des erreurs très différentes
entre elles. D'où ce nouveau RFC qui normalise un mécanisme permettant des
codes d'erreur étendus, les EDE, ce qui facilitera le diagnostic des
problèmes DNS.
Ces codes de retour DNS (RCODE, pour Response
CODE) sont décrits dans le RFC 1035,
section 4.1.1. Voici un exemple de requête faite avec dig. Le code de
retour est indiqué par l'étiquette status:. Ici,
c'est REFUSED
, indiquant que le serveur faisant autorité pour
google.com
ne veut pas répondre aux requêtes
pour mon .org
:
% dig @ns1.google.com AAAA www.bortzmeyer.org ; <<>> DiG 9.11.3-1ubuntu1.12-Ubuntu <<>> @ns1.google.com AAAA www.bortzmeyer.org ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 4181 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 512 ;; QUESTION SECTION: ;www.bortzmeyer.org. IN AAAA ;; Query time: 7 msec ;; SERVER: 2001:4860:4802:32::a#53(2001:4860:4802:32::a) ;; WHEN: Sat Jun 20 11:07:13 CEST 2020 ;; MSG SIZE rcvd: 47
Codés sur seulement quatre bits, ces codes de retour ne peuvent
pas être très nombreux. Le RFC 1035 en
normalisait six, et quelques
autres ont été ajoutés par la suite, mais sont rarement vus
sur le terrain. En pratique, les plus fréquents sont
NOERROR
(pas de problème),
NXDOMAIN
(ce nom de domaine n'existe pas) et
SERVFAIL
(la plupart des autres erreurs). Notez
que le RFC 1034 n'utilisait pas ces sigles à
l'origine ils ont été introduits après.
On l'a dit, ces codes de retour sont insuffisants. Lorsqu'un
client DNS reçoit SERVFAIL
, il doit essayer de
deviner ce que cela voulait dire. Lorsqu'on parle à un résolveur, le
SERVFAIL
indique-t-il un problème de validation
DNSSEC ? Ou bien que les serveurs faisant autorité sont
injoignables ? Lorsqu'on parle à un serveur faisant
autorité, le SERVFAIL
indiquait-il que
ce serveur n'avait pas pu charger sa zone (peut-être parce que le
serveur maître était injoignable) ? Ou bien un autre problème ?
L'un des scénarios de débogage les plus communs est celui de
DNSSEC. Au moins, il existe un truc simple :
refaire la requête avec le bit CD
(Checking Disabled). Si ça marche
(NOERROR
), alors on est raisonnablement sûr que
le problème était un problème DNSSEC. Mais cela ne nous dit pas
lequel (signatures expirées ? clés ne correspondant pas à la
délégation ?). DNSSEC est donc un des principaux demandeurs d'erreurs
plus détaillées.
Alors, après cette introduction (section 1 du RFC), la solution
(section 2). Les erreurs étendues (EDE, pour Extended DNS Errors) sont transportées via une option
EDNS (RFC 6891) dans la
réponse. Cette option comporte un type (numéro 15)
et une longueur (comme toutes les options EDNS) suivis d'un code
d'information (sur deux octets) et de texte libre, non structuré (en
UTF-8, cf. RFC 5198, et
attention à ne pas le faire trop long, pour éviter la troncation
DNS). Les codes d'information possibles sont dans un
registre à l'IANA. En ajouter est simple, par la procédure
« Premier Arrivé, Premier Servi » du RFC 8126.
Cette option EDNS peut être envoyée par le serveur DNS, quel que
soit le code de retour (y compris donc
NOERROR
). Il peut y avoir plusieurs de ces options. La seule condition
est que la requête indiquait qu'EDNS est accepté par le client. Le
client n'a pas à demander explicitement des erreurs étendues et ne
peut pas forcer l'envoi d'une erreur étendue, cela dépend uniquement
du serveur.
Les codes d'information initiaux figurent en section 4 du RFC. Le RFC ne précise pas avec quels codes de retour les utiliser (ce fut une grosse discussion à l'IETF…) mais, évidemment, certains codes d'erreur étendus n'ont de sens qu'avec certains codes de retour, je les indiquerai ici. Voici quelques codes d'erreur étendus :
NOERROR
pour expliquer
pourquoi on n'a pas validé (en DNSSEC, un domaine signé avec des
algorithmes inconnus est considéré comme non signé).NXDOMAIN
) indique une
réponse rassise (RFC 8767). Voir aussi le
22, plus loin.NOERROR
puisqu'il y a une
réponse.SERVFAIL
pour indiquer
que la validation DNSSEC a déterminé un problème (signature
invalide ou absente). Plusieurs autres codes traitent des
problèmes DNSSEC plus spécifiques.SERVFAIL
pour le
cas des signatures expirées (sans doute un des problèmes DNSSEC les plus
fréquents).REFUSED
et
indique au client qu'il n'est pas le bienvenu (par exemple une
requête à un résolveur venue de l'extérieur du réseau
local).SERVFAIL
. J'en profite
pour vous rappeler que, si vous gérez une zone DNS, attention à sa
robustesse : évitez les SPOF. Ayez plusieurs serveurs faisant autorité, et dans des lieux
séparés (et de préférence dans des AS distincts).Voici un exemple où un résolveur se présentant comme « pour les
familles » ment sur la réponse à une requête pour
pornhub.com
et ajoute l'EDE 17, puisque c'est
l'utilisateur qui, en choisissant ce résolveur public, a demandé ce
filtrage :
% dig @1.1.1.3 pornhub.com ; <<>> DiG 9.18.28-0ubuntu0.24.04.1-Ubuntu <<>> @1.1.1.3 pornhub.com ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34585 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 1232 ; EDE: 17 (Filtered) ;; QUESTION SECTION: ;pornhub.com. IN A ;; ANSWER SECTION: pornhub.com. 60 IN A 0.0.0.0 ;; Query time: 9 msec ;; SERVER: 1.1.1.3#53(1.1.1.3) (UDP) ;; WHEN: Wed Sep 25 14:48:50 CEST 2024 ;; MSG SIZE rcvd: 62
Le client DNS qui reçoit un code d'erreur étendu est libre de l'utiliser comme il le souhaite (les précédentes versions de ce mécanisme prévoyaient des suggestions faites au client mais ce n'est plus le cas). Le but principal de cette technique est de fournir des informations utiles pour le diagnostic. (Une discussion à l'IETF avait porté sur des codes indiquant précisément si le problème était local - et devait donc être soumis à son administrateur système - ou distant, auquel cas il n'y avait plus qu'à pleurer, mais cela n'a pas été retenu. Notez que la différence entre les codes 15 et 16 vient de là, du souci d'indiquer à qui se plaindre.)
Attention à la section 6 du RFC, sur la sécurité. Elle note
d'abord que certains clients, quand ils reçoivent un
SERVFAIL
, essaient un autre résolveur (ce qui
n'est pas une bonne idée si le SERVFAIL
était
dû à un problème DNSSEC). Ensuite, les codes d'erreur étendus ne
sont pas authentifiés (comme, d'ailleurs, les
codes de retour DNS habituels), sauf si on utilise, pour parler au
serveur, un canal sécurisé comme DoT (RFC 7858) ou DoH (RFC 8484). Il ne faut
donc pas s'y fier trop aveuglément. Enfin, ces codes étendus vont
fuiter de l'information à un éventuel observateur (sauf si on
utilise un canal chiffré, comme avec DoT ou DoH) ; par exemple, 18
permet de savoir que vous n'êtes pas le bienvenu sur le serveur.
Et, désolé, je ne connais pas de mise en œuvre de cette option à l'heure actuelle. J'avais développé le code nécessaire pour Knot lors du hackathon de l'IETF à Prague en mars 2019 (le format a changé depuis donc ce code ne peut pas être utilisé tel quel). La principale difficulté n'était pas dans le formatage de la réponse, qui est trivial, mais dans le transport de l'information, depuis les entrailles du résolveur où le problème était détecté, jusqu'au code qui fabriquait la réponse. Il fallait de nouvelles structures de données, plus riches.
Sinon, le RFC mentionne le groupe Infected Mushroom. Si vous voulez voir leurs clips…
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : W. Kumari (Google), E. Kline (Loon)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF capport
Première rédaction de cet article le 22 septembre 2020
Les portails captifs sont une des plaies de l'accès à l'Internet. À défaut de pouvoir les supprimer, ce nouveau RFC propose des options aux protocoles DHCP et RA pour permettre de signaler la présence d'un portail captif, au lieu que la malheureuse machine ne doive le détecter elle-même. Il remplace le RFC 7710, le premier à traiter ce sujet. Il y a peu de changements de fond mais quand même deux modifications importantes. D'abord, il a fallu changer le code qui identifiait l'option DHCP en IPv4 (du code 160 au 114). Et ensuite l'URI annoncé sur le réseau est désormais celui de l'API et pas une page Web conçue pour un humain.
On trouve de tels portails captifs en de nombreux endroits où de l'accès Internet est fourni, par exemple dans des hôtels, des trains ou des cafés. Tant que l'utilisateur ne s'est pas authentifié auprès du portail captif, ses capacités d'accès à l'Internet sont très limitées. Quels sont les problèmes que pose un portail captif ?
Pourquoi est-ce que ces hôtels et cafés s'obstinent à imposer le passage par un portail captif ? On lit parfois que c'est pour authentifier l'utilisateur mais c'est faux. D'abord, certains portails captifs ne demandent pas d'authentification, juste une acceptation des conditions d'utilisation. Ensuite, il existe une bien meilleure solution pour authentifier, qui n'a pas les défauts indiqués plus haut. C'est 802.1X, utilisé entre autres dans eduroam (voir RFC 7593). La vraie raison est plutôt une combinaison d'ignorance (les autres possibilités comme 802.1X ne sont pas connues) et de désir de contrôle (« je veux qu'ils voient mes publicités et mes CGU »).
L'IETF travaille à développer un protocole complet d'interaction avec les portails captifs, pour limiter leurs conséquences. En attendant que ce travail soit complètement terminé, ce RFC propose une option qui permet au moins au réseau local de signaler « attention, un portail captif est là, ne lance pas de tâches - comme Windows Update - avant la visite du portail ». Cette option peut être annoncée par le serveur DHCP (RFC 2131 et RFC 8415) ou par le routeur qui envoie des RA (RFC 4861).
Cette option (section 2 du RFC) annonce au client qu'il est derrière un portail captif et lui fournit l'URI de l'API à laquelle accéder (ce qui évite d'être détourné, ce qui est grave quand on utilise HTTPS). Les interactions avec l'API de ce serveur sont spécifiées dans le RFC 8908.
Les sections 2.1, 2.2 et 2.3 de notre RFC donnent le format de l'option en DHCP v4, DHCP v6 et en RA. Le code DHCP v4 est 114 (un changement de notre RFC), le DHCP v6 est 103 et le type RA est 37. Pour la création d'options DHCPv6 avec des URI, voir le RFC 7227, section 5.7.
Un URI spécial,
urn:ietf:params:capport:unrestricted
est
utilisé pour le cas où il n'y a pas de portail
captif. (Cet URI spécial est enregistré
à l'IANA, avec les autres suffixes du RFC 3553.)
Bien que la première version de ce RFC date de plus de quatre
ans, la grande majorité des réseaux d'accès avec portail captif
n'annoncent toujours pas la présence de ce portail et il faudra
encore, pendant de nombreuses années, que les machines terminales
« sondent » et essaient de détecter par elles-même s'il y a un
portail captif ou pas. Elles font cela, par exemple, en essayant de
se connecter à une page Web connue (comme
avec Firefox). Ici, en cas de vrai
accès Internet :
http://detectportal.firefox.com/
% curl http://detectportal.firefox.com/ success
Et ici lorsqu'il y a un portail captif qui détourne le trafic :
% curl -v http://detectportal.firefox.com/ > GET / HTTP/1.1 > Host: detectportal.firefox.com > User-Agent: curl/7.58.0 > Accept: */* > < HTTP/1.1 302 Moved Temporarily < Server: nginx < Date: Mon, 27 Jul 2020 18:29:52 GMT ... < Location: https://wifi.free.fr/?url=http://detectportal.firefox.com/ < <html> <head><title>302 Found</title></head> <body bgcolor="white"> <center><h1>302 Found</h1></center> <hr><center>nginx</center> </body> </html>
La section 5 de notre RFC étudie les conséquences de cette option pour la sécurité. Elle rappelle que DHCP et RA ne sont pas sécurisés, de toute façon. Donc, un méchant peut envoyer une fausse option « il y a un portail captif, allez à cet URI pour vous authentifier » mais le même méchant peut aussi bien annoncer un faux routeur et ainsi recevoir tout le trafic... Si on n'utilise pas des précautions comme le RA Guard (RFC 6105) ou le DHCP shield (RFC 7610), on ne peut de toute façon pas se fier à ce qu'on a obtenu en DHCP ou RA.
Il est même possible que cette option nouvelle améliore la sécurité, en n'encourageant pas les utilisateurs à couper les mécanismes de validation comme la vérification des certificats, contrairement à ce que font les portails captifs actuels, qui se livrent à une véritable attaque de l'Homme du Milieu.
Pour DHCP, la plupart des serveurs permettent de servir une option quelconque, en mettant ses valeurs manuellement et une future mise à jour ne servira donc qu'à rendre l'usage de cette option plus simple. (Pour RA, c'est plus complexe, cf. l'expérience lors d'une réunion IETF.) Autrement, je ne connais pas encore de mise en œuvre côté clients DHCP ou RA, mais il semble (je n'ai pas testé personnellement) que ça marche sur iOS à partir de la version 14.
L'annexe B de notre RFC cite les changements depuis le RFC 7710. Les principaux :
urn:ietf:params:capport:unrestricted
pour le
cas où il n'y a pas de portail captif,Date de publication du RFC : Novembre 2020
Auteur(s) du RFC : G. Lozano (ICANN)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 14 novembre 2020
Prenons un registre, par exemple un registre de noms de domaine. Des tas de gens comptent sur lui. Si ce registre disparait en emportant sa base de données, ce serait une catastrophe. Il est donc nécessaire de garder les données à part, ce qu'on nomme un séquestre (escrow). Ce RFC décrit un format standard pour les données de séquestre, indépendant du type de registre (même si la demande prioritaire est pour les registres de noms de domaine). Il s'agit d'un format générique, qui sera complété par des spécifications pour les différents types de registre, par exemple le RFC 9022 pour les registres de noms de domaine.
L'idée (section 1 du RFC) est donc que le registre dépose à intervalles réguliers une copie de sa base de données auprès de l'opérateur de séquestre. Il ne s'agit pas d'une sauvegarde. La sauvegarde est conçue pour faire face à des accidents comme un incendie. Le séquestre, lui, est conçu pour faire face à une disparition complète du registre, par exemple une faillite si le registre est une entreprise commerciale. Ou bien une redélégation complète du domaine à un autre registre. Les sauvegardes, non standardisées et liées à un système d'information particulier, ne peuvent pas être utilisées dans ces cas. D'où l'importance d'un format standard pour un séquestre, précisément ce que normalise notre RFC. Lors d'une défaillance complète de l'ancien registre, l'opérateur de séquestre transmettra les données de séquestre au nouveau registre, qui pourra l'importer dans son système d'information, qui sera alors prêt à prendre le relais. Pour un registre de noms de domaine, le dépôt des données de séquestre devra comprendre la liste des noms, les titulaires et contacts pour chacun des noms, les serveurs de noms, les enregistrements DS (utilisés pour DNSSEC), etc. Par contre, certaines informations ne pourront pas être incluses, comme les parties privées des clés utilisées pour DNSSEC (qui sont peut-être enfermées dans un HSM) et le nouveau registre aura donc quand même un peu de travail de réparation.
Ainsi, la
convention entre l'État et l'AFNIC
pour la gestion du .fr
prévoit dans sa section 5 que « L'Office
d'enregistrement [sic] s'engage à mettre en place et à maintenir sur
le sol français un séquestre de données quotidien ». Même
chose pour les TLD sous contrat avec l'ICANN. Le Base
Registry Agreement impose un séquestre dans sa
spécification 2 « DATA ESCROW
REQUIREMENTS ». Elle dit « Deposit’s
Format. Registry objects, such as domains, contacts, name servers,
registrars, etc. will be compiled into a file constructed as
described in draft-arias-noguchi-registry-data-escrow, see Part A,
Section 9, reference 1 of this Specification and
draft-arias-noguchi-dnrd-objects-mapping, see Part A, Section 9,
reference 2 of this Specification (collectively, the “DNDE
Specification”). ». [Le document
draft-arias-noguchi-registry-data-escrow
est
devenu ce RFC 8909.] L'ICANN reçoit actuellement 1 200
séquestres, pour chaque TLD qu'elle régule, les dépôts ayant lieu
une fois par semaine.
Ces exigences sont tout à fait normales : la disparition d'un
registre de noms de domaine, s'il n'y avait pas de séquestre,
entrainerait la disparition de tous les noms, sans moyen pour les
titulaires de faire valoir leurs droits. Si
.org
disparaissait sans
séquestre,
bortzmeyer.org
n'existerait plus, et si un
registre prenait le relais avec une base de données vide, rien ne me
garantit d'obtenir ce nom, l'adresse de ce blog devrait donc
changer. Cette question de continuité de
l'activité de registre, même si des organisations disparaissent, est
la motivation pour ce RFC (section 3).
Un exemple de service de « registre de secours » (third-party beneficiary, dans la section 2 du RFC, on pourrait dire aussi backup registry) est l'EBERO (Emergency Back-End Registry Operator) de l'ICANN, décrit dans les « Registry Transition Processes ».
Ce RFC ne spécifie qu'un format de données. Il ne règle pas les questions politiques (faut-il faire un séquestre, à quel rythme, qui doit être l'opérateur de séquestre, qui va désigner un registre de secours, etc).
Passons donc à la technique (section 5). Le format est du
XML.
L'élément racine est <deposit>
(dépôt). Un attribut type
indique si ce dépôt
est complet (la totalité des données) ou bien incrémental
(différence avec le dépôt précédent). L'espace de
noms est
urn:ietf:params:xml:ns:rde-1.0
, enregistré
à l'IANA (voir la section 8). RDE signifie Registry
Data Escrow. Parmi les éléments obligatoires sous
<deposit>
, il y a :
<watermark>
qui indique le
moment où ce dépôt a été fait, au format du RFC 3339 (section 4). On peut traduire
watermark par jalon, point de synchronisation,
point d'étape ou marque.<rdeMenu>
, diverses métadonnées.<contents>
contient les données
(rappelez-vous que ce RFC est générique, le format exact des
données, qui dépend du type de registre, sera dans un autre
RFC).<deletes>
ne sert que pour les
dépôts incrémentaux, et indique les objets qui ont été détruits
depuis la dernière fois.<adds>
; dans un dépôt incrémental, les
éléments ajoutés sont sous <contents>
.Voici un exemple très partiel d'un dépôt complet :
<?xml version="1.0" encoding="UTF-8"?> <d:deposit xmlns:d="urn:ietf:params:xml:ns:rde-1.0" xmlns:myobj="urn:example:my-objects-1.0" type="FULL" id="20201006" resend="0"> <d:watermark>2020-10-06T13:12:15Z</d:watermark> <d:rdeMenu> <d:version>1.0</d:version> <d:objURI>urn:example:my-objects-1.0</d:objURI> </d:rdeMenu> <d:contents> <myobj:object> <myobj:value>42</myobj:value> </myobj:object> </d:contents> </d:deposit>
Dans cet exemple, le fichier XML contient un dépôt complet, fait le
6 octobre 2020, et dont le contenu est un seul élément, de valeur
42. (Pour un vrai dépôt lors d'un séquestre d'un registre de noms
de domaine, on aurait, au lieu de
<myobj:object>
, les domaines, leurs
titulaires, etc.) Vous avez des exemples un peu plus réalistes dans
les sections 11 à 13 du RFC.
Attention si vous utilisez des dépôts incrémentaux, l'ordre des
<contents>
et
<deletes>
compte : il faut mettre les
<deletes>
en premier.
La syntaxe complète est décrite dans la section 6 du RFC, en XML Schema. Du fait de l'utilisation de XML, l'internationalisation est automatique, on peut par exemple mettre les noms des contacts en Unicode (section 7).
Le format décrit dans ce RFC est seulement un format. Des tas de
questions subsistent si on veut un système de séquestre complet, il
faut par exemple spécifier un mécanisme de transport des données
entre le registre et l'opérateur de séquestre (par exemple avec
SSH, ou bien un
POST
HTTPS). Pour l'ICANN, c'est spécifié
dans l'Internet-Draft
draft-lozano-icann-registry-interfaces
.
Il faut également se préoccuper de tout ce qui concerne la sécurité. La section 9 du RFC rappelle que, si on veut que les données de séquestre soient confidentielles (pour un registre de noms de domaine, les coordonnées des titulaires et contacts sont certainement en bonne partie des données personnelles, cf. section 10), il faut chiffrer la communication entre le registre et l'opérateur de séquestre. Et il faut bien sûr tout authentifier. L'opérateur de séquestre doit vérifier que le dépôt vient bien du registre et n'est pas un dépôt injecté par un pirate, le registre doit vérifier qu'il envoie bien le dépôt à l'opérateur de séquestre et pas à un tiers. Comme exemple des techniques qui peuvent être utilisées pour atteindre ce but, l'ICANN cite OpenPGP (RFC 9580, désormais) : « Files processed for compression and encryption will be in the binary OpenPGP format as per OpenPGP Message Format - RFC 4880, see Part A, Section 9, reference 3 of this Specification. Acceptable algorithms for Public-key cryptography, Symmetric-key cryptography, Hash and Compression are those enumerated in RFC 4880, not marked as deprecated in OpenPGP IANA Registry, see Part A, Section 9, reference 4 of this Specification, that are also royalty-free. ».
Comme le séquestre ne servirait qu'en cas de terminaison complète du registre, on peut penser que le registre actuel n'est pas très motivé pour assurer ce service (et c'est pour cela qu'il doit être imposé). Il y a un risque de négligence (voire de malhonnêté) dans le production des dépôts. Voici pourquoi l'opérateur de séquestre doit tester que les dépôts qu'il reçoit sont corrects. Au minimum, il doit vérifier leur syntaxe. Ici, on va se servir de xmllint pour cela. D'abord, on utilise un schéma pour les données spécifiques de notre type de registre. Ici, il est très simple, uniquement des données bidon :
% cat myobj.xsd <?xml version="1.0" encoding="utf-8"?> <schema targetNamespace="urn:example:my-objects-1.0" xmlns:myobj="urn:example:my-objects-1.0" xmlns:rde="urn:ietf:params:xml:ns:rde-1.0" xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <annotation> <documentation> Test </documentation> </annotation> <element name="object" type="myobj:objectType" substitutionGroup="rde:content"/> <complexType name="objectType"> <complexContent> <extension base="rde:contentType"> <sequence> <element name="value" type="integer"/> </sequence> </extension> </complexContent> </complexType> </schema>
Ensuite, on écrit un petit schéma qui va importer les deux schémas, le nôtre (ci-dessus), spécifique à un type de registre, et le schéma du RFC, générique :
% cat wrapper.xsd <?xml version="1.0" encoding="utf-8"?> <schema targetNamespace="urn:example:tmp-1.0" xmlns="http://www.w3.org/2001/XMLSchema"> <import namespace="urn:ietf:params:xml:ns:rde-1.0" schemaLocation="rde.xsd"/> <import namespace="urn:example:my-objects-1.0" schemaLocation="myobj.xsd"/> </schema>
Ensuite, on produit le dépôt :
% cat test.xml <?xml version="1.0" encoding="UTF-8"?> <d:deposit xmlns:d="urn:ietf:params:xml:ns:rde-1.0" xmlns:myobj="urn:example:my-objects-1.0" type="FULL" id="20201006" resend="0"> <d:watermark>2020-10-06T13:12:15Z</d:watermark> <d:rdeMenu> <d:version>1.0</d:version> <d:objURI>urn:example:my-objects-1.0</d:objURI> </d:rdeMenu> <d:contents> <myobj:object> <myobj:value>42</myobj:value> </myobj:object> </d:contents> </d:deposit>
Et on n'a plus qu'à valider ce dépôt :
% xmllint --noout --schema wrapper.xsd test.xml test.xml validates
Si les données étaient incorrectes (dépôt mal fait), xmllint nous préviendrait :
% xmllint --noout --schema wrapper.xsd test.xml test.xml:12: element value: Schemas validity error : Element '{urn:example:my-objects-1.0}value': 'toto' is not a valid value of the atomic type 'xs:integer'. test.xml fails to validate
Idéalement, il faudrait même que l'opérateur de séquestre teste un chargement complet des données dans un autre logiciel de gestion de registre (oui, je sais, c'est beaucoup demander). Bon, s'il vérifie la syntaxe, c'est déjà ça.
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : T. Pauly (Apple), D. Thakore (CableLabs)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF capport
Première rédaction de cet article le 22 septembre 2020
Un des nombreux problèmes posés par les portails captifs est l'interaction avec le portail, par exemple pour accepter les CGU et continuer. Ces portails ne sont en général prévus que pour une interaction avec un humain. Ce RFC décrit une API ultra-simple qui permet à des programmes, au moins de savoir s'il y a un portail, quelles sont ses caractéristiques et comment sortir de captivité.
L'API suit les principes du RFC 8952. Elle permet donc de récupérer l'état de la captivité (est-ce que j'ai un accès à l'Internet ou pas), et l'URI de la page Web avec laquelle l'humain devra interagir.
Comment est-ce que la machine qui tente de se connecter a appris l'existence de l'API et son URI d'entrée ? Typiquement via les options de DHCP ou de RA décrites dans le RFC 8910. On accède ensuite à l'API avec HTTPS (RFC 2818). Il faudra naturellement authentifier le serveur, ce qui peut poser des problèmes tant qu'on n'a pas un accès à l'Internet complet (par exemple à OCSP, RFC 6960, et à NTP, RFC 5905, pour mettre l'horloge à l'heure et ainsi vérifier que le certificat n'a pas encore expiré). De même, des certificats intermédiaires qu'il faut récupérer sur l'Internet via Authority Information Access (AIA, section 5.2.7 du RFC 5280) peuvent poser des problèmes et il vaut mieux les éviter.
L'API elle-même est présentée en section 5 du RFC. Le contenu est
évidement en JSON (RFC 8259), et
servi avec le type application/captive+json
. Dans
les réponses du serveur, l'objet JSON a obligatoirement un membre
captive
dont la valeur est un booléen et qui
indique si on on est en captivité ou pas. Les autres membres
possibles sont :
user-portal-uri
indique une page Web
pour humains, avec laquelle l'utilisateur peut interagir,venue-info-url
est une page Web
d'information,can-extend-session
, un booléen qui
indique si on peut prolonger la session, ce qui veut dire que cela
peut être une bonne idée de ramener l'humain vers la page Web
user-portal-uri
lorsque la session va
expirer,seconds-remaining
, le nombre de
secondes restant pour cette session, après quoi il faudra se
reconnecter (si can-extend-session
est à
Vrai),bytes-remaining
, la même chose mais
pour des sessions limitées en quantité de données et plus en temps.Ces réponses de l'API peuvent contenir des données spécifiques au
client, et donc privées. Auquel cas, le serveur doit penser à
utiliser Cache-control: private
(RFC 9111) ou un mécanisme
équivalent, pour éviter que ces données se retrouvent dans des
caches.
Un exemple complet figure en section 6 du RFC. On suppose que le
client a découvert l'URL
https://example.org/captive-portal/api/X54PD39JV
via un des mécanismes du RFC 8910. Il
envoie alors une requête HTTP :
GET /captive-portal/api/X54PD39JV HTTP/1.1 Host: example.org Accept: application/captive+json
Et reçoit une réponse :
HTTP/1.1 200 OK Cache-Control: private Date: Mon, 02 Mar 2020 05:07:35 GMT Content-Type: application/captive+json { "captive": true, "user-portal-url": "https://example.org/portal.html" }
Il sait alors qu'il est en captivité, et que l'utilisateur doit
aller en https://example.org/portal.html
pour
accepter des CGU léonines, s'authentifier,
etc. Une fois que c'est fait, il peut continuer à faire des requêtes
à l'API et avoir, par exemple :
{ "captive": false, "user-portal-url": "https://example.org/portal.html", "venue-info-url": "https://flight.example.com/entertainment", "seconds-remaining": 326, "can-extend-session": true }
D'autres membres de l'objet JSON pourront apparaitre, selon la procédure « Spécification nécessaire » (RFC 8126), un registre IANA a été créé pour les stocker.
Un peu de sécurité pour finir (section 7 du RFC). Le protocole de notre RFC impose l'utilisation de HTTPS (donc de TLS) ce qui règle pas mal de problèmes, notamment de confidentialité et d'authentification. À noter donc (mais cela en vaut la peine) que cela complique un peu les choses pour l'administrateur du portail captif, qui ne peut pas se contenter de HTTP en clair, et qui doit avoir un certificat valide. (Cet argument de la complexité avait été mentionné lors des discussions à l'IETF, où certains trouvaient notre RFC trop exigeant.) Combien de fois ai-je vu des portails captifs avec un certificat auto-signé et/ou expiré !
Mais attention, TLS va seulement sécuriser le fait qu'on se connecte bien au serveur indiqué via les méthodes du RFC 8910. Or, comme ces méthodes ne sont pas elle-mêmes très sûres, la sécurité du portail captif ne doit jamais être surestimée.
Le protocole décrit dans le RFC 8910 et dans ce RFC a été testé lors de réunions IETF, sur le grand réseau de ces réunions. En dehors de cela, il n'y a pas encore de déploiement. Je crains que, vu la nullité technique de la plupart des points d'accès WiFi, vite installés, mal configurés et plus maintenus après, il ne faille attendre longtemps un déploiement significatif.
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : M. Andrews (ISC), R. Bellis (ISC)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 23 septembre 2020
Normalement, le protocole DNS est très simple : le client écrit au serveur, posant une question, et le serveur répond. Il peut répondre qu'il ne veut pas ou ne sait pas répondre, mais il le dit explicitement. Cela, c'est la théorie. En pratique, beaucoup de serveurs DNS bogués (ou, plus fréquemment, situés derrière une middlebox boguée) ne répondent pas du tout, laissant le client perplexe se demander s'il doit réeessayer différemment ou pas. Ce nouveau RFC documente le problème et ses conséquences. Il concerne surtout la question des serveurs faisant autorité.
(Cette absence de réponse est en général due, non pas aux serveurs DNS eux-même, qui sont la plupart du temps corrects de ce point de vue, mais plutôt à ces middleboxes codées avec les pieds et mal configurées, que certains managers s'obstinent à placer devant des serveurs DNS qui marcheraient parfaitement sans cela. Ainsi, on voit des pare-feux inutiles mis parce que « il faut un pare-feu, a dit l'auditeur » et qui bloquent tout ce qu'ils ne comprennent pas. Ainsi, par exemple, le pare-feu laissera passer les requêtes de type A mais pas celles de type NS, menant à une expiration du délai de garde. La section 4 de notre RFC détaille ces erreurs communes des middleboxes.)
Voici un exemple d'un service DNS bogué. Le domaine
mabanque.bnpparibas
est délégué à deux serveurs
mal configurés (ou placés derrière une middlebox
mal faite), sns6.bnpparibas.fr
et
sns5.bnpparibas.net
:
% dig @sns6.bnpparibas.fr A mabanque.bnpparibas ; <<>> DiG 9.11.5-P4-5.1-Debian <<>> @sns6.bnpparibas.fr A mabanque.bnpparibas ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57381 ;; flags: qr aa rd ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; WARNING: recursion requested but not available ;; QUESTION SECTION: ;mabanque.bnpparibas. IN A ;; ANSWER SECTION: mabanque.bnpparibas. 30 IN A 159.50.187.79 ;; Query time: 24 msec ;; SERVER: 159.50.105.65#53(159.50.105.65) ;; WHEN: Wed Jun 17 08:21:07 CEST 2020 ;; MSG SIZE rcvd: 53 % dig @sns6.bnpparibas.fr NS mabanque.bnpparibas ; <<>> DiG 9.11.5-P4-5.1-Debian <<>> @sns6.bnpparibas.fr NS mabanque.bnpparibas ; (1 server found) ;; global options: +cmd ;; connection timed out; no servers could be reached
Face à un service aussi mal fait, le client DNS est très désarmé. Est-ce que le serveur a ignoré la requête ? Est-ce que le paquet a été perdu (c'est l'Internet, rien n'est garanti) ? Dans le deuxième cas, il faut réessayer, dans le premier, cela ne servirait à rien, qu'à perdre du temps, et à en faire perdre à l'utilisateur. Le client peut aussi supposer que l'absence de réponse est due à telle ou telle nouveauté du protocole DNS qu'il a utilisé, et se dire qu'il faudrait réessayer sans cette nouveauté, voire ne jamais l'utiliser. On voit que ces services grossiers, qui ne répondent pas aux requêtes, imposent un coût conséquent au reste de l'Internet, en délais, en trafic réseau, et en hésitation à déployer les nouvelles normes techniques.
Il n'y a aucune raison valable pour une absence de réponse ?
Notre RFC en note
une : une attaque par déni de service en
cours. Dans ce cas, l'absence de réponse est légitime (et, de toute
façon, le serveur peut ne pas avoir le choix). En dehors d'une telle
attaque, le serveur doit répondre, en utilisant
un des codes de retour DNS existants, qui couvrent tous les cas
possibles (de NOERROR
, quand tout va bien, à
REFUSED
, le refus délibéré, en passant par
SERVFAIL
, l'impossibilité de produire une
réponse sensée). Ici, un cas où je demande à un serveur de
.fr
des informations sur
un .com
, qu'il n'a
évidemment pas, d'où le refus explicite (cf. le champ
status:) :
% dig @d.nic.fr A www.microsofthub.com ; <<>> DiG 9.11.5-P4-5.1-Debian <<>> @d.nic.fr A www.microsofthub.com ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 4212 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1432 ; COOKIE: 452269774eb7b7c574c779de5ee9b8e6efe0f777274b2b17 (good) ;; QUESTION SECTION: ;www.microsofthub.com. IN A ;; Query time: 4 msec ;; SERVER: 2001:678:c::1#53(2001:678:c::1) ;; WHEN: Wed Jun 17 08:32:06 CEST 2020 ;; MSG SIZE rcvd: 77
Le RFC note qu'il n'y a pas que l'absence de réponse, il y a aussi parfois des réponses incorrectes. Ainsi, certains serveurs (ou, là encore, la middlebox placée devant eux) copient le bit AD de la requête dans la réponse au lieu de déterminer par eux-mêmes si ce bit - qui signifie Authentic Data - doit être mis dans la réponse.
Donc, ne pas répondre, c'est mal, et égoïste. Mais quelles sont les conséquences exactes de cette absence de réponse ? Parmi elles :
Quels sont les cas où il est particulièrement fréquent qu'il n'y ait pas de réponse, ou bien une réponse erronée ? La section 3 en décrit un certain nombre (et le RFC vient avec un script qui utilise dig pour tester une grande partie de ces cas). Elle rappelle également les réponses correctes attendues. Par exemple, une requête de type SOA (Start Of Authority) à un serveur faisant autorité pour une zone doit renvoyer une réponse (contrairement aux serveurs de BNP Paribas cités plus haut, qui ne répondent pas). Même si le type de données demandé est inconnu du serveur, il doit répondre (probablement NOERROR s'il n'a tout simplement pas de données du type en question).
On voit parfois également des serveurs (ou plutôt des combinaisons serveur / middlebox boguée située devant le serveur) qui achoppent sur les requêtes utilisant des options (flags) DNS spécifiques. Ici, l'option Z fait que le serveur de la RATP ne répond plus :
% dig @193.104.162.15 +noedns +noad +norec +zflag soa ratp.fr ; <<>> DiG 9.11.5-P4-5.1-Debian <<>> @193.104.162.15 +noedns +noad +norec +zflag soa ratp.fr ; (1 server found) ;; global options: +cmd ;; connection timed out; no servers could be reached
Alors qu'il marche parfaitement sans cette option :
% dig @193.104.162.15 +noedns +noad +norec soa ratp.fr ; <<>> DiG 9.11.5-P4-5.1-Debian <<>> @193.104.162.15 +noedns +noad +norec soa ratp.fr ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30635 ;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 2 ;; QUESTION SECTION: ;ratp.fr. IN SOA ;; ANSWER SECTION: ratp.fr. 3600 IN SOA ns0.ratp.fr. hostmaster.ratp.fr. 2020051201 21600 3600 4204800 300 ... ;; ADDITIONAL SECTION: ns0.ratp.fr. 3600 IN A 193.104.162.15 ns1.ratp.fr. 3600 IN A 193.104.162.14 ;; Query time: 6 msec ;; SERVER: 193.104.162.15#53(193.104.162.15) ;; WHEN: Mon May 18 17:42:33 CEST 2020 ;; MSG SIZE rcvd: 213
Autre exemple, l'option AD (Authentic Data) dans la requête, qui indique que le client espère une validation DNSSEC, déclenche parfois des bogues, comme de ne pas répondre ou bien de répondre en copiant aveuglément le bit AD dans la réponse. La section 6 du RFC détaille un cas similaire, celui des serveurs qui réutilisent une réponse déjà mémorisée, mais pour des options différentes dans la requête.
Et les opérations (opcodes) inconnus ? Des
opérations comme NOTIFY
ou
UPDATE
n'existaient pas au début du DNS et
d'autres seront encore ajoutées dans le futur. Si le serveur ne
connait pas une opération, il doit répondre
NOTIMP
(Not
Implemented). Ici, avec l'opération 1
(IQUERY
, ancienne mais abandonnée par le RFC 3425) :
% dig @d.nic.fr +opcode=1 toto.fr ; <<>> DiG 9.11.5-P4-5.1+deb10u1-Debian <<>> @d.nic.fr +opcode=1 toto.fr ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: IQUERY, status: NOTIMP, id: 20180 ;; flags: qr; QUERY: 0, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1432 ; COOKIE: 71f9ced2f29076a65d34f3ed5ef2f718bab037034bb82106 (good) ;; Query time: 4 msec ;; SERVER: 2001:678:c::1#53(2001:678:c::1) ;; WHEN: Wed Jun 24 08:47:52 CEST 2020 ;; MSG SIZE rcvd: 51
Au contraire, voici un serveur qui répond incorrectement
(REFUSED
au lieu de
NOTIMP
) :
% dig @ns3-205.azure-dns.org. +noedns +noad +opcode=15 +norec microsoft.com ; <<>> DiG 9.11.5-P4-5.1+deb10u1-Debian <<>> @ns3-205.azure-dns.org. +noedns +noad +opcode=15 +norec microsoft.com ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: RESERVED15, status: REFUSED, id: 41518 ;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;microsoft.com. IN A ;; Query time: 22 msec ;; SERVER: 2a01:111:4000::cd#53(2a01:111:4000::cd) ;; WHEN: Wed Jun 24 09:16:19 CEST 2020 ;; MSG SIZE rcvd: 31
Soyons positifs : c'était bien pire il y a encore seulement cinq ou
dix ans, malgré des tests techniques obligatoires dans certains
registres comme
Zonecheck pour
.fr
. Des efforts comme
le DNS Flag
Day ont permis d'arranger sérieusement les choses.
Bien sûr, les serveurs DNS doivent accepter les requêtes venant sur TCP et pas seulement sur UDP. Cela a toujours été le cas, mais le RFC 7766 rend cette exigence encore plus stricte. Il y a fort longtemps, l'outil de test Zonecheck de l'AFNIC testait ce fonctionnement sur TCP, et avait attrapé beaucoup d'erreurs de configuration, suscitant parfois des incompréhensions de la part d'administrateurs système ignorants qui prétendaient que le DNS n'utilisait pas TCP.
Et il y a bien sûr EDNS (RFC 6891). Introduit après la norme originale du DNS, mais
quand même ancienne de plus de vingt ans, EDNS est toujours mal
compris par certains programmeurs, et bien des
middleboxes déconnent toujours sur des
particularités d'EDNS. Déjà, un serveur doit répondre aux requêtes
EDNS (même si lui-même ne connait pas EDNS, il doit au moins
répondre FORMERR
). Ensuite, s'il connait EDNS,
il doit gérer les numéros de version d'EDNS (attention, la version
actuelle est la 0, il n'y a pas encore eu de version 1 mais, pour
qu'elle puisse être déployée un jour, il ne faut pas que les
serveurs plantent sur les versions supérieures à zéro). EDNS permet
d'indiquer des options (la liste complète est dans un
registre IANA) et les options inconnues doivent être ignorées
(autrement, on ne pourrait jamais déployer une nouvelle option).
Pour tester si vos serveurs gèrent correctement tous ces cas, le
RFC (section 8) vient avec une série de commandes utilisant
dig, dont
il faudra analyser le résultat manuellement en suivant le RFC. J'en
ai fait un script de test, check-dns-respond-rfc8906.sh
. Si vous testez vos serveurs avec ce script, faites-le depuis
l'extérieur (pour intégrer dans le test les éventuelles
middleboxes, cf. section 4). Un exemple
d'exécution du test :
% ./check-dns-respond-rfc8906.sh cyberstructure.fr 2001:4b98:dc0:41:216:3eff:fe27:3d3f >& /tmp/mytest.txt
Et il vous reste à lire le fichier des résultats et à comparer avec les résultats attendus.
Date de publication du RFC : Octobre 2020
Auteur(s) du RFC : F. Dold (Taler Systems SA), C. Grothoff (BFH)
Pour information
Première rédaction de cet article le 26 octobre 2020
Le paiement de services ou de biens est un problème crucial sur
l'Internet. En l'absence de mécanisme simple, léger et respectant la
vie privée, on n'a aujourd'hui que des solutions centralisées dans
des gros monstres, comme Amazon pour vendre
des objets physiques, ou YouTube pour
monétiser ses vidéos. Ce RFC ne propose pas de solution magique mais il
spécifie au moins une syntaxe pour indiquer un mécanisme de
paiement : le plan d'URI payto:
, qui
permettra peut-être un jour de faciliter les paiements.
Le paysage d'aujourd'hui est le suivant. Si vous êtes un créateur (d'articles, de vidéos, de dessins, peu importe) et que vous voulez être payé pour vos créations (ce qui est tout à fait légitime), vous n'avez comme solutions que de faire appel à du financement participatif (style Liberapay ou Ulule) ou bien de passer par une grosse plate-forme comme YouTube, qui imposera ses règles, comme la captation de données personnelles. Sans compter le contrôle du contenu, qui fait qu'une vidéo parlant de sujets sensibles sera démonétisée. (Voyez par exemple cette vidéo de Charlie Danger où, à partir de 10:45 et surtout 11:45, elle explique comment YouTube n'a pas aimé sa vidéo sur l'IVG et ce qui en est résulté.) Mais cette solution d'hébergement sur un GAFA est sans doute la plus simple pour le créateur, et elle ne dépend pas de la bonne volonté des lecteurs/spectacteurs. Lorsqu'on discute avec un·e vidéaste de son hébergement chez Google et qu'on lui propose d'utiliser plutôt un service libre et décentralisé fait avec PeerTube, la réponse la plus fréquente est « mais je perdrais la monétisation, et je ne peux pas vivre seulement d'amour et d'eau fraîche ». Je l'ai dit, il n'existe pas encore de solution parfaite à ce problème. Pour un cas plus modeste, celui de ce blog, j'ai tenté Flattr et Bitcoin mais avec très peu de succès.
Ce nouveau RFC
ne propose pas une solution de paiement, juste un moyen de faire des
URI qu'on
pourra mettre dans ces pages Web pour pointer vers un mécanisme de
paiement. Par exemple,
va indiquer qu'il faut envoyer 10 milli-bitcoins à cette adresse (c'est la
mienne), avec un gentil message. Si votre navigateur
Web gère le plan d'URI payto://bitcoin/1HtNJ6ZFUc9yu9u2qAwB4tGdGwPQasQGax?amount=BITCOIN:0.01&message="Ce%20blog%20est%20super"
payto:
(ce
qu'aucun ne fait à l'heure actuelle), que vous cliquez puis
confirmez, je recevrai 0,01 bitcoin. (Notez qu'il existe une syntaxe
spécifique à Bitcoin, pour un paiement, décrite dans le BIP 0021, et qui
ressemble beaucoup à celle du RFC.)
Un peu de technique maintenant : les plans d'URI
(scheme) sont définis dans le RFC 3986, section 3.1. Vous connaissez certainement des plans
comme http:
ou mailto:
. Le
plan payto:
est désormais dans le
registre IANA des plans. Après le plan et les deux
barres se trouve l'autorité. Ici, elle
indique le type de paiement. Notre RFC en décrit certains (comme
bitcoin
montré dans l'exemple) et on pourra en
enregistrer d'autres. L'idée est de pouvoir présenter à
l'utilisateur un mécanisme uniforme de paiement, quel que soit le
type de paiement. (À l'heure actuelle, si vous acceptez les
paiements par Flattr et
PayPal, vous devez mettre deux boutons
différents sur votre site Web, et qui déclencheront deux expériences
utilisateur très différentes. Sans compter les traqueurs qu'il y a
probablement derrière le bouton de Paypal.)
Question interface utilisateur, le RFC recommande que le navigateur permette ensuite à l'utilisateur de choisir le compte à utiliser, s'il en a plusieurs et, bien sûr, lui demande clairement une confirmation claire. Pas question qu'un simple clic déclenche le paiement ! (Cf. section 8 du RFC, qui pointe entre autres le risque de clickjacking.) Notez aussi que le RFC met en garde contre le fait d'envoyer trop d'informations (par exemple l'émetteur) dans le paiement, ce qui serait au détriment de la vie privée.
On peut ajouter des options à l'URI (section 5 du RFC). Par
exemple la quantité à verser (amount
,
cf. l'exemple Bitcoin plus haut, le code de la monnaie, s'il a trois
lettres, devant être
conforme à ISO 4217, le
Bitcoin n'ayant pas de code officiel, j'ai utilisé un nom plus long),
receiver-name
et
sender-name
, message
(pour
le destinataire) et
instruction
, ce dernier servant à préciser le
traitement à faire par l'organisme de paiement.
Voici un autre exemple d'URI payto:
, après
Bitcoin, IBAN (ISO
20022) :
.
L'exemple est tiré du RFC. Je n'ai pas mis mon vrai numéro IBAN car
je ne suis pas expert en sécurité des IBAN et je ne sais pas s'il
n'y a pas des inconvénients à le rendre public. Un expert pour
confirmer ? À part ça, avec le type payto://iban/DE75512108001245126199?amount=EUR:200.0&message=hello
iban
,
l'option message
correspond à ce que
SEPA appelle unstructured remittance
information et instruction
au
end to end identifier.
Puisqu'il y a une option permettant d'envoyer un message, la section 6 du RFC note qu'il n'y a pas de vrai mécanisme d'internationalisation, entre autres parce qu'on ne peut pas garantir de manière générale comment ce sera traité par les établissements de paiement.
Outre Bitcoin et IBAN, déjà vus, notre RFC enregistre plusieurs
autres types de mécanismes de paiement. ACH, BIC/Swift et UPI appartiennent au
monde bancaire classique. Le type ilp
vient,
lui, du monde Internet, comme Bitcoin, est pour les paiements via
InterLedger, s'appuyant sur les adresses
InterLedger. Et il y a aussi un type
void
, qui indique que le paiement se fera en
dehors du Web, par exemple en liquide.
Cette liste n'est pas fermée, mais est stockée dans un registre évolutif, registre peuplé selon la politique « Premier arrivé, premier servi ». Vous noterez que ce registre n'est pas géré par l'IANA mais par GANA.
La section 7 indique les critères
d'enregistrement souhaitables d'un nouveau type (les enregistrements
existants peuvent servir d'exemple) : fournir des références
précises, trouver un nom descriptif, préférer un nom neutre à celui
d'un organisme particulier, etc. J'ai regardé ces critères pour le
cas de PayPal, mécanisme de paiement très
répandu sur l'Internet, mais ce n'était pas évident. Même si on
spécifie des URI du genre
payto://paypal/smith@example.com
, il ne serait
pas évident pour le navigateur de les traduire en une requête PayPal
(PayPal privilégie l'installation de son propre bouton actif, ou
bien le système PayPal.me). Bref, je n'ai pas continué mais si
quelqu'un de plus courageux veut enregistrer le type
paypal
, il n'est pas nécessaire d'être
représentant officiel de PayPal, et ce n'est pas trop
difficile. Idem pour d'autres systèmes de paiement par encore
spécifiés comme Liberapay. Notez que le
système conçu par les auteurs du RFC, Taler,
n'est pas encore enregistré non plus.
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : A. Vesely
Pour information
Première rédaction de cet article le 18 septembre 2020
Le RFC 8601 décrit un en-tête pour le
courrier qui
indique le résultat d'une tentative
d'authentification. Cet en-tête Authentication-Results:
permet
plusieurs méthodes d'authentification, telles que SPF ou DKIM. Notre nouveau
RFC 8904 ajoute une nouvelle méthode,
dnswl
(pour DNS White List), qui indique le
résultat d'une lecture dans une liste blanche, ou liste
d'autorisation via le DNS. Ainsi, si le client SMTP avait son
adresse IP dans cette liste, un en-tête Authentication-Results:
d'authentification
positive sera ajouté.
Accéder via le DNS à des listes blanches (autorisation) ou des
listes noires (rejet) de MTA est une pratique courante dans la gestion du
courrier. Elle
est décrite en détail dans le RFC 5782. Typiquement, le serveur SMTP qui voit une connexion entrante
forme un nom de domaine à partir de l'adresse IP du client SMTP et fait une requête DNS
pour ce nom et le type de données A (adresse IP). S'il obtient une
réponse non-nulle, c'est que l'adresse IP figurait dans la liste
(blanche ou noire). On peut aussi faire une requête de type TXT pour
avoir du texte d'information. Ensuite, c'est au serveur SMTP de
décider ce qu'il fait de l'information. La liste (noire ou blanche)
lui donne une information, c'est ensuite sa responsabilité de
décider s'il accepte ou rejette la connexion. La décision n'est pas
forcément binaire, le serveur peut décider d'utiliser cette
information comme entrée dans un algorithme de calcul de la
confiance (« il est listé dans bl.example
, 20
points en moins dans le calcul »).
Les plus connues des listes sont les listes noires (liste de
clients SMTP mauvais) mais il existe aussi des listes blanches
(liste de clients SMTP qu'on connait et à qui on fait confiance), et
elles font l'objet de ce RFC. Il crée une nouvelle méthode pour
l'en-tête Authentication-Results:
, permettant ainsi d'enregistrer, dans un message, le
fait qu'il ait été « authentifié » positivement via une liste
blanche. On peut après se servir de cette « authentification » pour,
par exemple, vérifier que le nom de
domaine annoncé dans l'enregistrement TXT correspondant
soit celui attendu (dans l'esprit de DMARC
- RFC 7489 - ce qui est d'ailleurs très
casse-gueule mais c'est une autre histoire).
Et le RFC fait un rappel utile : se servir d'une liste (noire ou blanche) gérée à l'extérieur, c'est sous-traiter. Cela peut être pratique mais cela peut aussi avoir des conséquences néfastes si la liste est mal gérée (comme le sont la plupart des listes noires, adeptes du « on tire d'abord et on négocie après »). Comme le dit le RFC, « vous épousez la politique du gérant de la liste ». Lisez aussi le RFC 6471, au sujet de la maintenance de ces listes.
La nouvelle méthode d'authentification
(dnswl
, section 2 de notre RFC) figure
désormais dans le
registre IANA des méthodes d'authentification, spécifié dans
le RFC 8601 (notamment section 2.7). Notre
RFC 8904 décrit également les propriétés (RFC 8601, section 2.3 et RFC 7410) associées à cette authentification.
La méthode dnswl
peut renvoyer
pass
(cf. le RFC 8601
pour ces valeurs renvoyées), qui indique que l'adresse IP du client
est dans la liste blanche interrogée, none
(client absent de la liste), ou bien une erreur (si la résolution
DNS échoue). Contrairement à d'autres méthodes, il n'y a pas de
résultat fail
, ce qui est logique pour une
liste blanche (qui liste les gentils et ne connait pas les
méchants). Les principales propriétés possibles sont :
dns.zone
: le nom de domaine de la
liste blanche,policy.ip
: l'adresse IP renvoyée par
la liste blanche, elle indique de manière codée les raisons pour
lesquelles cette adresse est dans la liste,policy.txt
: l'enregistrement TXT
optionnel, peut indiquer le nom de domaine associé
à ce client SMTP,dns.sec
: indique si la requête DNS a
été validée avec DNSSEC (yes
si c'est
le cas, no
si la zone n'est pas signée,
na
si le résolveur ne sait pas
valider).
Le type dns
pour les propriétés est une
nouveauté de ce RFC, désormais enregistrée
à l'IANA.
La section 3 de notre RFC décrit l'enregistrement de type TXT qui donne des explications sur la raison pour laquelle l'adresse IP est dans la liste blanche (cf. RFC 5782, sections 2.1 et 2.2). Par exemple, il permet d'indiquer le domaine concerné (ADMD, ADministrative Management Domain, cf. RFC 8601 pour ce concept).
Tiré de l'annexe A du RFC, voici un exemple d'un message qui a
reçu un Authentication-Results:
, qui contient les quatre propriétés indiquées plus haut :
Authentication-Results: mta.example.org; dnswl=pass dns.zone=list.dnswl.example dns.sec=na policy.ip=127.0.10.1 policy.txt="fwd.example https://dnswl.example/?d=fwd.example"
Il se lit ainsi : le MTA
mta.example.org
estime son client authentifié
par la méthode dnswl
(DNS White
List) de ce RFC. La liste blanche a renvoyé la valeur
127.0.10.1
(sa signification exacte dépend de
la liste blanche) et le TXT associé disait que le client SMTP
appartenait au domaine fwd.example
.
Dans l'exemple plus détaillé du RFC, le message avait été
retransmis d'une manière qui cassait SPF et n'aurait donc pas été accepté sans la
liste blanche, qui certifie que fwd.example
est
un retransmetteur connu et légitime.
Enfin, la section 5 du RFC traite de sécurité. Notamment, elle insiste sur le fait que le DNS n'est pas, par défaut, protégé contre diverses manipulations et qu'il est donc recommandé d'utiliser DNSSEC (ce que ne fait pas la liste blanche d'exemple citée plus loin).
Voyons maintenant des exemples avec une liste blanche réelle,
. Prenons
le serveur de messagerie de l'AFNIC,
https://www.dnswl.org/
2001:67c:2218:2::4:12
. On inverse l'adresse
(par exemple, avec ipv6calc -a 2001:67c:2218:2::4:12
) et
on fait une requête sous list.dnswl.org
:
% dig A 2.1.0.0.4.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.8.1.2.2.c.7.6.0.1.0.0.2.list.dnswl.org ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1634 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 8, ADDITIONAL: 11 ... ;; ANSWER SECTION: 2.1.0.0.4.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.8.1.2.2.c.7.6.0.1.0.0.2.list.dnswl.org. 7588 IN A 127.0.9.1 ... ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Sat Jul 04 09:16:37 CEST 2020 ;; MSG SIZE rcvd: 555
Que veut dire la valeur retournée, 127.0.9.1
?
On consulte la
documentation de la liste et on voit que 9 veut dire
Media and Tech companies (ce qui est exact) et 1
low trustworthiness. Il s'agit de la confiance
dans le classement, pas dans le serveur. Pour le même serveur, la
confiance est plus grande en IPv4 (3 au lieu de 1) :
% dig A 12.4.134.192.list.dnswl.org ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 266 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ... ;; ANSWER SECTION: 12.4.134.192.list.dnswl.org. 10788 IN A 127.0.9.3 ... ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Sat Jun 20 12:31:57 CEST 2020 ;; MSG SIZE rcvd: 72
Et les enregistrements TXT ? Ici, il valent :
% dig TXT 2.1.0.0.4.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.8.1.2.2.c.7.6.0.1.0.0.2.list.dnswl.org ... ;; ANSWER SECTION: 2.1.0.0.4.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.8.1.2.2.c.7.6.0.1.0.0.2.list.dnswl.org. 8427 IN TXT "nic.fr https://dnswl.org/s/?s=8580"
En visant l'URL
indiquée, on peut avoir tous les détails que la liste blanche
connait de ce serveur. (Je n'ai pas investigué en détail mais j'ai
l'impression que certains serveurs faisant autorité pour le domaine
dnswl.org
renvoient des NXDOMAIN à tort, par
exemple sur les ENT - Empty Non-Terminals - ce
qui pose problème si votre résolveur utilise une QNAME
minimisation - RFC 9156 - stricte.)
Pour utiliser cette liste blanche depuis votre MTA favori, vous pouvez regarder la documentation de dnswl.org. par exemple, pour Courier, ce sera :
-allow=list.dnswl.org
(Tous les détails dans la documentation de
Courier). Pour Postfix, voyez la section dédiée
dans la documentation de
dnswl.org. SpamAssassin, quant à lui,
utilise dnswl.org
par défaut.
Date de publication du RFC : Mai 2021
Auteur(s) du RFC : R. Dobbins (Arbor Networks), D. Migault (Ericsson), R. Moskowitz (HTT Consulting), N. Teague (Iron Mountain Data Centers), L. Xia (Huawei), K. Nishizuka (NTT Communications)
Pour information
Réalisé dans le cadre du groupe de travail IETF dots
Première rédaction de cet article le 31 mai 2021
Le travail autour de DOTS (DDoS Open Threat Signaling) vise à permettre la communication, pendant une attaque par déni de service, entre la victime et une organisation qui peut aider à atténuer l'attaque. Ce nouveau RFC décrit quelques scénarios d'utilisation de DOTS. Autrement, DOTS est normalisé dans les RFC 8811 et ses copains.
Si vous voulez un rappel du paysage des attaque par déni de service, et du rôle de DOTS (DDoS Open Threat Signaling) là-dedans, je vous recommande mon article sur le RFC 8612. Notre RFC 8903 ne fait que raconter les scénarios typiques qui motivent le projet DOTS.
Par exemple, pour commencer, un cas simple où le fournisseur d'accès Internet d'une organisation est également capable de fournir des solutions d'atténuation d'une attaque. Ce fournisseur (ITP dans le RFC, pour Internet Transit Provider) est sur le chemin de tous les paquets IP qui vont vers l'organisation victime, y compris ceux de l'attaque. Et il a déjà une relation contractuelle avec l'utilisateur. Ce fournisseur est donc bien placé, techniquement et juridiquement, pour atténuer une éventuelle attaque. Il peut déclencher le système d'atténuation, soit à la demande de son client qui subit une attaque, soit au contraire contre son client si celui-ci semble une source (peut-être involontaire) d'attaque. (Le RFC appelle le système d'atténuation DMS pour DDoS Mitigation Service ; certains opérateurs ont des noms plus amusants pour le DMS comme OVH qui parle d'« aspirateur ».)
Dans le cas d'une demande du client, victime d'une attaque et qui souhaite l'atténuer, le scénario typique sera qu'une machine chez le client (un pare-feu perfectionné, par exemple), établira une session DOTS avec le serveur DOTS inclus dans le DMS du fournisseur d'accès. Lorsqu'une attaque est détectée (cela peut être automatique, avec un seuil pré-défini, ou bien manuel), la machine du client demandera l'atténuation. Pendant l'atténuation, le client pourra obtenir des informations de la part du DMS du fournisseur. Si l'attaque se termine, le client pourra décider de demander l'arrêt de l'atténuation. Notez que la communication entre le client DOTS (chez la victime) et le serveur DOTS (chez le fournisseur d'accès) peut se faire sur le réseau « normal » (celui qui est attaqué) ou via un lien spécial, par exemple en 4G, ce qui sera plus cher et plus compliqué mais aura l'avantage de fonctionner même si l'attaque sature le lien normal.
Ce scénario simple peut aussi couvrir le cas du réseau domestique. On peut imaginer une box disposant de fonctions de détection d'attaques (peut-être en communiquant avec d'autres, comme le fait la Turris Omnia) et d'un client DOTS, pouvant activer automatiquement l'atténuation en contactant le serveur DOTS du FAI.
Le second cas envisagé est celui où le fournisseur d'accès n'a pas de service d'atténuation mais où le client (mettons qu'il s'agisse d'un hébergeur Web), craignant une attaque, a souscrit un contrat avec un atténuateur spécialisé (de nombreuses organisations ont aujourd'hui ce genre de services, y compris sans but lucratif). Dans ce cas, il faudra « détourner » le trafic vers cet atténuateur, avec des trucs DNS ou BGP, qui sont sans doute hors de portée de la petite ou moyenne organisation. L'accord avec l'atténuateur doit prévoir la technique à utiliser en cas d'attaque. Si c'est le DNS, la victime va changer ses enregistrements DNS pour pointer vers les adresses IP de l'atténuateur (attention au TTL). Si c'est BGP, ce sera à l'atténuateur de commencer à annoncer le préfixe du client (attention aux règles de sécurité BGP, pensez IRR et ROA), et au client d'arrêter son annonce. Le reste se passe comme dans le scénario précédent.
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : S. Huque, P. Aras (Salesforce), J. Dickinson (Sinodun), J. Vcelak (NS1), D. Blacka (Verisign)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 25 septembre 2020
Aujourd'hui, il est courant de confier l'hébergement de ses serveurs DNS faisant autorité à un sous-traitant. Mais si vous avez, comme c'est recommandé, plusieurs sous-traitants et qu'en prime votre zone, comme c'est recommandé, est signée avec DNSSEC ? Comment s'assurer que tous les sous-traitants ont bien l'information nécessaire ? S'ils utilisent le protocole standard du DNS pour transférer la zone, tout va bien. Mais hélas beaucoup d'hébergeurs ne permettent pas l'utilisation de cette norme. Que faire dans ce cas ? Ce nouveau RFC explique les pistes menant à des solutions possibles.
Pourquoi est-ce que des hébergeurs DNS ne permettent pas d'utiliser la solution normalisée et correcte, les transferts de zone du RFC 5936 ? Il peut y avoir de mauvaises raisons (la volonté d'enfermer l'utilisateur dans un silo, en lui rendant plus difficile d'aller voir la concurrence) mais aussi des (plus ou moins) bonnes raisons :
Dans ces cas, le transfert de zones classique n'est pas une solution, puisqu'il ne permet pas de transmettre, par exemple, les instructions pour la génération dynamique de données.
Résultat, bien des titulaires de noms de domaine se limitent donc à un seul hébergeur, ce qui réduit la robustesse de leur zone face aux pannes… ou face aux conflits commerciaux avec leur hébergeur.
La section 2 de notre RFC décrit les modèles possibles pour avoir à
la fois DNSSEC et plusieurs
hébergeurs. Si aucune des trois raisons citées plus haut ne
s'applique, le cas est simple : un hébergeur (qui peut être le
titulaire lui-même, par exemple avec un serveur maître caché) signe
la zone, et elle est transférée ensuite, avec clés et signatures,
vers les autres. C'est tout simple. (Pour information, c'est par
exemple ainsi que fonctionne
.fr
, qui a plusieurs
hébergeurs en plus de l'AFNIC : les serveurs dont le nom comprend
un « ext » sont sous-traités.)
Mais si une ou davantage des trois (plus ou moins bonnes) raisons citées plus haut s'applique ? Là, pas de AXFR (RFC 5936), il faut trouver d'autres modèles. (Et je vous préviens tout de suite : aucun de ces modèles n'est géré par les logiciels existants, il va falloir faire du devops.) Celui présenté dans le RFC est celui des signeurs multiples. Chaque hébergeur reçoit les données non-signées par un mécanime quelconque (par exemple son API) et les signe lui-même avec ses clés, plus exactement avec sa ZSK (Zone Signing Key). Pour que tous les résolveurs validants puissent valider ces signatures, il faut que l'ensemble des clés, le DNSKEY de la zone, inclus les ZSK de tous les hébergeurs de la zone. (Un résolveur peut obtenir les signatures d'un hébergeur et les clés d'un autre.) Comment faire en sorte que tous les hébergeurs reçoivent les clés de tous les autres, pour les inclure dans le DNSKEY qu'ils servent ? Il n'existe pas de protocole pour cela, et le problème n'est pas purement technique, il est également que ces hébergeurs n'ont pas de relation entre eux. C'est donc au titulaire de la zone d'assurer cette synchronisation. (Un peu de Python ou de Perl avec les API des hébergeurs…)
Il y a deux variantes de ce modèle : KSK unique (Key Signing Key) et partagée, ou bien KSK différente par hébergeur. Voyons d'abord la première variante, avec une seule KSK. Elle est typiquement gérée par le titulaire de la zone, qui est responsable de la gestion de la clé privée, et d'envoyer l'enregistrement DS à la zone parente. Par contre, chaque hébergeur a sa ZSK et signe les données. Le titulaire récupère ces ZSK (par exemple via une API de l'hébergeur) et crée l'ensemble DNSKEY, qu'il signe. (Le RFC note qu'il y a un cas particulier intéressant de ce modèle, celui où il n'y a qu'un seul hébergeur, par exemple parce que le titulaire veut garder le contrôle de la KSK mais pas gérer les serveurs de noms.)
Deuxième variante du modèle : chaque hébergeur a sa KSK (et sa ZSK). Il ouvre son API aux autres hébergeurs pour que chacun connaisse les ZSK des autres et puisse les inclure dans l'ensemble DNSKEY. Le titulaire doit récolter toutes les KSK, pour envoyer les DS correspondants à la zone parente. Le remplacement d'une KSK (rollover) nécessite une bonne coordination.
Dans ces deux modèles, chaque serveur faisant autorité connait toutes les ZSK utilisées, et un résolveur validant récupérera forcément la clé permettant la validation, quel que soit le serveur faisant autorité interrogé, et quelle que soit la signature que le résolveur avait obtenue (section 3 du RFC). À noter qu'il faut que tous les hébergeurs utilisent le même algorithme de signature (section 4 du RFC) puisque la section 2.2 du RFC 4035 impose de signer avec tous les algorithmes présents dans l'ensemble DNSKEY.
En revanche, les hébergeurs ne sont pas forcés d'utiliser le même mécanisme de déni d'existence (NSEC ou NSEC3, cf. RFC 7129). Chacun peut faire comme il veut (section 5 de notre RFC) puisqu'une réponse négative est toujours accompagnée de ses NSEC (ou NSEC3). Évidemment, si un hébergeur utilise NSEC et un autre NSEC3, le gain en sécurité de NSEC3 sera inutile.
Comme signalé plus haut, le remplacement (rollover) des clés, déjà une opération compliquée en temps normal, va être délicat. La section 6 du RFC détaille les procédures que l'on peut suivre dans les cas « une seule KSK » et « une KSK par hébergeur ».
La discussion plus haut supposait qu'il y avait deux clés, la KSK et la ZSK, ce qui est le mode le plus fréquent d'organisation des clés. Mais ce n'est pas obligatoire : on peut avec n'avoir qu'une seule clé (baptisée alors CSK, pour Common Signing Key). Dans ce cas, il n'y a qu'un seul modèle possible, chaque hébergeur a sa CSK. Cela ressemble au modèle « une KSK par hébergeur » : le titulaire de la zone doit récolter toutes les CSK et envoyer les DS correspondants à la zone parente (section 7 du RFC).
Cette configuration DNSSEC à plusieurs signeurs peut fonctionner avec les enregistrements CDS et CDNSKEY des RFC 7344 et RFC 8078 (section 8 de notre RFC). Dans le premier modèle « une seule KSK », le titulaire de la zone crée CDS et/ou CDNSKEY qu'il envoie à tous les hébergeurs avec les autres données. Dans le deuxième modèle « une KSK par hébergeur », il faut échanger les CDS et/ou CDNSKEY pour que chaque hébergeur ait les enregistrements des autres.
On voit que dans cette configuration, il est nécessaire de communiquer, au moins entre le titulaire de la zone et ses hébergeurs. Ces hébergeurs doivent fournir une API, typiquement de type REST. Notre RFC ne normalise pas une telle API mais sa section 9 indique les fonctions minimum qu'elle doit fournir :
Autre problème pratique avec la co-existence de plusieurs hébergeurs DNS, qui signent eux-mêmes la zone, la taille des réponses. Comme il faut inclure toutes les ZSK dans l'ensemble DNSKEY, les réponses aux requêtes DNSKEY vont forcément être d'assez grande taille (section 10). Le RFC fait remarquer que les algorithmes cryptographiques utilisant les courbes elliptiques ont des clés plus courtes et que cela peut aider.
Enfin, dans la section 12, dédiée aux questions de sécurité restantes, le RFC note que ces mécanismes à plusieurs signeurs nécessitent d'avoir confiance dans chacun des signeurs. Au contraire, le système où le maître signe tout et transfère ensuite aux serveurs esclaves ne demande aucune confiance dans les hébergeurs.
Allez, un joli dessin pour terminer (trouvé ici) : .
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : R. Bonica (Juniper Networks), F. Baker, G. Huston (APNIC), R. Hinden (Check Point Software), O. Troan (Cisco), F. Gont (SI6 Networks)
Réalisé dans le cadre du groupe de travail IETF intarea
Première rédaction de cet article le 12 septembre 2020
Un concept important d'IP est la fragmentation, le découpage d'un paquet trop gros en plusieurs fragments, chacun acheminé dans un datagramme différent. Excellente idée sur le papier, la fragmentation est handicapée, dans l'Internet actuel, par les nombreuses erreurs de configuration de diverses middleboxes. Ce nouveau RFC constate la triste réalité : en pratique, la fragmentation marche mal, et les machines qui émettent les paquets devraient essayer de faire en sorte qu'elle ne soit pas utilisée.
Rappelons d'abord ce qu'est la fragmentation, dans IP. Tout lien entre deux machines a une MTU, la taille maximale des datagrammes qui peuvent passer. C'est par exemple 1 500 octets pour l'Ethernet classique. Si le paquet IP est plus grand, il faudra le fragmenter, c'est-à-dire le découper en plusieurs fragments, chacun de taille inférieure à la MTU. En IPv4, n'importe quel routeur sur le trajet peut fragmenter un paquet (sauf si le bit DF - Don't Fragment - est mis à un dans l'en-tête IP), en IPv6, seule la machine émettrice peut fragmenter (tout ce passe comme si DF était systématiquement présent).
Ces fragments seront ensuite réassemblés en un paquet à la destination. Chacun étant transporté dans un datagramme IP différent, ils auront pu arriver dans le désordre, certains fragments ont même pu être perdus, le réassemblage est donc une opération non-triviale. Historiquement, certaines bogues dans le code de réassemblage ont même pu mener à des failles de sécurité.
Une légende urbaine s'est constituée petit à petit, racontant que les fragments, en eux-mêmes, posaient un problème de sécurité. C'est faux, mais ce genre de légendes a la vie dure, et a mené un certain nombre d'administrateurs de pare-feux à bloquer les fragments. Le RFC 7872 faisait déjà le constat que les fragments, souvent, n'arrivaient pas à destination. Outre ce RFC, on peut citer une étude très ancienne, qui montre que le problème ne date pas d'aujourd'hui, « "Fragmentation Considered Harmful (SIGCOMM '87 Workshop on Frontiers in Computer Communications Technology) ou, pour le cas spécifique du DNS, la plus récente « IPv6, Large UDP Packets and the DNS ».
La section 2 de notre RFC explique en grand détail la fragmentation IP. Notez par exemple qu'IPv4 impose (RFC 791, section 3.2) une MTU minimale de 68 octets mais, en pratique, on a toujours au moins 576 octets (ou, sinon, une autre fragmentation/réassemblage, dans la couche 2). IPv6, lui, impose (RFC 8200), 1 280 octets. Il a même été suggéré de ne jamais envoyer de paquets plus grands, pour éviter la fragmentation. Un Ethernet typique offre une MTU de 1 500 octets, mais elle peut être réduite par la suite en cas d'utilisation de tunnels.
Chaque lien a sa MTU mais ce qui est important, pour le paquet, c'est la MTU du chemin (Path MTU), c'est-à-dire la plus petite des MTU rencontrées sur le chemin entre le départ et l'arrivée. C'est cette MTU du chemin qui déterminera si on fragmentera ou pas. (Attention, le routage étant dynamique, cette MTU du chemin peut changer dans le temps.) Il est donc intéressant, pour la machine émettrice d'un paquet, de déterminer cette MTU du chemin. Cela peut se faire en envoyant des paquets avec le bit DF (Don't Fragment, qui est implicite en IPv6) et en regardant les paquets ICMP renvoyés par les routeurs, indiquant que la MTU est trop faible pour ce paquet (et, depuis le RFC 1191, le message ICMP indique la MTU du lien suivant, ce qui évite de la deviner par essais/erreurs successifs.) Cette procédure est décrite dans les RFC 1191 et RFC 8201. Mais (et c'est un gros mais), cela suppose que les erreurs ICMP « Packet Too Big » arrivent bien à l'émetteur et, hélas, beaucoup de pare-feux configurés par des ignorants bloquent ces messages ICMP. D'autre part, les messages ICMP ne sont pas authentifiés (RFC 5927) et un attaquant peut générer de fausses erreurs ICMP pour faire croire à une diminution de la MTU du chemin, affectant indirectement les performances. (Une MTU plus faible implique des paquets plus petits, donc davantage de paquets.)
Quand une machine fragmente un paquet (en IPv4, cette machine peut être l'émetteur ou un routeur intermédiaire, en IPv6, c'est forcément l'émetteur), elle crée plusieurs fragments, dont seul le premier porte les informations des couches supérieures, comme le fait qu'on utilise UDP ou TCP, ou bien le numéro de port. La description détaillée figure dans le RFC 791 pour IPv4 et dans le RFC 8200 (notamment la section 4.5) pour IPv6.
Voici un exemple où la fragmentation a eu lieu, vu par
tcpdump (vous pouvez récupérer le
pcap complet en
). La machine
dns-frag-md.pcap
2605:4500:2:245b::bad:dcaf
a fait une requête
DNS à 2607:5300:201:3100::2f69
, qui est un des
serveurs faisant autorité pour le TLD
.md
. La réponse a dû
être fragmentée en deux :
% tcpdump -e -n -r dns-frag-md.pcap 16:53:07.968917 length 105: 2605:4500:2:245b::bad:dcaf.44104 > 2607:5300:201:3100::2f69.53: 65002+ [1au] ANY? md. (43) 16:53:07.994555 length 1510: 2607:5300:201:3100::2f69 > 2605:4500:2:245b::bad:dcaf: frag (0|1448) 53 > 44104: 65002*- 15/0/16 SOA, RRSIG, Type51, RRSIG, RRSIG, DNSKEY, DNSKEY, RRSIG, NS dns-md.rotld.ro., NS nsfr.dns.md., NS nsb.dns.md., NS ns-ext.isc.org., NS nsca.dns.md., NS ns-int.dns.md., RRSIG (1440) 16:53:07.994585 length 321: 2607:5300:201:3100::2f69 > 2605:4500:2:245b::bad:dcaf: frag (1448|259)
Le premier paquet est la requête, le second est le premier fragment de la réponse (qui va des octets 0 à 1447), le troisième paquet est le second fragment de cette même réponse (octets 1448 à la fin). Regardez les longueur des paquets IP, et le fait que seul le premier fragment, qui porte l'en-tête UDP, a pu être interprété comme étant du DNS.
Notez que certaines versions de traceroute
ont une option qui permet d'afficher la MTU du lien (par exemple
traceroute -n --mtu IP-ADDRESS
.)
Les couches supérieures (par exemple UDP ou TCP) peuvent ignorer ces questions et juste envoyer leurs paquets, comptant qu'IP fera son travail, ou bien elles peuvent tenir compte de la MTU du chemin, par exemple pour optimiser le débit, en n'envoyant que des paquets assez petits pour ne pas être fragmentés. Cela veut dire qu'elles ont accès au mécanisme de découverte de la MTU du chemin, ou bien qu'elles font leur propre découverte, via la procédure PLPMTUD (Packetization Layer Path MTU Discovery) du RFC 4821 (qui a l'avantage de ne pas dépendre de la bonne réception des paquets ICMP).
Bon, maintenant qu'on a vu la framentation, voyons les difficultés qui surviennent dans l'Internet d'aujourd'hui (section 3 du RFC). D'abord, chez les pare-feux et, d'une manière générale, tous les équipements intermédiaires qui prennent des décisions en fonction de critères au-dessus de la couche 3, par exemple un répartiteur de charge, ou un routeur qui enverrait les paquets à destination du port 443 vers un autre chemin que le reste des paquets. Ces décisions dépendent d'informations qui ne sont que dans le premier fragment d'un datagramme fragmenté. Décider du sort des fragments suivants n'est pas évident, surtout si on veut le faire sans maintenir d'état. Un pare-feu sans état peut toujours essayer d'accepter tous les fragments ultérieurs (ce qui pourrait potentiellement autoriser certaines attaques) ou bien tous les bloquer (ce qui arrêterait du trafic légitime fragmenté). Et les pare-feux avec état ne sont pas une solution idéale, puisque stocker et maintenir cet état est un gros travail, qui plante souvent, notamment en cas d'attaque par déni de service.
Certains types de NAT ont également des problèmes avec la fragmentation. Ainsi, les techniques A+P (RFC 6346) et CGN (RFC 6888) nécessitent toutes les deux que les fragments soient réassemblés en un seul paquet, avant de traduire.
Qui dit fragmentation dit réassemblage à un moment. C'est une opération délicate, et plusieurs programmeurs se sont déjà plantés en réassemblant sans prendre de précautions. Mais il y a aussi un problème de performance. Et il y a les limites d'IPv4. L'identificateur d'un fragment ne fait que 16 bits et cela peut mener rapidement à des réutilisations de cet identificateur, et donc à des réassemblages incorrects (les sommes de contrôle de TCP et UDP ne sont pas toujours suffisantes pour détecter ces erreurs, cf. RFC 4693). IPv6, heureusement, n'a pas ce problème, l'identificateur de fragment faisant 32 bits.
On l'a dit plus haut, la fragmentation, et surtout le réassemblage, ont une longue histoire de failles de sécurité liées à une lecture trop rapide du RFC par le programmeur qui a écrit le code de réassemblage. Ainsi, les fragments recouvrants sont un grand classique, décrits dans les RFC 1858, RFC 3128 et RFC 5722. Normalement, le récepteur doit être paranoïaque, et ne pas faire une confiance aveugle aux décalages (offset) indiqués dans les paquets entrants, mais tous les programmeurs ne sont pas prudents. Il y a aussi le risque d'épuisement des ressources, puisque le récepteur doit garder en mémoire (le réassemblage implique le maintien d'un état) les fragments pas encore réassemblés. Un attaquant peut donc épuiser la mémoire en envoyant des fragments d'un datagramme qui ne sera jamais complet. Et il y a des identificateurs de fragment non-aléatoires, qui permettent d'autres attaques, documentées dans le RFC 7739 ou dans des articles comme « Fragmentation Considered Poisonous de Herzberg et Shulman (cf. aussi mon résumé). Enfin, la fragmentation peut aider à échapper au regard des IDS (cf. « Insertion, Evasion and Denial of Service: Eluding Network Intrusion Detection »).
Un autre problème très fréquent avec la fragmentation est causé par la configuration erronée de pare-feux. Souvent, des administrateurs réseau incompétents bloquent les messages ICMP Packet Too Big, nécessaires pour la découverte de la MTU du chemin. C'est de leur part une grosse erreur (expliquée dans le RFC 4890) mais cela arrive trop souvent. Résultat, si la MTU du chemin est inférieure à la MTU du premier lien, la machine émettrice envoie des paquets trop gros, et ne sait pas que ces paquets n'ont pas pu passer. On a donc créé un trou noir (les paquets disparaissent sans laisser de trace).
Ce bloquage injustifié des messages ICMP peut également être dû à des causes plus subtiles. Par exemple, si un pare-feu laisse sortir tous les paquets mais, en entrée, n'autorise que les paquets dont l'adresse IP source a été utilisée comme destination récemment, alors, les erreurs ICMP, émises par des routeurs intermédiaires et ayant donc une adresse IP source jamais vue, seront jetées. Notre RFC note que cette bogue dans les pare-feux est apparemment assez fréquente dans les boxes.
Toujours côté mauvaise configuration, le RFC cite aussi le problème des routeurs qui jettent les paquets ayant options ou extensions qu'ils ne connaissent pas, ce qui peut inclure les fragments. L'analyse du RFC 7872, ou celle dans l'article de Huston « IPv6, Large UDP Packets and the DNS », montre bien que ce problème est fréquent, trop fréquent. Ainsi, même si la découverte de la MTU du chemin se passe bien, les fragments n'arriveront pas à destination. Pourquoi cette mauvaise configuration ? C'est évidemment difficile à dire, cela peut aller de logiciels bogués jusqu'à un choix délibéré d'un administrateur réseau ignorant qui a vaguement entendu une légende urbaine du genre « les fragments sont un risque de sécurité ».
Dans les cas précédents, la perte du message ICMP Packet Too Big était clairement de la faute d'un humain, l'administrateur du pare-feu. Mais il peut y avoir des obstacles plus fondamentaux au bon fonctionnement de la découverte de la MTU du chemin. Par exemple, si un serveur DNS anycasté envoie un paquet trop gros, et qu'un routeur intermédiaire envoie le message Packet Too Big, ledit message ira vers l'adresse anycast du serveur, et atterrira peut-être vers une autre instance du serveur DNS, si le routeur qui a signalé le problème n'est pas dans le même bassin d'attraction que le client original. Le message ICMP ne sera donc pas reçu par l'instance qui aurait eu besoin de l'information. Le problème est d'autant plus génant que le DNS est le plus gros utilisateur d'UDP, et est donc particulièrement sensible aux problèmes de fragmentation (TCP gère mieux ces problèmes, avec la négociation de MSS).
Une variante du problème anycast survient lorsque le routage est unidirectionnel. Si l'émetteur n'est pas joignable, il ne recevra pas les messages ICMP. (Le cas est cité par le RFC mais me semble peu convaincant ; il y a peu de protocoles où l'émetteur peut se passer de recevoir des réponses. Et beaucoup de routeurs jettent les paquets pour lesquels ils n'ont pas de voie de retour, cf. RFC 3704.)
Maintenant qu'on a présenté en détail les problèmes liés à la fragmentation IP dans l'Internet actuel, quelles sont les approches possibles pour traiter ce problème ? La section 4 du RFC les présente. D'abord, les solutions situées dans la couche Transport. Comme indiqué plus haut, TCP peut éviter la fragmentation en découpant les données en segments dont chacun a une taille inférieure à la MTU du chemin (paramètre MSS, Maximum Segment Size). Cela suppose que la MSS soit réglée à une telle valeur (cf. mon article). Cela peut être manuel (si on sait qu'on va toujours passer par un tunnel avec faible MTU, on peut toujours configurer sa machine pour réduire la MSS), cela peut utiliser la procédure classique de découverte de la MTU du chemin ou bien cela peut utiliser la découverte de MTU sans ICMP du RFC 4821. D'autres protocoles que TCP peuvent fonctionner ainsi, comme DCCP (RFC 4340) ou SCTP (RFC 9260). À noter qu'UDP, lui, n'a pas de tel mécanisme, même si des travaux sont en cours pour cela.
Pour TCP, la méthode manuelle se nomme TCP clamping et peut se faire, par exemple avec Netfilter en mettant sur le routeur :
% iptables -t mangle -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN \ -j TCPMSS --clamp-mss-to-pmtu
La méthode avec ICMP, on l'a vu, est fragile car les messages ICMP peuvent être bloqués. La méthode sans ICMP consiste pour TCP, en cas de détection de perte de paquets, à envoyer des paquets plus petits pour essayer de s'ajuster à la MTU du chemin. (Bien sûr, des paquets peuvent se perdre pour d'autres raisons que la MTU trop basse, et la mise en œuvre de cette technique est donc délicate.)
Autre solution, plutôt que d'impliquer la couche Transport, faire appel à la couche Application. Le RFC 8085 conseille aux applications qui font de l'UDP d'essayer d'éviter la fragmentation. Par exemple, si vous gérez un serveur DNS avec NSD, cela peut se faire en mettant dans le fichier de configuration :
ipv4-edns-size: 1432 ipv6-edns-size: 1432
Vous pouvez voir le résultat sur, par exemple, un des serveurs
faisant autorité pour .bostik
:
% dig +ignore @d.nic.fr DNSKEY bostik ; <<>> DiG 9.11.5-P4-5.1-Debian <<>> +ignore @d.nic.fr DNSKEY bostik ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12029 ;; flags: qr aa tc rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 1432 ; COOKIE: ef8669d08e2d9b26bb1a8ab85e21830a8931f9ab23403def (good) ;; QUESTION SECTION: ;bostik. IN DNSKEY ;; Query time: 2 msec ;; SERVER: 2001:678:c::1#53(2001:678:c::1) ;; WHEN: Fri Jan 17 10:48:58 CET 2020 ;; MSG SIZE rcvd: 63
Vous voyez que le serveur n'envoie pas de réponses de taille
supérieure à 1 432 octets (la OPT
PSEUDOSECTION). Au moment du test, la réponse faisait
1 461 octets, d'où le flag
tc
(TRuncated). Normalement,
un client DNS, voyant que la réponse a été tronquée, réessaie en TCP
(j'ai mis l'option +ignore
à dig pour empêcher
cela et illustrer le fonctionnement du DNS.)
En parlant du DNS, la section 5 du RFC liste des applications pour lesquelles la fragmentation joue un rôle important :
Compte-tenu de tout ceci, quelles recommandations concrètes donner ? Cela dépend évidemment du public cible. La section 6 de notre RFC donne des conseils pour les concepteurs et conceptrices de protocoles et pour les différents types de développeurs et développeuses qui vont programmer des parties différentes du système. D'abord, les protocoles, sujet principal pour l'IETF. Compte-tenu des importants problèmes pratiques que pose la fragmentation dans l'Internet actuel, le RFC prend une décision douloureuse : plutôt que de chercher à réparer l'Internet, on jette l'éponge et on ne conçoit plus de protocoles qui dépendent de la fragmentation. De tels protocoles ne seraient raisonnables que dans des environnements fermés et contrôlés. Comme souvent à l'IETF, le choix était difficile car il faut choisir entre les principes (la fragmentation fait partie d'IP, les composants de l'Internet ne doivent pas l'empêcher) et la réalité d'un monde de middleboxes mal programmées et mal gérées. Comme pour la décision de faire passer beaucoup de nouveaux protocoles sur HTTPS, le choix ici a été de prendre acte de l'ossification de l'Internet, et de s'y résigner.
Pour ne pas dépendre de la fragmentation, les nouveaux protocoles peuvent utiliser une MTU suffisamment petite pour passer partout, ou bien utiliser un système de découverte de la MTU du chemin suffisamment fiable, comme celui du RFC 4821. Pour UDP, le RFC renvoie aux recommandations de la section 3.2 du RFC 8085.
Ensuite, les conseils aux programmeurs et programmeuses. D'abord, dans les systèmes d'exploitation. Le RFC demande que la PLPMTUD (RFC 8899) soit disponible dans les bibliothèques proposées aux applications.
Ensuite, pour ceux et celles qui programment les middleboxes, le RFC rappelle quand même qu'elles doivent respecter les RFC sur IPv4 (RFC 791) et IPv6 (RFC 8200). Pour beaucoup de fonctions assurées par ces boitiers (comme le filtrage), cela implique de réassembler les paquets fragmentés, et donc de maintenir un état. Les systèmes avec état sont plus compliqués et plus chers (il faut davantage de mémoire) ce qui motive parfois à préférer les systèmes sans état. Ceux-là n'ont que deux choix pour gérer la fragmentation : violer les RFC ou bien jeter tous les fragments. Évidemment, aucune de ces deux options n'est acceptable. Le RFC demande qu'au minimum, si on massacre le protocole IP, cela soit documenté. (Ces middleboxes sont souvent traitées comme des boites noires, installées sans les comprendre et sans pouvoir déboguer les conséquences.)
Enfin, les opérateurs des réseaux doivent s'assurer que la PMTUD fonctionne, donc émettre des Packet Too Big si le paquet est plus gros que la MTU, et ne pas bloquer les paquets ICMP. Notre RFC permet toutefois de limiter leur rythme (RFC 1812 et RFC 4443). En tout cas, comme le rappelle le RFC 4890, filtrer les paquets ICMP Packet Too Big est mal !
De la même façon, le RFC rappelle aux opérateurs réseau qu'on ne doit pas filtrer les fragments. Ils sont utiles et légitimes.
Notez que les recommandations de ce RFC peuvent sembler contradictoires : on reconnait que la fragmentation marche mal et qu'il ne faut donc pas compter dessus mais, en même temps, on rappelle qu'il faut se battre pour qu'elle marche mieux. En fait, il n'y a pas de contradiction, juste du réalisme. L'Internet n'ayant pas de Chef Suprême qui coordonne tout, on ne peut pas espérer qu'une recommandation de l'IETF soit déployée immédiatement partout. On se bat donc pour améliorer les choses (ne pas bloquer les fragments, ne pas bloquer les messages ICMP Packet Too Big) tout en étant conscient que ça ne marchera pas à 100 % et que les administrateurs système et réseau doivent en être conscients.
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : G. Fairhurst, T. Jones (University of Aberdeen), M. Tuexen, I. Ruengeler, T. Voelker (Muenster University of Applied Sciences)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tsvwg
Première rédaction de cet article le 12 septembre 2020
Ce RFC qui vient d'être publié décrit une méthode pour découvrir la MTU d'un chemin sur l'Internet (Path MTU, la découverte de la MTU du chemin se nommant PMTUD pour Path MTU Discovery). Il existe d'autres méthodes pour cette découverte, comme celle des RFC 1191 et RFC 8201, qui utilisent ICMP. Ici, la méthode ne dépend pas d'ICMP, et se fait avec les datagrammes normaux de la couche 4 (ou PL, pour Packetization Layer). Elle étend la technique du RFC 4821 à d'autres protocoles de transport que TCP (et SCTP).
Mais, d'abord, pourquoi est-ce important de découvrir la MTU maximale du chemin ? Parce que, si on émet des paquets qui sont plus gros que cette MTU du chemin, ces paquets seront jetés, au grand dam de la communication. En théorie, le routeur qui prend cette décision devrait fragmenter le paquet (IPv4 seulement), ou bien envoyer à l'expéditeur un message ICMP Packet Too Big mais ces fragments, et ces messages ICMP sont souvent bloqués par de stupides pare-feux mal gérés (ce qu'on nomme un trou noir). On ne peut donc pas compter dessus, et ce problème est connu depuis longtemps (RFC 2923, il y a vingt ans, ce qui est une durée insuffisante pour que les administrateurs de pare-feux apprennent leur métier, malgré le RFC 4890). La seule solution fiable est de découvrir la MTU du chemin, en envoyant des paquets de plus en plus gros, jusqu'à ce que ça ne passe plus, montrant ainsi qu'on a découvert la MTU.
Le RFC donne aussi quelques raisons plus subtiles pour lesquelles la découverte classique de la MTU du chemin ne marche pas toujours. Par exemple, s'il y a un tunnel sur le trajet, c'est le point d'entrée du tunnel qui recevra le message ICMP Packet Too Big, et il peut faillir à sa tâche de le faire suivre à l'émetteur du paquet. Ou bien, en cas de routage asymétrique, le message ICMP peut être jeté car il n'y a pas de route de retour.
Et ce n'est pas tout que le message ICMP revienne à l'émetteur, encore faut-il que celui-ci l'accepte. Les messages ICMP n'étant pas authentifiés, une machine prudente va essayer de les valider, en examinant le paquet originel contenu dans le message ICMP, et en essayant de voir s'il correspond à du trafic en cours. Si le routeur qui émet le message ICMP n'inclut pas assez du paquet original (malgré ce que demande le RFC 1812) pour que cette validation soit possible, le message ICMP « Vous avez un problème de taille » sera jeté par l'émetteur du paquet original. Et il peut y avoir d'autres problèmes, par exemple avec le NAT (cf. RFC 5508). Autant de raisons supplémentaires qui font que la découverte de la MTU du chemin ne peut pas compter sur les messages ICMP.
Notre RFC utilise le terme de couche de découpage en paquets (Packetization Layer, PL). Il désigne la couche où les données sont séparées en paquets qui seront soumis à IP. C'est en général la couche de transport (protocoles TCP, DCCP, SCTP, etc) mais cela peut aussi être fait au-dessus de celle-ci. Ce sigle PL donne naissance au sigle PLPMTUD, Packetization Layer Path MTU Discovery. Contrairement à la classique PMTUD (Path MTU Discovery) des RFC 1191 et RFC 8201, la PLPMTUD ne dépend pas des messages ICMP. C'est la PL, la couche de découpage en paquets qui se charge de trouver la bonne taille, en envoyant des données et en regardant lesquelles arrivent. La PLPMTUD est plus solide que la traditionnelle PMTUD, qui est handicapée par le blocage fréquent d'ICMP (cf. RFC 4821, qui a introduit ce terme de PLPMTUD, et RFC 8085).
La découverte de la MTU du chemin par le PL est ancienne pour TCP (RFC 4821). Et pour les autres protocoles de transport ? C'est l'objet de notre RFC. Pour UDP, le RFC 8085 (section 3.2) recommande d'utiliser une telle procédure (sauf si les couches inférieures fournissent l'information). Et, pour SCTP, la section 10.2 du RFC 4821 le recommandait également mais sans donner de détails, désormais fournis par notre nouveau RFC.
Un peu de vocabulaire avant de continuer la lecture du RFC (section 2) : un trou noir est un endroit du réseau d'où des paquets ne peuvent pas sortir. Par exemple, parce que les paquets trop gros sont jetés, sans émission de messages ICMP, ou bien avec des messages ICMP filtrés. Un paquet-sonde est un paquet dont l'un des buts est de tester la MTU du chemin. Il est donc de grande taille, et, en IPv4, a le bit DF (Don't Fragment), qui empêchera la fragmentation par les routeurs intermédiaires. Ceci étant défini, attaquons-nous au cahier des charges (section 3 du RFC). Le RFC 4821 était très lié à TCP, mais la généralisation de la PLPMTUD qui est dans notre nouveau RFC va nécessiter quelques fonctions supplémentaires :
ACK
). Mais
pour UDP et des protocoles similaires, il faut ajouter ce
mécanisme de notification, ce qui implique en général une
coopération avec l'application (par exemple, pour le DNS, le client DNS peut
notifier le PL qu'il a reçu une réponse à sa question, prouvant
ainsi que le datagramme est passé).En parlant de validation, cela vaut aussi pour les paquets-sonde (section 9 du RFC) : il faut empêcher un attaquant situé hors du chemin d'injecter de faux paquets. Cela peut se faire par exemple en faisant varier le port source (cf. section 5.1 du RFC 8085), comme le fait le DNS.
La section 4 du RFC passe ensuite aux mécanismes concrets à utiliser. D'abord, les paquets-sonde. Que doit-on mettre dedans, sachant qu'ils doivent être de grande taille, le but étant d'explorer les limites du chemin ? On peut utiliser des données utiles (mais on n'en a pas forcément assez, par exemple les requêtes DNS sont toujours de petite taille), on peut utiliser uniquement des octets bidons, du remplissage (mais c'est ennuyeux pour les protocoles qui doivent avoir une faible latence, comme le DNS, qui n'ont pas envie d'attendre pour envoyer les vraies données), ou bien on peut combiner les deux (des vraies données, plus du remplissage). Et, si on utilise des données réelles, il faut gérer le risque de perte, et pouvoir réémettre. Le RFC ne donne pas de consigne particulière aux mises en œuvre de PLPMTUD, les trois stratégies sont autorisées.
Ensuite, il faut évidemment un mécanisme permettant de savoir si le paquet-sonde est bien arrivé, si le protocole de transport ne le fournit pas, ce qui est le cas d'UDP, ce qui nécessite une collaboration avec l'application.
Dans le cas où le paquet-sonde n'arrive pas, il faut détecter la perte. C'est relativement facile si on a reçu un message ICMP PTB mais on ne veut pas dépendre uniquement de ces messages, vu les problèmes d'ICMP dans l'Internet actuel. Le PL doit garder trace des tailles des paquets envoyés et des pertes (cf. le paragraphe précédent) pour détecter qu'un trou noir avale les paquets de taille supérieure à une certaine valeur. Il n'est pas facile de déterminer les seuils de détection. Si on réduit la MTU du chemin au premier paquet perdu, on risque de la réduire trop, alors que le paquet avait peut-être été perdu pour une tout autre raison. Si on attend d'avoir perdu plusieurs paquets, on risque au contraire de ne pas réagir assez vite à un changement de la MTU du chemin (changement en général dû à une modification de la route suivie).
(J'ai parlé de MTU du chemin mais PLMTUD détecte en fait une valeur plus petite que la MTU, puisqu'il y a les en-têtes IP.)
Si vous aimez les détails des protocoles, les machines à état et la liste de toutes les variables nécessaires, la section 5 du RFC est pour vous, elle spécifie complètement la PLMTUD pour des protocoles utilisant des datagrammes. Cette section 5 est générique, et la section 6 décrit les détails spécifiques à un protocole de transport donné.
Ainsi, pour UDP (RFC 768) et UDP-Lite (RFC 3828), le protocole de transport n'a tout simplement pas les mécanismes qu'il faut pour faire de la PLPMTUD ; cette découverte de la MTU du chemin doit être faite par l'application, dans l'esprit du RFC 8085. L'idéal serait que cette PLPMTUD soit mise en œuvre dans une bibliothèque partagée, pour éviter que chaque application ne la réinvente mal. Mais je ne connais pas actuellement de telle bibliothèque.
Le RFC insiste sur le fait que l'application doit, pour effectuer cette tâche, pouvoir se souvenir de quels paquets ont été envoyés, donc mettre dans chaque paquet un identificateur, comme le Query ID du DNS.
Pour SCTP (RFC 9260), c'est un peu plus facile, puisque SCTP, comme TCP, a un système d'accusé de réception. Et les chunks de SCTP fournissent un moyen propre d'ajouter des octets au paquet-sonde pour atteindre la taille souhaitée (cf. RFC 4820), sans se mélanger avec les données des applications.
Pour le protocole QUIC, la façon de faire de la PLMTUD est spécifiée dans le RFC 9000. DCCP, lui, n'est pas spécifiquement cité dans cette section.
Ah, et quelles mises en œuvre de protocoles font déjà comme décrit dans ce RFC ? À part divers tests, il parait (mais je n'ai pas vérifié personnellement) que c'est le cas pour SCTP dans FreeBSD, et dans certains navigateurs Web pour WebRTC (WebRTC tourne sur UDP et rappelez-vous qu'en UDP, il faut une sérieuse coopération par l'application pour faire de la PLPMTUD). Côté QUIC, il y a lsquic, qui gère les techniques de notre RFC.
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : V. Dolmatov (JSC "NPK Kryptonite"), D. Baryshkov (Auriga)
Pour information
Première rédaction de cet article le 15 septembre 2020
Ce RFC décrit un algorithme russe de chiffrement symétrique, « GOST R 34.12-2015 », surnommé Magma. N'attendez pas de ma part des conseils sur l'utilisation ou non de cet algorithme, je résume juste le RFC.
GOST est le nom des normes de l'organisme de normalisation officiel en Russie (et, par abus de langage, leurs algorithmes sont souvent cités sous le nom de « GOST » tout court). Cet organisme a normalisé plusieurs algorithmes de chiffrement symétrique dont Kuznyechik (décrit dans le RFC 7801) et l'ancien « GOST 28147-89 » (RFC 5830). Ce dernier est remplacé par « GOST R 34.12-2015 », qui fait l'objet de ce RFC. (La norme russe officielle est disponible en ligne mais, de toute façon, le russe plus la cryptographie, ce serait trop pour moi). Comme d'autres algorithmes de cryptographie normalisés par GOST, Magma a été développé en partie par le secteur public (Service des communications spéciales et d'information du Service fédéral de protection de la Fédération de Russie) et par le secteur privé (InfoTeCS). Le décret n° 749 du 19 juin 2015, pris par l'Agence fédérale pour la régulation technique et la métrologie en a fait un algorithme russe officiel.
Du fait de ce caractère officiel, si vous vendez des produits ou des prestations de sécurité en Russie, vous serez probablement obligés d'inclure les algorithmes GOST.
Je vous laisse lire le RFC pour la description de l'algorithme (ou l'original russe, si vous lisez le russe). Notez que, contrairement à son prédécesseur (cf. RFC 5830, sections 5 à 7), il ne décrit pas les modes utilisables, chacun choisit celui qui lui semble adapté.
Question mises en œuvre, il y en a une compatible avec WebCrypto, ou bien une pour OpenSSL.
Date de publication du RFC : Août 2020
Auteur(s) du RFC : M. Nottingham
Pour information
Première rédaction de cet article le 28 août 2020
Ah, mais c'est une excellente question, ça. L'Internet est pour qui ? Qui sont les « parties prenantes » et, parmi elles, quelles sont les plus importantes ? Plus concrètement, la question pour l'IETF est « pour qui bossons-nous ? » Quels sont les « clients » de notre activité ? Ce RFC de l'IAB met les pieds dans le plat et affirme bien haut que ce sont les intérêts des utilisateurs finaux qu'il faut considérer avant tout. Et explique aussi comment prendre en compte ces intérêts, en pratique. C'est donc un RFC 100 % politique.
Il y a encore quelques personnes à l'IETF qui ne veulent pas voir les conséquences de leur travail (« la technique est neutre ») ou, pire, qui ne veulent en retenir que les conséquences positives. Mais les activités de l'IETF, comme la production des RFC, est en fait politique, affirme ce document. Car l'Internet est aujourd'hui un outil crucial pour toute la vie sociale, il a permis des changements importants, il a enrichi certains et en a appauvri d'autres, il a permis l'accès à un savoir colossal librement accessible, et il a facilité le déploiement de mécanismes de surveillance dont Big Brother n'aurait jamais osé rêver. Et toute décision apparemment « purement technique » va avoir des conséquences en termes de ce qui est possible, impossible, facile, ou difficile sur le réseau. Compte-tenu de leur impact, on ne peut pas dire que ces décisions ne sont pas politiques (section 1 du RFC).
Une fois qu'on reconnait que ce qu'on fait est politique, se pose la question : pour qui travaille-t-on ? Dresser la liste des « parties prenantes », les intéressé·e·s, les organisations ou individus qui seront affectés par les changements dans l'Internet est une tâche impossible ; c'est quasiment tout le monde. Le RFC donne une liste non limitative : les utilisateurs finaux, les opérateurs réseau, les écoles, les vendeurs de matériel, les syndicats, les auteurs de normes (c'est nous, à l'IETF), les programmeurs qui vont mettre en œuvre les normes en question, les ayant-droits, les États, les ONG, les mouvements sociaux en ligne, les patrons, la police, les parents de jeunes enfants… Tous et toutes sont affectés et tous et toutes peuvent légitimement réclamer que leurs intérêts soient pris en compte. Ce n'est pas forcément au détriment des autres : un changement technique peut être bénéfique à tout le monde (ou, en tout cas, être bénéfique à certains sans avoir d'inconvénients pour les autres). Mais ce n'est pas toujours le cas. Pour prendre un exemple classique (mais qui n'est pas cité dans ce RFC), voyons le chiffrement : l'écriture du RFC 8446, qui normalisait la version 1.3 de TLS, a remué beaucoup de monde à l'IETF car le gain en sécurité pour les utilisateurs finaux se « payait » par de moindres possibilités de surveillance pour les États et les patrons. Ici, pas question de s'en tirer en disant que tout le monde serait heureux : il fallait accepter de faire des mécontents.
Bon, là, c'étaient les grands principes, maintenant, il faut devenir un peu concret. D'abord, qui sont ces « utilisateurs finaux » ? Si on veut donner la priorité à leurs intérêts, il faudrait les définir un peu plus précisément. La section 2 explique : ce sont les humains pour qui l'Internet rend un service. Cela n'inclut donc pas les professionnels qui font marcher le réseau : les utilisateurs finaux du protocole BGP ne sont pas les administrateurs réseau, qui configurent les routeurs BGP, mais les gens à qui le réseau en question permet de communiquer.
Le RFC note que ces utilisateurs finaux ne forment donc pas un groupe homogène. Ils ont des intérêts différents et des opinions différentes. (Je suis personnellement très agacé par les gens qui, dans les réunions de « gouvernance Internet », plastronnent qu'ils représentent « les utilisateurs ». Comme la mythique « société civile », les utilisateurs ne sont pas d'accord entre eux.) Parfois, le désaccord est au sein du même individu, lorsqu'il occupe plusieurs rôles. Même dans un seul rôle, l'utilisateur final peut être le siège de tensions, par exemple entre la protection de sa vie privée et la facilité d'utilisation du réseau, deux objectifs honorables mais qui sont parfois difficiles à concilier.
Le RFC note aussi que l'utilisateur final peut… ne pas être un utilisateur, ou en tout cas pas directement. Si on prend une photo de moi et qu'on la met sur le Web avec un commentaire, je suis concerné, même si je n'utilise pas du tout l'Internet. Même chose si j'entre dans un magasin truffé de capteurs qui détectent mes mouvements et les signalent. Les utilisateurs finaux, au sens de ce RFC, peuvent donc être des utilisateurs indirects.
Une fois qu'on sait qui sont les utilisateurs finaux, pourquoi faudrait-il prioriser leurs intérêts ? La section 3 rappelle d'abord que l'IETF a une longue histoire d'affirmation de cette priorité. Le tout premier RFC, le RFC 1, disait déjà « One of our goals must be to stimulate the immediate and easy use by a wide class of users. » (Bon, le RFC 1 parlait d'accessibilité et de facilité d'usage plutôt que de politique, mais c'est une jolie référence.) La charte de l'IETF, dans le RFC 3935, est plus précise : « The IETF community wants the Internet to succeed because we believe that the existence of the Internet, and its influence on economics, communication, and education, will help us to build a better human society. ». Et, encore plus explicite, « We embrace technical concepts such as decentralized control, edge-user empowerment and sharing of resources, because those concepts resonate with the core values of the IETF community. These concepts have little to do with the technology that's possible, and much to do with the technology that we choose to create. ». Bref, le but est le bonheur de l'humanité, et celle-ci est composée des utilisateurs finaux.
(Pour ne fâcher personne, le RFC oublie de signaler l'existence d'autres RFC qui au contraire donnent explicitement la priorité à d'autres parties prenantes, par exemple les gérants du réseau dans le RFC 8404.)
Le RFC note que le progrès quantitatif (davantage de machines connectées, une capacité réseau plus importante, une latence plus faible) n'est pas un but en soi car l'Internet peut être utilisé pour des mauvaises causes (surveiller les utilisateurs, exercer un pouvoir sur eux). La technique pouvant être utilisée pour le bien comme pour le mal, les améliorations techniques (comme présentées en couleur rose par les techno-béats, par exemple les promoteurs de la 5G) ne doivent pas être considérées comme forcément positives.
Après ces arguments humanistes, le RFC mentionne aussi des arguments plus internes au réseau. D'abord, d'un point de vue égoïste, l'IETF a tout intérêt à garder la confiance de ces utilisateurs finaux, car l'IETF perdrait sa pertinence et son rôle si elle se mettait, par exemple, uniquement à la remorque des vendeurs de matériel ou de logiciel. (Ou même si elle était simplement vue comme se mettant à cette remorque.)
On pourrait même voir les utilisateurs se détourner massivement, non seulement du travail de l'IETF, mais aussi de l'Internet en général, si leurs intérêts ne sont pas mis en premier. Prioriser les utilisateurs finaux aide aussi à lutter contre certaine formes de technophobie.
Maintenant, on a défini les utilisateurs finaux, affirmé qu'il fallait penser à eux et elles en premier, et expliqué pourquoi. Il reste le comment. C'est bien joli de dire, dans une grande envolée « nous pensons avant tout à M. et Mme Toutlemonde » mais, concrètement, cela veut dire quoi ? La section 4 du RFC décortique les conséquences pratiques du choix politique.
D'abord, déterminer ce qui est bon pour les utilisateurs n'est pas évident. Paradoxalement, le fait que les participants à l'IETF connaissent et comprennent très bien le fonctionnement de l'Internet n'aide pas, au contraire ; cela rend plus difficile de se mettre à la place des utilisateurs finaux. Pourtant, on l'a vu, l'IETF se réclame depuis longtemps d'une vague « Internet community » mais sans trop savoir qui elle est. Une solution évidente au problème « quels sont les intérêts des utilisateurs finaux ? » serait de leur demander. C'est plus facile à dire qu'à faire, mais c'est en effet la première chose à envisager : se rapprocher des utilisateurs.
Cela ne va pas de soi. Déjà, le travail de l'IETF est très pointu techniquement, et nécessite une forte expertise, sans compter la nécessité de se familiariser avec la culture spécifique de l'IETF. les utilisateurs finaux qu'on veut prioriser ne sont pas des experts techniques. Pire, les connaissances qu'ils ont sur l'Internet ne sont pas seulement insuffisantes, elles sont souvent fausses. Bref, inviter M. ou Mme Toutlemonde sur les listes de diffusion de l'IETF n'est pas la bonne approche.
Les États sont prompts à dire « pas de problème, les utilisateurs ont une représentation, et c'est nous ». Il suffirait donc que les envoyés de ces États participent à l'IETF et on aurait donc automatiquement accès à « la voix des utilisateurs ». Il y a déjà de ces envoyés qui participent à l'IETF. (À chaque réunion, il y a au moins une personne avec un badge NSA, sans compter ceux qui n'ont pas le badge, mais ont le même employeur.) La question de leur représentativité (l'envoyé du gouvernement français est-il vraiment le porte-parole des soixante millions d'utilisateurs français ?) a été une des questions essentielles lors des discussions menant à ce RFC. Chaque gouvernement prétend qu'il est représentatif. C'est clairement faux pour les dictatures mais cela ne veut pas dire que les démocraties sont parfaites, sans compter la difficulté de classer les pays dans l'une ou l'autre catégorie. Bref, personne n'a envie de transformer l'IETF en un organisme multi-gouvernemental paralytique, comme l'ONU. (Les experts en « gouvernance Internet » noteront que l'ICANN a le même genre de problèmes, et son GAC - Governmental Advisory Committee - ne satisfait personne.)
À ce sujet, bien que cela ne soit pas mentionné explicitement dans le RFC, il faut aussi dire que les envoyés des États sont en général contraints par un processus de décision interne très rigide, et ne peuvent pas s'exprimer librement. Cela ne colle évidemment pas avec le mécanisme de discussion très ouvert et très vif de l'IETF. Je me souviens d'une réunion où deux personnes portant la mention FBI sur leur badge étaient venus me parler de problèmes avec un des documents sur lesquels je travaillais. Lorsque je leur ai fait remarquer que leurs analyses, assez pertinentes, devraient être faites dans la réunion officielle du groupe de travail et pas juste dans les couloirs, ils m'avaient répondu que leurs supérieurs ne les y autorisaient pas. Difficile d'envisager une participation effective des États dans ces conditions.
Bon, si on ne fait pas appel aux États, à qui ? Le RFC mentionne la classique « société civile » dont personne ne sait trop en quoi elle consiste, mais à qui tout le monde rend hommage. Selon l'interlocuteur, « société civile » peut vouloir dire « tout le monde sauf l'État » (incluant, par exemple, le MEDEF), ou bien « tous les individus » ou encore « tous les individus organisés (associations, syndicats, etc) » sans compter ceux qui disent « société civile » pour « les gens qui sont d'accord avec moi ». (Disons franchement les choses : l'un des problèmes de fond de la « gouvernance de l'Internet » est qu'il n'y a que peu ou pas de représentation des utilisateurs. Tout le monde parle pour eux et elles, mais ielles n'ont pas de voix propre. Ce syndrome « tout le monde se réclame de l'utilisateur final » avait été très net, par exemple, lors des débats sur DoH, mais aussi dans d'autres questions de gouvernance.)
Mais le RFC note à juste titre qu'il existe des organisations qui ont sérieusement travaillé les sujets politiques liés à l'Internet, et qui connaissent donc bien les problèmes, et les conséquences des choix techniques. (En France, ce serait par exemple La Quadrature du Net, Framasoft et certainement plusieurs autres.) Bien que rien ne garantisse leur représentativité, note le RFC, ces organisations sont sans doute le premier canal à utiliser pour essayer de comprendre les intérêts des utilisateurs finaux. La recommandation est donc d'essayer d'identifier ces groupes et de travailler avec eux.
L'accent est mis sur la nécessité d'aller les voir, de ne pas juste leur dire « venez participer à l'IETF » (ils n'ont pas forcément le temps ou les moyens, et pas forcément envie de se lancer dans ce processus). Outre ses réunions formelles et ses listes de diffusion, l'IETF a quelques canaux de communication plus adaptés mais certainement très peu connus (« venez à la Bar BoF, on en parlera autour d'une bière »). Idéalement, c'est l'IETF qui devrait prendre l'initiative, et essayer d'aller vers les groupes organisés d'utilisateurs, par exemple en profitant des réunions existantes. Le RFC recommande de faire davantage d'efforts de sensibilisation, faire connaitre le travail de l'IETF, ses enjeux, etc. (Mon expérience est qu'il est très difficile de faire s'intéresser les gens à l'infrastructure de l'Internet, certes moins sexy qu'une page d'accueil colorée d'un site Web. Après tout, on ne peut pas faire boire un âne qui n'a pas soif.)
Le RFC donne un exemple d'un atelier ayant réuni des participants à l'IETF, et des gens qui n'ont pas l'habitude d'aller à l'IETF, sur un sujet assez chaud politiquement, la réunion ESCAPE, documentée dans le RFC 8752.
On peut aussi penser que cette tâche de sensibilisation à l'importance de la normalisation, et à ses conséquences politiques, ne devrait pas revenir entièrement à l'IETF, qui n'est pas forcément bien préparée à cela. Le RFC cite à juste titre l'Internet Society, qui fait en effet un important travail dans ce domaine.
Le RFC continue avec une section sur le concept de systèmes
centrés sur l'utilisateur. Il part de l'exemple du Web, certainement un des plus gros
succès de l'Internet. Dans le Web, l'IETF normalise le protocole
HTTP (le
W3C faisant
le reste). La norme HTTP, le RFC 7230 décrit
explicitement le rôle du client HTTP, appelé user
agent dans la norme (et c'est de là que vient l'en-tête
HTTP User-Agent:
). À noter que le RFC mélange
client HTTP et navigateur Web : le client
d'un serveur HTTP n'est pas forcément un navigateur. Quoi qu'il en
soit, la discussion continue sur le navigateur : celui-ci sert
d'intermédiaire entre l'utilisateur et le serveur Web. Au lieu d'un
client spécifique d'un service, et qui a accès à toute la machine de
l'utilisateur pour faire sa tâche, le passage par cet intermédiaire
qu'est le navigateur permet de créer un bac à sable. Quelles que
soient les demandes faites par le serveur Web, il ne sera pas
possible de sortir du bac à sable et, par exemple, de lire et
d'écrire arbitrairement des fichiers sur la machine de
l'utilisateur.
Au contraire, les services sur le Web qui exigent l'installation d'un client local demandent à l'utilisateur une confiance aveugle : ces clients peuvent faire des choses que le navigateur bloquerait normalement. Ce n'est pas par hasard que les sites des médias demandent si souvent l'installation de leur « app » quand on navigue depuis un ordiphone : ces clients locaux ont bien plus de possibilité, notamment de pistage et de surveillance que ce qui est possible via le navigateur.
(Le RFC ne mentionne pas un autre moyen de créer la confiance : le logiciel libre. La totalité de ces « apps » sont du logiciel privateur. Si le logiciel est sous une licence libre, il y a nettement moins de craintes à avoir lorsqu'on l'installe.)
Le RFC estime que le fait d'avoir défini explicitement le user agent et ses propriétés a facilité le succès du Web, en permettant la création de cet intermédiaire de confiance qu'est le navigateur Web, un exemple de système « centré sur l'utilisateur ». Bien sûr, cette vision est très contestable. Le RFC note par exemple que, à vouloir tout faire passer par le Web, on aboutit à des navigateurs qui sont devenus très complexes, ce qui se paie en sécurité et en performances. En outre, cette complexité diminue la concurrence : il n'y a que très peu de navigateurs et beaucoup de composants cruciaux, comme WebKit sont communs à plusieurs navigateurs, diminuant la diversité et le choix. Aujourd'hui, créer un nouveau navigateur en partant de zéro semble impossible, ce qui a de lourdes conséquences sur la distribution du pouvoir dans le Web.
Mais le RFC estime que c'est quand même une meilleure approche que celle, par exemple, de l'Internet des objets, où il n'y a pas de norme d'interaction entre l'utilisateur et le système, pas de « système centré sur l'utilisateur », ce qui fait que l'utilisateur doit faire une confiance aveugle à des systèmes opaques, dont la mauvaise qualité (notamment en sécurité) et la mauvaise éthique ont déjà été largement montrées.
On a dit plus haut que, dans le meilleur des cas, le travail de l'IETF menait à des solutions qui étaient positives pour tout le monde. L'IETF peut alors laisser les différentes parties prenantes interagir (cf. l'article « Luttes dans l'Internet », même si, aujourd'hui, je traduirais tussle par un terme moins violent que lutte). Mais ce cas idéal n'est pas systématique. Parfois, les solutions techniques normalisées dans les RFC ne sont positives que pour certains, et neutres pour d'autres (ne leur apportent aucun avantage). Et parfois, c'est pire, les solutions sont négatives pour certains. Dans le monde réel, fait de différences d'opinion et d'intérêts (et de lutte des classes, ajouterait un marxiste), il ne faut pas être naïf : on ne va pas plaire à tout le monde. Conformément aux principes établis plus haut, le RFC affirme que, si une solution envisagée a forcément des conséquences négatives, il faut faire en sorte que ces conséquences négatives ne soient pas supportées par les utilisateurs finaux, mais par d'autres parties prenantes.
L'IETF a déjà été confrontée à de tels choix, et a documenté cette décision, par exemple dans les RFC 7754 (sur le filtrage), RFC 7258 et RFC 7624 (sur la surveillance), RFC 7288 (sur les pare-feux des machines terminales) et enfin RFC 6973 (sur la vie privée).
Le RFC note aussi que certaines décisions politiques de l'IETF peuvent être correctes mais insuffisantes. Ainsi, le RFC 3724 insiste sur l'importance du modèle de bout en bout. Mais cela ne suffit pas si c'est la machine avec qui on communique qui trahit. Chiffrer grâce à HTTPS quand on parle avec un GAFA n'est une protection que contre les tiers, pas contre le GAFA.
Au sujet des GAFA, le RFC note que la concentration des services dans les mains d'un petit groupe de sociétés est un problème pour les utilisateurs. Il affirme aussi que cela peut être encouragé par des propriétés du protocole IETF. (C'est nettement plus contestable : quelles sont les caractéristiques de SMTP qui expliquent la concentration du courrier chez Gmail et Outlook.com ? Cet argument semble plutôt une allusion maladroite aux débats sur DoH.)
Comme indiqué au début, les utilisateurs finaux ne forment pas un groupe homogène. Ils n'ont ni les mêmes intérêts, ni les mêmes opinions. Cela peut entrainer des difficultés pour l'application du principe « les utilisateurs d'abord ». Par exemple, dans un cas hypothétique où une solution technique entrainerait des conséquences positives pour les utilisateurs d'un pays et négatives dans un autre pays, que faire ? Le RFC suggère de privilégier l'évitement des conséquences négatives. Dans certains cas, il ne sera pas possible de les éviter complètement, et il faudra vraiment faire des compromis. Mais, au moins, il ne faudra pas cacher le problème sous le tapis, et bien documenter ce compromis.
Le principe « les utilisateurs d'abord » s'applique aussi à l'IETF elle-même. Le RFC se termine en affirmant qu'il ne faut pas que l'IETF privilégie ses propres besoins. Ainsi, s'il faut compliquer la tâche des auteurs de RFC pour mieux préserver les intérêts de l'utilisateur, eh bien il faut le faire. De même, dit le RFC, la beauté de l'architecture technique n'est pas un but en soi, elle doit passer après les intérêts des utilisateurs finaux.
En anglais, vous pouvez aussi lire la synthèse qu'avait publié l'auteur du RFC, où il s'exprime plus librement que dans le RFC, par exemple sur DoH. Un article de synthèse, allant plus loin que le RFC a été écrit par Vesna Manojlovic. D'autre part, la norme HTML (du W3C) a une mention qui va dans le même esprit que ce RFC : « In case of conflict, consider users over authors over implementors over specifiers over theoretical purity. » (les mauvaises langues pourront faire remarquer que ce mépris de la « theoretical purity » explique le b...l technique qu'est le Web). Ou, en sens opposé, un article qui s'était vigoureusement opposé à cet empouvoirement des utilisateurs.
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : W. Kumari (Google), C. Doyle
(Juniper Networks)
Pour information
Réalisé dans le cadre du groupe de travail IETF opsawg
Première rédaction de cet article le 6 octobre 2020
Quand vous êtes opérateur réseau et que vous avez à installer une nouvelle machine, par exemple un routeur, dans un centre de données lointain, vous avez deux solutions : envoyer un employé faire l'installation sur place, ce qui peut être coûteux, ou bien demander à la société qui gère le centre de données de le faire pour vous, ce qui n'est pas forcément génial question sécurité, puisque, selon la façon dont c'est fait, ils auront peut-être la possibilité de regarder ou de bricoler la configuration de la machine. Ce nouveau RFC propose un moyen simple d'améliorer la sécurité dans le deuxième cas, en chiffrant un fichier de configuration avec la clé publique de la machine. Celle-ci pourra le déchiffrer après téléchargement, en utilisant sa clé privée. (Cette solution nécessitera donc un changement des équipements pour que chacun ait une paire clé publique / clé privée.)
Précisons la question à résoudre. Ce problème est une variante d'un problème bien classique en informatique, celui du bootstrap. Par exemple, si la nouvelle machine est protégée par un mot de passe, il faut bien rentrer ce mot de passe la première fois. Si c'est un employé du centre de données qui le fait, il connaitra le mot de passe. Et si on le change juste après ? Rien ne dit qu'il n'aura pas laissé une porte dérobée. Il n'y a pas de solution générale et simple à ce problème. Notre RFC se contente d'une solution partielle, qui améliore la sécurité.
On va donc s'imaginer à la place de la société Machin, un gros opérateur réseau dont l'essentiel des équipes est à Charleville-Mézières (pourquoi pas ?). La société a des POP dans beaucoup d'endroits, et la plupart du temps dans ces centres de données qu'elle ne contrôle pas, où elle loue juste de l'espace. Comme les équipements réseau tombent parfois en panne, ou, tout simplement, ne parviennent plus à gérer le trafic toujours croissant, la société doit de temps en temps installer de nouveaux matériels, routeurs, commutateurs, etc. Si le centre de données où il faut installer est proche de Charleville-Mézières, tout va bien : un employé de l'opérateur Machin se rend sur place, le fabricant du routeur a déjà livré la nouvelle machine, il n'y a plus qu'à la racker et à la configurer (« Enter the password for the administrative account ») ou, en tout cas, faire une configuration minimale qui permettra de faire ensuite le reste via SSH, RESTCONF (RFC 8040) ou une autre technique d'administration à distance.
Mais supposons que la machine à installer, mettons que ce soit un gros routeur, doive être placé chez un centre de données éloigné, mettons Singapour (exemple choisi parce que c'est vraiment loin). Même avant la pandémie de Covid-19, un voyage à Singapour n'était guère raisonnable, du point de vue du temps passé, et du point de vue financier. Mais les centres de données ont tous un service d'action à distance (remote hands) où un employé de la société qui gère le centre peut appuyer sur des boutons et taper des commandes, non ? C'est en effet très pratique, mais ce service est limité à des cas simples (redémarrer…) et pas à une configuration complète. Celle-ci poserait de toute façon un problème de sécurité car l'employé en question aurait le contrôle complet de la machine pendant un moment.
(Une autre solution, apparemment non mentionnée dans le RFC, est que le fabricant du routeur livre à Charleville-Mézières l'appareil, qui soit alors configuré puis envoyé à Singapour. Mais cela impose deux voyages.)
Bon, et une solution automatique ? La machine démarre, acquiert une adresse IP, par exemple par DHCP (RFC 2131 et RFC 8415), et charge sa configuration sous forme d'un fichier récupéré, par exemple avec TFTP (RFC 1350). Mais cette configuration peut contenir des informations sensibles, par exemple des secrets RADIUS (RFC 2865). TFTP n'offre aucune sécurité et ce n'est pas très rassurant que de laisser ces secrets se promener en clair dans le centre de données singapourien. Il existe des contournements variés et divers pour traiter ces cas, mais aucun n'est complètement satisfaisant.
Bref, le cahier des charges de notre RFC est un désir de confidentialité des configurations du nouveau routeur. (Il y a une autre question de sécurité importante, l'authenticité de cette configuration, mais elle n'est pas traitée dans ce RFC. Des solutions, un peu complexes, existent, comme SZTP - RFC 8572 ou BRSKI - RFC 8995.) La solution présentées dans notre RFC n'est pas complète. Elle améliore les procédures d'installation (voir par exemple la documentation de celle de Cisco) mais ne prétend pas résoudre tout le problème.
Ce RFC ne spécifie pas un protocole, mais décrit une méthode, que chaque constructeur d'équipements réseau devra adapter à ses machines et à leurs particularités. Cette méthode est conçue pour ces équipements (routeurs et commutateurs, notamment), mais pourrait être adaptée à des engins comme les serveurs. Mais en attendant, ce RFC se focalise sur le matériel réseau.
La section 2 du RFC résume la solution. Elle nécessite que la machine à configurer ait un mécanisme de démarrage automatique de l'installation (autoinstall ou autoboot dans les documentations), permettant d'obtenir une adresse IP (typiquement avec DHCP) puis de récupérer un fichier de configuration (typiquement avec TFTP mais cela peut être HTTP ou un autre protocole de transfert de fichiers, les options DHCP 66 - Server-Name - ou 150 - TFTP server address pouvant être utilisées pour indiquer le serveur). Le nom du fichier est par exemple issue de l'option DHCP 67 - Bootfile-Name.
La méthode nécessite un identificateur, comme le numéro de série ou bien l'adresse MAC de l'équipement réseau qui est en cours d'installation. (Rien n'interdit d'utiliser un autre identificateur, par exemple un UUID - RFC 9562, mais l'avantage du numéro de série est qu'il est en général marqué sur la machine et facilement accessible, donc la personne dans le centre de données à Singapour peut le lire et le transmettre à la société Machin.)
Le RFC donne un exemple concret. La société Machin commande un nouveau routeur à un fabricant de routeur, mettons Truc, en indiquant l'adresse de livraison à Singapour. Truc doit indiquer à Machin le numéro de série de l'engin. Et il faut qu'une paire clé publique / clé privée spécifique à cet engin soit générée, la clé publique étant communiquée à l'acheteur (Machin). [Cette étape est l'une des deux seules que ne font pas les routeurs actuels.] Pendant que le routeur voyage vers Singapour, un employé de Machin prépare la configuration du routeur, et la chiffre avec la clé publique de ce routeur particulier. Puis il la place sur le serveur de fichiers, indexée par le numéro de série. Les employés du centre de données mettent le routeur dans l'armoire et le câblent. Le routeur démarre et voit qu'il n'a pas encore été configuré. Il récupère une adresse IP puis contacte le serveur de fichiers. Il récupère la configuration, la déchiffre [deuxième étape qui n'est pas encore mise en œuvre dans un routeur typique d'aujourd'hui] et l'installe. Désormais, le routeur est autonome. Les équipes de l'opérateur Machin peuvent le contacter par les moyens habituels (SSH, par exemple). Un éventuel attaquant pourra regarder le trafic ou, plus simplement, demander le fichier de configuration au serveur de fichiers, mais il ne pourra pas le déchiffrer.
Cette solution ne nécessite que peu de changements aux mécanismes d'aujourd'hui. La section 3 du RFC décrit ce que les fabricants de routeurs devront faire, pour qu'elle soit disponible. Le routeur devra disposer d'une paire clé publique / clé privée, générée lors de sa fabrication. Et le fabricant doit pouvoir accéder à cette clé facilement (les clés cryptographiques ne peuvent typiquement pas être affichées sur un petit écran LCD…), afin de la communiquer à l'acheteur. (On peut aussi envisager le RFC 7030 ou bien le RFC 8894.) Le fabricant de routeurs doit avoir un mécanisme de communication de ces clés au client. (Comme il s'agit de clés publiques, il n'est pas indispensable que ce soit un mécanisme confidentiel, mais il faut évidemment qu'un tiers ne puisse pas y ajouter des clés de son choix.) Le RFC recommande X.509 (RFC 5280) comme format pour ces clés, même s'il n'y a pas forcément besoin des méta-données de X.509 comme la date d'expiration.
Et le client, l'acheteur du routeur ? La section 4 décrit ce qu'il doit faire. Il faut réceptionner et transmettre le numéro de série du routeur (typiquement, cela veut dire une communication entre le service Achats et les Opérations, ce qui n'est pas toujours facile), puis récupérer la clé publique. Et enfin, il faut chiffrer la configuration (le RFC suggère S/MIME - RFC 5751).
Quelques détails supplémentaires figurent en section 5. Par exemple, si le routeur le permet (il faut du haut de gamme…), la clé privée peut être stockée dans un TPM. (Le RFC suggère également de jeter un œil à la Secure Device Identity de la norme IEEE Std 802-1AR.)
Et la section 7 du RFC revient sur la sécurité de ce mécanisme. Il sera de toute façon toujours préférable à la solution actuelle, qui est de transmettre la configuration en clair. Mais cela ne veut pas dire que tout soit parfait. Ainsi, un attaquant qui a un accès physique à la machine pendant son trajet, ou bien une fois qu'elle est arrivée dans le centre de données, peut extraire la clé privée (sauf si elle est stockée dans un TPM) et déchiffrer à loisir.
On a dit au début que le but de ce RFC n'était pas de trouver un moyen d'authentifier la configuration. (Une signature ne serait pas réaliste, car il y a peu de chances que le fabricant mette les clés publiques de chaque opérateur dans le routeur.) Un attaquant qui contrôle, par exemple, le réseau sur lequel se trouve l'équipement réseau qui se configure, peut donc diriger les requêtes TFTP vers un serveur de son choix et envoyer une configuration « pirate » à cet équipement. (Il peut même la chiffrer puisque la clé publique de l'équipement sera sans doute facilement accessible publiquement, pour faciliter la tâche du client.)
Et, bien sûr, rien ne protège contre un vendeur malhonnête. Si le fabricant du routeur ou du commutateur est malveillant (ou piraté par un de ses employés, ou par un pirate extérieur), il peut tout faire. C'est le problème de la supply chain en sécurité, et il est très difficile à résoudre. (Cf. les craintes concernant les pratiques de Huawei mais il serait naïf de penser que seuls les fabricants chinois soient malhonnêtes. Voyez aussi mon article sur les portes dérobées dans les routeurs.)
Si vous aimez le concret, l'annexe A du RFC donne les commandes
OpenSSL pour effectuer les différentes
manipulations décrites par ce RFC. (J'ai modifié les commandes du
RFC, qui ne marchaient pas pour moi.) Le fabricant du routeur va
d'abord générer la paire de clés sur le routeur, la clé publique
étant ensuite emballée dans un certificat
(SN19842256
étant le numéro de série du
routeur), et auto-signer :
% openssl req -new -nodes -newkey rsa:2048 -keyout key.pem -out SN19842256.csr ... Common Name (e.g. server FQDN or YOUR name) []:SN19842256 ... % openssl req -x509 -days 36500 -key key.pem -in SN19842256.csr -out SN19842256.crt
L'opérateur réseau va fabriquer un fichier de configuration (ici, trivial) :
% echo 'Test: true' > SN19842256.cfg
Ensuite, récupérer la clé (SN19842256.crt
) et
chiffrer la configuration :
% openssl smime -encrypt -aes-256-cbc -in SN19842256.cfg -out SN19842256.enc -outform PEM SN19842256.crt % cat SN19842256.enc -----BEGIN PKCS7----- MIIB5gYJKoZIhvcNAQcDoIIB1zCCAdMCAQAxggGOMIIBigIBADByMFoxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX ...
On copie ensuite la configuration chiffrée sur le serveur de fichiers.
Lorsque le routeur démarre, il charge ce fichier et exécute une commande de déchiffrement :
% openssl smime -decrypt -in SN19842256.enc -inform PEM -out config.cfg -inkey key.pem % cat config.cfg Test: true
Pour l'instant, aucun fabricant de routeur ne semble avoir annoncé une mise en œuvre de ce RFC. Comme le RFC décrit un mécanisme générique, et pas un protocole précis, les détails dépendront sans doute du vendeur.
Date de publication du RFC : Octobre 2020
Auteur(s) du RFC : J. Seedorf (HFT Stuttgart - Univ. of Applied Sciences), M. Arumaithurai (University of Goettingen), A. Tagami (KDDI Research), K. Ramakrishnan (University of California), N. Blefari Melazzi (University Tor Vergata)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF icnrg
Première rédaction de cet article le 24 octobre 2020
Si vous aimez les films catastrophe, voici un RFC pour vous ; il explore l'utilisation de l'ICN lors de grands désastres. N'espérez pas trouver de solutions, c'est un travail purement théorique. (Comme beaucoup de choses qui touchent à l'ICN.)
L'ICN (Information Centric Networking) ? C'est quoi ? Il s'agit d'une approche des réseaux informatiques où tout est vu comme du contenu, auquel les clients veulent accéder. Les routeurs ICN vont donc se charger de récupérer l'information, sans se soucier d'où elle vient. L'ICN est décrit dans des documents comme le RFC 7476 et le RFC 7927.
Parmi tous les RFC, il n'y a pas que l'ICN qui peut apporter des idées neuves en matière de robustesse lors de grandes catastrophes. Le DTN (RFC 9171), réseau acceptant les déconnexions fréquentes, est également une bonne approche. En effet, en cas de désastre, il est sûr que le réseau sera affecté, et le concept « stocke et réessaie » de DTN est donc un outil utile. Mais ICN offre des possibilités supplémentaires. D'ailleurs, le RFC 7476 (section 2.7.2) citait déjà cette possibilité d'utiliser l'ICN en cas de catastrophe. L'idée est que la couche réseau peut aider à partiellement contourner les problèmes post-catastrophe. Les applications ont leur rôle à jouer également, bien sûr, mais ce n'est pas l'objet de ce RFC.
La section 2 du RFC liste des cas d'usage. Comme déjà le RFC 7476, on commence par le tremblement de terre de Tohoku, qui avait détruit une partie importante de l'infrastructure, notamment en ce qui concerne la fourniture d'électricité. Or, après une telle catastrophe, il y a une grosse demande de communication. Les autorités veulent envoyer des messages (par exemple par diffusion sur les réseaux de téléphonie mobile), transmettre des informations, distribuer des consignes. Les habitants veulent donner des nouvelles à leurs proches, ou bien en recevoir. Les victimes veulent savoir où se trouvent les secours, les points de ravitaillement, etc.
Les gens de l'ICN étant toujours à la recherche de subventions, ils citent fréquemment les thèmes à la mode, qui promettent l'attention des pouvoirs publics. C'est ainsi que la liste des cas d'usage inclus évidemment le terrorisme (pourquoi pas la cyberguerre, tant qu'on y est ?). Dans ce cas, des difficultés supplémentaires surviennent : les attaquants peuvent effectuer des attaques par déni de service pour empêcher l'utilisation du réseau, si l'attaque elle-même ne l'a pas arrêté, ils peuvent surveiller l'utilisation du réseau pour, par exemple, découvrir des cibles intéressantes pour une nouvelle attaque, ils peuvent envoyer des messages mensongers pour créer une panique, etc. Le problème est donc plus difficile que celui d'une catastrophe naturelle.
Aujourd'hui, les réseaux existants ne permettent pas forcément de traiter les cas de catastrophes, même « simplement » naturelles. La section 3 du RFC liste les principaux défis qu'il faudrait traiter pour cela :
Bon, ça, ce sont les problèmes. Maintenant, en quoi est-ce que l'ICN peut aider ? Plusieurs arguments sont avancés par le RFC (dont certains, à mon avis, plus faibles que d'autres) :
En parlant de DTN, notons que DTN seul manque de certaines fonctions que pourrait fournir l'ICN. C'est le cas par exemple du publish/subscribe. Dans certains cas, ces fonctions pourraient être ajoutées au DTN, comme présenté dans « Efficient publish/ subscribe-based multicast for opportunistic networking with self-organized resource utilization » (par Greifenberg, J. et D. Kutscher) ou bien « A Socio-Aware Overlay for Publish/Subscribe Communication in Delay Tolerant Networks » (par Yoneki, E., Hui, P., Chan, S., et J. Crowcroft).
La section 4 du RFC précise ensuite les scénarios d'usage, et les exigences qui en découlent. Par exemple, pour le scénario « diffuser de l'information aux gens », la question de l'authentification est cruciale, de fausses informations diffusées par un malveillant ou par un plaisantin pourraient avoir des conséquences graves.
Est-ce que l'ICN peut assurer ces missions, là, aujourd'hui ? Clairement non, et la section 5 du RFC décrit tout ce qu'il reste à faire (rappelez-vous que l'ICN, c'est de la recherche fondamentale). Par exemple, dans le contexte envisagé, celui d'une vraie catastrophe, il est possible que les données doivent être transportées par des « mules », des porteurs physiques (on peut penser au RFC 1149 mais aussi, plus sérieusement, à UUCP où les messages étaient parfois transportés ainsi, notamment dans les pays du Sud). Cette proposition est envisagée dans l'article de Tagami, A., Yagyu, T., Sugiyama, K., Arumaithurai, M., Nakamura, K., Hasegawa, T., Asami, T., et K. Ramakrishnan, « Name-based Push/Pull Message Dissemination for Disaster Message Board ».
Enfin, la section 5 du RFC décrit ce qui reste à faire (l'ICN est aujourd'hui incapable d'assurer les missions décrites au début de ce RFC). La première chose serait d'évaluer en vrai les solutions ICN existantes. Un test à la prochaine catastrophe ?
À noter que le travail ayant mené à ce RFC a été fait en partie dans le cadre du projet GreenICN.
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : T. Herbert (Intel)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 25 septembre 2020
Dans l'Internet, un routeur est toujours autorisé à jeter un paquet quand il ne peut pas le traiter, parce que ses files d'attente sont pleines, par exemple. Après tout, à l'impossible, nul n'est tenu, et les protocoles de transport et d'application savent à quoi s'attendre, et qu'ils devront peut-être gérer ces paquets perdus. Mais un routeur peut aussi jeter un paquet parce que des caractéristiques du paquet rendent impossible le traitement. Dans ce cas, il serait sympa de prévenir l'émetteur du paquet, pour qu'il puisse savoir que ses paquets, quoique légaux, ne peuvent pas être traités par certains routeurs. Notre nouveau RFC crée donc plusieurs nouveaux messages ICMP pour signaler à l'émetteur d'un paquet IPv6 qu'il en demande trop au routeur, par les en-têtes d'extension IPv6 qu'il ajoute.
La section 1 du RFC commence par lister les cas où un routeur peut légitimement jeter des paquets IPv6, même si les ressources matérielles du routeur ne sont pas épuisées :
La section 2 du RFC définit donc six nouveaux codes pour indiquer les problèmes, à utiliser avec le type ICMP 4 (Parameter Problem, cf. RFC 4443, section 3.4). Petit rappel : les messages ICMP ont un type (ici, 4) et les messages d'erreur d'un même type sont différenciés par un code qui apporte des précisions. Ainsi, le type 1, Destination Unreachable, peut servir par exemple pour des messages d'erreur ICMP de code 1 (filtrage par décision délibérée), 4 (port injoignable), etc. Les codes de notre RFC sont dans le registre IANA. Les voici :
Et la section 3 définit un nouveau code pour le type d'erreur Destination Unreachable, le code 8, en-têtes trop longs (Headers too long). Il a également été mis dans le registre IANA. Les messages utilisant ce code suivent le format du RFC 4884.
Et que fait-on quand on envoie ou reçoit des erreurs ICMP liées au traitement des en-têtes ? La section 4 du RFC rappelle les règles. D'abord, il faut évidemment suivre le RFC 4443, par exemple la limitation du rythme d'envoi des erreurs (RFC 4443, section 2.4f). Et, à la réception d'un message d'erreur ICMP, il est crucial de le valider (n'importe quelle machine sur l'Internet a pu générer ce message, en mentant sur son adresse IP source). Pour cela, on peut utiliser la portion du paquet original qui a été incluse dans le message d'erreur : il faut vérifier qu'elle correspond à une conversation en cours (connexion TCP existante, par exemple). Ensuite, il y a des règles spécifiques aux erreurs de ce RFC :
La section 5 rappelle un point d'ICMP qu'il vaut mieux garder en tête. ICMP n'est pas fiable. Les messages d'erreur ICMP peuvent se perdre, soit, comme tout paquet IP, parce qu'un routeur n'arrivait pas à suivre le rythme, soit parce qu'une middlebox sur le trajet jette tous les paquets ICMP (une erreur de configuration fréquente quand les amateurs configurent un pare-feu cf. RFC 8504). Bref, il ne faut pas compter que vous recevrez forcément les messages indiqués dans ce RFC, si vos paquets avec en-têtes d'extension ne sont pas transmis.
La même section 5 explique les motivations pour ce RFC : on constate une tendance à augmenter le nombre et la taille des en-têtes d'extension IPv6. Cela peut être pour faire du routage influencé par la source, pour de l'OAM (RFC 9486), ou pour d'autres innovations récentes.
Comme indiqué au début, cette augmentation peut se heurter aux limites des routeurs. La section 5.2.3 donne des exemples concrets. Les routeurs ne sont pas du pur logiciel, beaucoup de fonctions sont mises dans du matériel spécialisé. Ainsi, les circuits de traitement des paquets peuvent ne pas avoir de notion de boucle. Pour traiter les en-têtes d'extension ou les options dans un en-tête, la solution évidente qu'est la boucle n'est pas disponible, et on fait donc un petit nombre de tests suivis d'un saut. Le nombre maximal de tests est donc limité.
Enfin, la section 6 de notre RFC discute des questions de sécurité. Il y a le problème du filtrage par les pare-feux (voir le RFC 4890) mais aussi le risque (assez lointain, je trouve) que l'observation de la génération de ces nouveaux messages d'erreur donnent des indications à un observateur sur le logiciel utilisé. C'est un problème commun à tous les choix optionnels. En tout cas, le RFC recommande que l'envoi de ces messages d'erreur soit configurable.
Actuellement, il ne semble pas qu'il existe déjà de mise en œuvre de ce RFC.
Date de publication du RFC : Août 2020
Auteur(s) du RFC : S. Cheshire (Apple), D. Schinazi (Google)
Chemin des normes
Première rédaction de cet article le 1 septembre 2020
La technique NAT64 pour permettre à des
machines d'un réseau purement IPv6
d'accéder à des services toujours uniquement en
IPv4 repose sur un préfixe IPv6 spécial,
utilisé pour donner l'impression aux machines IPv6 que le service
archaïque a de l'IPv6. Dans certains cas, il est pratique que
toutes les machines du réseau connaissent ce préfixe. Une
technique possible a été proposée dans le RFC 7050, utilisant un nom de domaine prévu à cet effet,
ipv4only.arpa
. Mais ce nom de domaine n'avait
pas été documenté rigoureusement comme nom de domaine spécial. C'est
désormais fait, avec ce nouveau RFC.
Le NAT64 est normalisé dans le RFC 6146, et la découverte, par une machine, du
préfixe IPv6 utilisé, est dans le RFC 7050. Ce dernier RFC
avait créé le nom de
domaine ipv4only.arpa
, mais sans
préciser clairement son statut, et notamment sans demander son
insertion dans le registre
des noms de domaine spéciaux. (Cette bavure bureaucratique
est d'ailleurs mentionnée dans le RFC 8244.)
Le but de notre nouveau RFC 8880 est de réparer cet
oubli et de documenter proprement le nom de domaine spécial
ipv4only.arpa
.
Un petit rappel si vous n'avez pas le courage de lire le RFC 7050 : le problème qu'on cherche à résoudre
est celui d'une machine qui voudrait bénéficier de
NAT64 mais sans utiliser systématiquement
le résolveur DNS64 (RFC 6147). Pour cela, elle émet une requête DNS de type AAAA (adresse
IPv6) pour le nom ipv4only.arpa
. Comme son
nom l'indique, ce nom n'a que des données de type A (adresses
IPv4). Si on récupère des adresses IPv6, c'est que le résolveur
DNS faisait du DNS64, et on peut déduire le préfixe IPv6 utilisé
de la réponse. Sans DNS64, on aura une réponse normale, rien sur
IPv6, et deux adresses stables en IPv4 :
% dig AAAA ipv4only.arpa ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18633 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1 ... ;; AUTHORITY SECTION: ipv4only.arpa. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. ( 2020040300 ; serial 7200 ; refresh (2 hours) 3600 ; retry (1 hour) 604800 ; expire (1 week) 3600 ; minimum (1 hour) ) ;; Query time: 95 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Thu May 07 15:22:20 CEST 2020 ;; MSG SIZE rcvd: 127 % dig A ipv4only.arpa ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2328 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 14, ADDITIONAL: 27 ... ;; ANSWER SECTION: ipv4only.arpa. 86400 IN A 192.0.0.170 ipv4only.arpa. 86400 IN A 192.0.0.171 ... ;; Query time: 238 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Thu May 07 14:24:19 CEST 2020 ;; MSG SIZE rcvd: 1171
En quoi est-ce que ce nom ipv4only.arpa
est « spécial » (section 2 du RFC) ? D'abord, il n'y a aucune
raison de le visiter en temps normal, il n'a aucune ressource
utile, et ses deux adresses IP sont stables et bien
connues. Paradoxalement, si on l'interroge, c'est qu'on espère un
mensonge, l'apparition d'adresses IPv6 qui ne sont pas dans la
zone originale. Ce nom de domaine permet en fait une communication
de la machine terminale vers un intermédiaire (le résolveur DNS),
pour savoir ce qu'il fait. C'est en cela que
ipv4only.arpa
est spécial.
Mais, rappelez-vous, le RFC 7050 avait
oublié de déclarer ipv4only.arpa
comme étant
spécial. Résultat, les différents logiciels qui traitent des noms
de domaine le traitent de la manière normale et, comme le note la
section 3 de notre RFC, cela a quelques conséquences ennuyeuses :
ipv4only.arpa
peut ne pas donner le
résultat attendu,ipv4only.arpa
alors qu'ils devraient avoir
le droit de tout gérer localement et de fabriquer des réponses
directement.Bref, il fallait compléter le RFC 7050, en suivant le cadre du RFC 6761. Ce RFC 6761 impose de lister, pour chaque catégorie de logiciel, en quoi le nom de domaine est spécial. C'est fait dans la section 7 de notre RFC, qui indique que :
ipv4only.arpa
comme un domaine normal. Si
elles le désirent, les applications peuvent résoudre ce nom
ipv4only.arpa
et apprendre ainsi si un
résolveur DNS64 est sur le trajet, mais ce n'est pas
obligatoire.ipv4only.arpa
comme spécial. Notamment, elles doivent utiliser le résolveur
configuré par le réseau (par exemple via DHCP)
pour résoudre ce nom. Le demander à un résolveur public n'aurait
en effet pas de sens.ipv4only.arpa
comme normal, et leurs
clients apprendront donc ainsi que leur résolveur ne fait pas de
DNS64.ipv4only.arpa
, et leurs
clients apprendront donc ainsi que leur résolveur fait du
DNS64, et sauront quel préfixe IPv6 est utilisé. Ils n'ont pas
besoin de consulter les serveurs faisant autorité, qui ne
pourraient rien leur apprendre qui n'est pas déjà dans le
RFC. L'annexe A du RFC donne un exemple de configuration pour
BIND pour attendre cet objectif.À noter qu'outre ipv4only.arpa
, notre RFC
réserve deux autres noms spéciaux,
170.0.0.192.in-addr.arpa
et
171.0.0.192.in-addr.arpa
, pour permettre la
« résolution inverse ». Contrairement à
ipv4only.arpa
, ils ne sont pas actuellement
délégués, et un résolveur normal, qui ne connait pas DNS64,
répondra donc NXDOMAIN (ce nom n'existe pas).
Sinon, la section 5 de notre RFC est dédiée à la sécurité et
note notamment que les noms synthétisés ne peuvent évidemment pas
être validés avec DNSSEC. C'est pour cela que la délégation
de ipv4only.arpa
n'est même pas
signée. (C'est un changement depuis le RFC 7050, qui demandait au contraire une zone signée, ce
que ipv4only.arpa
avait été au début.)
Vu par les sondes RIPE Atlas, voici les réponses de résolveurs à ce domaine spécial :
% blaeu-resolve -r 1000 -q AAAA ipv4only.arpa [] : 986 occurrences [64:ff9b::c000:aa 64:ff9b::c000:ab] : 4 occurrences [2a01:9820:0:1:0:1:c000:aa 2a01:9820:0:1:0:1:c000:ab] : 1 occurrences [2a0a:e5c0:0:1::c000:aa 2a0a:e5c0:0:1::c000:ab] : 1 occurrences Test #24069173 done at 2020-02-24T14:49:43Z
Sur mille sondes Atlas, la grande majorité ne trouve pas d'adresse
IPv6 pour ipv4only.arpa
ce qui, comme le nom
de domaine l'indique, est le comportement par défaut. Quelques
sondes sont derrière un résolveur DNS64, utilisant en général le
préfixe bien connu du RFC 7050 (les réponses
64:ff9b::…
), mais parfois d'autres préfixes
(NSP - Network-Specific Prefix - dans la
terminologie du RFC 7050).
Date de publication du RFC : Septembre 2020
Auteur(s) du RFC : T. Mizrahi (Huawei Smart Platforms iLab), J. Fabini (TU Wien), A. Morton (AT&T Labs
Pour information
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 24 septembre 2020
De nombreux protocoles Internet contiennent un champ où se trouve une estampille temporelle (timestamp en anglais), une indication de date et d'heure. Mais chaque protocole a défini le format de cette estampille de son côté, il n'y a pas de format standard. Ce nouveau RFC essaie de mettre un peu d'ordre là-dedans en proposant aux concepteurs de futurs protocoles :
Dans quels cas des protocoles contiennent dans leurs données une estampille temporelle ? Cela peut être bien sûr parce que le protocole est voué à distribuer de l'information temporelle, comme NTP (RFC 5905). Mais cela peut être aussi parce que le protocole a besoin d'indiquer le moment d'un événement, par exemple pour mesurer une latence (OWAMP, RFC 4656 ou les extensions à TCP du RFC 7323). La section 6 du RFC présente une liste non exhaustive de protocoles qui utilisent des estampilles temporelles, ce qui fournit d'autres exemples comme MPLS (RFC 6374), IPFIX (RFC 7011), TRILL (RFC 7456), etc. Il serait souhaitable que les formats de ces estampilles soient normalisés. Ainsi, utiliser le format de NTP dans un autre protocole permettrait facilement d'utiliser le temps NTP, sans perte de précision. Et la standardisation des formats permettait de les mettre en œuvre dans du matériel, comme les horloges. Bref, si vous concevez des protocoles réseau et qu'ils intègrent une information temporelle, lisez ce RFC.
Pour suivre la suite du RFC, il faut un peu de vocabulaire, rappelé en section 2. Notamment :
Les formats recommandés par le RFC sont décrits en section 4. Pourquoi trois formats plutôt qu'un seul ? Parce que tous les protocoles réseau n'ont pas forcément les mêmes contraintes en terme de taille du champ, de résolution temporelle, ou d'intégration avec d'autres protocoles (sur un système d'exploitation conçu pour des PC connectés à l'Internet, utiliser le format de NTP est sans doute le plus raisonnable, mais pour du matériel de métrologie complexe, celui de PTP peut être une meilleure idée).
D'abord, le grand classique, le format de NTP (RFC 5905, section 6). Il est très répandu sur l'Internet, et utilisé dans de nombreux protocoles (RFC 6374, RFC 4656, RFC 5357, et bien d'autres). Il stocke l'estampille sous forme d'un entier de 32 bits pour les secondes depuis l'epoch 1900, et d'un autre entier de 32 bits pour les fractions de seconde. Sa taille totale est de 64 bits, sa résolution de 2 puissance -32 seconde, soit 0,2 milliardièmes de seconde. Et sa période de bouclage est de 136 ans.
NTP a un autre format, sur seulement 32 bits, pour les cas où la taille compte, par exemple des objets contraints. Dans ce cas, le nombre de secondes et la fraction de seconde sont sur 16 bits chacun. Cela lui donne une période de bouclage très courte, seulement 18 heures.
Et le troisième format standard proposé est celui de PTP, dans sa version « tronquée » à 64 bits. Ce format est utilisé dans les RFC 6374, RFC 7456 ou RFC 8186. Il est surtout intéressant lorsqu'il faut interagir avec de l'instrumentation utilisant PTP. Sa résolution est d'une nanoseconde, sa période de bouclage est la même que celle de NTP mais comme son epoch est en 1970, cela remet le prochain bouclage à 2106.
(Vous noterez que notre RFC ne parle que des formats binaires
pour représenter les estampilles temporelles. Il existe également
des formats texte, notamment celui du RFC 3339, utilisé par exemple dans les RFC 5424, RFC 5646, RFC 6991, ou RFC 7493. Les formats de
HTTP
- en-têtes Date:
et
Last-Modified:
- ainsi que
l'Internet Message Format - IMF, RFC 5322 -
n'utilisent hélas pas le RFC 3339.)
Deux exemples d'utilisation de ces formats binaires standards sont donnés dans notre RFC, aux sections 6.1 et 6.2, si vous êtes un ou une concepteur(trice) de protocoles qui veut inclure une estampille temporelle.
Mais, si, malgré les sages conseils du RFC, vous êtes l'auteur(e) d'un protocole et vous voulez quand même créer votre propre format binaire pour indiquer date et heure, y a-t-il des règles à suivre ? (À part la règle « ne le faites pas ».) Oui, la section 3 du RFC donne des indications (la plupart sont assez évidentes) :
À propos de secondes intercalaires, que vous utilisiez un format à vous, ou bien la méthode recommandée d'un format existant, pensez aux aspects liés à la synchronisation des horloges entre elles (section 5 du RFC), sauf pour les cas d'horloges complètement isolées (mais les RFC sont écrits pour l'Internet, donc il est prévu de communiquer). Quel(s) protocole(s) utiliser, quelle référence de temps (UTC ? TAI ?), et comment gérer ces fameuses secondes intercalaires. Que faut-il faire si l'horloge recule, faut-il faire un changement brusque ou bien du smearing (cf. RFC 8633), etc.
Un avant-dernier détail : parfois, le format de l'estampille temporelle, et ses propriétés, sont décrits dans le protocole, ou dans un modèle formel, par exemple en YANG. C'est ce que fait OWAMP (RFC 4656) avec le champ donnant une estimation de l'erreur de mesure (section 4.1.2 du RFC 4656). Notre RFC nomme ces « méta » champs « champs de contrôle » et donne quelques règles à suivre quand on les utilise : extensibilité (on aura toujours besoin d'autre chose), taille (assez gros mais pas trop…), importance (obligatoire ou optionnel), catégorie (statique ou dynamique).
Enfin, il y a évidemment la question de la sécurité (section 9). Il est bien sûr obligatoire dans la description d'un protocole de fournir une analyse de sa sécurité (RFC 3552). Cela doit inclure les conséquences des estampilles temporelles sur la sécurité. Par exemple, de telles estampilles, si elles sont transmises en clair, peuvent renseigner un observateur sur les performances d'un réseau. Et, si elle ne sont pas protégées contre les modifications non autorisées (ce qui peut se faire, par exemple, avec un MAC), un attaquant peut fausser sérieusement le fonctionnement d'un protocole en changeant la valeur des estampilles.
Autre problème de sécurité, un attaquant actif qui retarderait délibérement les messages. Cela perturberait évidemment tout protocole qui utilise le temps. Et, contrairement à la modification des estampilles, cela ne peut pas se résoudre avec de la cryptographie. Enfin, la synchronisation des horloges est elle-même exposée à des attaques (RFC 7384). Si on déploie NTP sans précautions particulières, un attaquant peut vous injecter une heure incorrecte.
Date de publication du RFC : Août 2020
Auteur(s) du RFC : A. Cooper (Cisco), P. Hoffman (ICANN)
Pour information
Réalisé dans le cadre du groupe de travail IETF github
Première rédaction de cet article le 29 août 2020
Certains groupes de travail IETF, ou bien certains participant·e·s à la normalisation au sein de cette organisation, utilisent le service GitHub de Microsoft pour gérer le processus de production des normes. Ce RFC donne des conseils pratiques aux groupes de travail qui utilisent GitHub. Il suit les principes généraux énoncés dans le RFC 8874, en étant plus concret, davantage orienté « tâches ».
Il s'agit cependant juste de conseils : des groupes peuvent parfaitement faire différemment, des individus peuvent utiliser GitHub (ou un service libre équivalent, ce que le RFC ne mentionne guère) en suivant d'autres méthodes. Le RFC concerne surtout les groupes de travail qui se disent que GitHub pourrait être intéressant pour eux, mais qui ne sont pas sûr de la meilleure manière de faire. Les groupes plus expérimentés qui utilisent GitHub parfois depuis des années savent déjà.
La section 2 du RFC décrit le processus recommandé pour le cycle de vie du groupe de travail. Ce processus peut s'automatiser, GitHub ayant une API documentée. Je ne sais pas si à l'heure actuelle cette automatisation est réellement utilisée par l'IETF Secretariat, mais le RFC suggère l'usage d'outils comme ietf-gh-scripts ou comme i-d-template.
Le RFC demande que l'interface utilisée par les directeurs de
zones IETF et les présidents des groupes de travail dans le
DataTracker
permette de créer une organisation GitHub. Elle doit être nommée
ietf-wf-NOMDUGROUPE
(en pratique, cette
convention semble rarement suivie), les directeurs de la zone doivent
en être les propriétaires et les présidents du groupe doivent avoir
le statut d'administrateur (voyez par exemple les membre du groupe GitHub
QUIC). Elle est ensuite indiquée depuis le
DataTracker (cf. par exemple pour le groupe
capport). Il n'est pas possible actuellement d'associer
clairement les identités GitHub aux identités IETF (ça a été
demandé mais l'identité sur l'Internet est un sujet
complexe).
De même qu'il faudrait pouvoir créer facilement une organisation
dans GitHub, il serait bien de pouvoir créer facilement un nouveau
dépôt, et de le peupler avec les fichiers minimaux
(LICENSE
- en suivant ces
principes, README
,
CONTRIBUTING
- en s'inspirant de ces
exemples, etc). L'outil i-d-template
fait justement cela.
Une fois organisation et dépôt(s) créés, le groupe peut se mettre
à travailler (section 3 du RFC). Un des points importants est la
sauvegarde. Il est crucial de sauvegarder
tout ce qui est mis dans GitHub au cas où, par exemple, Microsoft
déciderait tout à coup de limiter l'accès ou de le rendre
payant. Les listes de diffusion de l'IETF sont archivées via MailArchive, mais tout ce
qui se passe sur GitHub doit l'être également. Pour les documents,
c'est facile, git est un VCS décentralisé,
avoir une copie complète (avec l'historique) du dépôt est le
comportement par défaut. L'IETF n'a donc qu'à faire un git
fetch
toutes les heures pour stocker une copie.
Il reste les tickets et les pull requests. Ils (elles ?) ne sont pas gérés par git. Il faut alors utiliser l'API de GitHub pour les sauvegarder.
Ce risque de pertes, si on ne fait pas de sauvegarde, fait d'ailleurs partie des risques mentionnés dans la section 4, sur la sécurité. Par contre, ce RFC ne mentionne pas les risques plus stratégiques liés à la dépendance vis-à-vis d'un système centralisé. C'est pour cela que certains documents sont développés en dehors de GitHub, par exemple le RFC 9156, successeur du RFC 7816 était sur un GitLab, FramaGit.
Date de publication du RFC : Août 2020
Auteur(s) du RFC : M. Thomson (Mozilla), B. Stark (AT&T)
Pour information
Réalisé dans le cadre du groupe de travail IETF git
Première rédaction de cet article le 29 août 2020
De nombreux groupes de travail de l'IETF utilisent GitHub pour coordonner leur travail dans cet organisme de normalisation. Ce nouveau RFC donne des règles et des conseils à suivre dans ce cas. Personnellement, je déplore qu'il ne contienne pas de mise en garde contre l'utilisation d'un service centralisé extérieur à l'IETF.
L'IETF développe des normes pour l'Internet, notamment la plupart des RFC. Ce travail met en jeu des documents, les brouillons de futurs RFC, des discussions, des problèmes à résoudre. Les discussions sont assez complexes, durent souvent des mois, et nécessitent de suivre pas mal de problèmes, avec beaucoup d'éléments à garder en tête. Bref, des outils informatiques peuvent aider. Mais attention, l'IETF a des particularités (cf. RFC 2418). D'abord, tout doit être public, le but étant que le développement des normes soit aussi transparent que possible. Et il ne s'agit pas que de regarder, il faut que tous les participants (l'IETF est ouverte) puissent intervenir. Ensuite, tout doit être archivé, car il faudra pouvoir rendre compte au public, même des années après, et montrer comment l'IETF est parvenue à une décision. (D'autres SDO, comme l'AFNOR, sont bien plus fermées, et n'ont pas ces exigences d'ouverture et de traçabilité.)
Traditionnellement, l'IETF n'avait que peu d'outils de travail en
groupe et chacun se débrouillait de son côté pour suivre le travail
tant bien que mal. Mais les choses ont changé et l'IETF a désormais
une importante série d'outils, aussi bien les « officiels »,
maintenus par un prestataire extérieur, AMSL,
que des officieux, disponibles sur
et maintenus par des
volontaires. Les outils officiels de l'IETF, mis à la disposition de
tous, comportent notamment :
https://tools.ietf.org/
Il n'y a pas par contre de VCS (dans les outils officieux, il y a un
service Subversion) ou de mécanisme de
suivi de tickets (il y a un
Trac officieux en
, il est très peu
utilisé).https://trac.ietf.org/
Un VCS est pourtant un outil très utile, et pas seulement pour les programmeurs. Travaillant en général sur des fichiers texte quelconques, il permet de coordonner le travail de plusieurs personnes sur un document, que celui-ci soit du code source ou bien un Internet-Draft. C'est le cas par exemple du VCS décentralisé git, très utilisé aujourd'hui.
On peut utiliser git tout seul, d'autant plus que le fait qu'il doit décentralisé ne nécessite pas d'autorité de coordination (c'est ainsi qu'est développé le noyau Linux, par exemple). Mais beaucoup de programmeurs (ou d'auteurs d'Internet Drafts) utilisent une forge complète, intégrant un VCS mais aussi un système de gestion de rapports, un wiki et d'autres fonctions. La plus connue de ces forges est GitHub, propriété de Microsoft. Comme souvent avec les réseaux sociaux (GitHub est le Facebook du geek), la taille compte : plus il y a de gens sur le réseau social, plus il est utile. C'est pour cela qu'il y a des fortes pressions à la centralisation, et une difficulté, en pratique, à partir, sauf à perdre les possibilités de coopération (c'est pour cela que prétendre qu'il y a consentement des utilisateurs aux pratiques déplorables des réseaux sociaux centralisés est une farce : on n'a pas le choix). L'utilisation de GitHub n'est évidemment pas obligatoire à l'IETF mais elle est répandue. Regardez par exemple le groupe de travail QUIC, celui sur HTTP, ou celui sur les portails captifs. GitHub est également utilisé en dehors des groupes de travail, par exemple pour les résultats et présentations des hackathons. Le but de ce RFC est de fournir quelques règles utiles (mais facultatives) pour les groupes qui travailleront sur GitHub. Un autre RFC, le RFC 8875, est plus concret, donnant des règles précises de configuration de GitHub pour un groupe de travail.
L'introduction du RFC rappelle juste en passant qu'il existe d'autres forges comme Bitbucket et du logiciel libre qui permet d'installer diverses forges locales. Ainsi, le logiciel de GitLab permet d'installer des forges indépendantes comme FramaGit. (J'ai récemment migré tous mes dépôts vers un GitLab.) C'est FramaGit qui est utilisé pour le développement de la norme sur la QNAME minimisation, le RFC 9156, qui a remplacé le RFC 7816. Mais GitHub est de loin le plus utilisé et, par exemple, pour le successeur du RFC 7816, quasiment tous les contributeurs avaient été obligés de se créer un compte sur FramaGit, ils n'en avaient pas avant. Le RFC ne rappelle même pas les dangers qu'il y a à utiliser un service centralisé, géré par une entreprise privée. Si GitLab est mentionné dans le RFC, la possibilité d'une instance IETF (gérée par l'actuel prestataire qui administre les ressources IETF, AMSL) n'apparait pas. Simple question d'argent ou problème plus fondamental ?
Bon, au boulot, maintenant, quelles sont les règles listées par ce RFC ? (Rappel : ce RFC reste à un haut niveau, les instructions précises sont dans le RFC 8875.) D'abord, comment décider d'utiliser (ou non) GitHub (section 3 du RFC) ? Fondamentalement, ce sont les présidents du groupe qui décident, après consultation avec les directeurs de la zone (une zone regroupe plusieurs groupes de travail). Ils vont également définir les conditions d'utilisation (par exemple, décider d'utiliser le système de rapports - issues - ou pas). Notez que, même si le groupe en tant que tel n'utilise pas GitHub, certains contributeurs peuvent évidemment s'en servir pour leurs besoins propres. D'une manière générale, le travail fait sur GitHub n'a pas d'autorité particulière, c'est juste un élément parmi d'autres pour le groupe. Ainsi, ce n'est pas parce qu'un ticket est fermé sur GitHub que le groupe de travail est lié par cette fermeture et ne peut plus discuter le point en question.
Le groupe peut ensuite créer un dépôt par document, ce qui est recommandé (c'est ce que fait le groupe capport) ou bien, s'il préfère, un seul dépôt pour tous les documents du groupe (sans aller jusque là, le groupe QUIC met tous les documents de base du protocole dans le même dépôt).
Les documents sont a priori en XML, format standard des documents IETF (cf. RFC 7991) mais on a le droit d'utiliser Markdown (cf. RFC 7328). Par contre, les formats doivent être du texte, pas de binaire comme avec LibreOffice, binaire qui passerait mal avec git (pas moyen de voir les différences d'une version à l'autre).
Ensuite, un peu de paperasserie (section 2 du RFC), avec les
règles administratives. Le RFC recommande de créer une organisation
dans GitHub pour chaque groupe de travail qui utilise GitHub (il n'y
a pas d'organisation « IETF »). Les propriétaires de cette
organisation doivent être les directeurs de la zone dont fait partie
le groupe, et les présidents ou présidentes du groupe. Les auteurs
des documents doivent évidemment avoir un accès en écriture. Les
dépôts du groupe de travail sur GitHub doivent être clairement
documentés, indiquant la charte du groupe, sa politique, sa gestion
des contributeurs (fichier CONTRIBUTING
, que
GitHub met en avant), etc. Et il faut évidemment pointer sur la
politique générale de l'IETF, le Note
Well (qui est tout le temps cité dans les réunions
physiques, mais qui doit également être connu des gens qui
n'interagissent que via GitHub). En sens inverse, le dépôt GitHub
doit être clairement indiqué sur les pages du groupe sur le site de
l'IETF, et sur les Internet-Drafts produits.
Un des intérêts d'une forge comme GitHub est qu'on dispose de plusieurs moyens de contribuer à un projet. Lesquels utiliser pour des documents IETF (section 4 du RFC) ? Il y a d'abord le système de suivi des questions. Il permet de noter les questions en cours et les décisions prises peuvent être inscrites dans le ticket, avant de le fermer. D'un coup d'œil, on peut voir facilement le travail en cours, et ce qui reste à faire. Dans chaque ticket, on peut voir l'ensemble des éléments liés à une question. Le système de suivi de questions (issue tracker) de GitHub permet bien d'autres choses, comme l'affectation d'un ticket à une personne. Un service très utile est l'étiquetage des questions (« rédaction » pour un texte mal écrit, « technique » pour un problème technique, etc). Comme dans la plupart des cas, le RFC n'impose pas une politique de gestion des étiquettes, seulement le fait qu'il y ait une politique et qu'elle soit communiquée aux participants. Même chose pour la politique de fermeture des tickets (un sujet parfois sensible).
Une fonction qui a beaucoup contribué à la popularité du GitHub est la possibilité de pull request (qui a une bonne traduction en français ? Alexis La Goutte suggère « demande d'intégration »), c'est-à-dire d'enregistrer une série de modifications qui sont collectivement soumises à l'approbation ou au rejet d'un responsable. Cela permet de proposer une contribution sans avoir l'autorisation d'écriture dans le dépôt, tout en permettant une grande traçabilité des contributions. C'est la méthode conseillée par le RFC pour soumettre des contributions significatives, notamment en raison de cette traçabilité. Par contre, il n'est pas conseillé de discuter de questions complexes dans les commentaires de la pull request ; comme la requête peut être modifiée, les commentaires risquent de devenir décalés par rapport au dernier état de la pull request.
GitHub permet d'avoir un flux de syndication pour les dépôts. C'est ce que j'utilise personnellement pour suivre les activités des dépôts qui m'intéressent. Mais il existe d'autres méthodes comme l'outil github-notify-ml, très utilisé à l'IETF.
J'ai dit plusieurs fois que ce RFC n'imposait pas une façon d'utiliser GitHub pour un groupe de travail IETF. Il y a plusieurs politiques possibles, chaque groupe de travail peut faire différemment, l'important étant que les participants au groupe soient au courant. Pour faciliter le choix, la section 5 du RFC propose un jeu de politiques typiques, parmi lesquelles on peut choisir. Elles sont classées de la plus élémentaire, à celle qui utilise le plus les possibilités de GitHub :
La section 5 discute également des politiques de nommage pour les étiquettes. Au minimum, il faut des étiquettes pour différencier les détails (« coquille dans le deuxième paragraphe ») des questions de fond. Ensuite, les étiquettes peuvent être utilisées pour :
Une notion importante à l'IETF est celle de consensus. L'IETF ne vote pas (dans une organisation sans adhésion explicite, qui aurait le droit de vote ?) et prend normalement ses décisions par consensus (cf. RFC 2418). Pour estimer s'il y a consensus ou pas, les présidents du groupe de travail peuvent utiliser tous les moyens à leur disposition (section 7 du RFC) dont GitHub, mais doivent s'assurer, sur la liste de diffusion officielle du groupe, qu'il s'agit bien d'un consensus. (Certains participants ou participantes peuvent ne pas utiliser GitHub, et il faut quand même tenir compte de leur avis.)
Le RFC note aussi (section 8) que, parmi les fonctions de GitHub, l'intégration continue peut être très utile. Par exemple, si les documents rédigés comportent des extraits dans un langage formel (comme YANG ou Relax NG), mettre en place un système d'intégration continue permet de détecter les erreurs rapidement, et de présenter automatiquement la dernière version, automatiquement vérifiée, des documents.
La section 9 du RFC donne quelque conseils aux auteurs (editors dans le RFC car le concept d'auteur est délicat, pour des documents collectifs). Avec GitHub, il y aura sans doute davantage de contributions extérieures (par exemple sous forme de pull requests), puisque le processus de soumission est plus facile pour les gens qui ne sont pas à l'IETF depuis dix ans. C'est bien l'un des buts de l'utilisation d'une forge publique. Mais cela entraine aussi des difficultés. Des tickets vont être dupliqués, des pull requests être plus ou moins à côté de la plaque, etc. Les auteurs doivent donc se préparer à un travail de tri important.
Enfin, la section 10, consacrée à la sécurité, détaille plusieurs des problèmes que pose l'utilisation de GitHub. D'abord, il y a la dépendance vis-à-vis d'une plate-forme extérieure à l'IETF ; si GitHub est en panne, perturbant le fonctionnement d'un ou plusieurs groupes de travail, l'IETF ne peut rien faire. (Le caractère décentralisé de git limite un peu l'ampleur du problème.) Ensuite, il y a la nécessité de faire des sauvegardes, y compris des tickets (qui ne sont pas, eux, gérés de manière décentralisée). Le RFC 8875 donne des consignes plus précises à ce sujet. Et il y a le risque de modifications non souhaitées, si un participant ayant droit d'écriture se fait pirater son compte. Le fait que tout soit public à l'IETF, et que git permette assez facilement de revenir en arrière sur des modifications, limitent l'importance de cette menace mais, quand même, il est recommandé que les auteurs sécurisent leur compte (par exemple en activant la MFA). En revanche, un autre risque n'est pas mentionné dans le RFC, celui du blocage par GitHub des méchants pays. GitHub bloque ou limite l'accès à certains pays et ne s'en cache d'ailleurs pas.
Dernier problème avec GitHub, non mentionné dans le RFC (merci à Alexis La Goutte pour l'observation) : l'IETF travaille de nombreuses années à développer et promouvoir IPv6 et GitHub n'a toujours que les adresses de la version du siècle précédent.
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : C. Holmberg (Ericsson), J. Uberti (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ice
Première rédaction de cet article le 19 janvier 2021
Le protocole ICE, normalisé dans le RFC 8445 permet de découvrir et de choisir, quand on est coincé derrière un routeur NAT, les adresses IP que vos correspondants verront. Parfois, au début du processus ICE, il n'y a pas d'adresses acceptables. Au lieu de renoncer tout de suite, ce nouveau RFC modifie le RFC 8445 pour demander qu'on patiente un peu : des adresses IP acceptables peuvent apparaitre par la suite.
Résumons le fonctionnement de base d'ICE. ICE sert lorsqu'une des deux machines qui correspondent a le malheur d'être derrière du NAT : le but est de choisir une paire d'adresses IP, pour les deux machines qui correspondent. ICE compte sur des protocoles comme STUN pour la découverte d'adresses possibles. Ensuite, il teste ces adresses et réussit lorsque la communication a pu s'établir avec l'autre machine. Un effet de bord de ce processus est qu'il « crée » parfois de nouvelles adresses IP possibles, qu'il faut donc ajouter à la liste et tester. Le problème que résout notre nouveau RFC est que ces nouvelles adresses peuvent être détectées trop tard, alors qu'ICE a déjà renoncé à trouver une paire qui marche. La solution ? Attendre un peu, même si la liste des paires candidates est vide, ou qu'aucune des paires d'adresses de la liste n'a marché.
La section 3 de notre RFC note que le problème n'est pas si fréquent en pratique car ICE est lent ; cela prend du temps d'établir la liste des paires d'adresses IP possibles, et de les tester, d'autant plus qu'on ne reçoit pas forcément tout de suite un rejet, il faut souvent attendre la fin du délai de garde. Bref, la plupart du temps, les nouvelles adresses apparaissant en cours de route auront le temps d'être intégrées à la liste des paires à tester. Il y a toutefois quelques cas où le risque d'échec complet existe. Par exemple, si une machine purement IPv6 essaie de contacter une machine purement IPv4, aucune paire d'adresses IP satisfaisante ne sera trouvée, menant ICE à renoncer tout de suite. Or, si le réseau local de la machine IPv6 avait NAT64, la connexion reste possible, il faut juste tester et apprendre ainsi qu'une solution existe. Cela demande un peu de patience.
La section 4 formalise la solution : un chronomètre global est créé, le PAC timer (PAC pour Patiently Awaiting Connectivity) et mis en route au tout début du processus ICE. ICE ne doit pas renoncer, même s'il croit ne plus avoir de solutions, avant que ce chronomètre n'ait mesuré au moins 39,5 secondes (cette durée vient du RFC 5389, section 7.2.1, mais peut être modifiée dans certains cas).
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : R. Jesup (Mozilla), S. Loreto
(Ericsson), M. Tuexen (Muenster Univ. of
Appl. Sciences)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF rtcweb
Première rédaction de cet article le 19 janvier 2021
Ce RFC est un des composants de WebRTC. Il spécifie comment on établit le canal de communication des données entre deux pairs WebRTC.
WebRTC est résumé dans le RFC 8825 et la notion de « canal de communication de données » est dans le RFC 8831. Ces données sont protégées par DTLS et transportées en SCTP (RFC 8261).
Le principe est de connecter deux flux de données WebRTC, un entrant
et un sortant, pour faire un seul canal bi-directionnel. Le
protocole est simple, reposant sur seulement deux messages, un
DATA_CHANNEL_OPEN
envoyé sur le flux sortant,
auquel le pair WebRTC répond par un
DATA_CHANNEL_ACK
qui va arriver sur le flux
entrant. Pour éviter les ouvertures simultanées (un protocole à
deux messages n'est pas très robuste), le client DTLS
utilise des flux ayant un identificateur (stream
identifier) pair, et le serveur des flux à
identificateurs impairs. Le format de ces deux messages est décrit
dans la section 5 du RFC.
Les paquets SCTP contiennent un PPID (Payload Protocol IDentifier, RFC 9260, section 3.3.1 et 15.5) qui indique le protocole applicatif qui utilise SCTP. Dans le cas de notre protocole d'établissement de canal WebRTC, DCEP (Data Channel Establishement Protocol), le PPID vaut 50 (et figure dans le registre IANA sous le nom de WebRTC DCEP).
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : R. Jesup (Mozilla), S. Loreto (Ericsson), M. Tuexen (Muenster Univ. of Appl. Sciences)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF rtcweb
Première rédaction de cet article le 19 janvier 2021
Ce RFC décrit le canal de données (à l'exclusion de l'audio et de la vidéo) entre deux navigateurs Web se parlant avec WebRTC. Ce canal utilise le protocole de transport SCTP.
Le canal de données WebRTC est une modélisation d'un tuyau virtuel entre les deux parties qui communiquent, tuyau dans lequel circuleront les données. SCTP (RFC 9260) n'est pas utilisé pour les données « multimédia » comme la vidéo ou l'audio, mais seulement en cas de transfert d'autres données. On peut ainsi utiliser WebRTC pour se transmettre un fichier. (Je ne connais pas à l'heure actuelle de service WebRTC permettant cela. Vous avez un exemple ?) Le multimédia, lui, passe via SRTP, les données nécessitant une plus grande fiabilité passent par SCTP lui-même transporté sur DTLS (RFC 9147) lui même sur UDP. Du fait de cette encapsulation, vous ne verrez pas SCTP avec un logiciel comme Wireshark. La raison pour laquelle UDP est utilisé ? Permettre le passage des données même à travers le pire routeur NAT (cf. RFC 6951).
La section 3 de notre RFC décrit certains scénarios d'usage. Notons que dans certains, la fiabilité du canal de données n'est pas indispensable :
La section 4, elle, décrit les exigences du canal de données. Notamment :
La section 5 décrit une solution à ces exigences, l'utilisation de SCTP sur DTLS. SCTP (RFC 9260) fournit (entre autres) le contrôle de congestion (par contre, on n'utilise pas ses capacités de multihoming). Son encapsulation dans DTLS est décrite dans le RFC 8261. DTLS lui fournit authentification et confidentialité. L'utilisation d'ICE (RFC 8445) lui permet de passer à travers les routeurs NAT et certains pare-feux.
Petit détail : le fait de mettre SCTP sur DTLS (et non pas le
contraire comme dans le RFC 6083) fait que,
si DTLS est mis en œuvre dans l'espace
utilisateur du système
d'exploitation, SCTP devra l'être aussi. (Voir par exemple
third_party/usrsctp/usrsctplib
dans le code
de Chromium, qui vient de
.) Autre
conséquence de ce choix : SCTP ne recevra pas les messages
ICMP associés, et il devra donc compter,
pour la découverte de la MTU du chemin, sur
le RFC 4821.https://github.com/sctplab/usrsctp
Quelques extensions de SCTP sont nécessaires (section 6) comme celles du RFC 6525, du RFC 5061 (enfin, une partie d'entre elles) et du RFC 3758.
La section 6 décrit les détails protocolaires de l'ouverture du canal de données, puis de son utilisation et enfin de sa fermeture.
Pour les programmeurs, cette bibliothèque WebRTC met en œuvre, entre autres, le canal de données.
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : E. Rescorla (RTFM)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF rtcweb
Première rédaction de cet article le 19 janvier 2021
Le système WebRTC permet des communications audio et vidéo entre deux navigateurs Web. Comme tout service Internet, il pose des problèmes de sécurité (RFC 8826), et ce RFC étudie l'architecture générale de WebRTC, du point de vue de la sécurité, et comment résoudre les problèmes.
D'abord, il faut réviser les généralités sur
WebRTC (RFC 8825). WebRTC a pour but de permettre à deux
utilisateurs ou utilisatrices de navigateurs
Web de s'appeler et de bavarder en audio et vidéo. Pas
besoin d'installer un logiciel de communication spécifique (comme
le Skype de
Microsoft, ou comme un
softphone tel que
Twinkle), il suffit d'un navigateur
ordinaire. Pour permettre cela, WebRTC repose sur une
API JavaScript
normalisée par le W3C et sur un ensemble de
protocoles normalisés par l'IETF, et
résumés dans le RFC 8825. Si on
prend l'exemple du service WebRTC
FramaTalk, Alice et Bob visitent
tous les deux
, récupèrent
du code JavaScript qui va utiliser
l'API WebRTC pour accéder au micro et à la
caméra, pendant que, derrière, FramaTalk va établir une
communication entre eux, permettant à leurs navigateurs d'échanger
du son et des images. Cet établissement de connexion (qui peut
impliquer plusieurs serveurs, s'il y a fédération) n'est
pas spécifié par WebRTC. (Il n'y a pas
automatiquement d'interopérabilité entre services WebRTC.) Cela
peut utiliser SIP (RFC 3261), par exemple.https://framatalk.org/
Cela soulève plein de problèmes de sécurité amusants, étudiés dans le RFC 8826, et que notre RFC 8827 tente de résoudre.
Toute solution de sécurité dépend d'un modèle de confiance (section 3 de notre RFC). Si on ne fait confiance à rien ni personne, on ne peut résoudre aucun problème de sécurité. Pour WebRTC, la racine de toute confiance est le navigateur. C'est lui qui devra faire respecter un certain nombre de règles. (En conséquence de quoi, si le navigateur n'est pas de confiance, par exemple parce qu'il s'agit de logiciel non libre, ou bien parce que vous êtes sur une machine sur laquelle vous n'avez aucun contrôle, par exemple un cybercafé, tout est fichu.) Mais le navigateur ne suffit pas, il faut également faire confiance à des entités extérieures, comme le site Web où vous allez récupérer le code JavaScript. Certaines de ces entités peuvent être authentifiées par le navigateur, ce qui donne certaines garanties. Et d'autres, hélas, ne le peuvent pas. Dans la première catégorie, on trouve :
https://framatalk.org/
(notez le
https
), vous êtes sûr que c'est bien
Framasoft derrière.Le reste des entités avec qui on communique est considéré comme potentiellement malveillant, et il faut donc se méfier (mais, évidemment, dans la vie, on ne communique pas qu'avec des gens qu'on connait bien et à qui on fait confiance : ce serait trop limité).
Pour revenir aux entités authentifiées, notez qu'on n'a parlé que d'authentification, pas de confiance. La cryptographie permet de vérifier qu'on parle bien au diable, pas que le diable est honnête !
La section 4 résume le fonctionnement général de WebRTC, afin
de pouvoir analyser sa sécurité, et définir une architecture de
sécurité. Notez que WebRTC permet pas mal de variantes et que le
schéma présenté ici est un cas « typique », pas forcément
identique partout dans tous ses détails. Donc, ici,
Alice et Bob veulent se parler. Tous les
deux utilisent un navigateur. Chacun va visiter le site Web du
service qu'ils utilisent, mettons
https://meet.example/
(en
HTTPS, donc, avec
authentification via un certificat). Ils récupèrent un code
JavaScript qui, appelant
l'API WebRTC, va
déclencher l'envoi d'audio et de vidéo, transportés sur
SRTP, lui-même sur
DTLS (les données en dehors du
« multimédia » passent sur du SCTP sur DTLS). Avant d'autoriser l'accès au micro et
à la caméra, chaque navigateur demandera l'autorisation à son
utilisat·eur·rice. Quant au fait que la machine en face accepte de
recevoir (en direct, en pair-à-pair) ce déluge de données
multimédia, la vérification sera faite par
ICE (cf. RFC 8826, section 4.2). ICE n'intervient qu'à la connexion
initiale, des messages périodiques seront nécessaires pendant la
communication pour s'assurer qu'il y a toujours consentement à recevoir. Pendant l'échange de données audio
et vidéo, le site Web
https://meet.example/
va assurer la
signalisation et, par exemple, si Alice part, indiquer à Bob qu'il
n'y a plus personne en face (notez que cet échange entre site Web
et utilisateur ne se fait pas avec un protocole normalisé). Notre RFC ajoute une possibilité :
qu'Alice et Bob utilisent un fournisseur d'identité, permettant à
chacun d'identifier et d'authentifier l'autre (par exemple via
OpenID Connect).
Ce cas où les deux participant·e·s partagent le même serveur
est le cas le plus courant aujourd'hui (c'est ce que vous verrez
si vous faites une visioconférence via
). Le RFC
le complique ensuite légèrement en introduisant de la fédération :
Alice et Bob se connectent à des sites Web différents, mais qui
ont un mécanisme de fédération commun. Les deux sites vont alors
devoir s'échanger de l'information de signalisation, utilisant des
protocoles existants comme SIP ou
XMPP (qui n'étaient pas utilisés dans le
cas initial).https://meet.jit.si/
Armés de ces informations, nous pouvons lire la section 6, qui plonge dans les détails techniques. D'abord, la securité de la connexion entre navigateur et site Web. Elle dépend de HTTPS (RFC 2818), de la vérification du certificat, bien sûr (RFC 6125 et RFC 5280) et de la vérification de l'origine (RFC 6454). La sécurité entre les pairs qui communiquent impose également l'usage de la cryptographie et il faut donc utiliser SRTP (RFC 3711) sur DTLS (RFC 9147 et RFC 5763). Pas question de faire du RTP en clair directement sur UDP ! Le niveau de sécurité (par exemple les paramètres TLS choisis) doivent être accessibles à l'utilisateur.
Ensuite, la securité de l'accès au micro et à la caméra. Le site Web qu'on visite peut être malveillant, et le navigateur ne permet évidemment pas à du code JavaScript de ce site d'ouvrir silencieusement la caméra. Il demande donc la permission à l'utilisateur, qui ne la donne que s'il était vraiment en train de passer un appel. De même, le navigateur doit afficher si micro et caméra sont utilisés, et ne doit pas permettre à du code JavaScript de supprimer cet affichage.
Dans WebRTC, l'échange des données audio et vidéo se fait en pair-à-pair, directement entre Alice et Bob. Il y a donc le risque qu'un méchant envoie plein de données vidéo à quelqu'un qui n'a rien demandé (la section 9.3 détaille le risque de déni de service). Il faut donc vérifier le consentement à recevoir. Comme indiqué plus haut, cela se fait avec ICE (RFC 8445). Comme le consentement initial n'est pas éternel, et comme on ne peut pas se fier aux accusés de réception, que DTLS n'envoie pas, ni aux réponses (il n'y en a pas forcément, par exemple si on regarde juste une vidéo), il faut envoyer de temps en temps des nouveaux messages ICE (cf. RFC 7675).
Parmi les questions de sécurité liées à WebRTC, il en est une qui a fait beaucoup de bruit, celle de la vie privée, notamment le problème de l'adresse IP qui est transmise au pair avec qui on communique. (Notez que le serveur du site Web où on se connecter initialement verra toujours, lui, cette adresse IP, sauf si on utilise une technique comme Tor.) Notre RFC impose donc, en cas d'appel entrant, de ne pas commencer la négociation ICE avant que l'utilisatrice n'ait décidé de répondre à l'appel, et que WebRTC permette de décider de n'utiliser que TURN, qui ne communique pas l'adresse IP au pair puisqu'on ne se parle plus directement, mais via le relais TURN. Évidemment, cela se fait au détriment des performances (les détails sont dans la section 9.2).
On a vu plus haut que les deux pairs qui communiquent peuvent désirer s'authentifier (sans se fier au site Web qu'ils utilisent pour la signalisation), par exemple via des fournisseurs d'identité externes (comme FranceConnect ?) C'est notamment indispensable pour une fédération puisque, dans ce cas, on n'a même plus de site Web commun. La section 7 de notre RFC détaille ce cas. WebRTC ne normalise pas une technique unique (comme OAuth) pour cela (WebRTC est un cadre, pas vraiment un protocole, encore moins un système complet), mais fixe quelques principes. Notamment, l'utilisateur s'authentifie auprès du fournisseur d'identité, pas auprès du site Web de signalisation. Le navigateur Web doit donc faire attention à tout faire lui-même et à ne pas laisser ce site Web interférer avec ce processus.
Enfin, il reste des problèmes de sécurité de WebRTC qui ne sont pas couverts par le RFC 8826 ni par les précédentes sections de notre RFC 8827. Ainsi, il est important que le code JavaScript téléchargé depuis le site Web du fournisseur de service WebRTC ne puisse pas directement fabriquer des messages chiffrés et signés pour DTLS, car il pourrait alors fabriquer de vraies/fausses signatures. Tout doit passer par le navigateur qui est le seul à pouvoir utiliser les clés de DTLS pour chiffrer et surtout signer.
Une bonne analyse de la sécurité de WebRTC se trouve en « A Study of WebRTC Security ».
Date de publication du RFC : Janvier 2021
Auteur(s) du RFC : H. Alvestrand (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF rtcweb
Première rédaction de cet article le 19 janvier 2021
WebRTC sert à faire communiquer en temps réel (avec audio et vidéo) deux navigateurs Web. Il est déjà largement déployé, ce RFC arrive bien après la bataille, et décrit les généralités sur WebRTC. Ce protocole est complexe et offre beaucoup de choix au concepteur d'applications.
En fait, WebRTC n'est pas vraiment un protocole. Deux applications utilisant WebRTC peuvent parfaitement être dans l'incapacité de communiquer. WebRTC est plutôt un cadre (framework) décrivant des composants, parmi lesquels le concepteur d'applications choisira. Vu cette complexité, ce RFC 8825 est un point de départ nécessaire, pour comprendre l'articulation des différents composants. D'ailleurs, il y a un abus de langage courant : WebRTC désigne normalement uniquement l'API JavaScript normalisée par le W3C. La communication sur le réseau se nomme rtcweb, et c'est le nom de l'ex-groupe de travail à l'IETF. Mais tout le monde fait cet abus de langage et moi aussi, j'utiliserai WebRTC pour désigner l'ensemble du système.
L'auteur du RFC est un « vieux de la vieille » et a vu passer beaucoup de protocoles. Il est donc logique qu'il commence par quelques rappels historiques en section 1. L'idée de faire passer de l'audio et de la vidéo sur l'Internet est aussi ancienne que l'Internet lui-même, malgré les prédictions pessimistes des telcos, qui avaient toujours prétendu que cela ne marcherait jamais. (La variante d'aujourd'hui est « si on ne nous laisse pas violer la neutralité du réseau, la vidéo ne marchera jamais ».)
Mais c'est vrai que les débuts ont été laborieux, la capacité du réseau étant très insuffisante, et les processeurs trop lents. Ça s'est amélioré par la suite. Mais si le matériel a progressé, les protocoles continuaient à manquer : il existe bien sûr des protocoles standards pour traiter certains aspects de la communication (comme SIP - RFC 3261 - pour la signalisation) mais, pour une communication complète, il faut un jeu de protocoles couvrant tous les aspects, de la signalisation à l'encodage du flux vidéo, en passant par les identificateurs à utiliser (comme les adresses de courrier électronique), et qu'on puisse compter sur ce jeu, de même qu'on peut compter sur la disponibilité de clients et serveurs HTTP, par exemple. Comme le note l'auteur, « la Solution Universelle s'est avérée difficile à développer ». (Cela a d'ailleurs laissé un boulevard à des entreprises privées, proposant un produit très fermé et complètement intégré comme le Skype de Microsoft.)
Depuis quelques années, la disponibilité très répandue de navigateurs Web ayant des possibilités très avancées a changé la donne. Plus besoin d'installer tel ou tel plugin, telle ou telle application, désormais, les possibilités qu'offre HTML5 sont largement répandues, et accessibles en JavaScript via une API standard. Un groupe de travail du W3C développe de nouvelles possibilités et WebRTC peut donc être vu comme un effort commun du W3C et de l'IETF. C'est sur ces nouvelles possibilités du navigateur que se fonde WebRTC (son cahier des charges figure dans le RFC 7478).
Notre RFC note que WebRTC change pas mal l'architecture des techniques de communication temps-réel et multimédia. Autant les solutions fondées sur SIP comptaient sur des éléments intermédiaires dans le réseau pour travailler (par exemple des ALG), autant WebRTC est nettement plus « bout en bout », et utilise la cryptographie pour faire respecter ce principe, bien menacé par les FAI.
Question histoire, WebRTC a lui-même eu une histoire compliquée. La première réunion du groupe à l'IETF a eu lieu en 2011, pour une fin prévue en 2012. En fait, il y a eu des retards, et même une longue période d'arrêt du travail. Ce RFC de survol de WebRTC, par exemple, a vu sa première version publiée il y a huit ans. Le premier RFC du projet n'a été publié qu'en 2015. Globalement, ce fut l'un des plus gros efforts de l'IETF, mais pas le plus efficace en terme de rapidité. Le groupe de travail rtcweb a finalement été clos en août 2019, le travail étant terminé.
La section 2 de notre RFC résume les principes de WebRTC : permettre une communication audio, vidéo et données la plus directe possible entre les deux participants. Ce RFC 8825 est la description générale, les protocoles effectifs figurent dans d'autres RFC comme le RFC 8832 pour le protocole de création du canal de données ou le RFC 8831 pour la communication via le canal de données. Techniquement, WebRTC a deux grandes parties :
La section 2 décrit également le vocabulaire, si vous voulez réviser des concepts comme la notion de protocole ou de signalisation.
La section 3 de notre RFC présente l'architecture générale. Chaque navigateur WebRTC se connecte à un serveur Web où il récupère automatiquement un code JavaScript qui appelle les fonctions WebRTC, qui permettent d'accéder au canal de données, ouvert avec l'autre navigateur, et aux services multimédia de son ordinateur local (le micro, la caméra, etc). On y voit notamment (figure 2) que les données (la voix, la vidéo, etc) passent directement (enfin, presque, voir plus loin) entre les deux navigateurs, alors que la signalisation se fait entre les deux serveurs Web. On note, et c'est très important, que WebRTC ne gère que le canal de données, pas celui de signalisation, qui peut utiliser SIP (RFC 3261), XMPP (RFC 6120) ou même un protocole privé. C'est entre autres pour cette raison que deux services WebRTC différents ne peuvent pas forcément interopérer. Voici un schéma de WebRTC lorsque les deux parties se connectent au même site Web (la signalisation est alors faite à l'intérieur de ce site) :
Et ici une image d'une fédération, où les deux parties qui communiquent utilisent des services différents, mais qui se sont mis d'accord sur un protocole de signalisation :
Le canal de données nécessite déjà beaucoup de spécifications, notamment le transport et l'encodage du contenu. Il est spécifié dans le RFC 8831.
Le transport, justement (section 4 du RFC). Il faut envoyer les données à l'autre navigateur, et assurer la retransmission des données perdues, le contrôle de congestion, etc. Tout cela est décrit dans le RFC 8835. Ce RFC spécifie, pour transporter les données multimédia, l'utilisation de SRTP sur DTLS, lui-même sur UDP (cf. RFC 8834). Des systèmes de relais comme TURN (RFC 5766) peuvent être nécessaires si les deux malheureux navigateurs Web soint coincés derrière des middleboxes méchantes, NAT ou pare-feu par exemple.
Ce n'est pas tout d'avoir un protocole de transport. Il faut aussi découper les données d'une manière compréhensible par l'autre bout (framing, section 5 de notre RFC). WebRTC utilise RTP (RFC 3550, et voir aussi le RFC 8083) et SRTP (RFC 3711 et RFC 5764). Le RFC 8834 détaille leur utilisation. La sécurité est, elle, couverte dans les RFC 8826 et RFC 8827. L'établissement du canal de données est normalisé dans le RFC 8832 et le canal de données lui-même dans le RFC 8831.
On le sait, les formats d'audio et de vidéo sont un problème compliqué, le domaine est pourri de brevets souvent futiles, et la variété des formats rend difficile l'interopérabilité. Notre RFC impose au minimum l'acceptation des codecs décrits dans le RFC 7874 pour l'audio (Opus est obligatoire) et RFC 7742 pour la vidéo (H.264 et VP8, après une longue lutte). D'autres codecs pourront s'y ajouter par la suite.
La section 7 mentionne la gestion des connexions, pour laquelle la solution officielle est le JSEP (JavaScript Session Establishment Protocol, RFC 8829).
La section 9 du RFC décrit les fonctions locales (l'accès par le navigateur aux services de la machine qui l'héberge). Le RFC rappelle que ces fonctions doivent inclure des choses comme la suppression d'écho, la protection de la vie privée (demander l'autorisation avant d'accéder à la caméra…) Le RFC 7874 fournit des détails. Ici, un exemple d'une demande d'autorisation par Firefox :
Parmi les logiciels qui permettent de faire du WebRTC, on peut
citer Jitsi, qui est derrière
le service Framatalk, mais
qui est également accessible via d'autres services comme
.https://meet.jit.si/
Regarder du trafic WebRTC avec un logiciel comme Wireshark est frustrant, car tout est chiffré. Pour Jitsi, on voit beaucoup de STUN, pour contourner les problèmes posés par le NAT, puis du DTLS évidemment impossible à décrypter.
Il y a de nombreux autres logiciels avec WebRTC. C'est le cas par exemple de PeerTube mais attention, seul l'échange de vidéo entre pairs qui regardent au même moment utilise WebRTC (plus exactement WebTorrent, qui utilise WebRTC). La récupération de la vidéo depuis le serveur se fait en classique HTTPS.
Si vous voulez voir une session WebRTC, le mieux est d'utiliser
un service comme
, qui journalise tout, et d'activer la console de
Firefox. Vous voyez alors :
https://appr.tc/
Console (Webdeveloper tools) Firefox 9.569: Initializing; server= undefined.
Puis l'établissement des canaux nécessaires :
37.252: Opening signaling channel. 39.348: Joined the room. 39.586: Retrieved ICE server information. 39.927: Signaling channel opened. 39.930: Registering signaling channel. 39.932: Signaling channel registered.
Là, Firefox vous demande l'autorisation d'utiliser micro et caméra, que vous acceptez :
44.452: Got access to local media with mediaConstraints: '{"audio":true,"video":true}' 44.453: User has granted access to local media.
Le navigateur peut alors utiliser ICE (RFC 8445) pour trouver le bon moyen de communiquer avec le pair (dans mon test, les deux pairs étaient sur le même réseau local, et s'en aperçoivent, ce qui permet une communication directe par la suite) :
44.579: Creating RTCPeerConnnection with: config: '{"rtcpMuxPolicy":"require","bundlePolicy":"max-bundle","iceServers":[{"urls":["stun:74.125.140.127:19302","stun:[2a00:1450:400c:c08::7f]:19302"]},{"urls":["turn:74.125.140.127:19305?transport=udp","turn:[2a00:1450:400c:c08::7f]:19305?transport=udp","turn:74.125.140.127:19305?transport=tcp","turn:[2a00:1450:400c:c08::7f]:19305?transport=tcp"],"username":"CP/Bl+UXXXXXXX","credential":"XXXXXXXXXXX=","maxRateKbps":"8000"}],"certificates":[{}]}'; constraints: '{"optional":[]}'. 44.775: Created PeerConnectionClient
On peut alors négocier les codecs à utiliser (ici VP9) :
44.979: Set remote session description success. 44.980: Waiting for remote video. 44.993: No preference on audio receive codec. 44.993: Prefer video receive codec: VP9
Et, ici, on voit passer le descripteur de session (section 7 de notre RFC), au format SDP (RFC 4566 et RFC 3264, et notez la blague entre SDP et le film 300) :
44.997: C->WSS: {"sdp":"v=0\r\no=mozilla...THIS_IS_SDPARTA-60.5.1 6947646294855805442 0 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=fingerprint:sha-256 23:80:53:8A:DD:D7:BF:77:B5:C7:4E:0F:F4:6D:F2:FB:B8:EE:59:D1:91:6A:F5:21:11:22:C0:E3:E0:ED:54:39\r\na=group:BUNDLE 0 1\r\na=ice-options:trickle\r\na=msid-semantic:WMS *\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 126\r\nc=IN IP4 0.0.0.0\r\na=sendrecv\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=fmtp:111 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\na=fmtp:126 0-15\r\na=ice-pwd:bb7169d301496c0119c5ea3a69940a55\r\na=ice-ufrag:3d465335\r\na=mid:0\r\na=msid:{d926a161-3011-48af-9236-06e15377dfea} {a2597b07-7e80-47fe-8542-761dd93efb94}\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtpmap:126 telephone-event/8000/1\r\na=setup:active\r\na=ssrc:2096893919 cname:{e60fd6dc-6c2d-4132-bb6a-1178e1611d16}\r\nm=video 9 UDP/TLS/RTP/SAVPF 98\r\nc=IN IP4 0.0.0.0\r\na=recvonly\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=fmtp:98 max-fs=12288;max-fr=60\r\na=ice-pwd:bb7169d301496c0119c5ea3a69940a55\r\na=ice-ufrag:3d465335\r\na=mid:1\r\na=rtcp-fb:98 nack\r\na=rtcp-fb:98 nack pli\r\na=rtcp-fb:98 ccm fir\r\na=rtcp-fb:98 goog-remb\r\na=rtcp-mux\r\na=rtpmap:98 VP9/90000\r\na=setup:active\r\na=ssrc:4025254123 cname:{e60fd6dc-6c2d-4132-bb6a-1178e1611d16}\r\n","type":"answer"}
Et c'est bon, tout marche :
45.924: Call setup time: 1399ms.
Du fait de son caractère pair-à-pair (les données sont échangées, autant que le NAT le permet, directement entre les navigateurs), WebRTC soulève des questions de vie privée. Votre correspondant va voir votre adresse IP. Le point est discuté plus longuement dans le RFC 8826. (PeerTube met en bas un mot d'avertissement à ce sujet.)
WebRTC est présent depuis pas mal d'années dans tous les navigateurs graphiques comme Firefox, Chrome ou Edge. Côté bibliothèques, il existe de nombreuses mises en œuvre de WebRTC comme OpenWebRTC (abandonné depuis) ou comme l'implémentation de référence. Il y a également du WebRTC dans des serveurs comme Asterisk, WebEx ou MeetEcho (ce dernier étant utilisé par l'IETF pour ses réunions à distance). Mais rappelez-vous que WebRTC n'est pas un ensemble cohérent permettant l'interopérabilité. Vous pouvez avoir deux services WebRTC qui n'arrivent pas à interagir, et ce n'est pas une bogue, c'est un choix de conception.
Et, pour finir, quelques lectures et activités supplémentaires :
https://test.webrtc.org/
, c'est vraiment le
premier endroit où aller si vous avez des soucis techniques avec
un service WebRTC,chrome://webrtc-internals
pour
Chrome, opera://webrtc-internals
pour
Opera, et about:webrtc
pour Firefox,Date de publication du RFC : Juin 2020
Auteur(s) du RFC : M. Nottingham
Première rédaction de cet article le 1 juillet 2020
Ah, les URI... Comme ces identificateurs sont très
souvent vus et manipulés par un grand nombre d'utilisateurs, ils
suscitent forcément des passions et des discussions sans fin. Ce
RFC de bonne
pratique s'attaque à un problème fréquent : les applications ou
extensions qui imposent des contraintes sur l'URI, sans que cela
soit justifié par le format de ces URI. Par exemple, on voit des
CMS
imposer, au moment de l'installation, que le CMS soit accessible par
un URI commençant par le nom du logiciel. S'il s'appelle Foobar, on
voit parfois des logiciels qui ne marchent que si l'URI commence par
http://www.example.org/Foobar/
. Pourquoi est-ce
une mauvaise idée et que faudrait-il faire à la place ? (Ce document
remplace le RFC 7320, dans le sens d'un plus
grand libéralisme : moins de directives et davantage de
suggestions.)
Ce RFC est destiné aux concepteurs d'application (par exemple les API REST) et aux auteurs de protocoles qui étendent les protocoles existants.
D'abord, l'argument d'autorité qui tue : la norme des URI, le RFC 3986, dit clairement que la structure d'un URI
est déterminé par son plan
(scheme en anglais) et que donc l'application n'a
pas le droit d'imposer des règles supplémentaires. Les deux seuls
« propriétaires » sont la norme décrivant le plan (qui impose une
syntaxe, et certains éléments, par exemple le nom du serveur avec le
plan http://
) et l'organisation qui contrôle
cet URI particulier (par exemple, toujours pour
http://
, l'organisation qui contrôle le
domaine dans
l'URI). Imposer des règles, pour une application ou une extension,
c'est violer ce principe de propriété. (Il est formalisé dans une
recommandation du W3C, section 2.2.2.1.)
Un exemple de structure dans les URI est l'interprétation du
chemin (path en anglais) comme étant un endroit
du système de fichiers. Ainsi, bien des
serveurs HTTP, en voyant l'URI
http://www.example.org/foo/bar/toto.html
,
chercheront le fichier en
$DOCUMENT_ROOT/foo/bar/toto.html
. C'est une
particularité de la mise en œuvre de ce serveur, pas une obligation
du plan d'URI http://
. Un autre exemple est
l'utilisation de l'extension du nom de fichier comme moyen de
trouver le type de média de
la ressource. (« Ça se termine en .png
? On
envoie le type image/png
. ») Ces deux cas ne
violent pas forcément le principe de propriété des URI car
l'utilisateur, après tout, choisit son serveur.
Mais, dans certains cas, l'imposition d'une structure (autre que celle déjà imposée par la norme du plan d'URI) a des conséquences néfastes :
/Foobar
comme dans l'exemple au début, il
contraint les choix pour l'administrateur système (et si je veux des
URI avec un chemin vide, genre
http://foobar.example.org/
, je fais
quoi ?)sig
comme étant forcément une signature cryptographique, il y a une
possibilité qu'un logiciel client croit, dès qu'il voit un
paramètre de ce nom, que c'est une signature.Donc, pour toutes ces raisons, notre RFC déconseille fortement de rajouter des règles de structure dans les URI. Ces règles diminuent la liberté du propriétaire de l'URI.
Qui risque de violer ce principe ? Les auteurs d'applications,
comme dans l'exemple Foobar plus haut, mais aussi des gens qui font
des extensions aux URI, par le biais de nouvelles spécifications
(par exemple pour mettre des signatures ou autres métadonnées dans
l'URI). En revanche, ce principe ne s'applique pas au propriétaire
lui-même, qui a évidemment le droit de définir ses règles pour la
gestion de ses URI (exemple : le webmestre
qui crée un schéma de nommage des URI de son site
Web). Et cela ne s'applique pas non plus au cas où le
propriétaire de l'URI reçoit lui-même une délégation pour gérer ce
site (par exemple, un RFC qui crée un registre IANA et
spécifie la structure des URI sous
https://www.iana.org/
est dans son droit,
l'IANA agissant sur délégation de l'IETF). Le principe ? On
n'interdit pas de mettre de la structure dans les URI, on dit juste
qui a le droit de le faire.
La partie normative de notre RFC est la section 2. Elle explicite
le principe. D'abord, éviter de contraindre l'usage d'un plan
particulier. Par exemple, imposer http://
peut
être trop contraignant, certaines applications ou extensions
pourraient marcher avec d'autres plans d'URI comme par exemple
file://
(RFC 8089).
D'autre part, si on veut une structure dans les URI d'un plan
particulier, cela doit être spécifié dans le document qui définit le
plan (RFC 7230 pour
http://
, RFC 6920 pour
ni:
, etc) pas dans une extension faite par
d'autres (« touche pas à mon plan »).
Les URI comprennent un champ « autorité » juste après le
plan. Les extensions ou applications ne doivent
pas imposer de contraintes particulières sur ce
champ. Ainsi, pour http://
, l'autorité est un
nom de domaine et notre
RFC ne permet pas qu'on lui mette des contraintes (du genre « le
premier composant du nom de domaine doit commencer par
foobar-
, comme dans
foobar-www.example.org
»).
Même chose pour le champ « chemin », qui vient après
l'autorité. Pas question de lui ajouter des contraintes (comme dans
l'exemple du CMS qui imposerait /Foobar
comme
préfixe d'installation). La seule exception est la définition des
URI bien connus du RFC 8615 (ceux dont le
chemin commence par /.well-known
). Le RFC 6415 donne un exemple d'URI bien connus, avec
une structure imposée.
Autre conséquence du principe « bas les pattes » et qui est, il
me semble, plus souvent violée en pratique, le champ « requête »
(query). Optionnel, il se trouve après le
point d'interrogation dans l'URI. Notre RFC
interdit aux applications d'imposer l'usage des requêtes, car cela
empêcherait le déploiement de l'application dans d'autres contextes
où, par exemple, on veut utiliser des URI sans requête (je dois dire
que mes propres applications Web violent souvent ce principe). Quant
aux extensions, elles ne doivent pas contraindre le format des
requêtes. L'exemple cité plus haut, d'une extension hypothétique,
qui fonctionnerait par l'ajout d'un paramètre
sig
aux requêtes pour indiquer une signature
est donc une mauvaise idée. Une telle extension causerait des
collisions (applications ou autres extensions qui voudraient un
paramètre de requête nommé sig
) et des risques
de suppositions injustifié (un logiciel qui se dirait « tiens, un
paramètre sig
, je vais vérifier la signature,
ah, elle est invalide, cet URI est erroné »). Au passage, les
préfixes n'aident pas. Supposons qu'une extension, voulant limiter
le risque de collisions, décide que tous les paramètres qu'elle
définit commencent par myapp_
(donc, la
signature serait myapp_sig
). Cela ne supprime
pas le risque de collisions puisque le préfixe lui-même ne serait
pas enregistré.
(Sauf erreur, Dotclear gère bien cela, en
permettant, via les « méthodes de lecture »
PATH_INFO
ou QUERY_STRING
d'avoir les deux types d'URI, sans requête ou bien avec.)
Pourtant, HTML lui-même fait cela, dans la norme 4.01, en restreignant la syntaxe lors de la soumission d'un formulaire. Mais c'était une mauvaise idée et les futures normes ne devraient pas l'imiter.
Comme précédemment, les URI bien connus ont, eux, droit à changer
la syntaxe ou contraindre les paramètres puisque, d'une certaine
façon, l'espace sous .well-known
est délégué à
l'IETF et n'est plus « propriété » de l'autorité.
Dernier champ de l'URI à étudier, l'identificateur de fragment (ce qui est après le croisillon). Les définitions d'un type de média (RFC 6838) ont le droit de spécifier la syntaxe d'un identificateur de fragment spécifique à ce type de média (comme ceux du texte brut, dans le RFC 5147). Les autres extensions doivent s'en abstenir sauf si elles normalisent une syntaxe pour de futurs types de médias (comme le fait le RFC 6901).
Bon, assez de négativité et d'interdiction. Après les « faites pas ci » et les « faites pas ça », la section 3 de notre RFC expose les alternatives, les bonnes pratiques pour remplacer celles qui sont interdites ici. D'abord, si le but est de faire des liens, il faut se rappeler qu'il existe un cadre complet pour cela, décrit dans le RFC 8288. Une application peut utiliser ce cadre pour donner la sémantique qu'elle veut à des liens. Autre technique rigolote et peu connue, les gabarits du RFC 6570, qui permettent de gérer facilement le cas de données spécifiques à l'application dans un URI.
Et, comme cité plusieurs fois, les URI bien connus du RFC 8615 sont un moyen de déclarer sa propre structure sur des URI. Par contre, ils sont censés avoir un usage limité (accéder à des métadonnées avant de récupérer une ressource) et ne sont pas un moyen générique d'échapper aux règles qui nous dérangent !
Les changements depuis le premier RFC qui avait décrit ces règles
(RFC 7320) sont résumés dans l'annexe A. En
gros, moins d'interdictions, et davantage de conseils. Par exemple,
la règle comme quoi une application ne devrait pas empêcher
l'utilisation de futurs plans (comme file:
en
plus de http:
) n'utilise maintenant plus le
vocabulaire normatif du RFC 2119 et est donc
désormais davantage un avis. De même, pour le composant « requête »
de l'URI, l'interdiction de spécifier sa syntaxe dans les
applications n'est désormais plus une règle absolue. Autre cas, le
RFC 7320 parlait négativement, non seulement
du préfixe de chemin d'URI fixe (le /Foobar
dans mes exemples) mais même d'une partie fixe du chemin, y compris
si elle n'était pas au début du chemin. Cela avait suscité
des débats houleux dans la discussion du RFC 9162 (successeur du RFC 6962) et cette restriction a donc été
supprimée.
Date de publication du RFC : Août 2020
Auteur(s) du RFC : A. Mortensen (Forcepoint), T. Reddy (McAfee), F. Andreasen (Cisco), N. Teague (Iron Mountain), R. Compton (Charter)
Pour information
Réalisé dans le cadre du groupe de travail IETF dots
Première rédaction de cet article le 18 août 2020
Ce nouveau RFC décrit l'architecture du système DOTS (Distributed-Denial-of-Service Open Threat Signaling), un ensemble de mécanismes pour permettre aux victimes d'attaques par déni de service de se coordonner avec les fournisseurs de solution d'atténuation. C'est juste l'architecture, les protocoles concrets sont dans d'autres RFC, comme le RFC 9132.
Il n'y a pas besoin d'expliquer que les attaques par déni de service sont une plaie. Tout le monde en a déjà vécu. Une des approches pour atténuer l'effet d'une de ces attaques est de sous-traiter votre trafic à un tiers, l'atténuateur (« Victor, atténuateur ») qui va recevoir les données, les classer, et jeter ce qui est envoyé par l'attaquant. Cette approche nécessite de la communication entre la victime et l'atténuateur, communication qui se fait actuellement de manière informelle (téléphone…) ou via des protocoles privés. L'idée de DOTS (Distributed-Denial-of-Service Open Threat Signaling) est d'avoir des protocoles normalisés pour ces fonctions de communication. Les scénarios typiques d'utilisation de DOTS sont décrits dans le RFC 8903.
Dans le cas le plus fréquent, DOTS sera utilisé entre des organisations différentes (la victime, et le fournisseur de solutions anti-dDoS). A priori, ils auront une relation contractuelle (du genre contrat, et paiement) mais cette question ne fait pas l'objet du RFC, qui mentionne seulement l'architecture technique. Mais en tout cas, ce caractère multi-organisations va nécessiter des mécanismes d'authentification sérieux (le cahier des charges complet de DOTS est le RFC 8612).
La section 1 de notre RFC rappelle également que DOTS, par définition, sera utilisé dans des moments difficiles, pendant une attaque (RFC 4732), et qu'il est donc conçu en pensant à des cas où les ressources sont insuffisantes (les paquets ont du mal à passer, par exemple). Parfois, il y aura un lien intact entre le client DOTS et le serveur, ou bien un réseau dédié pour cette communication, ou encore une qualité de service garantie pour les échanges DOTS, mais on ne pourra pas toujours compter dessus. DOTS doit fonctionner sur l'Internet normal, possiblement affecté par l'attaque. C'est un élément à systématiquement garder en tête lorsqu'on examine le protocole DOTS, et qui explique bien des choix, comme UDP pour le protocole de signalisation du RFC 9132.
D'autre part, les RFC sur DOTS décrivent des techniques, pas des politiques. Comment on définit une attaque DoS, à partir de quand on déclenche l'atténuation, comment on choisit un atténuateur, toutes ces questions dépendent de la victime, chacun peut faire des choix différents.
Ceci étant posé, place à la description de haut niveau de DOTS, en section 2. Dans le cas le plus simple, il y a :
Et client et serveur DOTS communiquent avec les deux protocoles DOTS, celui de signalisation (RFC 9132) et celui de données (RFC 8783). Il y a donc deux canaux de communication. DOTS permet également des schémas plus complexes, par exemple avec plusieurs serveurs, à qui le client demande des choses différentes, ou bien avec des serveurs différents pour la signalisation et pour les données. Notez bien que DOTS est uniquement un protocole de communication entre la victime et l'atténuateur qui va essayer de la protéger. Comment est-ce que l'atténuateur filtre, ou comment est-ce qu'on lui envoie le trafic à protéger, n'est pas normalisé. De même, DOTS ne spécifie pas comment le serveur répond aux demandes du client. Le serveur peut refuser d'aider, par exemple parce que le client n'a pas payé. (Pour l'envoi du trafic à protéger, il y a deux grandes techniques, fondées sur BGP ou sur DNS. Le trafic une fois filtré est ensuite renvoyé à la victime. Une autre solution est d'avoir le mitigateur dans le chemin en permanence.)
On a vu qu'il y avait deux canaux de communication. Celui de signalisation, normalisé dans le RFC 9132 sert surtout à demander à l'atténuateur une action de protection, et à voir quelles réponses l'atténuateur donne. C'est ce canal qui devra fonctionner au plus fort de l'attaque, ce qui lui impose des contraintes et des solutions particulières. Le canal de données, spécifié dans le RFC 8783, n'est pas en toute rigueur indispensable à DOTS, mais il est quand même pratique : il sert à envoyer des informations de configuration, permettant au client de spécifier plus précisement ce qu'il veut protéger et contre qui. Par exemple, il va permettre de donner des noms à des ressources (une ressource peut être, par exemple, un ensemble de préfixes IP), envoyer une liste noire d'adresses d'attaquants à bloquer inconditionnellement, une liste blanche de partenaires à ne surtout pas bloquer, à définir des ACL, etc. En général, ce canal de données s'utilise avant l'attaque, et utilise des protocoles habituels, puisqu'il n'aura pas à fonctionner pendant la crise.
Le RFC note aussi que DOTS n'a de sens qu'entre partenaires qui ont une relation pré-existante (par exemple client / fournisseur payant). Il n'y a pas de serveur DOTS public. L'authentification réciproque du client et du serveur est donc nécessaire, d'autant plus qu'on utilise DOTS pour faire face à des attaques et que l'attaquant peut donc chercher à subvertir DOTS.
Le serveur DOTS doit non seulement authentifier le client mais aussi l'autoriser à demander une mitigation pour telle ou telle ressource (préfixe IP ou nom de domaine). Par exemple, le serveur DOTS peut utiliser les IRR pour déterminer si son client est vraiment légitime pour demander une intervention sur telle ressource. Mais il pourrait aussi utiliser ACME (RFC 8738).
Typiquement, le client établit une session de signalisation avec le serveur, qu'il va garder pendant l'attaque. Il n'y a pas actuellement de norme sur comment le client trouve le serveur DOTS. On peut supposer qu'une fois l'accord avec le serveur fait, le gérant du serveur communique au client le nom ou l'adresse du serveur à utiliser.
La section 3 du RFC détaille certains points utiles. À lire si vous voulez comprendre toute l'architecture de DOTS, notamment les configurations plus complexes, que j'ai omises ici.
Et si vous vous intéressez aux mises en œuvre de DOTS, elles sont citées à la fin de mon article sur le RFC 9132.
Date de publication du RFC : Août 2020
Auteur(s) du RFC : J. Scudder (Juniper Networks)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 18 août 2020
Un petit RFC purement bureaucratique : un léger changement, dans le sens de la libéralisation, des procédures d'enregistrement d'une capacité BGP dans le registre IANA de ces capacités.
Les routeurs BGP ont des capacités optionnelles que ne partagent pas forcément leurs pairs, comme par exemple la gestion du redémarrage en douceur (RFC 4724) ou bien la configuration du filtrage sortant (RFC 5291). Il est donc utile de pouvoir annoncer formellement ce qu'on sait faire et ce qu'on ne sait pas faire. L'annonce des capacités BGP est normalisée dans le RFC 5492. Elle se fait en envoyant un code numérique, d'un octet. La liste des codes possibles figure dans un registre IANA. Une partie des codes, allant de 128 à 255, était réservée pour l'utilisation locale. Cette plage est désormais éclatée en trois, en utilisant la terminologie de politiques d'enregistrement du RFC 8126 :
Les deux autres plages, de 1 à 63 (« Examen par l'IETF ») et de 64 à 127 (« Premier Arrivé, Premier Servi ») ne changent pas depuis le RFC 5492.
Lorsqu'on change une politique d'enregistrement, il faut se demander quoi faire des utilisations précédentes. Puisque la plage considérée était utilisable en privé, des gens ont pu légitimement s'en servir et annoncer des codes de capacité. En 2015, les auteurs de ce RFC avaient cherché les utilisations de cette plage et trouvé une liste (pas forcément complète), souvent composée de codes qui ont reçu un « vrai » numéro après, liste qui est présentée dans le RFC, et ajoutée au registre IANA. Et, bien sûr, comme avec tout registre, il y a toujours le risque de squatteurs s'appropriant tel ou tel code sans suivre les procédures (cf. RFC 8093).
Date de publication du RFC : Août 2020
Auteur(s) du RFC : Q. Wu (Huawei), B. Lengyel
(Ericsson), Y. Niu (Huawei)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF netmod
Première rédaction de cet article le 17 septembre 2020
Ce RFC décrit un modèle YANG pour permettre la « remise aux réglages d'usine » d'un équipement, quand celui-ci est trop bizarrement configuré pour qu'il y ait une autre solution que l'effacement radical.
YANG (RFC 6020) est un langage de description des équipements réseau et de leurs capacités, afin de permettre de la gestion automatisée de ces équipements via des protocoles comme NETCONF (RFC 6241) ou RESTCONF (RFC 8040).
Ce RFC définit
un nouveau RPC,
factory-reset
, qui va remettre tous les
réglages de la machine aux réglages qu'elle avait à la sortie
d'usine. C'est l'équivalent YANG de manipulations physiques comme
« appuyez sur les boutons Power et VolumeDown simultanément pendant
cinq secondes » ou bien « insérez un trombone dans ce petit trou ».
La section 2 du RFC décrit plus précisément ce que veut dire
« retourner aux réglages d'usine ». Les entrepôts
(datastore, cf. RFC 6020,
RFC 7950 et RFC 8342)
comme <running>
reprennent les valeurs
qu'ils contenaient lorsque l'équipement a quitté l'usine. Toutes les
données générées depuis sont jetées. Cela inclut (le RFC utilise des
exemples de répertoires Unix, que je reprends
ici, mais ce sont juste des exemples, l'équipement n'utilise pas
forcément Unix) les certificats
(/etc/ssl
), les journaux
(/var/log
), les fichiers temporaires
(/tmp
), etc. Par contre, il faut garder des
informations qui étaient spécifiques à cet engin particulier (et
différent des autres du même modèle) mais qui ont été fixés avant le
premier démarrage, par exemple des identificateurs uniques ou des
clés privées (RFC 8995) ou mots
de passe générées automatiquement au début du cycle de vie. Les
données sensibles doivent être effacées de manière sûre, par exemple
en écrivant plusieurs fois sur leur emplacement (cf. section 6). À
noter que cette remise au début peut couper la communication avec la
machine de gestion : l'équipement se comportera comme s'il sortait
du carton, ce qui peut nécessiter une configuration nouvelle.
Notre RFC normalise également un nouvel entrepôt
(datastore),
factory-default
, qui contient ces réglages
d'usine et permet donc à un client NETCONF ou RESTCONF de savoir en
quoi ils consistent. Appeler factory-reset
revient donc à appliquer factory-default
. Cet
entrepôt suit les principes du RFC 8342,
annexe A, et est en lecture seule.
La section 4 présente le module complet, qui est disponible en
ietf-factory-default.yang
. Il s'appuie sur le module du RFC 8342 et
sur celui du RFC 8341.
L'URN de ce
RFC,
urn:ietf:params:xml:ns:yang:ietf-factory-default
a été enregistré à
l'IANA (registre du RFC 3688), et le
module ietf-factory-default
dans le registre
des modules.
Quelques mots sur la sécurité pour terminer. Comme le but de ce
module est de permettre à des clients NETCONF ou RESTCONF d'appeler
ce RPC
factory-reset
, et que ce RPC a des
conséquences… sérieuses pour l'équipement, il faut comme d'habitude
veiller à ce que les accès NETCONF et RESTCONF soient bien sécurisés
(par exemple via SSH,
cf. RFC 6242 et via les contrôles d'accès du
RFC 8341).
Date de publication du RFC : Août 2020
Auteur(s) du RFC : J. Gould, M. Pozun (VeriSign)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 8 août 2020
Le protocole EPP d'avitaillement de ressources (par exemple de noms de domaine) a un mécanisme de sécurité très simple, à base de mots de passe indiqués lors de la connexion. Ce mécanisme avait plein de limitations ennuyeuses, dans le RFC original, et ce nouveau RFC vise à les réduire.
L'authentification dans EPP est décrite
dans le RFC 5730, section 2.9.1.1 (voir
aussi sa section 7). Le client EPP envoie un mot de passe, qui doit
faire entre 6 et 16 caractères (cf. le type
pwType
dans le schéma
XSD du RFC 5730, section 4.1). Le
client peut changer son mot de passe en indiquant un nouveau mot via le
protocole
EPP, sans avoir à passer par un autre service. En outre, la session EPP est typiquement portée sur TLS, ce qui assure la
confidentialité du mot de passe, et la
possibilité d'ajouter une authentification par le biais d'un
certificat client. Mais c'est tout. Le RFC 5730 ne permet pas de mot de passe plus long,
ne permet pas au client de savoir combien de tentatives erronées
ont eu lieu, ne permet pas d'indiquer l'expiration d'un mot de passe
(si ceux-ci ont une durée de vie limitée) et ne permet pas au
serveur EPP d'indiquer, s'il refuse un nouveau mot de passe,
pourquoi il le juge inacceptable.
Cette extension à EPP figure désormais dans le registre des extensions, créé par le RFC 7451.
Notre RFC
ajoute donc plusieurs nouveaux éléments XML qui peuvent apparaitre en EPP
(section 3). D'abord, la notion d'évènement. Un évènement permet
d'indiquer, dans un élément <event>
:
En utilisant loginSec
comme abréviation pour
l'espace de noms de l'extension EPP de ce
RFC, urn:ietf:params:xml:ns:epp:loginSec-1.0
,
voici un exemple d'évènement, indiquant qu'il faudra bientôt changer
de mot de passe :
<loginSec:event type="password" level="warning" exDate="2020-04-01T22:00:00.0Z" lang="fr"> Le mot de passe va bientôt expirer. </loginSec:event>
On pourrait voir aussi cet évènement indiquant le nombre de connexions ratées, ce qui peut alerter sur une tentative de découverte du mot de passe par force brute :
<loginSec:event type="stat" name="failedLogins" level="warning" value="100" duration="P1D" lang="fr"> Trop de connexions ratées. </loginSec:event>
Si on utilise des mots de passe qui suivent les nouvelles règles,
il faut l'indiquer en mettant dans l'ancien élément
<pw>
la valeur
[LOGIN-SECURITY]
. Si elle est présente, c'est
qu'il faut aller chercher le mot de passe dans le nouvel élément
<loginSec:pw>
(idem pour un changement de
mot de passe).
La section 4 du RFC donne les changements pour les différentes
commandes EPP. Seule <login>
est
concernée. Ainsi, <login>
gagne un
élément <loginSec:userAgent>
qui permet
d'indiquer le type de logiciel utilisé côté client. Mais le
principal ajout est évidemment
<loginSec:pw>
, qui permet d'utiliser les
mots de passe aux nouvelles règles (mots de passe plus longs, par
exemple). Il y a aussi un
<loginSec:newPw>
pour indiquer le nouveau
mot de passe à utiliser. Notez que, si le mot de passe comprend des
caractères Unicode, il est recommandé qu'ils
se conforment au RFC 8265 (profil
OpaqueString
).
Voici un exemple d'une commande
<login>
nouveau style :
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <command> <login> <clID>ClientX</clID> <pw>[LOGIN-SECURITY]</pw> ... </login> <extension> <loginSec:loginSec xmlns:loginSec= "urn:ietf:params:xml:ns:epp:loginSec-1.0"> <loginSec:userAgent> <loginSec:app>EPP SDK 1.0.0</loginSec:app> <loginSec:tech>Vendor Java 11.0.6</loginSec:tech> <loginSec:os>x86_64 Mac OS X 10.15.2</loginSec:os> </loginSec:userAgent> <loginSec:pw>this is a long password not accepted before</loginSec:pw> </loginSec:loginSec> </extension> ... </command> </epp>
Et une réponse positive du serveur EPP à cette connexion, mais qui avertit que le mot de passe va expirer le 1er juillet :
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <response> <result code="1000"> <msg>Command completed successfully</msg> </result> <extension> <loginSec:loginSecData xmlns:loginSec= "urn:ietf:params:xml:ns:epp:loginSec-1.0"> <loginSec:event type="password" level="warning" exDate="2020-07-01T22:00:00.0Z" lang="en"> Password expiring in a week </loginSec:event> </loginSec:loginSecData> </extension> ... </response> </epp>
Et, ici, lorsqu'un client a voulu changer son mot de passe expiré,
avec <loginSec:newPw>
, mais que le nouveau
mot de passe était trop simple (cf. les recommandations
de l'ANSSI) :
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <response> <result code="2200"> <msg>Authentication error</msg> </result> <extension> <loginSec:loginSecData xmlns:loginSec= "urn:ietf:params:xml:ns:epp:loginSec-1.0"> <loginSec:event type="password" level="error" exDate="2020-03-24T22:00:00.0Z"> Password has expired </loginSec:event> <loginSec:event type="newPW" level="error" lang="fr"> Mot de passe vraiment trop trivial. </loginSec:event> </loginSec:loginSecData> </extension> ... </response> </epp>
Le schéma complet figure dans la section 5 du RFC.
Un changement plus radical aurait été de passer à un cadre d'authentification plus général comme SASL (RFC 4422) mais l'IETF a choisi une évolution plus en douceur.
À l'heure actuelle, je ne connais que deux mises en œuvre de ce
RFC, dans le SDK
de Verisign, en
et dans le logiciel
libre Net::DRI. Apparemment,
aucun serveur EPP de « grand » registre n'annonce l'extension, à
part Verisign.https://www.verisign.com/en_US/channel-resources/domain-registry-products/epp-sdks
Date de publication du RFC : Juin 2020
Auteur(s) du RFC : W. Kumari (Google), P. Hoffman (ICANN)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 19 juin 2020
Toute résolution DNS commence par la racine (de l'arbre des noms de domaine). Bien sûr, la mémorisation (la mise en cache) des réponses fait qu'on n'a pas besoin tout le temps de contacter un serveur racine. Mais c'est quand même fréquent et les performances de la racine sont donc cruciales. L'idée documentée dans ce RFC est donc d'avoir en local un serveur esclave de la racine, copiant celle-ci et permettant donc de répondre localement aux requêtes. Ce RFC remplace le premier RFC qui avait documenté l'idée, le RFC 7706, avec des changements significatifs, notamment vers davantage de liberté (le précédent RFC restreignait sérieusement les possibilités).
Le problème est particulièrement important pour les noms qui
n'existent pas. Si les TLD existants comme
.com
ou
.fr
vont vite se
retrouver dans la mémoire (le cache) du résolveur DNS, les fautes de
frappe ou autres cas où un TLD n'existe pas vont nécessiter la
plupart du temps un aller-retour jusqu'au serveur racine le plus
proche. Les réponses négatives seront également mémorisées mais 1)
il y a davantage de noms non existants que de noms existants 2) le
TTL est plus court
(actuellement deux fois plus court). Ces noms non existants
représentent ainsi la majorité du trafic de la racine.
Bien qu'il existe aujourd'hui des centaines de sites dans le monde où se trouve une instance d'un serveur racine, ce nombre reste faible par rapport au nombre total de réseaux connectés à l'Internet. Dans certains endroits de la planète, le serveur racine le plus proche est assez lointain. Voici les RTT en millisecondes avec les serveurs racine observés depuis un réseau tunisien (notez les deux serveurs qui répondent bien plus vite que les autres, car ils ont une instance à Tunis) :
% check-soa -4 -i . a.root-servers.net. 198.41.0.4: OK: 2015112501 (54 ms) b.root-servers.net. 192.228.79.201: OK: 2015112501 (236 ms) c.root-servers.net. 192.33.4.12: OK: 2015112501 (62 ms) d.root-servers.net. 199.7.91.13: OK: 2015112501 (23 ms) e.root-servers.net. 192.203.230.10: OK: 2015112501 (18 ms) f.root-servers.net. 192.5.5.241: OK: 2015112501 (69 ms) g.root-servers.net. 192.112.36.4: OK: 2015112501 (62 ms) h.root-servers.net. 128.63.2.53: OK: 2015112501 (153 ms) i.root-servers.net. 192.36.148.17: OK: 2015112501 (67 ms) j.root-servers.net. 192.58.128.30: OK: 2015112501 (55 ms) k.root-servers.net. 193.0.14.129: OK: 2015112501 (72 ms) l.root-servers.net. 199.7.83.42: ERROR: Timeout m.root-servers.net. 202.12.27.33: OK: 2015112501 (79 ms)
Ces délais peuvent sembler courts mais ils ne forment qu'une partie du travail de résolution, il est donc légitime de vouloir les réduire encore.
En outre, ces requêtes à la racine peuvent être observées, que ce soit par les opérateurs de serveurs racine, ou par des tiers sur le projet, ce qui n'est pas forcément souhaitable, question vie privée (cf. RFC 7626).
Donc, l'idée de base de ce RFC est de :
Cette idée est documentée dans ce RFC mais n'est pas encouragée (c'est un très vieux débat, dont j'avais déjà parlé). En effet, cela ajoute un composant à la résolution (le serveur local faisant autorité pour la racine), composant peu ou pas géré et qui peut défaillir, entrainant ainsi des problèmes graves et difficiles à déboguer. Mais pourquoi documenter une idée qui n'est pas une bonne idée ? Parce que des gens le font déjà et qu'il vaut mieux documenter cette pratique, et en limiter les plus mauvais effets. C'est pour cela, par exemple, que notre RFC demande que le serveur racine local ne réponde qu'à la même machine, pour limiter les conséquences d'une éventuelle défaillance à une seule machine.
Pas découragé ? Vous voulez encore le faire ? Alors, les détails
pratiques. D'abord (section 2 du RFC), les pré-requis. DNSSEC est
indispensable (pour éviter de se faire refiler un faux fichier de
zone par de faux serveurs racine). Ensuite (section 3), vous mettez
un serveur faisant autorité (par exemple NSD ou Knot) qui écoute sur une des adresses
locales (en 127.0.0.0/8
,
IPv6 est moins pratique car il ne fournit
paradoxalement qu'une seule adresse locale à la machine) et qui est
esclave des serveurs racine. À noter que votre serveur, n'étant pas
connu des serveurs racine, ne recevra pas les notifications (RFC 1996) et sera donc parfois un peu en retard sur
la vraie racine (ce qui n'est pas très grave, elle bouge peu).
Il est important de lister plusieurs serveurs maîtres dans sa configuration. En effet, si la mise à jour de la racine dans votre serveur esclave échoue, ce sera catastrophique (signatures DNSSEC expirées, etc) et cette configuration locale, contrairement à la « vraie » racine, n'a aucune redondance. (Une autre raison pour laquelle ce n'est pas une idée géniale.) Quels serveurs maîtres indiquer ? Certains serveurs racine permettent le transfert de zone (RFC 5936) mais ce n'est jamais officiel, ils peuvent cesser à tout moment (l'annexe A du RFC donne une liste et discute de ce choix). Une raison de plus de se méfier.
Il est donc important d'avoir un mécanisme de supervision, pour être prévenu si quelque chose échoue. On peut par exemple interroger le numéro de série dans l'enregistrement SOA de la racine et vérifier qu'il change.
Ensuite, une fois ce serveur faisant autorité configuré, il ne reste qu'à indiquer à un résolveur (comme Unbound) de l'utiliser (toujours section 3 du RFC).
Voici un exemple testé. J'ai choisi NSD et Unbound. Le RFC, dans son annexe B, donne plusieurs autres exemples, tous utilisant le même serveur comme résolveur et comme serveur faisant autorité. C'est en général une mauvaise idée mais, pour le cas particulier de ce RFC, cela peut se défendre.
D'abord, la configuration de NSD (notez la longue liste de
maîtres, pour maximiser les chances que l'un d'eux fonctionne ;
notez aussi l'adresse IP choisie,
127.12.12.12
) :
# RFC 8806 server: ip-address: 127.12.12.12 zone: name: "." request-xfr: 199.9.14.201 NOKEY # b.root-servers.net request-xfr: 192.33.4.12 NOKEY # c.root-servers.net request-xfr: 192.5.5.241 NOKEY # f.root-servers.net request-xfr: 192.112.36.4 NOKEY # g.root-servers.net request-xfr: 193.0.14.129 NOKEY # k.root-servers.net request-xfr: 192.0.47.132 NOKEY # xfr.cjr.dns.icann.org request-xfr: 192.0.32.132 NOKEY # xfr.lax.dns.icann.org request-xfr: 2001:500:200::b NOKEY # b.root-servers.net request-xfr: 2001:500:2f::f NOKEY # f.root-servers.net request-xfr: 2001:7fd::1 NOKEY # k.root-servers.net request-xfr: 2620:0:2830:202::132 NOKEY # xfr.cjr.dns.icann.org request-xfr: 2620:0:2d0:202::132 NOKEY # xfr.lax.dns.icann.org
Le démarrage de NSD (notez qu'il faut patienter un peu la première fois, le temps que le premier transfert de zone se passe) :
[2020-05-04 17:51:05.496] nsd[25649]: notice: nsd starting (NSD 4.3.1) [2020-05-04 17:51:05.496] nsd[25649]: notice: listen on ip-address 127.12.12.12@53 (udp) with server(s): * [2020-05-04 17:51:05.496] nsd[25649]: notice: listen on ip-address 127.12.12.12@53 (tcp) with server(s): * [2020-05-04 17:51:05.600] nsd[25650]: notice: nsd started (NSD 4.3.1), pid 25649 [2020-05-04 17:51:08.380] nsd[25649]: info: zone . serial 0 is updated to 2020050400
C'est bon, on a transféré la zone. Testons (notez le bit AA - Authoritative Answer - dans la réponse) :
% dig @127.12.12.12 SOA . ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24290 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 13, ADDITIONAL: 27 ... ;; ANSWER SECTION: . 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2020050400 1800 900 604800 86400 ... ;; Query time: 3 msec ;; SERVER: 127.12.12.12#53(127.12.12.12) ;; WHEN: Mon May 04 17:51:51 CEST 2020 ;; MSG SIZE rcvd: 868
C'est bon.
Maintenant, la configuration d'Unbound (différente de celle du RFC, qui utilise Unbound à la fois comme résolveur et comme serveur faisant autorité) :
server: # RFC 8806 do-not-query-localhost: no # Requires a slave auth. running (normally, nsd) stub-zone: name: "." stub-prime: no stub-addr: 127.12.12.12
(John Shaft me fait remarquer que la directive
stub-first
devrait permettre d'utiliser le
mécanisme de résolution classique si la requête échoue, ce qui
apporterait une petite sécurité en cas de panne du serveur local
faisant autorité pour la racine.) Et le test :
% dig www.cnam.fr ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30881 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ;; ANSWER SECTION: www.cnam.fr. 86400 IN CNAME kaurip.cnam.fr. kaurip.cnam.fr. 3600 IN A 163.173.128.40 ...
Ça a marché. Avec tcpdump, on voit le trafic (faible, en raison du cache) vers le serveur racine local :
18:01:09.865224 IP 127.0.0.1.54939 > 127.12.12.12.53: 55598% [1au] A? tn. (31) 18:01:09.865359 IP 127.12.12.12.53 > 127.0.0.1.54939: 55598- 0/8/13 (768)
Pour BIND, et d'autres logiciels, consultez l'annexe B du RFC.
À noter qu'il existe un brevet futile (comme tous les brevets...) de Verisign sur cette technique : déclaration #2539 à l'IETF. Il portait sur l'ancien RFC mais il est peut-être aussi valable (ou aussi ridicule) avec le nouveau.
La section 1.1 de notre RFC documente les changements depuis le
RFC 7606. Le principal est que le serveur
racine local n'a plus l'obligation d'être sur une adresse IP locale
à la machine (comme ::1
). Les autres
changements, qui reflètent l'expérience pratique avec cette
technique, après plus de quatre ans écoulés, vont en général dans le
sens de la « libéralisation ». Il y a moins de restrictions que dans
le RFC 7706.
Date de publication du RFC : Juillet 2020
Auteur(s) du RFC : P. Pfister, E. Vyncke (Cisco), T. Pauly (Apple), D. Schinazi (Google), W. Shao (Cisco)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF intarea
Première rédaction de cet article le 29 juillet 2020
Vous vous demandez certainement, chère lectrice et cher lecteur, ce qu'est un PvD (ProVisioning Domain). Cette notion n'est pas nouvelle, mais elle n'avait été formalisée rigoureusement qu'avec le RFC 7556. Un PvD est un domaine de l'Internet où des règles cohérentes de configuration du réseau s'appliquent. Au sein, d'un PvD, il y a un résolveur DNS à utiliser, des adresses IP spécifiques, des règles de routage et de sécurité que n'ont pas forcément les autres PvD. Ce nouveau RFC décrit une option IPv6 qui permet au PvD de signaler explicitement son existence, via un RA (Router Advertisement.)
La notion de PvD (ProVisioning Domain) est utile pour les machines qui ont plusieurs connexions, à des réseaux (et donc peut-être des PvD) différents, par exemple un ordinateur connecté à la fois à un FAI grand public et au VPN de l'entreprise. Il y a des PvD implicites : une machine qui connait le concept de PvD affecte un PvD implicite à chaque réseau et, par défaut, ne considère pas que le résolveur DNS de l'un convient pour les autres. Et il y a des PvD explicites, et c'est à eux qu'est consacré ce RFC. Un PvD explicite a un identificateur, qui a la syntaxe d'un FQDN. Attention, même si l'identificateur est un nom de domaine, le terme de « domaine » (le D de PvD) ne doit pas être confondu avec le sens qu'il a dans le DNS. L'intérêt d'un PvD explicite est qu'il permet d'avoir plusieurs domaines distincts sur la même interface réseau, et qu'on peut y associer des informations supplémentaires, récupérées sur le réseau ou bien préconfigurées.
On l'a vu, le concept de PvD avait été décrit dans le RFC 7556. Ce que notre nouveau RFC 8801 rajoute, c'est :
Le RA va donc indiquer le ou les identificateurs du ou des PvD du réseau. Notez qu'on peut avoir aussi le même PvD sur plusieurs réseaux, par exemple le Wi-Fi et le filaire chez moi font partie du même PvD et ont les mêmes règles et la même configuration.
Dans un monde idéal, l'utilisatrice dont
l'ordiphone a à la fois une connexion
4G, une connexion Wi-Fi et un VPN par dessus, aurait un
PvD pour chacun de ces trois réseaux différentes, et pourrait donc
décider intelligemment quelle interface utiliser pour quel type de
trafic. Par exemple, une requête DNS pour
wiki.private.example.com
(le
wiki interne de l'entreprise) sera envoyée
uniquement via le VPN, puisque les résolveurs des réseaux publics ne
connaissent pas ce domaine. Autre exemple, l'information envoyée
pour chaque PvD pourrait permettre aux applications de faire passer
le trafic important (la vidéo en haute définition, par exemple),
uniquement sur le réseau à tarification forfaitaire.
Bon, assez de considérations sur les PvD, passons aux nouvelles normes décrites dans ce RFC. D'abord, l'option RA. Elle est décrite dans la section 3. C'est très simple : une nouvelle option RA est créée, la 21, « PvD ID Router Advertisement Option », suivant le format classique des options RA, TLV (Type-Longueur-Valeur). Elle contient, entre autres :
Si jamais il y a plusieurs PvD sur le réseau, le routeur doit envoyer des RA différents, il n'est pas autorisé à mettre plusieurs options PvD dans un même RA.
Que va faire la machine qui reçoit une telle option dans un RA ?
Elle doit marquer toutes les informations de configuration qui
étaient dans le RA comme étant spécifiques à ce PvD. Par exemple, si
la machine a deux interfaces, et reçoit un RA sur chacune, ayant des
PvD différents, et donnant chacun l'adresses d'un résolveur DNS
(RFC 8106), alors ce résolveur ne doit être
utilisé que pour le réseau où il a été annoncé (et avec une adresse
IP source de ce réseau). Rappelez-vous que différents résolveurs DNS
peuvent donner des résultats différents, par exemple un des réseaux
peut avoir un Pi-hole qui donne des réponses
mensongères, afin de bloquer la publicité. (En parlant du DNS, des
détails sur cette question complexe figurent dans la section 5.2.1
du RFC 7556.) Autre exemple, si on utilise un
VPN vers son
employeur, les requêtes DNS pour les ressources internes
(congés.rh.example.com
…) doivent être envoyées
au résolveur de l'employeur, un résolveur extérieur peut ne pas être
capable de les résoudre. En fait, tous les cas où la réponse DNS
n'est pas valable publiquement nécessitent d'utiliser le bon
résolveur DNS. C'est aussi ce qui se passe avec
NAT64 (RFC 6147).
Idem pour d'autres informations de configuration comme le routeur par défaut : elles doivent être associées au PvD. Une machine qui connait les PvD ne peut donc pas se contenter d'une table simple listant le résolveur DNS, le routeur par défaut, etc. Elle doit avoir une table par PvD.
Si la machine récupère par ailleurs des informations de configuration avec DHCP, elle doit également les associer à un PvD, par exemple en utilisant comme PvD implicite celui de l'interface réseau utilisée.
Et pour IPv4, si le bit L est à un ? Dans ce cas, l'information IPv4 reçue sur la même interface doit être associée à ce PvD. (Notez que si plusieurs PvD sur la même interface ont le bit L, on ne peut rien décider, à part que quelqu'un s'est trompé.)
Les machines qui partagent leur connexion (tethering), ce qui est fréquent pour les connexions mobiles comme la 4G (RFC 7278), doivent relayer le RA contenant l'option PvD vers le réseau avec qui elles partagent (comme elles relaient d'autres messages similaires, cf. RFC 4389). Cela permettra à des techniques qui facilitent le partage, comme la délégation de préfixe du RFC 8415, de bien fonctionner. Même si la machine partageuse ne reçoit pas d'option PvD, il est recommandé qu'elle en ajoute une pour le bénéfice des engins avec qui elle partage sa connectivité.
Tout cela, c'était pour une machine PvD-aware, une machine qui sait ce qu'est un PvD et qui sait le gérer. Mais avec du vieux logiciel, écrit avant le concept de PvD, que se passe-t-il ? Une machine munie de ce logiciel va évidemment ignorer l'option PvD du RA et donc utiliser un seul résolveur DNS, un seul routeur par défaut pour tous les cas. Elle aura sans doute beaucoup de problèmes si plusieurs interfaces réseau sont actives et, en pratique, il vaudra mieux n'avoir qu'une seule interface à un moment donné.
Nous avons vu plus haut que, si le bit H dans l'option PvD est à
1, la machine peut obtenir davantage d'informations (PvD
Additional Information) en HTTP. Cette information sera encodée
en I-JSON (RFC 7493.)
Pourquoi ne pas avoir mis ces informations dans le RA ? Parce
qu'elles sont trop grosses ou, tout simplement, pas critiques, et
pouvant être ignorées. Et on les récupère où, ces informations ? Si
le PvD est radio.example.com
, l'URL à utiliser est
https://radio.example.com/.well-known/pvd
(préfixe .well-known
désormais enregistré.)
Attention, on ne doit tenter d'accéder à cet URL que si le bit H
était à 1. C'est du HTTPS (RFC 2818) et il
faut donc vérifier le certificat (cf. RFC 6125.) Vous voyez qu'on ne peut pas choisir un
PvD au hasard : il faut qu'il soit un nom de domaine qu'on contrôle, et pour lequel
on peut obtenir un certificat. Si on configure un des ces affreux
portails captifs, il
faut s'assurer que les clients auront le droit de se connecter au
serveur HTTP indiqué.
Les données auront le type application/pvd+json
. Elles
contiennent obligatoirement :
Ainsi que, si on veut :
D'autres membres peuvent être présents, un registre IANA existe et, pour y ajouter des termes, c'est la procédure « Examen par un expert » du RFC 8126.
Voici un exemple, avec le PvD smart.mpvd.io
(qui a servi à un hackathon
IETF) :
% curl https://smart.mpvd.io/.well-known/pvd { "name": "PvD for smart people", "prefixes": ["2001:67c:1230:101::/64", "2001:67c:1230:111::/64"], "noInternet": false, "metered": false, "captivePortalURL" : "https://smart.mpvd.io/captive.php" }
On notera que le membre expires
manque, ce qui
n'est pas bien, et que le membre identifier
, qui devrait
indiquer le PvD, est également absent, ce qui est encore plus mal. On voit qu'il
s'agissait d'une expérimentation. Voici ce qu'aurait dû être l'objet
JSON :
{ "identifier": "smart.mpvd.io", "expires": "2020-04-08T15:30:00Z", "prefixes": ["2001:67c:1230:101::/64", "2001:67c:1230:111::/64"], "noInternet": false, "name": "PvD for smart people", "metered": false, "captivePortalURL" : "https://smart.mpvd.io/captive.php" }
Si vous connaissez d'autres serveurs d'information PvD, indiquez-les moi. Mais il est probable que la plupart ne seront accessibles que depuis un réseau interne.
Voilà, vous savez l'essentiel désormais. La section 5 du RFC ajoute quelques considérations pratiques. D'abord, le bit R qui permettait à une option PvD d'inclure un RA. À quoi ça sert ? L'idée est que, pendant longtemps, la plupart des réseaux accueilleront un mélange de machines qui connaissent les PvD, et de machines qui ne sont pas conscientes de ce concept. Le bit R sert à envoyer des informations qui ne seront comprises que des premières. Par exemple, on met un préfixe dans le RA et, dans l'option PvD, un autre RA qui contient un autre préfixe. Les machines qui connaissent les PvD verront et utiliseront les deux préfixes, celles qui ne connaissent pas les PvD ignoreront l'option et n'auront que le premier préfixe. On peut également envoyer deux RA, un ne contenant pas d'option PvD, et un en contenant une (plus le « vrai » RA).
Un peu de sécurité, pour finir (section 6). D'abord, il faut bien se rappeler que les RA, envoyés en clair et non authentifiés, n'offrent aucune sécurité. Il est trivial, pour un méchant connecté au réseau, d'envoyer de faux RA et donc de fausses options PvD. En théorie, des protocoles comme IPsec ou SEND (RFC 3971) peuvent sécuriser les RA mais, en pratique, on ne peut guère compter dessus. Les seules solutions de sécurisation effectivement déployées sont au niveau 2, par exemple 802.1X combiné avec le RFC 6105. Ce n'est pas un problème spécifique à l'option PvD, c'est la sécurité (ou plutôt l'insécurité) des RA.
Ah, et un petit mot sur la vie
privée (section 7 du RFC). La connexion au serveur HTTP
va évidemment laisser des traces, et il est donc recommandé de la
faire depuis une adresse source temporaire (RFC 8981), et sans envoyer User-Agent:
ou
cookies.
Question mise en œuvre, il y a eu du code libre pour des tests (incluant une modification du noyau Linux, et un dissecteur pour Wireshark) mais je ne sais pas ce que c'est devenu et où en sont les systèmes d'exploitation actuels.
Date de publication du RFC : Juillet 2020
Auteur(s) du RFC : B. E. Carpenter (Univ. of Auckland), B. Liu (Huawei)
Pour information
Première rédaction de cet article le 15 juillet 2020
Il y a depuis longtemps un débat sur l'architecture de l'Internet : dans quelle mesure faut-il un Internet uniforme, où tout est pareil partout, et dans quelle mesure faut-il au contraire des particularités qui ne s'appliquent que dans telle ou telle partie de l'Internet ? Bien sûr, la réponse n'est pas simple, et ce nouveau RFC explore la question et discute ses conséquences, par exemple pour le développement des normes.
Sur le long terme, la tendance est plutôt au développement de règles locales, et de services qui ne sont accessibles que d'une partie de l'Internet. Le NAS avec les photos de famille accessible uniquement depuis la maison, le site Web permettant aux employés d'indiquer leurs demandes de congés, qu'on ne peut voir que depuis le réseau de l'entreprise, parce qu'on y est présent physiquement, ou bien via le VPN, les lois d'un pays qu'il essaie, avec plus ou moins de succès, d'appliquer sur son territoire… Mais, d'abord, une précision : le terme « local » ne signifie pas forcément une région géographique précise. « Local » peut, par exemple, désigner le réseau d'une organisation spécifique, qui y applique ses règles. « Local » peut être un bâtiment, un véhicule, un pays ou bien un réseau s'étendant au monde entier.
À noter que certains ont poussé cette tendance très loin en estimant que l'Internet allait se fragmenter en réseaux distincts, ne communiquant plus, voire que cela avait déjà été fait. C'est clairement faux, aujourd'hui, comme l'explique bien, par exemple, le RFC 7754, ou bien Milton Mueller dans son livre « Will The Internet Fragment? ». On peut toujours visiter ce blog depuis tous les réseaux connectés à l'Internet…
Un petit point de terminologie : le RFC utilise le terme « domaine » pour désigner une région de l'Internet ayant des règles spécifiques. Ce terme ne doit pas être confondu avec celui de « nom de domaine » qui désigne quelque chose de tout à fait différent. « Domaine », dans ce RFC, est plutôt utilisé dans le même sens que dans les RFC 6398 et RFC 8085, pour indiquer un « domaine administratif ». C'est un environnement contrôlé, géré par une entité (entreprise, association, individu) et qui peut définir des règles locales et les appliquer.
Aujourd'hui, le concept de « domaine local » ou « domaine limité » n'est pas vraiment formalisé dans l'Internet. Des RFC comme le RFC 2775 ou le RFC 4924 ont plutôt défendu l'inverse, la nécessité de maintenir un Internet unifié, insistant sur le problème des middleboxes intrusives (RFC 3234, RFC 7663 et RFC 8517). Actuellement, il y a déjà une fragmentation de l'Internet en ilots séparés, puisque certaines fonctions ne marchent pas toujours, et qu'on ne peut donc pas compter dessus. C'est le cas par exemple des en-têtes d'extension IPv6 (RFC 7872), de la découverte de la MTU du chemin (RFC 4821) ou de la fragmentation (RFC 8900). Sans compter le filtrage par les pare-feux. Bref, tous ces problèmes font que, de facto, l'Internet n'est plus transparent, et que des communications peuvent échouer, non pas en raison d'une panne ou d'une bogue, mais en raison de règles décidées par tel ou tel acteur sur le trajet. Donc, en pratique, on a des « domaines limités » mais sans que cela soit explicite, et cela joue un rôle important dans l'utilisation du réseau.
Si on avait des « domaines limités » ou « domaines locaux » explicites, quel serait le cahier des charges de ce concept ? La section 3 du RFC donne quelques idées à ce sujet. Par exemple, ces scénarios sont envisagés :
Bref, le cas est fréquent. On se dit, en regardant tous ces scénarios où un réseau, quoique connecté à l'Internet, a de très bonnes raisons d'avoir des règles locales spécifiques, qu'il serait bon qu'une organisation de normalisation, comme l'IETF, trouve une solution générale. Le problème est que, justement, chaque politique locale est différente. Néanmoins, les auteurs du RFC suggèrent de réfléchir à une nouvelle façon d'analyser les propositions à l'IETF, en ne considérant pas simplement leur usage sur l'Internet public (le vrai Internet, dirais-je) mais aussi ce qui se passera sur ces domaines limités.
Le RFC cite aussi le cas de protocoles qu'on avait cru pouvoir déployer sur l'Internet public mais qui en fait n'ont marché que dans des environnements fermés. Ce fut le cas bien sûr de RSVP (RFC 2205). Ses auteurs avaient pensé qu'un protocole de réservation de ressources pourrait marcher sur un réseau public, oubliant que, dans ce cas, chaque égoïste allait évidemment se réserver le plus de ressources possibles. (Ce n'est pas un problème spécifique à RSVP : aucun mécanisme de qualité de service ne peut marcher sur un réseau public décentralisé. Imaginez un bit « trafic très important, à ne jamais jeter » ; tout le monde le mettrait à 1 !)
Bon, maintenant qu'on a décrit les scénarios d'usage, quelle(s) solution(s) ? La section 4 du RFC liste quelques exemples de techniques conçues spécialement pour des domaines limités. Notons que le RFC parle des techniques en couche 3, la couche 2 étant évidemment toujours pour un domaine local (encore que, avec TRILL - RFC 6325, la couche 2 peut aller loin). Par exemple :
La section 5 du RFC conclut qu'il peut être souhaitable d'avoir des protocoles explicitement réservés à des domaines limités, et pour qui les règles pourraient être différentes de celles des protocoles conçus pour le grand Internet ouvert. Le RFC donne un exemple : si faire ajouter des en-têtes d'extension à un paquet IPv6 par le réseau qu'il traverse serait une abominable violation de la neutralité, en revanche, dans un domaine local, pourquoi ne pas se dire que ce serait possible, et peut-être intéressant dans certains cas ? Pour de tels protocoles, on ne pourrait pas garantir leur interopérabilité sur l'Internet, mais ils pourraient former une intéressante addition au socle de base du travail de l'IETF, qui sont les protocoles de l'Internet public.
Une fois posée l'opinion que les protocoles « à usage local » sont intéressants (et qu'ils existent déjà), comment les améliorer ? Actuellement, comme le concept de « domaine limité » n'est pas explicitement défini, le domaine est créé par la configuration des machines, des pare-feux, des résolveurs DNS, des ACL, du VPN… Un processus compliqué et où il est facile de se tromper, surtout avec le BYOD, le télétravail… Imaginez un serveur HTTP privé, qui ne sert que le domaine. Comment le configurer pour cela ? Compter sur les pare-feux ? Mettre des ACL sur le serveur (ce qui nécessite de connaître la liste, évolutive, des préfixes IP du domaine) ? Définir un domaine nécessite d'avoir un intérieur et un extérieur, et que chaque machine, chaque application, sache bien de quel côté elle est (ou à l'interface entre les deux, si elle sert de garde-frontière). Et, quand on est à l'intérieur, et qu'on envoie ou reçoit un message, il est important de savoir si on l'envoie à l'extérieur ou pas, si on le reçoit de l'intérieur ou pas. Bref, actuellement, il n'y a pas de solution propre permettant de répondre à cette question.
Le RFC définit alors un cahier des charges pour qu'on puisse définir des règles locales et que ça marche. Notamment :
À l'heure actuelle, tout ceci relève du vœu pieux.
Une motivation fréquente des domaines locaux (ou limités) est la sécurité. La section 7 de notre RFC, dédiée à ce sujet, fait remarquer qu'on voit souvent des gens qui croient que, dans un domaine limité, on peut réduire la sécurité car « on est entre nous ». Cela oublie, non seulement le fait que la plupart des menaces sont internes (par une malhonnêteté ou par une infection), mais aussi celui qu'un protocole qu'on croyait purement interne se retrouve parfois utilisé en extérieur. Un exemple classique est le site Web « interne » pour lequel on se dit que HTTPS n'est pas nécessaire. Si, suite à un problème de routage ou de pare-feu, il se retrouve exposé à l'extérieur, il n'y aura pas de seconde chance.
Enfin, l'annexe B du RFC propose une taxonomie des domaines limités : utile si vous voulez creuser la question.
Date de publication du RFC : Juin 2020
Auteur(s) du RFC : B. Wissingh (TNO), C. Wood (University of California Irvine), A. Afanasyev (Florida International University), L. Zhang (UCLA), D. Oran (Network Systems Research & Design, C. Tschudin (University of Basel)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF icnrg
Première rédaction de cet article le 18 juin 2020
Le terme d'ICN (Information-Centric Networking) désigne un paradigme de communication où tout est du contenu : plus d'adresses IP ou de noms de domaine, on désigne chaque ressource par un nom. Il en existe plusieurs déclinaisons comme le NDN (Named Data Networking) ou le CCNx (Content-Centric Networking). Ce nouveau RFC décrit le vocabulaire de ces deux déclinaisons.
La section 1 du RFC résume les concepts essentiels de l'ICN. Il s'agit d'une architecture de réseaux où le composant de base est un contenu auquel on veut accéder. Pour récupérer, mettons, une image de chaton, on donnerait le nom de cette image et elle arriverait sur votre écran. Dans cette architecture, la notion de machine disparait, il n'y a que du contenu. En théorie, cela permettra de faire du multicast, et de la mémorisation (caching) de contenu par les éléments du réseau, transformant chaque routeur en serveur miroir.
Le concept se décline ensuite en plusieurs architectures, utilisant des terminologies très variables, et peu normalisées. Toutes ne sont pas couvertes dans ce RFC, qui se focalise sur les sujets sur lesquels l'IRTF a le plus travaillé, NDN et CCNx (RFC 8569.)
La section 2 de notre RFC contient une description générale de l'ICN. Il y a un demandeur (requestor), également appelé consommateur, qui voudrait du contenu, mettons une photo de Scarlett Johansson. Il va manifester un intérêt (Interest) qui est un message demandant du contenu. Un ou plusieurs nœuds du réseau (l'équivalent des routeurs, mais la principale différence est qu'ils peuvent stocker du contenu) vont alors faire suivre cet intérêt jusqu'à une machine qui se trouve avoir le contenu en question, soit parce qu'elle en est la source (le producteur), soit parce qu'elle en a une copie. La machine répondra alors avec des données (Data) qui seront relayées en sens inverse, jusqu'au demandeur. Il y a donc deux types de messages, qui pourraient s'appeler requête et réponse mais que, dans le monde ICN, on nomme Intérêt et Données. Le message d'Intérêt contient le nom du contenu convoité. Chaque protocole particulier d'ICN a une façon différente de construire ces noms. Comme le message de Données arrive via plusieurs relais, l'authentification de ce message ne peut pas être déduit de sa source. Il faut qu'il soit signé, ou bien que le nom soit auto-certificateur, par exemple parce que le nom est un condensat du contenu. (Attention, l'authentification n'est pas la même chose que la confiance. Si un message est signé de la porte-parole du gouvernement, cela prouve qu'il vient bien d'elle, pas qu'elle dit la vérité. Voir la section 6 du RFC.)
Pour faire son travail, le nœud ICN va avoir besoin de trois base de données :
Parfois, les données sont de grande taille. En ICN, on dit qu'on va segmenter ces données en plusieurs messages. Le RFC utilise le terme de paquet pour une unité de données après la segmentation. Ces paquets sont potentiellement plus gros que la MTU et on parle de trame (frame) pour l'unité qui passe sur le réseau, après le découpage du paquet.
Une fois qu'on a ce résumé en tête, on peut passer aux termes utilisés dans l'ICN. C'est la section 3 du RFC, le gros morceau. Je ne vais pas les lister tous ici, je fais un choix arbitraire. Attention, comme il s'agit d'un domaine de recherche, et peu stabilisé, il y a beaucoup de synonymes. On a :
Date de publication du RFC : Juin 2020
Auteur(s) du RFC : K. Watsen (Watsen Networks), E. Auerswald, A. Farrel (Old Dog Consulting), Q. Wu (Huawei Technologies)
Pour information
Réalisé dans le cadre du groupe de travail IETF netmod
Première rédaction de cet article le 3 juillet 2020
Le sujet peut sembler un point de détail sans importance mais, dans un RFC, tout compte. Que serait une norme si les exemples de code et les descriptions en langage formel avaient la moindre ambiguïté ? C'est le cas par exemple des lignes trop longues : comment les couper pour qu'on comprenne bien ce qui s'est passé ? Ce RFC propose deux méthodes.
Le cas principal est celui des modules YANG (RFC 7950) et c'est pour cela que ce RFC sort du groupe de travail netmod. Mais le problème concerne en fait tous les cas où on veut mettre dans un RFC du texte dans un langage formel et que les lignes dépassent ce qui est généralement considéré comme acceptable. Notre RFC formalise deux solutions, toutes les deux traditionnelles et déjà souvent utilisées, mettre une barre inverse après les lignes coupées, ou bien mettre cette barre inverse et une autre au début de la ligne suivante. Donc, dans le premier cas, le texte :
Il n'est pas vraiment très long, mais c'est un exemple.
Ce texte, coupé à quarante-cinq caractères, donnerait :
Il n'est pas vraiment très long, mais c'est \ un exemple.
Ou bien :
Il n'est pas vraiment très long, mais c'est \ un exemple.
Et, avec la seconde solution :
Il n'est pas vraiment très long, mais c'est \ \un exemple.
Si la forme officielle des RFC n'est plus le texte brut depuis le RFC 7990, il y a toujours une version en texte brut produite, suivant le format décrit dans le RFC 7994. Il impose notamment des lignes de moins de 72 caractères. Or, les exemples de code, ou les textes décrits en un langage formel (modules YANG, ABNF, ou ASN.1, par exemple), dépassent facilement cette limite. Les outils actuels comme xml2rfc ne gèrent pas vraiment ce cas, se contentant d'afficher un avertissement, et laissant l'auteur corriger lui-même, d'une manière non officiellement standardisée.
Désormais, c'est fait, il existe un tel standard, ce nouveau RFC,
standard qui
permettra notamment de bien traiter les textes contenus dans
l'élement <sourcecode>
(RFC 7991, section 2.48) du source du RFC. Naturellement, rien
n'interdit d'utiliser cette norme pour autre chose que des RFC
(section 2 du RFC).
Les deux stratégies possibles, très proches, sont « une seule barre inverse » et « deux barres inverses ». Dans les deux cas, un en-tête spécial est mis au début, indiquant quelle stratégie a été utilisée, pour qu'il soit possible de reconstituer le fichier original.
Dans le premier cas, la stratégie à une seule barre inverse (section 7 du RFC), une ligne est considérée pliée si elle se termine par une barre inverse. La ligne suivante (moins les éventuels espaces en début de ligne) est sa suite. Dans le deuxième cas, la stratégie à deux barres inverses (section 8), une autre barre inverse apparait au début des lignes de continuation.
Différence pratique entre les deux stratégies ? Avec la première
(une seule barre inverse), le résultat est plus lisible, moins
encombré de caractères supplémentaires. Par contre, comme les
espaces au début de la ligne de continuation sont ignorés, cette
stratégie va échouer pour des textes comportant beaucoup d'espaces
consécutifs. La deuxième est plus robuste. La recommandation du RFC
est donc d'essayer d'abord la première stratégie, qui produit des
résultats plus jolis, puis, si elle échoue, la deuxième. L'outil
rfcfold
, présenté plus loin, fait exactement
cela par défaut.
La section 4 rappellait le cahier des charges des solutions adoptées :
rfcfold
présenté à la fin), notamment pour
le cas où un RFC ou autre document est automatiquement produit à
partir de sources extérieures,En revanche, la section 5 liste les « non-buts » : il n'est pas prévu que les solutions fonctionnent pour l'art ASCII. Et il n'est pas prévu de gérer les coupures spécifiques à un format de fichier. Les stratégies décrites dans ce RFC sont génériques. Au contraire, des outils comme pyang ou yanglint connaissent la syntaxe de YANG et peuvent plier les lignes d'un module YANG plus intelligemment (cf. aussi RFC 8340).
L'annexe A du RFC contient un script bash
qui met en œuvre les deux stratégies (le script
). Ici, je pars d'un fichier
JSON
compact (au format du RFC 8785). D'abord,
première stratégie (notez la ligne de documentation ajoutée au
début) :
rfcfold.bash
% ./rfcfold.bash -s 1 -i /tmp/i.txt -o /tmp/t.txt && cat /tmp/t.txt ========== NOTE: '\' line wrapping per RFC 8792 =========== [{"af":6,"avg":23.4686283333,"dst_addr":"2001:67c:2218:e::51:41","ds\ t_name":"2001:67c:2218:e::51:41","dup":0,"from":"2a01:4240:5f52:bbc4\ ::ba3","fw":5020,"group_id":26102101,"lts":30,"max":23.639063,"min":\ 23.285885,"msm_id":26102101,"msm_name":"Ping","mver":"2.2.1","prb_id\ ":1000276,"proto":"ICMP","rcvd":3,"result":[{"rtt":23.480937},{"rtt"\ :23.639063},{"rtt":23.285885}],"sent":3,"size":64,"src_addr":"2a01:4\ 240:5f52:bbc4::ba3","step":null,"stored_timestamp":1593694541,"times\ tamp":1593694538,"ttl":52,"type":"ping"},{"af":6,"avg":65.9926666667\ ...
Et avec la deuxième stratégie :
% ./rfcfold.bash -s 2 -i /tmp/i.txt -o /tmp/t.txt && cat /tmp/t.txt|more ========== NOTE: '\\' line wrapping per RFC 8792 ========== [{"af":6,"avg":23.4686283333,"dst_addr":"2001:67c:2218:e::51:41","ds\ \t_name":"2001:67c:2218:e::51:41","dup":0,"from":"2a01:4240:5f52:bbc\ \4::ba3","fw":5020,"group_id":26102101,"lts":30,"max":23.639063,"min\ \":23.285885,"msm_id":26102101,"msm_name":"Ping","mver":"2.2.1","prb\ \_id":1000276,"proto":"ICMP","rcvd":3,"result":[{"rtt":23.480937},{"\ \rtt":23.639063},{"rtt":23.285885}],"sent":3,"size":64,"src_addr":"2\ \a01:4240:5f52:bbc4::ba3","step":null,"stored_timestamp":1593694541,\ \"timestamp":1593694538,"ttl":52,"type":"ping"},{"af":6,"avg":65.992\
L'option -r
du script permet d'inverser,
c'està-dire de reconstituer le fichier original à partir du fichier
aux lignes pliées.
L'outil rfcfold
n'a pas de connaissance des
formats de fichiers et coupe donc brutalement, y compris au milieu
d'un nom de variable. Un outil plus intelligent (section 9.3)
pourrait couper au bon endroit, par exemple, pour XML, juste avant le
début d'un élément XML.
Date de publication du RFC : Juin 2020
Auteur(s) du RFC : J. M. Halpern (Ericsson), E. K. Rescorla (Mozilla)
Première rédaction de cet article le 18 juin 2020
Un très court RFC pour une décision de clarification : les RFC publiés par l'IETF (qui ne sont pas la totalité des RFC) doivent recueillir l'accord (le consensus) de l'IETF.
Oui, cela parait assez évident, mais ce n'était pas écrit (le RFC 2026 n'était pas clair à ce sujet). Il existe plusieurs voies (streams) pour les RFC et l'une des voies est nommée IETF stream. Pour être ainsi publié, le RFC doit avoir recueilli le fameux consensus approximatif de l'IETF. Les autres voies, comme la voie indépendante, n'ont pas cette obligation.
Notez que cette obligation de consensus approximatif existait pour les RFC sur le chemin des normes mais pas pour ceux pour information ou expérimentaux. La nouvelle règle tient en une phrase : sur la voie IETF, l'IESG doit veiller à ce que tout RFC, même expérimental, recueille un consensus approximatif. Les documents qui ne réunissent pas un tel consensus peuvent toujours être publiées en RFC via d'autres voies comme la voie indépendante (cf. RFC 8730), ou celle de l'IRTF (cf. RFC 7418).
Certaines décisions précédentes (comme celle de l'IESG en 2007) se posaient déjà la question, mais sans interdire de telles publications.
Notez que le RFC ne rappelle pas comment on vérifie ce consensus approximatif. Un des moyens est via un dernier appel lancé sur la liste de diffusion de l'IETF avant l'approbation. (Voici un exemple récent, notez le Stream: IETF dans l'outil de suivi.)
Date de publication du RFC : Mai 2020
Auteur(s) du RFC : B. Leiba (FutureWei Technologies)
Première rédaction de cet article le 28 mai 2020
Un très court RFC sorti dans l'urgence pour résoudre un petit problème politique. Les membres du NomCom (Nominating Committee) de l'IETF sont normalement choisis parmi des gens qui étaient à au moins trois des cinq précédentes réunions physiques de l'IETF. Que faire lorsqu'elles ont été annulées pour cause de COVID-19 ? (Ces critères sont ensuite devenus officiels avec le RFC 9389.)
Déjà deux réunions physiques ont été annulées, IETF 107 qui devait se tenir à Vancouver et IETF 108, qui devait se tenir à Madrid. Elles ont été remplacées par des réunions en ligne.
Le NomCom est un groupe qui désigne les membres de certaines instances de l'IETF. Les règles pour faire partie du NomCom sont exprimées dans le RFC 8713. Sa section 4.14 mentionne qu'un de ces critères est la présence à trois des cinq précédentes réunions physiques. Or, deux des réunions physiques de 2020 ont été annulées en raison de l'épidémie. Faut-il alors ne pas les compter dans « les cinq précédentes réunions » ? Ou bien compter la participation aux réunions en ligne comme équivalente à une réunion physique ?
La section 3 du RFC expose le choix fait : les réunions annulées ne comptent pas et le critère d'éligibilité au NomCom se fonde donc sur la participation à trois des cinq dernières réunions physiques qui se sont effectivement tenues. Cette interprétation n'a pas suscité d'objections particulières.
La même section 3 insiste sur le fait qu'il s'agit d'une décision exceptionnelle, ne s'appliquant que pour cette année, et qu'elle ne crée pas de nouvelles règles pour le futur.
Date de publication du RFC : Juin 2020
Auteur(s) du RFC : A. Rundgren, B. Jordan (Broadcom), S. Erdtman (Spotify AB)
Pour information
Première rédaction de cet article le 29 juin 2020
Des opérations cryptographiques comme la signature sont nettement plus simples lorsqu'il existe une forme canonique des données, une représentation normalisée qui fait que toutes les données identiques auront la même représentation. Le format de données JSON n'a pas de forme canonique standard, ce RFC documente une des canonicalisations possibles, JCS (JSON Canonicalization Scheme).
Pourquoi canonicaliser ? Parce que deux documents JSON dont le contenu est identique peuvent avoir des représentations texte différentes. Par exemple :
{"foo": 1, "bar": 3}
et
{ "foo": 1, "bar": 3 }
représentent exactement le même objet JSON. Mais la signature de ces deux formes textuelles ne donnera pas le même résultat. Pour les mécanismes de signature de JSON (comme JWS, décrit dans le RFC 7515), il serait préférable d'avoir un moyen de garantir qu'il existe une représentation canonique de l'objet JSON. Ce RFC en propose une. Je vous le dis tout de suite, la forme canonique de l'objet ci-dessus sera :
{"bar":3,"foo":1}
JSON est normalisé dans le RFC 8259. Il n'a pas de canonicalisation standard. Comme c'est une demande fréquente, plusieurs définitions d'une forme canonique de JSON ont été faites (cf. annexe H du RFC), comme Canonical JSON ou comme JSON Canonical Form. Mais aucune n'est encore la référence. (Vous noterez que ce RFC n'est pas une norme.) Cette situation est assez embêtante, alors que XML, lui, a une telle norme (cf. XML Signature).
Notez que la solution décrite dans ce RFC se nomme JCS, mais qu'il y a aussi un autre JCS, JSON Cleartext Signature.
Parmi les concepts importants de la proposition de ce RFC, JCS :
La section 3 du RFC décrit les opérations qui, ensemble, forment la canonicalisation JCS. À partir de données en mémoire (obtenues soit en lisant un fichier JSON, ce que je fais dans les exemples à la fin, soit en utilisant des données du programme), on émet du JSON avec :
"foo":1
et pas "foo":
1
),3000000
au lieu de 3E6
,
c'est la section
7.12.2.1 de ES6, si vous avez le courage, les puissances de
10 où l'exposant est inférieur à 21 sont développées),{"a":true,"b":false}
et non pas
{"b:"false,"a":true}
),Concernant le tri, André Sintzoff me fait remarquer que les règles de tri de JSON sont parfois complexes. Évidentes pour les caractères ASCII, elles sont plus déroutantes en dehors du BMP.
Plusieurs annexes complètent ce RFC. Ainsi, l'annexe B fournit des exemples de canonicalisation des nombres.
Et pour les travaux pratiques, on utilise quoi ? L'annexe A du
RFC contient une mise en œuvre en JavaScript,
et l'annexe G une liste d'autres mises en œuvre (que vous
pouvez aussi trouver en
ligne). Ainsi, en npm, il y a
et, en
Java, il y a
https://www.npmjs.com/package/canonicalize
.
(Si vous voulez programmer la canonicalisation de JSON vous-même,
l'annexe F contient d'utiles conseils aux programmeurs. Mais, ici,
on se contentera de logiciels déjà écrits.)https://github.com/erdtman/java-json-canonicalization
Commençons avec Python, et
.
Avec cet exemple de départ :
https://github.com/cyberphone/json-canonicalization/tree/master/python3
{ "numbers": [333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001], "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", "literals": [null, true, false] }
Et ce programme :
from org.webpki.json.Canonicalize import canonicalize import json import sys raw = open(sys.argv[1]).read() data = canonicalize(json.loads(raw)) print(data.decode())
On obtient :
% git clone https://github.com/cyberphone/json-canonicalization.git % export PYTHONPATH=json-canonicalization/python3/src % ./canonicalize.py test.json {"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"}
Et en Go ? Avec le même fichier JSON de départ, et ce programme :
package main import ("flag"; "fmt"; "os";) import "webpki.org/jsoncanonicalizer" func main() { flag.Parse() file, err := os.Open(flag.Arg(0)) if err != nil { panic(err) } data := make([]byte, 1000000) count, err := file.Read(data) if err != nil { panic(err) } result, err := jsoncanonicalizer.Transform(data[0:count]) if err != nil { panic(err); } else { fmt.Printf("%s", result); } }
On arrive au même résultat (ce qui est bien le but de la canonicalisation) :
% export GOPATH=$(pwd)/json-canonicalization/go % go build canonicalize.go % ./canonicalize.py test.json {"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"}
Comme notre RFC contraint le JSON à suivre le sous-ensemble I-JSON (RFC 7493), si le texte d'entrée ne l'est pas, il va y avoir un problème. Ici, avec un texte JSON qui n'est pas du I-JSON (deux membres de l'objet principal ont le même nom) :
% cat test3.json { "foo": 1, "bar": 3, "foo": 42 } % ./canonicalize test3.json panic: Duplicate key: foo
(Un autre code en Go est en
.)
https://github.com/ucarion/jcs
L'excellent programme jq n'a pas d'option
explicite pour canonicaliser, mais Sébastien Lecacheur me fait
remarquer que les options -c
et
-S
donnent apparemment le même résultat (y
compris sur les nombres, ce qui ne semble pas documenté) :
% cat example.json { "foo": 1, "bar": 3000000000000000000000000000000, "baz": true } % jq -cS . example.json {"bar":3e+30,"baz":true,"foo":1}
Par contre, je n'ai rien trouvé sur ce formateur en ligne.
Comme il y a un brevet pour tout, notez qu'apparemment VMware prétend avoir inventé la canonicalisation JSON (cf. ce signalement, et le commentaire « The patent is effectively a JSON based "remake" of XML's "enveloped" signature scheme which in the submitter's opinion makes it invalid. »).
Date de publication du RFC : Mai 2020
Auteur(s) du RFC : M. Boucadair (Orange), T. Reddy (McAfee)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dots
Première rédaction de cet article le 31 mai 2020
Le système DOTS (Distributed Denial-of-Service Open Threat Signaling) est conçu pour permettre la coordination des défenseurs pendant une attaque par déni de service (cf. RFC 8612). Pour cela, DOTS a deux protocoles, le protocole de signalisation, à utiliser en cas de crise, et le protocole de données, pour les temps plus calmes. Ce dernier fait l'objet de ce RFC.
Pourquoi deux protocoles ? Parce qu'il y a deux sortes de données : celles urgentes, de petite taille, transmises dans le feu de l'attaque, au moment où le réseau marche mal, c'est le domaine du protocole « signalisation », du RFC 9132. Et les données de grande taille, moins urgentes, mais qui doivent être transmises de manière fiable. Et c'est le but de notre RFC. Il y a donc deux canaux entre le client DOTS, qui demande de l'aide, et le serveur DOTS qui fournit des mécanismes d'atténuation des attaques : le canal « signalisation » du RFC 9132, et le canal « données », de notre RFC. Ce canal de données va servir, par exemple, à transporter :
Pour lire le reste du RFC, ne ratez pas la section 2, sur le vocabulaire à utiliser. Notamment, le terme d'ACL est utilisé dans un sens plus général que celui du RFC 8519.
Bon, le protocole, maintenant (section 3). Comme il n'a pas
besoin de fonctionner en permanence, même dans les cas d'attaque,
contrairement au protocole de signalisation, et comme il a par
contre besoin de fiabilité, il va utiliser des technologies
classiques : RESTCONF (RFC 8040) au-dessus de TLS (et donc au-dessus de TCP). RESTCONF
utilise HTTP donc on aura les méthodes classiques,
GET
, DELETE
, etc. Par
exemple, le client DOTS qui veut récupérer des informations sur la
configuration commence par faire un GET
. Les données
sont encodées en JSON (RFC 8259). Elles
sont spécifiées dans un module YANG (RFC 7950) et on passe du modèle YANG à l'encodage
concret en suivant les règles du RFC 7951.
La connexion entre le client DOTS et le serveur peut être
intermittente (on se connecte juste le temps de faire un
GET
, par exemple) ou bien permanente, ce qui
est pratique si le client faite des requêtes régulières, ou bien si
le serveur pousse des notifications (section 6.3 du RFC 8040).
Ah, et j'ai parlé de client et de serveur DOTS. Comment est-ce que le client trouve le serveur ? Cela peut être fait manuellement, ou bien via la procédure de découverte du RFC 8973.
Les détails sur ce qu'on peut récupérer en DOTS ? Ils sont dans
la section 4, sous forme d'un module YANG,
nommé ietf-dots-data-channel
(désormais dans
le
registre IANA). Par exemple, dans ce module, les ACL permettent d'indiquer les
adresses IP et les protocoles de transport concernés, ainsi que les actions à
entreprendre (laisser passer le paquet, jeter le paquet, etc), comme
avec n'importe quel pare-feu. Le serveur DOTS agit donc
comme un pare-feu distant. Si le serveur l'accepte, on peut aussi
mettre des ACL portant sur les protocoles de la couche 4, par exemple pour filtrer sur
le port.
Bon, passons maintenant à la pratique. Nous allons utiliser le
serveur public de test
dotsserver.ddos-secure.net
. Comme le protocole
de données de DOTS repose sur RESTCONF (RFC 8040) qui
repose lui-même sur HTTP, nous allons utiliser curl comme client. Ce serveur public exige
un certificat client, que nous récupérons
en ligne. Ensuite, je crée un alias
pour simplifier les commandes ultérieures :
% alias dotsdata='curl --cacert ./ca-cert.pem --cert ./client-cert.pem --key ./client-key.pem --header "Content-type: application/yang-data+json" --write-out "\n%{http_code}\n"'
Et enregistrons-nous, pour indiquer un identificateur de client DOTS :
% cat register.json { "ietf-dots-data-channel:dots-client": [ { "cuid": "s-bortzmeyer" } ] } % dotsdata --request POST --data @register.json https://dotsserver.ddos-secure.net/v1/restconf/data/ietf-dots-data-channel:dots-data/ 201
Voilà, l'utilisateur s-bortzmeyer
est
enregistré (201 est le code de retour HTTP
Created ; si cet identificateur avait déjà
existé, on aurait récupéré un 409 - Conflict).
Quand on aura fini, on pourra le détruire :
% dotsdata --request DELETE https://dotsserver.ddos-secure.net/v1/restconf/data/ietf-dots-data-channel:dots-data/dots-client=MONIDENTIFICATEUR 204
Suivant la logique REST de RESTCONF, l'identité du client figure dans l'URL.
Maintenant, créons un alias pour le préfixe
2001:db8::/32
:
% cat create-alias.json { "ietf-dots-data-channel:aliases": { "alias": [ { "name": "TEST-ALIAS", "target-prefix": [ "2001:cafe::/32" ] } ] } } % dotsdata --request POST --data @create-alias.json https://dotsserver.ddos-secure.net/v1/restconf/data/ietf-dots-data-channel:dots-data/dots-client=s-bortzmeyer {"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-message":"alias: post failed: dots r_alias: TEST-ALIAS: target_prefix \"2001:cafe::/32\" is not supported within Portal ex-portal1 (1.1.1.69,1.1.1.71,1.1.2.0/24,2001:db8:6401::/96)"}}} 400
Aïe, ça a raté (400 = Bad request). C'est parce que, comme nous le dit le message d'erreur, un vrai serveur DOTS n'accepte pas qu'un client joue avec n'importe quelle adresse IP, pour d'évidentes raisons de sécurité. Il faut prouver (par un moyen non spécifié dans le RFC) quels préfixes on contrôle, et ce seront les seuls où on pourra définir des alias et des ACL. Ici, le serveur de test n'autorise que trois préfixes, indiqués dans sa documentation et dans le message d'erreur. Reprenons :
% cat create-alias.json { "ietf-dots-data-channel:aliases": { "alias": [ { "name": "TEST-ALIAS", "target-prefix": [ "2001:db8:6401::f00/128" ] } ] } } % dotsdata --request POST --data @create-alias.json https://dotsserver.ddos-secure.net/v1/restconf/data/ietf-dots-data-channel:dots-data/dots-client=s-bortzmeyer 201
Ouf, c'est bon, vérifions que l'alias a bien été créé :
% dotsdata --request GET --data @create-alias.json https://dotsserver.ddos-secure.net/v1/restconf/data/ietf-dots-data-channel:dots-data/dots-client=s-bortzmeyer/aliases {"ietf-dots-data-channel:aliases":{"alias":[{"name":"TEST-ALIAS","pending-lifetime":10078,"target-prefix":["2001:db8:6401::f00/128"]}]}} 200
C'est parfait, on va essayer de demander du filtrage, maintenant. D'abord, les capacités du serveur dans ce domaine :
% dotsdata --request GET --data @create-alias.json https://dotsserver.ddos-secure.net/v1/restconf/data/ietf-dots-data-channel:dots-data/capabilities | jq . ... "ipv6": { "length": true, "protocol": true, "destination-prefix": true, "source-prefix": true, "fragment": true }, "tcp": { "flags": true, "flags-bitmask": true, "source-port": true, "destination-port": true, "port-range": true },
Bien, le serveur sait filtrer sur la longueur des paquets, sur les
adresses IP source et destination, et il sait traiter spécifiquement
les fragments. Et il sait filtrer le TCP sur divers critères, comme
les ports source et destination. On va lui demander de filtrer tout
ce qui vient de 2001:db8:dead::/48
:
% cat install-acl.json { "ietf-dots-data-channel:acls": { "acl": [ { "name": "TEST-ACL", "type": "ipv6-acl-type", "activation-type": "activate-when-mitigating", "aces": { "ace": [ { "name": "TEST-RULE", "matches": { "ipv6": { "source-ipv6-network": "2001:db8:dead::/48" } }, "actions": { "forwarding": "drop" } } ] } } ] } } % dotsdata --request POST --data @install-acl.json https://dotsserver.ddos-secure.net/v1/restconf/data/ietf-dots-data-channel:dots-data/dots-client=s-bortzmeyer 201
Parfait, le serveur a accepté notre ACL. Le paramètre
activation-type
indiquait de ne pas filtrer
tout de suite mais seulement lorsqu'une atténuation serait activée
(activate-when-mitigating
). Vous avez vu les
fonctions les plus importantes du protocole de données de DOTS. En
cas d'attaque, il ne vous reste plus qu'à utiliser le protocole de
signalisation (RFC 9132) pour utiliser alias
et ACLs.
Quelques considérations de sécurité, maintenant, en section 10 du RFC. La sécurité est évidemment cruciale pour DOTS puisque, si on l'utilise, c'est qu'on a des ennemis et qu'ils nous attaquent. Les préfixes IP qu'on veut protéger ou au contraire qu'on veut filtrer sont par exemple une information confidentielle (elle pourrait aider l'attaquant). Le RFC impose donc la cryptographie pour protéger les communications, en utilisant TLS, et avec chiffrement intègre. Même avec TLS et ce chiffrement intègre, comme la couche transport n'est pas protégée, un attaquant actif pourrait perturber la communication, par exemple en injectant des paquets TCP RST (qui coupent la connexion). Il existe un mécanisme de protection, AO, normalisé dans le RFC 5925, mais il est très peu déployé en pratique.
La cryptographie ne protège pas contre un client DOTS malveillant, ou simplement mal configuré. Le serveur doit donc vérifier que le client a le droit de protéger tel ou tel préfixe IP. Autrement, un client DOTS pourrait demander le filtrage du préfixe d'un de ses ennemis. Le RFC ne spécifie pas quel mécanisme utiliser pour cette vérification, mais cela peut être, par exemple, l'utilisation des registres des RIR, qui indiquent qui est le titulaire légitime d'un préfixe.
Le protocole de ce RFC peut utiliser des noms de domaine pour configurer les ACL. Dans ce cas, le serveur doit veiller à sécuriser la résolution DNS, à la fois contre des manipulations, grâce à DNSSEC, et contre la surveillance, en utilisant du DNS chiffré comme dans le RFC 7858.
Pour les mises en œuvre de ce protocole, voyez mon article sur le RFC 9132. Mais le protocole de notre RFC étant fondé sur HTTPS, on peut dans une certaine mesure utiliser des outils existants comme, vous l'avez vu, curl.
Date de publication du RFC : Mai 2020
Auteur(s) du RFC : T. Reddy (McAfee), M. Boucadair (Orange), P. Patil (Cisco), A. Mortensen (Arbor Networks), N. Teague (Iron Mountain Data Centers)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dots
Première rédaction de cet article le 31 mai 2020
Le protocole DOTS (Distributed Denial-of-Service Open Threat Signaling) vise à permettre au client d'un service anti-dDoS de demander au service de mettre en route des mesures contre une attaque. Ce RFC décrit le canal de signalisation de DOTS, celui par lequel passera la demande d'atténuation de l'attaque. Suite à un petit problème YANG, ce RFC a été remplacé depuis par le RFC 9132.
Si vous voulez mieux comprendre DOTS, il est recommandé de lire le RFC 8612, qui décrit le cahier des charges de ce protocole, et le RFC 8811, qui décrit l'architecture générale. Ici, je vais résumer à l'extrême : un client DOTS, détectant qu'une attaque par déni de service est en cours contre lui, signale, par le canal normalisé dans ce RFC, à un serveur DOTS qu'il faudrait faire quelque chose. Le serveur DOTS est un service anti-dDoS qui va, par exemple, examiner le trafic, jeter ce qui appartient à l'attaque, et transmettre le reste à son client.
Ces attaques par déni de service sont une des plaies de l'Internet, et sont bien trop fréquentes aujourd'hui (cf. RFC 4987 ou RFC 4732 pour des exemples). Bien des réseaux n'ont pas les moyens de se défendre seuls et font donc appel à un service de protection (payant, en général, mais il existe aussi des services comme Deflect). Ce service fera la guerre à leur place, recevant le trafic (via des manips DNS ou BGP), l'analysant, le filtrant et envoyant ce qui reste au client. Typiquement, le client DOTS sera chez le réseau attaqué, par exemple en tant que composant d'un IDS ou d'un pare-feu, et le serveur DOTS sera chez le service de protection. Notez donc que client et serveur DOTS sont chez deux organisations différentes, communiquant via le canal de signalisation (signal channel), qui fait l'objet de ce RFC.
La section 3 de notre RFC expose les grands principes du
protocole utilisé sur ce canal de signalisation. Il repose sur
CoAP, un équivalent léger de HTTP, ayant beaucoup
de choses communes avec HTTP. Le choix d'un protocole différent de
HTTP s'explique par les spécificités de DOTS : on l'utilise quand ça
va mal, quand le réseau est attaqué, et il faut donc pouvoir
continuer à fonctionner même quand de nombreux paquets sont
perdus. CoAP a les caractéristiques utiles pour DOTS, il est conçu
pour des réseaux où il y aura des pertes, il tourne sur UDP, il permet des
messages avec ou sans accusé de réception, il utilise peu de
ressources, il peut être sécurisé par DTLS… TCP est également
utilisable mais UDP est préféré, pour éviter le
head-of-line
blocking. CoAP est normalisé dans le RFC 7252. Parmi les choses à retenir, n'oubliez pas
que l'encodage du chemin dans l'URI est un peu spécial, avec une option
Uri-Path:
par segment du chemin (RFC 7252, section 5.10.1). Par abus de langage,
j'écrirai « le client CoAP demande
/foo/bar/truc.cbor
» alors qu'il y aura en fait
trois options Uri-Path:
:
Uri-Path: "foo" Uri-Path: "bar" Uri-Path: "truc.cbor"
Par défaut, DOTS va utiliser le port 4646 (et non pas le port par
défaut de CoAP, 5684, pour éviter toute confusion avec d'autres
services tournant sur CoAP). Ce port a été choisi pour une bonne
raison, je vous laisse la chercher, la solution est à la fin de cet
article. Le plan d'URI sera coaps
ou
coaps+tcp
(RFC 7252,
section 6, et RFC 8323, section 8.2).
Le fonctionnement de base est simple : le client DOTS se connecte au serveur, divers paramètres sont négociés. Des battements de cœur peuvent être utilisés (par le client ou par le serveur) pour garder la session ouverte et vérifier son bon fonctionnement. En cas d'attaque, le client va demander une action d'atténuation. Pendant que celle-ci est active, le serveur envoie de temps en temps des messages donnant des nouvelles. L'action se terminera, soit à l'expiration d'un délai défini au début, soit sur demande explicite du client. Le serveur est connu du client par configuration manuelle, ou bien par des techniques de découverte comme celles du RFC 8973.
Les messages sont encodés en CBOR (RFC 8949). Rappelez-vous que le modèle de données
de CBOR est très proche de celui de JSON, et notre RFC spécifie donc les
messages avec une syntaxe JSON, même si ce n'est pas l'encodage
utilisé sur le câble. Pour une syntaxe formelle des messages, le RFC
utilise YANG (cf. RFC 7951). Le type MIME des messages
est application/dots+cbor
.
La section 4 du RFC décrit les différents messages possibles plus
en détail. Je ne vais pas tout reprendre ici, juste donner quelques
exemples. Les URI commencent toujours par
/.well-known/dots
(.well-known
est normalisé dans le RFC 8615, et dots
est
désormais enregistré
à l'IANA). Les différentes actions ajouteront au chemin dans
l'URI /mitigate
pour les demandes d'actions
d'atténuation, visant à protéger de l'attaque,
/hb
pour les battements de cœur, etc.
Voici par exemple une demande de protection, effectuée avec la méthode CoAP PUT :
Header: PUT (Code=0.03) Uri-Path: ".well-known" Uri-Path: "dots" Uri-Path: "mitigate" Uri-Path: "cuid=dz6pHjaADkaFTbjr0JGBpw" Uri-Path: "mid=123" Content-Format: "application/dots+cbor" { ... Données en CBOR (représentées en JSON dans le RFC et dans cet article, pour la lisibilité). }
L'URI, en notation traditionnelle, sera donc
/.well-known/dots/mitigate/cuid=dz6pHjaADkaFTbjr0JGBpw/mid=123
. CUID
veut dire Client Unique IDentifier et sert à
identifier le client DOTS, MID est Mitigation
IDentifier et identifie une demande d'atténuation
particulière. Si ce client DOTS fait une autre demande de
palliation, le MID changera mais le CUID sera le même.
Que met-on dans le corps du message ? On a de nombreux champs définis pour indiquer ce qu'on veut protéger, et pour combien de temps. Par exemple, on pourrait avoir (je rappelle que c'est du CBOR, format binaire, en vrai) :
{ "ietf-dots-signal-channel:mitigation-scope": { "scope": [ { "target-prefix": [ "2001:db8:6401::1/128", "2001:db8:6401::2/128" ], "target-port-range": [ { "lower-port": 80 }, { "lower-port": 443 } ], "target-protocol": [ 6 ], "lifetime": 3600 } ] } }
Ici, le client demande qu'on protège
2001:db8:6401::1
et
2001:db8:6401::2
(target
veut dire qu'ils sont la cible d'une attaque, pas qu'on veut les
prendre pour cible), sur
les ports 80 et 443,
en TCP, pendant une heure. (lower-port
seul,
sans upper-port
indique un port unique, pas un
intervalle.)
Le serveur va alors répondre avec le code 2.01 (indiquant que la requête est acceptée et traitée) et des données :
{ "ietf-dots-signal-channel:mitigation-scope": { "scope": [ { "mid": 123, "lifetime": 3600 } ] } }
La durée de l'action peut être plus petite que ce que le client a demandé, par exemple si le serveur n'accepte pas d'actions trop longues. Évidemment, si la requête n'est pas correcte, le serveur répondra 4.00 (format invalide), si le client n'a pas payé, 4.03, s'il y a un conflit avec une autre requête, 4.09, etc. Le serveur peut donner des détails, et la liste des réponses possibles figure dans des registres IANA, comme celui de l'état d'une atténuation, ou celui des conflits entre ce qui est demandé et d'autres actions en cours.
Le client DOTS peut ensuite récupérer des informations sur une action de palliation en cours, avec la méthode CoAP GET :
Header: GET (Code=0.01) Uri-Path: ".well-known" Uri-Path: "dots" Uri-Path: "mitigate" Uri-Path: "cuid=dz6pHjaADkaFTbjr0JGBpw" Uri-Path: "mid=123"
Ce GET
/.well-known/dots/mitigate/cuid=dz6pHjaADkaFTbjr0JGBpw/mid=123
va renvoyer de l'information sur l'action d'identificateur (MID)
123 :
{ "ietf-dots-signal-channel:mitigation-scope": { "scope": [ { "mid": 123, "mitigation-start": "1507818393", "target-prefix": [ "2001:db8:6401::1/128", "2001:db8:6401::2/128" ], "target-protocol": [ 6 ], "lifetime": 1755, "status": "attack-stopped", "bytes-dropped": "0", "bps-dropped": "0", "pkts-dropped": "0", "pps-dropped": "0" } ] } }
Les différents champs de la réponse sont assez évidents. Par
exemple, pkts-dropped
indique le nombre de
paquets qui ont été jetés par le protecteur.
Pour mettre fin aux actions du système de protection, le client utilise évidemment la méthode CoAP DELETE :
Header: DELETE (Code=0.04) Uri-Path: ".well-known" Uri-Path: "dots" Uri-Path: "mitigate" Uri-Path: "cuid=dz6pHjaADkaFTbjr0JGBpw" Uri-Path: "mid=123"
Le client DOTS peut se renseigner sur les capacités du serveur avec
un GET de /.well-known/dots/config
.
Ce RFC décrit le canal de signalisation de DOTS. Le RFC 8783, lui, décrit le canal de données. Le canal de signalisation est prévu pour faire passer des messages de petite taille, dans un environnement hostile (attaque en cours). Le canal de données est prévu pour des données de plus grande taille, dans un environnement où les mécanismes de transport normaux, comme HTTPS, sont utilisables. Typiquement, le client DOTS utilise le canal de données avant l'attaque, pour tout configurer, et le canal de signalisation pendant l'attaque, pour déclencher et arrêter l'atténuation.
Les messages possibles sont modélisés en YANG. YANG est normalisé dans le RFC 7950. Notez que YANG avait été initialement créé pour décrire les commandes envoyées par NETCONF (RFC 6241) ou RESTCONF (RFC 8040) mais ce n'est pas le cas ici : DOTS n'utilise ni NETCONF, ni RESTCONF mais son propre protocole basé sur CoAP. La section 5 du RFC contient tous les modules YANG utilisés.
La mise en correspondance des modules YANG avec l'encodage
CBOR figure dans la section 6. (YANG permet
une description abstraite d'un message mais ne dit pas, à lui tout
seul, comment le représenter en bits sur le réseau.) Les clés CBOR
sont toutes des entiers ; CBOR permet d'utiliser des chaînes de
caractères comme clés mais DOTS cherche à gagner de la place. Ainsi,
les tables de la section 6 nous apprennent que le champ
cuid
(Client Unique
IDentifier) a la clé 4, suivie d'une chaîne de caractères
en CBOR. (Cette correspondance est désormais un
registre IANA.) D'autre part, DOTS introduit une étiquette
CBOR, 271 (enregistrée
à l'IANA, cf. RFC 8949, section 3.4)
pour marquer un document CBOR comme lié au protocole DOTS.
Évidemment, DOTS est critique en matière de sécurité. S'il ne fonctionne pas, on ne pourra pas réclamer une action de la part du service de protection. Et s'il est mal authentifié, on risque de voir le méchant envoyer de faux messages DOTS, par exemple en demandant l'arrêt de l'atténuation. La section 8 du RFC rappelle donc l'importance de sécuriser DOTS par TLS ou plutôt, la plupart du temps, par son équivalent pour UDP, DTLS (RFC 9147). Le RFC insiste sur l'authentification mutuelle du serveur et du client, chacun doit s'assurer de l'identité de l'autre, par les méthodes TLS habituelles (typiquement via un certificat). Le profil de DTLS recommandé (TLS est riche en options et il faut spécifier lesquelles sont nécessaires et lesquelles sont déconseillées) est en section 7. Par exemple, le chiffrement intègre est nécessaire.
La section 10 revient sur les questions de sécurité en ajoutant
d'autres avertissements. Par exemple, TLS ne protège pas contre
certaines attaques par déni de service, comme un paquet TCP RST
(ReSeT). On peut sécuriser la communication avec
TCP-AO (RFC 5925) mais c'est un vœu pieux, il
est très peu déployé à l'heure actuelle. Ah, et puis si les
ressources à protéger sont identifiées par un nom de domaine, et pas une adresse ou un
préfixe IP (target-fqdn
au lieu de
target-prefix
), le RFC dit qu'évidemment la
résolution doit être faite avec DNSSEC.
Question mises en œuvre, DOTS dispose d'au moins quatre implémentations, dont l'interopérabilité a été testée plusieurs fois lors de hackathons IETF (la première fois ayant été à Singapour, lors de l'IETF 100) :
Notez qu'il existe des serveurs de test DOTS
publics comme
coaps://dotsserver.ddos-secure.net:4646
.
Ah, et la raison du choix du port 4646 ? C'est parce que 46 est le code ASCII pour le point (dot en anglais) donc deux 46 font deux points donc dots.
Date de publication du RFC : Avril 2020
Auteur(s) du RFC : L. Colitti, J. Linkova (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 25 avril 2020
Lors qu'on utilise le système NAT64, pour permettre à des machines situées sur un réseau purement IPv6 de parler avec des machines restées en seulement IPv4, il faut utiliser un résolveur DNS spécial ou bien connaitre le préfixe utilisé pour la synthèse des adresses IPv6. Ce nouveau RFC décrit une option RA (Router Advertisement) pour informer les machines du préfixe en question.
NAT64 est normalisé dans le RFC 6146 et son copain DNS64 dans le RFC 6147. Il permet à des machines situées dans un
réseau n'ayant qu'IPv6 de parler à des
machines restées uniquement sur le protocole du siècle dernier,
IPv4. Le principe est que, pour parler à la
machine d'adresse 192.0.2.1
, la machine
purement IPv6 va écrire à 64:ff9b::192.0.2.1
(soit 64:ff9b::c000:201
.) C'est le routeur
NAT64 qui changera ensuite le paquet IPv6 allant vers
64:ff9b::192.0.2.1
en un paquet IPv4 allant
vers 192.0.2.1
. NAT64 est, par exemple,
largement utilisé dans les grandes réunions internationales comme
l'IETF
ou le FOSDEM, où le réseau WiFi par
défaut est souvent purement IPv6. Mais pour que la machine émettrice
pense à écrire à 64:ff9b::192.0.2.1
, il faut
qu'elle connaisse le préfixe à mettre devant les adresses IPv4
(64:ff9b::/96
n'est que le préfixe par défaut.)
La solution la plus courante, DNS64, est que le résolveur DNS mente, en fabriquant des
adresses IPv6 pour les machines n'en ayant pas, dispensant ainsi les
machines du réseau local IPv6 de tout effort. Mais si l'émetteur
veut faire sa résolution DNS lui-même, ou bien utiliser un autre
résolveur, qui ne fabrique pas les adresses v6 ? Une alternative est
donc que cet émetteur fasse lui-même la synthèse. Pour cela, il faut
lui fournir le préfixe IPv6 du routeur NAT64.
Cela résoudra le problème de la machine qui ne veut pas utiliser le résolveur DNS64 :
http://192.0.2.13/
, dans lequel il n'y a pas
de nom à
résoudre,Pourquoi envoyer cette information avec les RA (Router Advertisements) du RFC 4861 plutôt que par un autre mécanisme ? La section 3 du RFC explique que c'est pour être sûr que le sort est partagé ; si le routeur qui émet les RA tombe en panne, on ne recevra plus l'information, ce qui est cohérent, alors qu'avec un protocole séparé (par exemple le PCP du RFC 7225), on risquait de continuer à annoncer un préfixe NAT64 désormais inutilisable. En outre, cela diminue le nombre de paquets à envoyer (l'information sur le préfixe NAT64 peut être une option d'un paquet RA qu'on aurait envoyé de toute façon.) Et, en cas de changement du préfixe, le protocole du RFC 4861 permet de mettre à jour toutes les machines facilement, en envoyant un RA non sollicité.
Enfin, l'utilisation des RA simplifie le déploiement, puisque toute machine IPv6 sait déjà traiter les RA.
L'option elle-même est spécifiée dans la section 4 du RFC. Outre les classiques champs Type (valeur 38) et Longueur (16 octets), elle comporte une indication de la durée de validité du préfixe, un code qui indique la longueur du préfixe (le mécanisme classique préfixe/longueur n'est pas utilisé, car toutes les longueurs ne sont pas acceptables), et le préfixe lui-même.
La section 5 explique comment les machines doivent utiliser cette option. Elle s'applique à tous les préfixes IPv4 connus. Si on veut router certains préfixes IPv4 vers un routeur NAT64 et d'autres préfixes vers un autre routeur, il faut quand même que les adresses IPv6 soient dans le même préfixe, et router ensuite sur les sous-préfixes de ce préfixe. Un autre problème découlant de ce choix d'avoir un préfixe IPv6 unique pour tout est celui des préfixes IPv4 qu'on ne veut pas traduire, par exemple des préfixes IPv4 purement internes. Il n'y a pas à l'heure actuelle de solution. De toute façon, l'option décrite dans notre RFC est surtout conçu pour des réseaux purement IPv6, et qui n'auront donc pas ce problème.
Attention, le préfixe IPv6 reçu est spécifique à une interface réseau. La machine réceptrice devrait donc le limiter à cette interface. Si elle a plusieurs interfaces (pensez par exemple à un ordiphone avec WiFi et 4G), elle peut recevoir plusieurs préfixes pour le NAT64, chacun ne devant être utilisé que sur l'interface où il a été reçu (cf. RFC 7556.)
Un petit mot sur la sécurité pour terminer. Comme toutes les annonces RA, celle du préfixe NAT64 peut être mensongère (cf. le RFC 6104 au sujet de ces « RAcailles ».) La solution est la même qu'avec les autres options RA, utiliser des techniques « RA guard » (RFC 6105.)
À noter qu'une annonce mensongère d'un préfixe IPv6 prévue pour le NAT64 affectera évidemment les adresses IPv4 qu'on tente de joindre (parfois au point de les rendre injoignables) mais pas les adresses IPv6, qui ne sont pas traduites et donc non touchées par cet éventuel RA tricheur. Comme une machine qui arrive à émettre et à faire accepter des RAcailles peut déjà facilement réaliser un déni de service, on voit que l'option de ce nouveau RFC n'aggrave pas les choses, en pratique.
Le RFC conclut que la procédure de validation des préfixes de la section 3.1 du RFC 7050 n'est pas nécessaire, si on a ce RA Guard.
À l'heure actuelle, il ne semble pas que cette option soit souvent mise en œuvre, que ce soit dans les émetteurs ou dans les récepteurs. Mais Wireshark sait déjà le décoder.
Date de publication du RFC : Mars 2020
Auteur(s) du RFC : R. Housley (Vigil Security)
Expérimental
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 31 mars 2020
L'authentification dans TLS se fait typiquement, soit à partir d'un certificat, soit par une clé partagée à l'avance. Ce nouveau RFC spécifie une extension de TLS qui permet d'utiliser ces deux méthodes en même temps.
Rappelons d'abord qu'il y a deux sortes de clés partagées à l'avance (PSK, pour Pre-Shared Key) : celles qui ont été négociées dans une session précédentes (resumption PSK) et celles qui ont été négociées par un mécanisme extérieur (envoi par pigeon voyageur sécurisé…), les external PSK. Ce RFC ne concerne que les secondes. Les certificats et les clés partagées à l'avance ont des avantages et des inconvénients. Les certificats ne nécessitent pas d'arrangement préalable entre client et serveur, ce qui est pratique. Mais il faut se procurer un certificat auprès d'une AC. Et les certificats, comme ils reposent sur des algorithmes comme RSA ou ECDSA, sont vulnérables aux progrès de la cryptanalyse, par exemple en utilisant un (futur) ordinateur quantique. Utiliser une clé partagée à l'avance n'est pas forcément commode (par exemple quand on veut la changer) mais cela peut être plus sûr. Or, la norme TLS actuelle (RFC 8446) ne permet d'utiliser qu'une seule des deux méthodes d'authentification. Si on les combinait ? L'ajout d'une clé externe permettrait de rendre l'authentification plus solide.
Le principe est simple : notre RFC spécifie une extension à TLS,
tls_cert_with_extern_psk
(valeur
33.) Le client TLS l'envoie dans son
ClientHello
. Elle indique la volonté de
combiner certificat et PSK. Elle est accompagnée d'extensions
indiquant quelle est la clé partagée à utiliser. Si le serveur TLS
est d'accord, il met l'extension
tls_cert_with_extern_psk
dans son message
ServerHello
. (Le serveur ne peut pas décider
seul de l'utilisation de cette extension, il faut que le client ait
demandé d'abord.)
Les clés ont une identité, une série d'octets sur lesquels client
et serveur se sont mis d'accord avant (PSK = Pre-Shared
Key, clé partagée à l'avance.) C'est cette identité qui
est envoyée dans l'extension pre_shared_key
, qui
accompagne tls_cert_with_extern_psk
. La clé
elle-même est bien sûr un secret, connu seulement du client et du
serveur (et bien protégée : ne la mettez pas sur un fichier lisible
par tous.) Voyez la section 7 du RFC pour une discussion plus
détaillée de la gestion de la PSK.
Une fois que client et serveur sont d'accord pour utiliser l'extension, et ont bien une clé en commun, l'authentification se fait via le certificat (sections 4.4.2 et 4.4.3 du RFC 8446) et en utilisant ensuite, non pas seulement la clé générée (typiquement par Diffie-Hellman), mais la combinaison de la clé générée et de la PSK. L'entropie de la PSK s'ajoute donc à celle de la clé générée de manière traditionnelle.
Du point de vue de la sécurité, on note donc que cette technique de la PSK est un strict ajout à l'authentification actuelle, donc on peut garantir que son utilisation ne diminuera pas la sécurité.
Il n'y a apparemment pas encore de mise en œuvre de cette extension dans une bibliothèque TLS.
Notez qu'il s'agit apparemment du premier RFC à mentionner explicitement les calculateurs quantiques, et les risques qu'ils posent pour la cryptographie.
Date de publication du RFC : Avril 2020
Auteur(s) du RFC : K. Patel (Arrcus), P. Pillay-Esnault
(PPE
Consulting), M. Bhardwaj, S. Bayraktar
(Cisco)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ospf
Première rédaction de cet article le 13 avril 2020
Le protocole de routage OSPF, dans sa version 2, a une particularité amusante : dès qu'une machine participe au protocole, et échange des messages avec les autres, elle va être considérée comme un routeur, à qui on peut envoyer du trafic à faire suivre. Or, on peut souhaiter observer le routage sans être soi-même routeur. D'où ce RFC, qui prévoit un moyen de mettre dans les messages : « je suis juste un observateur, ne m'envoie pas de paquets à router ».
Dans quels cas trouve-t-on ces machines qui participent à un protocole de routage mais ne veulent pas transmettre les paquets des autres ? La section 1 du RFC cite plusieurs cas, dont :
La solution proposée dans ce RFC est d'ajouter aux messages OSPF un bit, le bit H (Host bit). Lors du calcul des routes avec SPF (section 16.1 du RFC 2328, qui normalise OSPF version 2), les routeurs ayant le bit H seront exclus. Ce bit utilise un des bits inutilisés du LSA (Link State Advertisement) de routeur (RFC 2328, section A.4.2), et il est enregistré à l'IANA.
Difficulté classique quand on modifie un protocole, surtout ancien et très répandu, comme OSPF, la cohabitation entre anciens et récents logiciels. Que se passe-t-il si un réseau mêle des routeurs qui envoient le bit H et d'autres qui ne connaissent pas ce bit ? La section 5 explique la solution : en utilisant les extensions du RFC 7770, un LSA Router Information est envoyé, indiquant qu'on sait gérer le bit H (capacité Host Router, cf. le registre IANA.) Les routeurs connaissant notre RFC ne tiendront compte du bit H que si tous les routeurs de la zone OSPF ont annoncé cette capacité.
Date de publication du RFC : Mars 2020
Auteur(s) du RFC : D. Lawrence (Oracle), W. Kumari (Google),
P. Sood (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 1 avril 2020
Ce nouveau RFC autorise les résolveurs DNS à servir des « vieilles » informations aux clients (« vieilles » parce que le TTL est dépassé), si et seulement si les serveurs faisant autorité ne sont pas joignables (par exemple parce qu'ils sont victimes d'une attaque par déni de service.) Cela devrait rendre le DNS plus robuste en cas de problèmes. Le principe est donc désormais « mieux vaut du pain rassis que pas de pain du tout ».
Normalement, avant ce RFC, le DNS fonctionne ainsi : le client interroge un résolveur. Ce résolveur :
C'est ce que décrit le RFC 1035, notamment sa section 3.2.1. Ici, faite avec dig, une interrogation DNS, les données étant déjà dans le cache (la mémoire), ce qui se voit au TTL qui n'est pas un chiffre rond :
% dig NS assemblee-nationale.fr ... ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57234 ;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1 ... ;; ANSWER SECTION: assemblee-nationale.fr. 292 IN NS ns2.fr.claradns.net. assemblee-nationale.fr. 292 IN NS ns1.fr.claradns.net. assemblee-nationale.fr. 292 IN NS ns0.fr.claradns.net. ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Thu Feb 20 11:09:57 CET 2020 ;; MSG SIZE rcvd: 120
Mais que se passe-t-il si le résolveur a des données, que le
TTL est expiré, mais que
les serveurs faisant autorité sont en panne (cas d'autant plus
fréquent que beaucoup de domaines ont trop peu de serveurs, et qu'il
y a souvent des SPOF), injoignables (par exemple suite à un
problème de routage) ou bien victimes d'une
DoS, comme
cela arrive trop souvent ? Dans ce cas, le résolveur ne peut pas
vérifier que les données sont à jour (alors que ce sera souvent le
cas) et doit renvoyer une réponse SERVFAIL (Server
Failure) qui empêchera le client d'avoir les informations
qu'il demande. Dommage : après tout, peut-être que le client voulait
se connecter au serveur imap.example.net
qui,
lui, marchait, même si les serveurs DNS faisant autorité pour
example.net
étaient en panne. C'est l'une des
motivations pour cette idées des données rassises. Comme le note
Tony Finch, cela rendra plus facile le débogage des problèmes
réseau. Au lieu d'un problème DNS mystérieux, qui masque le problème
sous-jacent, les opérateurs verront bien mieux ce qui se
passe. (Quasiment toutes les opérations sur l'Internet commencent
par une requête DNS, et les problèmes réseau sont donc souvent
perçus comme des problèmes DNS, même si ce n'est pas le cas.)
De tels problèmes sont relativement fréquents, et le RFC et moi vous recommandons l'excellent article « When the Dike Breaks: Dissecting DNS Defenses During DDoS ».
C'est pour cela que notre RFC prévoit, dans des cas exceptionnels, d'autoriser le résolveur à outrepasser le TTL et à renvoyer des données « rassises ». En temps normal, rien ne change au fonctionnement du DNS mais, si les circonstances l'exigent, le client recevra des réponses, certes peut-être dépassées, mais qui seront mieux que rien. Le compromis habituel entre fraîcheur et robustesse est donc déplacé un peu en faveur de la robustesse.
Bref, depuis la sortie de notre RFC, un résolveur est autorisé à servir des données rassises, si les serveurs faisant autorité ne répondent pas, ou bien s'ils répondent SERVFAIL (Server Failure.) Il doit dans ce cas (section 4 du RFC) mettre un TTL strictement positif, la valeur 30 (secondes) étant recommandée, pour éviter que ses clients ne le harcèlent, et aussi parce qu'un TTL de zéro (ne pas mémoriser du tout) est parfois mal compris par certains clients DNS bogués (cf. section 6).
La section 5 donne un exemple de comment cela peut être mis en œuvre. Elle suggère d'utiliser quatre délais :
Si le résolveur n'a pas eu de réponse avant que le temps maximal pendant lequel faire patienter le client soit écoulé, il cherche dans sa mémoire s'il n'a pas des données rassises et, si oui, il les envoie au client. (C'est la nouveauté de ce RFC.)
La section 6 donne quelques autres conseils pratiques. Par exemple, quel âge maximal des données rassises choisir ? Une durée trop courte diminuera l'intérêt de ce RFC, une durée trop longue augmentera la consommation mémoire du résolveur. Une demi-journée permet d'encaisser la grande majorité des attaques par déni de service. Une semaine permet d'être raisonnablement sûr qu'on a eu le temps de trouver les personnes responsables (c'est souvent beaucoup plus dur qu'on ne le croit !) et qu'elles résolvent le problème. D'où la recommandation du RFC, entre 1 et 3 jours.
Pour la consommation de mémoire, il faut aussi noter que, si la limite du résolveur a été atteinte, on peut prioriser les données rassises lorsqu'on fait de la place, et les sacrifier en premier. On peut aussi tenir compte de la « popularité » des noms, en supprimant en premier les noms les moins demandés. Attention, un tel choix pourrait pousser certains à faire des requêtes en boucle pour les noms qu'ils trouvent importants, de manière à les faire considérer comme populaires.
Beaucoup de résolveurs ont deux mémoires séparées, une pour les
données demandées par les clients, et une pour les données obtenues
lors du processus de résolution lui-même. Ainsi, lorsqu'un client
demande le MX de
foobar.example
, et que les serveurs faisant
autorité pour foobar.example seront
ns0.op.example
et
ns1.op.example
, le résolveur devra à un moment
découvrir l'adresse IP de ns0
et
ns1.op.example
(une « requête tertiaire », pour
reprendre la terminologie du RFC 7626.) Cette
adresse IP sera mémorisée, mais pas dans la même mémoire que le MX
de foobar.example
, car ces données n'ont pas
forcément le même niveau de confiance (il peut s'agir de colles, par
exemple, et pas de données issues d'un serveur faisant autorité). Le
RFC autorise également à utiliser des données rassises pour cette
seconde mémoire, donc pour la cuisine interne du processus de
résolution. Ainsi, si un TLD est injoignable (comme c'était arrivé au
.tr
en décembre
2015, suite à une attaque par déni de service), les serveurs
de noms sous ce TLD resteront peut-être utilisables, pour des
nouvelles requêtes.
Notez que le client du résolveur n'a aucun moyen de dire s'il accepte des données rassises, ou bien s'il veut uniquement du frais. Il avait été discuté à l'IETF une option EDNS permettant au client de signaler son acceptation de vieilles données, mais cette option n'a pas été retenue (section 9), le but étant de fournir une technique qui marche avec les clients actuels, afin de renforcer la robustesse du DNS dès maintenant. Ce point est sans doute celui qui avait suscité les plus chaudes discussions.
La section 10 discute de quelques problèmes de sécurité liés au fait de servir des données rassises. Par exemple, une attaque existante contre le DNS est celle des « domaines fantômes » où un attaquant continue à utiliser un domaine supprimé (par exemple parce qu'il servait à distribuer du logiciel malveillant) en comptant sur les mémoires des résolveurs. (Voir à ce sujet l'artricle « Cloud Strife: Mitigating the Security Risks of Domain-Validated Certificates ».) Le fait de servir des données rassises pourrait rendre l'attaque un peu plus facile, mais pas plus : après tout, la réponse NXDOMAIN (ce domaine n'existe pas) de la zone parente supprime toutes les entrées de ce domaine dans la mémoire. D'autre part, un attaquant pourrait mettre hors d'état de service les serveurs faisant autorité pour une zone afin de forcer l'utilisation de données anciennes. Ceci dit, sans ce RFC, un attaquant ayant ce pouvoir peut faire des dégâts plus graves, en bloquant tout service.
À noter que notre RFC change également les normes précédentes sur un autre point, l'interprétation des TTL lorsque le bit de plus fort poids est à un (section 4). Le RFC 2181 disait (dans sa section 8) « Implementations should treat TTL values received with the most significant bit set as if the entire value received was zero. ». C'était pour éviter les problèmes d'ambiguité entre entiers signés et non signés. Le RFC 8767 change cette règle dit désormais clairement que le TTL est un entier non signé et que ces valeurs avec le bit de plus fort poids à un sont des valeurs positives. Il ajoute qu'elles sont tellement grandes (plus de 68 ans…) qu'elles n'ont pas d'intérêt pratique et doivent donc être tronquées (un TTL de plus d'une semaine n'a pas de sens et les résolveurs sont donc invités à imposer cette limite). Donc, en pratique, cela ne change rien.
Questions mises en œuvre, de nombreux résolveurs offrent un moyen de servir les données anciennes. L'idée est loin d'être nouvelle, elle existait avant le RFC, et était largement acceptée, quoique violant la norme technique. Bref, les données rassises existent déjà un peu partout. Ainsi, les logiciels privateurs Nomimum and Xerocole (utilisés par Akamai) peuvent le faire. Idem pour OpenDNS. Du côté des logiciels libres, BIND, Knot et Unbound ont tous cette possibilité, à des degrés divers.
Pour Unbound, les versions avant la 1.10 ne permettaient pas de suivre rigoureusement notre RFC 8767. Les options étaient (je cite le fichier de configuration d'exemple) :
# Serve expired responses from cache, with TTL 0 in the response, # and then attempt to fetch the data afresh. # serve-expired: no # # Limit serving of expired responses to configured seconds after # expiration. 0 disables the limit. # serve-expired-ttl: 0 # # Set the TTL of expired records to the serve-expired-ttl value after a # failed attempt to retrieve the record from upstream. This makes sure # that the expired records will be served as long as there are queries # for it. # serve-expired-ttl-reset: no
Notez donc qu'avec serve-expired
, Unbound
servait des données rassises avant même de vérifier si les serveurs
faisant autorité étaient joignables. À partir de la version 1.10,
cette configuration fonctionne et colle au RFC :
# Enable serve-expired serve-expired: yes # Time to keep serving expired records. serve-expired-ttl: 86400 # One day # Do not reset the TTL above on failed lookups serve-expired-ttl-reset: no # default # TTL to reply with expired entries serve-expired-reply-ttl: 30 # default # Time to wait before replying with expired data serve-expired-client-timeout: 1800
Si on met serve-expired-client-timeout
à zéro
(c'est la valeur par défaut), on garde l'ancien comportement (qui ne
respecte ni les anciens RFC, ni le nouveau.)
Pour BIND, la possibilité de se contenter de vieilles données a été introduite dans la version 9.12. Les options pertinentes sont :
stale-answer-enable
: active le service
d'envoi de données dépassées, uniquement si les serveurs faisant
autorité sont injoignables, ce qui est la recommandation du RFC,max-stale-ttl
: l'âge maximal des
données rassises (une semaine par défaut),stale-answer-ttl
: le TTL des valeurs
retournées, une seconde par défaut (alors que le RFC suggère
trente secondes).Et sur Knot ? Knot est modulaire et la
possibilité de servir des données dépassées est dans un module
Lua séparé, serve_stale
(cf. modules/serve_stale/README.rst
dans le
source). Il sert des données dépassées pendant au maximum une
journée. Il n'est apparemment configurable qu'en éditant le source Lua.
Date de publication du RFC : Juin 2020
Auteur(s) du RFC : T. Pusateri, S. Cheshire
(Apple)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnssd
Première rédaction de cet article le 23 juin 2020
Lorsqu'une donnée DNS change dans les serveurs faisant autorité, les clients ne seront prévenus que lorsqu'ils reviendront demander, et cela peut être long s'ils ont mémorisé l'ancienne valeur. Ce RFC propose une autre approche, où les données DNS sont poussées vers les clients au lieu d'attendre qu'ils tirent.
Qu'est-ce qui fait que des données DNS changent ? Cela peut être l'administrateur système qui a changé la zone et rechargé le serveur faisant autorité. Ou bien il y a pu y avoir mise à jour dynamique (RFC 2136). Ou encore d'autres mécanismes. Dans tous les cas, certains clients DNS (par exemple pour la découverte de services du RFC 6763) aimeraient bien être prévenus tout de suite (et voir la nouvelle imprimante du réseau local apparaitre dans la liste). Le DNS est purement pull et ces clients voudraient un peu de push.
Cette idée de protocoles push, où on envoie les données vers les clients au lieu d'attendre qu'il les réclame, est nouvelle pour le DNS mais ancienne dans l'Internet (modèle publish/subscribe ou design pattern Observateur). Parmi les protocoles IETF, XMPP peut fonctionner ainsi (cf. XEP0060).
Il y a quand même un peu de push dans une variante du DNS, mDNS (RFC 6762, section 5.2), où les changements peuvent être poussés vers une adresse IP multicast. Mais des protocoles comme la découverte de services du RFC 6763 peuvent aussi fonctionner sans multicast et cette solution ne leur convient donc pas. Il existait une solution non-standard, LLQ (Long-Lived Queries), décrite dans le RFC 8764 et mise en œuvre dans les produits Apple (cf. RFC 6281). Utilisant UDP, elle ne convient guère au DNS moderne.
Il existe aujourd'hui un mécanisme plus pratique pour déployer des fonctions comme le push : DSO (DNS Stateful Operations, RFC 8490), qui repose sur des connexions de longue durée, sur TCP (et TLS pour la sécurité). La solution push est donc bâtie sur DSO, et il est donc bon d'avoir lu le RFC 8490 avant celui-ci.
La section 3 de notre RFC présente le protocole DNS
push. Un client s'abonne à un service de notification et,
une fois abonné, il reçoit les nouveautés. Quand il n'a plus besoin
des données, le client se déconnecte. L'abonnement est valable pour
un certain couple {nom de
domaine, type de données}. Client et serveur utilisent
le mécanisme DSO du RFC 8490. Comment le
client trouve-t-il le serveur ? Ce n'est pas forcément un des
serveurs listés comme faisant autorité pour la zone. Il peut être
trouvé en demandant à son résolveur normal (s'il accepte DSO, le
client peut tenter sa chance) dans le DNS, puis, si ça ne marche
pas, via une requête SRV pour le nom
_dns-push-tls._tcp.LA-ZONE
(cf. section 6, et
son enregistrement
à l'IANA). Rappelez-vous que, si un serveur DNS accepte DSO
mais pas DNS push, il répondra avec un code
de retour DNS DSOTYPENI
(RFC 8490, section 5.1.1).
Pour éviter de trop charger le serveur, le client ne doit s'abonner que s'il a une bonne raison, par exemple parce qu'une fenêtre de sélection d'une imprimante est ouverte, et qu'il veut la rafraichir en permanence. Lorsque le client va s'endormir ou estomper son écran pour économiser sa batterie, il devrait se désabonner d'abord. En tout cas, le mécanisme DNS push ne devrait pas être utilisé 24x7, ce n'est pas son but. (Et c'est inutile, le mécanisme est suffisamment rapide pour pouvoir être invoqué seulement quand on en a besoin.)
De toute façon, le serveur n'est pas obligé d'accepter tous les clients qui se présentent. Il peut rejeter les abonnements s'il manque de ressources (section 4 du RFC).
Comment est-ce que le client demande du DNS
push ? En s'abonnant, une fois la session DSO établie. Il
envoie une requête
de type DSO SUBSCRIBE
(la syntaxe des
TLV DSO est dans le RFC 8490, section 5.4.4). Dans les données de la requête se
trouvent le nom de domaine et le type de données qui intéressent le
client. Le serveur répond alors NOERROR
(ou
bien un code DNS d'erreur s'il y a un problème, par exemple
REFUSED
si le serveur ne veut pas) et envoie
une réponse de type SUBSCRIBE
(même type que la
requête, c'est le bit QR de l'en-tête DNS qui permet de différencier
requête et réponse). Il est parfaitement légitime pour un client de
s'abonner à un nom de domaine qui n'existe pas encore, et le serveur
ne doit donc jamais répondre NXDOMAIN
.
Une fois l'abonnement accepté, le client attend tranquillement
et, lorsqu'un changement survient, le serveur envoie des messages de
type DSO PUSH
(code 0x0041). Les
données contenues dans ce message sont formatées comme les messages
DNS habituels, avec une exception : un TTL valant 0xFFFFFFFF signifie que l'ensemble
d'enregistrements concerné a été supprimé.
À la fin, quand le client n'est plus intéressé, il envoie un
message DSO UNSUBSCRIBE
(code 0x0042). Encore
plus radical, le client peut mettre fin à la session DSO, annulant
ainsi tous ses abonnements.
Et la sécurité de ce DNS push (section 7 du RFC) ? DNS push impose l'utilisation de TLS (donc on a du DSO sur TLS sur TCP). Le client doit authentifier le serveur selon le profil strict du RFC 8310. Ensuite, DNSSEC est recommandé pour la découverte du serveur DNS push, lorsqu'on utilise les requêtes SRV, puis DANE pour vérifier le serveur auquel on se connecte (RFC 7673).
Ensuite, DNS push permet d'utiliser les données précoces du RFC 8446 (section 2.3), ce qui permet de diminuer la latence. Mais elles posent quelques problèmes de sécurité : plus de confidentialité persistante, et risque de duplication des messages. D'autre part, si on utilise la reprise de session du RFC 8446 (section 2.2), il faut noter que, si elle permet de ne pas recommencer toute la session TLS, en revanche, elle ne garde pas les abonnements DNS push, que le client devra donc renouveler.
Désolé, mais je ne connais pas encore de mise en œuvre de ce RFC, à part dans le monde Apple.
Date de publication du RFC : Avril 2020
Auteur(s) du RFC : A. Rahman (InterDigital Communications), D. Trossen (InterDigital Europe), D. Kutscher (University of Applied Sciences Emden/Leer), R. Ravindran (Futurewei)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF icnrg
Première rédaction de cet article le 17 avril 2020
Ce nouveau RFC annonce [mais c'est très prématuré] que l'ICN est désormais une technologie mûre et qu'il est temps de se pencher sur les problèmes pratiques de déploiement. Il documente ces problèmes, et des solutions possibles. Il décrit aussi des expériences de déploiement (très limitées et qui ont souvent disparu.)
Si vous voulez réviser le concept d'ICN, lisez le RFC 8793. Il y a plusieurs façons (section 4 du RFC) d'envisager le déploiement des techniques « fondées sur le contenu » (ICN) :
draft-irtf-icnrg-icniot
),
et les passerelles transformeraient du CoAP
(RFC 7252) en ICN et réciproquement.La section 5 du RFC décrit de manière plus détaillées des chemins de migration possibles. Et la section 6 décrit quelques expériences qui ont effectivement eu lieu (on peut aussi lire le RFC 7945) :
Ces projets tournaient sur l'Internet d'aujourd'hui. Mais il y avait aussi d'autres projets qui se situaient plus bas dans le modèle en couches :
Le RFC note qu'aucun de ces déploiements n'a dépassé mille utilisateurs. Pour moi, il s'agissait clairement d'expérimentation et appeler ça « déploiement » est trompeur.
La section 7 du RFC discute des points qui manquent pour des déploiements significatifs, notamment question normalisation. La liste est longue. Le RFC note entre autres :
Date de publication du RFC : Mars 2020
Auteur(s) du RFC : G. Mirsky (ZTE), G. Jun (ZTE), H. Nydell (Accedian Networks), R. Foote (Nokia)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ippm
Première rédaction de cet article le 20 mars 2020
Ce nouveau RFC décrit un protocole simple pour piloter des mesures comme celle de la latence ou comme le taux de pertes de paquets entre deux points. Il fonctionne pour des mesures aller-simple (unidrectionnelles) et aller-retour (bidirectionnelles.)
Mais, vous allez me dire, il existe déjà un tel protocole, TWAMP, normalisé dans le RFC 5357, et dans quelques RFC suivants comme le RFC 6038. TWAMP est mis en œuvre dans plusieurs systèmes, et fonctionne. Le problème est que TWAMP est riche et complexe, et que sa simplifiation TWAMP Light (annexe 1 du RFC 5357) est apparemment insuffisamment spécifiée, menant à des problèmes d'interopérabilité. Voir par exemple le rapport « Performance Measurement from IP Edge to Customer Equipment using TWAMP Light ».
Les grandes lignes de STAMP (Simple Two-way Active Measurement Protocol) sont décrites dans la section 3 de notre RFC. Comme TWAMP, STAMP sépare les fonctions de contrôle du test, et le test lui-même. Contrairement à TWAMP, la norme STAMP ne décrit que le test (rappelez-vous que STAMP se veut plus simple que TWAMP). Le contrôle est fait de l'extérieur, par des programmes en ligne de commande, par Netconf ou par un autre moyen.
Avec STAMP, il y a deux acteurs, le Session-Sender qui envoie les paquets, et le Session-Reflector, qui répond. Les paquets sont de l'UDP vers le port 862, et contiennent un numéro de séquence qui permet d'associer requête et réponse. (La réponse contient davantage de données que la requête, mais la requête est remplie, pour avoir la même taille, afin d'éviter les attaques avec amplification.) STAMP a deux modes de fonctionnement (section 4 du RFC) :
Le paquet STAMP inclus, outre le numéro de séquence déjà cité, une estampille temporelle, par défaut au format utilisé par NTP (cf. RFC 5905, section 6.) Un accord entre les deux parties (souvenez-vous que STAMP ne décrit pas comment l'envoyeur et le réflecteur sont coordonnés) permet d'utiliser d'autres formats comme celui de PTP (RFC 8186.)
L'authentification n'est pas indispensable et, si on ne l'utilise pas, le paquet peut être modifié par un attaquant actif qui cherche à fausser les mesures. STAMP a également une possibilité d'authentification, où le paquet contient en outre un HMAC (RFC 2104, et section 4.4 de notre RFC.)
Dans sa réponse, le Session-Reflector envoie un paquet qui indique son propre numéro de séquence, sa propre estampille temporelle, et les numéros de séquence et estampille temporelle du paquet reçu. (En outre, dans le mode avec état, le Session-Reflector enregistre les informations du paquet entrant.) Au retour, le Session-Sender peut donc calculer le RTT, la gigue et le taux de pertes. (Dans le mode avec état, le Session-Reflector peut lui aussi calculer ces informations, qui auront l'avantage de ne concerner qu'une seule direction des paquets.)
Notez que STAMP n'a aucune confidentialité. Si on veut empêcher un méchant de lire les informations, il faut emballer le tout, par exemple dans IPsec ou DTLS. Ce point est un de ceux qui avaient suscité les plus vives discussions à l'IETF, certains regrettant qu'il n'y ait pas de sécurité standard.
Notez également qu STAMP et TWAMP-Light peuvent partiellement interopérer, puisque le format de paquets et la sémantique sont les mêmes (en non authentifié, uniquement.)
Je n'ai pas trouvé de mise en œuvre de STAMP, ni en logiciel libre, ni en privateur. Je ne connais pas non plus de liste de serveurs STAMP publics.
Date de publication du RFC : Avril 2020
Auteur(s) du RFC : A. Filippov (Huawei), A. Norkin (Netflix), J.R. Alvarez (Huawei)
Pour information
Réalisé dans le cadre du groupe de travail IETF netvc
Première rédaction de cet article le 17 avril 2020
Si vous passez toutes vos soirées avachi·e devant un écran à regarder des séries sur Netflix, ce RFC est pour vous. Il est le cahier des charges d'un futur codec vidéo libre pour l'Internet. Ce codec devra gérer beaucoup de cas d'usage différents.
Concevoir un codec vidéo n'est jamais facile. Il y a beaucoup d'exigences contradictoires, par exemple minimiser le débit en comprimant, tout en n'exigeant pas de la part du processeur trop de calculs. Ce RFC, le premier du groupe de travail netvc de l'IETF, se limite au cas d'un codec utilisé au-dessus de l'Internet, mais cela fait encore plein d'usages différents, du streaming à la vidéoconférence en passant par la vidéosurveillance (celle que les politiciens hypocrites appellent la vidéoprotection, mais le RFC est plus sincère et utilise le bon terme.)
Le problème est d'autant plus difficile que plein de choses peuvent aller mal sur l'Internet, les paquets peuvent être perdus, les bits modifiés en route, etc. En pratique, tous ces incidents n'ont pas la même probabilité : les pertes sont bien plus fréquentes que les corruptions, par exemple. Si on n'est pas trop pressés (cas du téléchargement), TCP ou d'autres protocoles de transport similaires fournissent une bonne solution. Si on a des exigences temporelles plus fortes (diffusion en direct, « temps réel »), il faut parfois renoncer à TCP et, soit utiliser des réseaux idéaux (managed networks, dit le RFC, qui reprend le terme marketing de certains opérateurs qui tentent de nous faire croire qu'il existerait des réseaux sans pertes), soit trouver d'autres moyens de rattraper les pertes et les erreurs.
Depuis qu'on fait de la vidéo sur l'Internet, c'est-à-dire depuis très longtemps (malgré les prédictions des opérateurs de télécommunications traditionnels qui étaient unanimes, dans les années 1990, pour dire que cela ne serait jamais possible), toute une terminologie a été développée pour communiquer sur ce sujet. La section 2 de notre RFC rappelle les sigles importants, et quelques termes à garder en tête. Ainsi, la « compression visuellement sans perte » désigne les méthodes de compression avec perte, mais dont on ne voit pas d'effet secondaire à l'œil nu.
La section 3 de notre RFC fait ensuite le tour des applications de la vidéo sur l'Internet, pour cerner de plus près leurs caractéristiques et leurs exigences propres. (Le cahier des charges proprement dit sera en section 4.) Chaque cas d'usage est accompagné d'un tableau indiquant la résolution et la fréquence des trames souhaitées. On commence évidemment par le streaming (un des auteurs du RFC travaille chez Netflix.) Pouvoir regarder Le Messie via l'Internet est un objectif crucial. Comme les clients sont variés, et que les conditions de leur accès Internet varient en permanence (surtout s'il y a des liens radio), le codec doit tout le temps s'adapter, et pour cela tenir compte des caractéristiques de la perception humaine. L'encodage est typiquement fait une fois et une seule, lorsque la vidéo est chargée sur les serveurs. Pour cet usage :
Proche du streaming mais quand même différent, il y a le partage de vidéos, usage popularisé par YouTube mais également accessible en utilisant du logiciel libre et décentralisé, comme PeerTube. C'est le monde de l'UGC : M. Michu filme des violences policières avec son petit ordiphone (ou avec sa GoPro), et les diffuse au monde entier grâce aux services de partage de vidéos.
Après le streaming et le partage de vidéos, la télévision sur IP. Il s'agit de distribuer de la télévision traditionnelle sur IP, pour permettre de se réduire le cerveau en regardant Hanouna. Cet usage se décompose en deux groupes : l'unicast, où on envoie l'émission à un seul destinataire, c'est la VoD, et le multicast où on diffuse à un grand nombre d'utilisateurs simultanément. Ce dernier est sans doute de moins en moins utilisé, à part pour les événements sportifs et les cérémonies. Le RFC se concentre sur la distribution de télévision au-desus d'un réseau « géré » (comme si les autres ne l'étaient pas…) où on peut garantir une certaine QoS. C'est par exemple le cas d'un FAI qui envoie les chaînes de télévision aux boxes des abonnés. Si on diffuse un match de foot, on souhaite que le but qui décide du sort du match atteigne les ordinateurs sans trop de retard sur les hurlements bestiaux de supporters utilisant la diffusion hertzienne, hurlements qu'on entendra dehors. Pour compliquer un peu les choses, on souhaite que l'utilisateur puisse mettre en pause, reprendre, etc.
Et la vidéoconférence ? Là aussi, le délai d'acheminement est crucial, pour qu'on n'ait pas l'impression de parler avec un astronaute perdu sur Mars. (Le RFC suggère 320 millisecondes au grand maximum, et de préférence moins de 100 ms.)
Il y a aussi le partage d'écran, où on choisit de partager l'écran de son ordinateur avec une autre personne. Comme pour la vidéoconférence, il faut du temps réel, encoder au fur et à mesure avec le plus petit retard possible.
Et les jeux vidéo, alors ? Les jeux ont souvent un contenu riche (beaucoup de vidéos). Dans certains cas, le moteur de jeu est quelque part sur l'Internet et, à partir d'un modèle 3D, génère des vidéos qu'il faut envoyer aux joueurs. Là encore, il faut être rapide : si un joueur flingue un zombie, les autres participants au jeu doivent le voir « tout de suite ». (Nombreux détails et exemples, comme GeForce Grid ou Twitch, dans le document N36771 « Game streaming requirement for Future Video Coding », de l'ISO/IEC JTC 1/SC 29/WG 11.)
Enfin, le RFC termine la liste des applications avec la vidéosurveillance. Dans le roman « 1984 », George Orwell ne donne aucun détail technique sur le fonctionnement du télécran, et notamment sur la connexion qui permet au Parti de savoir ce qui se passe chez Winston. Ici, l'idée est de connecter les caméras de vidéosurveillance à l'Internet et de faire remonter les flux vidéo à un central où on les regarde et où on les analyse. Comme l'État peut souhaiter disposer de très nombreuses caméras, le coût matériel est un critère important.
Après cet examen des cas d'usage possibles, la section 4 du RFC est le cahier des charges à proprement parler. D'abord, les exigences générales, qui concernent toutes les applications. Évidemment, le futur codec vidéo de l'Internet doit encoder efficacement, donc, dit le RFC, mieux que les codecs existants, H.265 ou VP9. Évidemment encore, l'interopérabilité entre les différentes mises en œuvre du codec est cruciale, et cela dépend en partie de l'IETF, qui devra écrire une norme claire et cohérente. Cette norme se déclinera en profils, qui limiteront certains aspects du codec, pour des cas d'usages spécifiques.
Le RFC exige également qu'il soit possible de gérer des extensions au format, pour les questions futures, tout en maintenant la compatibilité (un nouveau décodeur doit pouvoir décoder les vieilles vidéos). Le format doit permettre huit bits de couleur (par couleur) au minimum, et de préférence douze, gérer le YCbCr, permettre des résolutions quelconques, autoriser l'accès direct à un point donné de la vidéo, et bien d'autres choses encore. Et le tout doit pouvoir marcher en temps réel (aussi bien l'encodage que le décodage.) Par contre, pour l'encodage de haute qualité, le RFC autorise les encodeurs à être jusqu'à dix fois plus complexes que pour les formats existants : les ressources des ordinateurs croissent plus vite que la capacité des réseaux, et il est donc raisonnable de faire davantage d'efforts pour encoder.
Les réseaux sont imparfaits, et il faut donc gérer les erreurs. Outre les mécanismes présents dans la couche transport, le RFC demande que des mécanismes spécifiques à la vidéo soient également inclus dans le format. En parlant de couche transport, comme le but est de faire un codec vidéo pour l'Internet, le RFC rappelle l'importance de prévoir comment encapsuler le futur format dans les protocoles de transport existants.
Les exigences ci-dessus étaient impératives. Il y en a également des facultatives, par exemple un nombre de bits pour coder chaque couleur allant jusqu'à 16, la gestion du RGB, celle du transparence… Toujours dans les demandes facultatives, le RFC note qu'il serait bon que le format choisi n'empêche pas le parallélisme dans le traitement des vidéos, ce qui implique, par exemple, qu'on ne soit pas obligé de traiter toutes les images précédant une image pour encoder celle-ci. Et qu'on puisse utiliser des algorithmes qui soient acceptables pour les SIMD/GPU.
Ce n'est pas tout de faire une liste au Père Noël avec toutes les jolies choses qu'on voudrait voir dans le nouveau format, encore faut-il une bonne méthodologie pour évaluer les futures propositions. La section 5 décrit les tests à effectuer, avec plusieurs débits différents. Ces tests seront ensuite faits en comparant un codec de référence (HEVC ou VP9) aux codecs candidats pour répondre à ce cahier des charges. Comme le codec utilisera certainement des techniques dépendant de la perception humaine, ces tests « objectifs » ne suffisent pas forcément, et le RFC prévoit également des tests subjectifs, avec la procédure MOS (cf. le document ISO/IEC « PDTR 29170-1 », première partie.)
Et un petit point de sécurité pour finir : le RFC rappelle qu'encodeur et surtout décodeur doivent être paranoïaques, afin d'éviter qu'une vidéo un peu inhabituelle ne mène à des consommations de ressources déraisonnables, voire épuisant la machine.
Pour l'instant, le candidat sérieux pour répondre à ce cahier des charges est le codec AV1, notamment poussé par l'Alliance for Open Media. Mais il ne semble pas tellement se rapprocher de la normalisation formelle. Le groupe de travail ne semble plus très actif.
Date de publication du RFC : Avril 2020
Auteur(s) du RFC : L. Camara, L. Velvindron (cyberstorm.mu)
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 29 avril 2020
L'algorithme de chiffrement symétrique RC4, trop fragile, est abandonné depuis longtemps. Ce nouveau RFC le retire officiellement de SSH. Notre RFC remplace donc le RFC 4345, dont le statut devient « Intérêt historique seulement ».
L'utilisation de RC4 dans SSH avait été enregistrée dans le RFC 4253, et précisée dans ce RFC 4345, désormais abandonné. Les faiblesses de RC4 sont connues depuis longtemps, et ont été documentées dans le RFC 7465. Résultat, RC4 est retiré peu à peu des protocoles Internet (cf. par exemple le RFC 8429.) Désormais, RC4 disparait également de SSH, et le registre IANA des algorithmes a été mis à jour en ce sens.
OpenSSH a retiré RC4 il y a longtemps :
% ssh -V OpenSSH_7.6p1 Ubuntu-4ubuntu0.3, OpenSSL 1.0.2n 7 Dec 2017 % ssh -Q cipher 3des-cbc aes128-cbc aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com aes256-gcm@openssh.com chacha20-poly1305@openssh.com
(RC4 apparaissait comme arcfour
, pour des
raisons légales.)
Date de publication du RFC : Avril 2020
Auteur(s) du RFC : J. Klensin, P. Faltstrom (Netnod)
Chemin des normes
Première rédaction de cet article le 16 avril 2020
La norme IDN, qui permet depuis 2003 les noms de domaine en Unicode était autrefois liée à une version particulière d'Unicode. La révision de cette norme en 2010 a libérédélivré IDN d'une version spécifique. La liste des caractères autorisés n'est plus statique, elle est obtenue par l'application d'un algorithme sur les tables Unicode. Tout va donc bien, et, à chaque nouvelle version d'Unicode, il suffit de refaire tourner l'algorithme ? Hélas non. Car, parfois, un caractère Unicode change d'une manière qui casse la compatibilité d'IDN et qui, par exemple, rend invalide un nom qui était valide auparavant. La norme IDN prévoit donc un examen manuel de chaque nouvelle version d'Unicode pour repérer ces problèmes, et décider de comment les traiter, en ajoutant une exception spécifique, pour continuer à utiliser ce caractère, ou au contraire en décidant d'accepter l'incompatibilité. Ce nouveau RFC ne change pas ce principe, mais il en clarifie l'application, ce qui est d'autant plus nécessaire que les examens prévus n'ont pas toujours été faits, et ont pris anormalement longtemps, entre autre en raison de l'absence de consensus sur la question.
Le RFC à lire avant celui-ci, pour réviser, est le RFC 5892. Il définit l'algorithme qui va décider, pour chaque caractère Unicode, s'il est autorisé dans les noms de domaine ou pas. À chaque version d'Unicode (la dernière est la 13.0), on refait tourner l'algorithme. Bien qu'Unicode soit très stable, cela peut faire changer un caractère de statut. Si un caractère auparavant interdit devient autorisé, ce n'est pas grave. Mais si l'inverse survient ? Des noms qui étaient légaux cessent de l'être, ne laissant le choix qu'entre deux solutions insatisfaisantes, supprimer le nom, ou bien ajouter une exception manuelle (procédure qui n'est pas parfaitement définie dans le RFC 5892, qui sera faite, si tout va bien, par de nouveaux RFC.)
[Notez que ce RFC affirme que les registres sont responsables des noms qu'ils autorisent, point de vue politique et non technique, contestable et d'ailleurs contesté.]
En quoi consiste donc le nouveau modèle d'examen qui accompagne la sortie d'une nouvelle version d'Unicode ? La section 3 du RFC l'expose. D'abord faire tourner l'algorithme du RFC 5892, puis noter les caractères qui ont changé de statut. Cela doit être fait par un expert nommé par l'IESG (DE, pour Designated Expert, actuellement Patrik Fältström, un des auteurs du RFC.) Puis réunir un groupe d'experts (il faut un groupe car personne n'est compétent dans tous les aspects d'IDN à la fois, ni dans toutes les écritures normalisées, cf. annexe B ; si l'IETF n'a pas de problèmes à trouver des experts en routage, elle doit par contre ramer pour avoir des experts en écritures humaines.) La recommandation pour ce groupe est d'essayer de préserver le statut des caractères et donc, si nécessaire, de les ajouter à une table d'exceptions (le RFC 6452 avait pris la décision opposée.) Le fait d'avoir des experts explicitement nommés va peut-être permettre d'éviter le syndrome du « il faudrait que quelqu'un s'en occupe » qui avait prévalu jusqu'à présent.
La promesse de publier les tables résultant de l'algorithme du RFC 5892 n'ayant pas été tenue (ce qui a fait râler), le RFC espère que cette fois, le travail sera fait. (Elle a finalement été faite deux ans plus tard dans le RFC 9233.)
L'annexe A résume les changements depuis le RFC 5892 (qui n'est pas remplacé, juste modifié légèrement) :
La section 2 du RFC rappelle l'histoire d'IDN. La norme originale, le RFC 3490 définissait l'acceptabilité de chaque caractère dans un nom de domaine. Lorsqu'une nouvelle version d'Unicode sortait, il n'y avait pas de mécanisme pour décider d'accepter ou non les nouveaux caractères. Il aurait fallu refaire un nouveau RFC pour chaque version d'Unicode (il y en a environ une par an.) La version 2 d'IDN, dans le RFC 5890, a changé cela, en décidant de normaliser l'algorithme et non plus la liste des caractères. L'algorithme, décrit en détail dans le RFC 5892, utilise les propriétés indiquées dans la base de données Unicode (cf. la norme, sections 4 et 3.5) pour décider du sort de chaque caractère. Dans un monde idéal, ce sort ne change pas d'une version d'Unicode à l'autre. Si les propriétés Unicode des caractères sont stables, le statut (accepté ou refusé) restera le même, l'algorithme ne servant vraiment que pour les nouveaux caractères introduits par la nouvelle version d'Unicode. Mais, bien qu'Unicode fasse certaines promesses de stabilité, elles ne sont pas totales : un caractère peut donc se retrouver accepté une fois, puis refusé. Le RFC 5892 prévoyait donc, à juste titre, un examen manuel, et le RFC 5892 décrivait une table d'exceptions permettant de maintenir la compatibilité (section 2.7). Cette table aurait pu être remplie au fur et à mesure, pour conserver une certaine stabilité. Mais un tel examen n'a été fait qu'une fois, pour Unicode 6, dans le RFC 6452, suite à quoi il a été décidé de ne pas ajouter d'exceptions (et donc de laisser des noms devenir invalides.) En 2015, l'IAB avait demandé que les mises à jour soient suspendues, suite au « problème » (dont tout le monde n'est pas convaincu) du caractère arabe bāʾ avec hamza (ࢡ, U+08A1). Puis, en 2018, avait insisté sur l'importance de terminer ce travail. Deux ans après, ce n'est toujours pas fait. Donc, en pratique, IDNA version 2 n'a pas encore tenu sa promesse de pouvoir être mis à jour « presque » automatiquement.
À noter un autre problème avec la nouvelle version d'IDN : le fait qu'un caractère soit autorisé ou pas dépend d'un algorithme, et non plus d'une table statique. Mais, pour faciliter la vie des utilisateurs, l'IANA avait produit des tables en appliquant l'algorithme aux données Unicode. Il a toujours été prévu que ces tables soient juste une aide, qu'elles ne fassent pas autorité mais, malheureusement, certaines personnes les ont compris comme étant la référence, ce qui n'était pas prévu. Comme ces tables IANA n'ont pas été mises à jour au fur et à mesure des versions d'Unicode, le problème devient sérieux. Le RFC demande à l'IANA de modifier la description des tables pour insister sur leur caractère non-normatif (« It should be stressed that these are not normative in that, in principle, an application can do its own calculations and these tables can change as IETF understanding evolves. ».)
J'ai écrit que tout le monde n'était pas convaincu de la nature du problème. Il y a en effet un désaccord de fond au sujet d'Unicode, entre ceux qui considèrent que toutes les lettres se valent, que toutes les écritures doivent avoir le même statut et, ceux, souvent des utilisateurs de l'alphabet latin, que la diversité dérange et qui voudraient mieux contrôler les caractères « dangereux », en utilisant des arguments de sécurité contestables. La question est d'autant plus sérieuse que les retards de mise à jour d'IDN (qui est toujours en version 6 alors qu'Unicode est en version 13) handicape surtout les utilisateurs des écritures les plus récemment ajoutées dans Unicode, en général des peuples minoritaires et peu présents dans le business international. Le retard n'est donc pas forcément gênant de la même manière pour tout le monde…
Date de publication du RFC : Mars 2020
Auteur(s) du RFC : M. Thomson, M. Nottingham
Pour information
Première rédaction de cet article le 1 mai 2020
Ce RFC est le compte-rendu d'un atelier de l'IAB qui s'est tenu en juillet 2019 au sujet du Web Packaging, une proposition technique permettant de regrouper un ensemble de pages Web en un seul fichier, pouvant être distribué par des moyens non-Web, tout en étant authentifié. Web Packaging (ou WebPackage) est un concentré de pas mal d'enjeux technico-politiques actuels.
Pour en savoir plus sur ce projet, vous pouvez consulter l'actuel dépôt du projet, géré par le WICG. (Il y avait un projet W3C à un moment mais qui a été abandonné). Vous pouvez commencer par ce document. (Il y a aussi un brouillon à l'IETF, et un nouveau groupe de travail, wpack.) Le projet avait été lancé à l'origine par Google.
La proposition a suscité pas mal de discussions, voire de contestations. N'est-ce pas encore un plan diabolique de Google pour entuber les webmestres ? L'IAB a donc organisé un atelier, joliment nommé ESCAPE (Exploring Synergy between Content Aggregation and the Publisher Ecosystem) au sujet du Web Packaging. Cela permettait notamment de faire venir des gens qui sont plutôt du côté « création de contenus » (entreprises de presse, par exemple), et qui viennent rarement au W3C et jamais à l'IETF. Cet atelier s'est tenu à Herndon en juillet 2019. Il n'avait pas pour but de prendre des décisions, juste de discuter. Vous pouvez trouver les documents soumis par les participants sur la page de l'atelier. Je vous recommande la lecture de ces soumissions.
Le principe de base de Web Packaging est de séparer l'écriture du contenu et sa distribution, tout en permettant de valider l'origine du contenu (l'annexe B du RFC décrit Web Packaging plus en détail). Ainsi, un des scénarios d'usage (section 2 du RFC) serait que le crawler de Google ramasse un paquetage de pages et de ressources sur un site Web, l'indexe, et puisse ensuite servir directement ce paquetage de pages et autres ressources au client qui a utilisé le moteur de recherche, sans renvoyer au site original. Et tout cela avec des garanties d'origine et d'authenticité, et en faisant afficher par le navigateur dans sa barre d'adresses l'URL original. Un autre usage possible serait la distribution de sites Web censurés, par des techniques pair-à-pair, tout en ayant des garanties sur l'origine (sur ce point particulier, voir aussi la section 3.3 du RFC). Notez que ces techniques font que le site original ne connait pas les téléchargements, ce qui peut être vu comme une bonne chose (vie privée) ou une mauvaise (statistiques pour le marketing). Et puis les inquiétudes vis-à-vis de Web Packaging ne viennent pas uniquement des problèmes pour avoir des statistiques. Des éditeurs ont dit lors de l'atelier qu'il étaient tout simplement inquiets des « copies incontrôlées ». En outre, l'argument de vie privée est à double tranchant : le site d'origine du contenu ne voit pas les téléchargements, mais un autre acteur, celui qui envoie le Web Packaging le voit.
Séparer création de contenu et distribution permet également la consultation hors-ligne, puisque un paquetage Web Packaging peut être auto-suffisant. Cela serait très pratique, par exemple pour Wikipédia. Actuellement, il existe des trucs plus ou moins pratiques (HTTrack…) mais le Web Packaging rendrait cette activité plus agréable. Notez que les participants à l'atelier ne se sont pas mis d'accord sur le caractère indispensable ou pas de la signature dans ce cas.
Potentiellement, un système comme Web Packaging pourrait également changer le monde du livre électronique : tout site Web pourrait être facilement « ebookisé ». Une amusante discussion à l'atelier a eu lieu sur l'intérêt des signatures. Comme souvent en cryptographie, les signatures ont une durée de validité limitée. Sept jours est proposé, par défaut, mais Moby Dick a été écrit il y a 61 000 jours.
Dernier scénario d'usage envisagé, l'archivage du Web. Ce n'est pas trivial, car il ne suffit pas de copier la page HTML, il faut garder toutes les ressources auxiliaires, d'où l'intérêt de Web Packaging. Et la signature serait utile là aussi, pour vérifier que l'archive est sincère. (Voir aussi le RFC 7089).
La section 3 du RFC discute ensuite la difficile question de la relation entre les producteurs de contenu et les intermédiaires comme Google. Par exemple, si un producteur de contenu sous-traite la distribution du contenu à un CDN, il doit lui faire confiance pour ne pas modifier le contenu. Web Packaging, avec son système de signature, résoudrait le problème. D'un autre côté, ça fait encore un format de plus dans lequel il faut distribuer le contenu, un coût pas forcément négligeable, et qui frappera de manière disproportionnée les petits producteurs, ou les moins pointus techniquement. Certains participants en ont profité pour râler contre AMP.
Comme toute nouvelle technique, Web Packaging pourrait mener à des déplacements de pouvoir dans l'écosystème du Web. Mais il est très difficile de prévoir ces effets (cf. RFC 5218). Est-ce que Web Packaging va favoriser les producteurs de contenu, les intermédiaires, les utilisateurs ? La section 4 du RFC explore la question. Par exemple, un des risques est la consolidation du pouvoir des gros intermédiaires. Si Facebook peut directement servir des paquetages Web, sans passer par le site original, les performances seront bien meilleures pour les paquetages déjà chargés par Facebook, qui gagnera donc en pouvoir. D'un autre côté, Web Packaging pourrait mener au résultat inverse : l'authentification des paquetages rendrait la confiance en l'intermédiaire inutile. (Personnellement, j'apprécie dans l'idée de Web Packaging que cela pourrait encourager le pair-à-pair, dont le RFC ne parle quasiment pas, en supprimant l'inquiétude quant à l'authenticité du contenu.)
La section 4 couvre d'autres questions soulevées par le concept de Web Packaging. Par exemple, il ne permet pas facilement d'adapter le contenu à l'utilisateur puisque, au moment de fabriquer le paquetage, on ne sait pas qui le lira. Une solution possible serait, pour un même site Web, de produire plusieurs paquetages et de laisser l'utilisateur choisir, mais elle complexifie encore le travail des producteurs. (Personnellement, je pense que beaucoup de ces adaptations sont mauvaises, par exemple l'adaptation au navigateur Web dans l'espoir de contrôler plus étroitement l'apparence, et cela ne me chagrine donc pas trop si elles seraient plus difficiles.)
Et la sécurité ? Un mécanisme de distribution par paquetages Web signés envoyés par divers moyens serait un changement profond du mécanisme de sécurité du Web. Actuellement, ce mécanisme repose essentiellement sur TLS, via HTTPS (RFC 2818). Mais c'est très insuffisant ; TLS ne protège que le canal, pas les données. Si un site Web a des miroirs, HTTPS ne va pas protéger contre des miroirs malveillants ou piratés. Et, comme noté plus haut, si un contenu Web est archivé, et distribué, par exemple, par Internet Archive, comment s'assurer de son authenticité ? L'absence d'un mécanisme d'authentification des données (Object-based security) est une des plus grosses faiblesses du Web (malgré des essais anciens mais jamais déployés comme celui du RFC 2660), et Web Packaging, qui sépare la validation du contenu de sa distribution, pourrait contribuer à traiter le problème. Ceci dit, l'expérience de la sécurité sur l'Internet montre aussi que tout nouveau système amène de nouvelles vulnérabilités et il faudra donc être prudent.
Et la vie privée ? De toute façon, quand on récupère un contenu, qu'il soit sous forme de paquetages ou de pages Web classiques, on donne au serveur des informations (et HTTP est terriblement bavard, il n'y a pas que l'adresse IP qui est transmise). Il semble qu'au moins, Web Packaging n'aggrave pas les nombreux problèmes du Web. (Personnellement, je pense qu'il pourrait même les limiter mais l'analyse exacte est compliquée. À l'heure actuelle, si vous suivez un lien depuis Facebook, le site Web d'origine et Facebook sont au courant. Avec Web Packaging seul Facebook le saurait. Est-ce un progrès ?)
La technologie AMP, très controversée a souvent été mentionnée pendant l'atelier. Elle n'a pas de rapport direct avec Web Packaging mais elle sort de la même société, et est souvent présentée comme faisant partie d'un même groupe de technologies modernes. La section 5 du RFC discute donc des problèmes spécifiques à AMP.
Je n'ai pas regardé les outils existants pour faire du Web Packaging donc ce site n'est pas encore sous forme de paquetage.
Date de publication du RFC : Mars 2020
Auteur(s) du RFC : W. Mekking, D. Mahoney (ISC)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 27 mars 2020
DLV (DNSSEC Lookaside Validation) était un mécanisme permettant de contourner le fait que plusieurs zones DNS importantes, dont la racine, n'étaient pas signées. Jamais très utilisé, et aujourd'hui inutile, DLV est ici officiellement abandonné, et les RFC le décrivant sont reclassés « intérêt historique seulement ».
Normalement, DNSSEC (RFC 4033 et
suivants) suit le modèle de confiance du DNS. La résolution DNS part de la racine,
puis descend vers les domaines de premier niveau, de deuxième
niveau, etc. De même, un résolveur validant avec DNSSEC connait la
clé publique
de la racine, s'en sert pour valider la clé du premier niveau, qui
sert ensuite pour valider la clé du deuxième niveau, etc. C'est
ainsi que DNSSEC était prévu, et qu'il fonctionne aujourd'hui. Mais
il y a eu une période de transition, pendant laquelle la racine, et
la plupart des TLD, n'étaient pas signés. Le résolveur validant
aurait donc dû gérer autant de clés publiques qu'il y avait de zones
signées. Pour éviter cela, DLV (DNSSEC Lookaside
Validation) avait été
créé. Le principe de DLV était de mettre les clés des zones signées
dans une zone spéciale (par exemple
dlv.operator.example
) et que les résolveurs
cherchent les clés à cet endroit. Ainsi, le résolveur validant
n'avait besoin que de la clé de la zone DLV. DLV a bien rempli son
rôle, et a sérieusement aidé au déploiement de DNSSEC. Mais,
aujourd'hui, les choses sont différentes, la racine (depuis 2010) et tous les
TLD importants sont signés (1 389 TLD sur 1 531 sont signés,
.fr
l'a également été en
2010), et DLV n'a donc plus de raison d'être.
Bien sûr, il reste encore des zones de délégation non signées et donc en théorie des gens qui pourraient avoir besoin de DLV. Mais le consensus était clair à l'IETF sur l'abandon de DLV, car (section 3 de notre RFC) :
Il n'y avait en pratique d'une seule zone DLV sérieuse,
dlv.isc.org
, et elle a
été arrêtée en 2017, il ne reste donc de toute façon plus
d'utilisateurs connus de DLV.
Donc (section 4 de notre RFC), les deux RFC qui décrivaient DLV, le RFC 4431 et le RFC 5074
passent du statut « Pour information » à celui de « Intérêt
historique seulement ». (Vous pouvez consulter les détails
du processus.) Même chose pour le type d'enregistrement DNS
DLV
(code 32769), qui apparait désormais dans
le
registre IANA comme (OBSOLETE).
Date de publication du RFC : Mars 2020
Auteur(s) du RFC : R. Carney (GoDaddy), G. Brown (CentralNic Group), J. Frakes
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 15 mars 2020
Historiquement, le protocole EPP d'avitaillement des
ressources Internet (notamment les noms de domaine) n'indiquait pas le prix de
création ou de renouvellement d'un nom. Typiquement, tous les noms
coûtaient le même prix. Mais certains
registres préfèrent vendre plus cher
sex.example
que
suitedelettressanstropdesignification.example
. D'où
cette extension EPP qui permet d'indiquer le coût d'un nom de
domaine particulier, extension qui est aujourd'hui très
répandue.
Il y a donc deux logiques possibles, une purement technique (les noms de domaine ont tous le même coût pour le registre et devraient donc coûter pareil au client) et une logique business, où on essaie de faire payer le client plus cher pour les noms les plus demandés. Aux débuts d'EPP (ou de son prédécesseur RGP, dans le RFC 3915), il allait de soi qu'il n'y avait qu'un prix puisque les coûts réels sont les mêmes pour tous les domaines, mais ce n'est plus le cas aujourd'hui. Il faut donc pouvoir informer sur les prix, EPP n'étant pas juste un canal technique mais aussi un canal de vente. Comme cela avait été dit lors d'une discussion à l'IETF, « Arguably, in a situation where many TLDs are now offering domains at various pricing tiers (with no further policy requirements), general availability is no longer just a matter of "domain taken/reserved/valid?", but also of "how much is the registrant willing to pay?". »
L'ancien modèle était donc :
<create>
,
<renew>
et
<transfer>
, d'autres gratuites (par
exemple <update>
),Le nouveau modèle, où le tarif est indiqué via le canal EPP, permet d'avoir des prix différents par domaine, mais permet également de découvrir automatiquement le tarif, sans se plonger dans la documentation.
La section 3 du RFC décrit ce qui se passe dans chaque commande
EPP facturable. L'extension utilise un espace de noms
XML qui vaut
urn:ietf:params:xml:ns:epp:fee-1.0
(abrégé à
fee:
dans les exemples du RFC mais bien sûr,
comme toujours avec les espaces de noms XML, chacun choisit son
abréviation.)
Voici un exemple où le client vérifie la disponibilité d'un domaine
et son prix, avec <check>
:
<?xml version="1.0" encoding="utf-8" standalone="no"?> <epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <command> <check> <domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> <domain:name>example.net</domain:name> </domain:check> </check> <extension> <fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0"> <fee:currency>USD</fee:currency> <fee:command name="create"> <fee:period unit="y">2</fee:period> </fee:command> </fee:check> </extension> </command> </epp>
Le client a demandé quel était le prix en dollars étatsuniens pour
une réservation de deux ans. Ici, le serveur lui répond que le
domaine est libre (avail="1"
) :
... <resData> <domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> <domain:cd> <domain:name avail="1">example.net</domain:name> </domain:cd> </domain:chkData> </resData> <extension> <fee:cd avail="1"> <fee:objID>example.net</fee:objID> <fee:class>standard</fee:class> <fee:command name="create" standard="1"> <fee:period unit="y">2</fee:period> <fee:fee description="Registration Fee" refundable="1" grace-period="P5D">10.00</fee:fee> </fee:command> ...
Et qu'il en coûtera dix dollars. Notez que le prix dépend de la
commande (d'où le <fee:command
name="create">
chez le client, et dans la réponse) ;
un renouvellement peut coûter moins cher qu'une création, par
exemple. Notez aussi que le RFC ne spécifie pas comment le prix est
déterminé ; cela peut être configuré manuellement par le registre,
ou bien suivre un algorithme (prix plus élevé si le nom est dans un
dictionnaire, ou s'il fait moins de N caractères…)
Le serveur EPP aurait pu refuser, si les paramètres étaient inconciliables avec sa politique :
<fee:cd avail="0"> <fee:objID>example.net</fee:objID> <fee:reason>Only 1 year registration periods are valid.</fee:reason> </fee:cd>
En quelle monnaie sont indiqués les coûts ? Un élément XML
<fee:currency>
va permettre de
l'indiquer. Sa valeur est un code à trois lettres tiré de la norme ISO 4217, par exemple
EUR
pour l'euro et
CNY
pour le yuan. Si le
registre se fait payer, non pas dans une monnaie reconnue mais dans
une unité de compte privée (des « crédits » internes, par exemple),
il peut utiliser le code XXX
. Le serveur ne
doit pas faire de conversion monétaire. S'il a
indiqué des coûts en dollars étatsuniens et
que le client indique ce qu'il paie en pesos
mexicains, le serveur doit rejeter la commande (ce qui
est logique, vu la volatilité des taux de conversion.)
Cette extension à EPP permet également d'indiquer des périodes pendant lesquelles les objets, par exemple les noms de domaine, sont enregistrés. L'unité de temps (mois ou année) est indiquée également.
L'extension permet également d'indiquer des actions commerciales comme une remise, un remboursement (par exemple en cas d'utilisation de la période de grâce du RFC 3915), etc.
Un mécanisme courant chez les registres est d'avoir un compte par client, où le client dépose une certaine somme, d'où les créations ultérieures de noms de domaine sont déduites. Cela donne au registre de la trésorerie, et cela simplifie la comptabilité. L'extension de ce RFC permet de consulter le montant restant (balance) et d'indiquer si l'épuisement de ce compte signifie un arrêt des opérations payantes, ou bien si le serveur fait crédit au client.
Les prix peuvent dépendre du nom de domaine
(hotels.example
étant plus cher que
fzoigqskjjazw34.example
) mais aussi de la phase
actuelle des enregistrements. Par exemple, une phase initiale, dite
de « lever de soleil » (RFC 8334) pour un
nouveau domaine d'enregistrement peut avoir des prix plus
élevés.
Le serveur peut exiger que le client marque son approbation en indiquant, dans ses requêtes, le prix à payer (section 4). Voilà ce que cela donnerait pour la commande de création :
... <command> <create> <domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> <domain:name>example.net</domain:name> <domain:period unit="y">2</domain:period> <domain:registrant>jd1234</domain:registrant> ... </domain:create> </create> <extension> <fee:create xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0"> <fee:currency>USD</fee:currency> <fee:fee>10.00</fee:fee> </fee:create> </extension> </command>
Pour une demande de vérification de disponibilité
(<check>
), le serveur peut répondre que
le domaine n'est pas libre si le client n'utilise pas l'extension de
coût. Le principe est que, si un <check>
indique qu'un domaine est libre, un
<create>
avec les mêmes
extensions ou la même absence d'extension doit
réussir. « Libre » veut donc dire « libre, aux conditions que tu as
indiquées ».
Les détails de l'extension dans toutes les commandes EPP figurent en section 5, et le schéma en section 6.
L'extension décrite dans le RFC a été ajoutée au registre des extensions EPP, spécifié par le RFC 7451.
Cette extension EPP est déjà mise en œuvre par CentralNic et par d'autres registres mais attention, pas forcément avec la version du RFC, cela peut être des brouillons antérieurs.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : C. Bormann (Universitaet Bremen TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cbor
Première rédaction de cet article le 29 février 2020
Ce nouveau RFC étend le format de fichiers CBOR (normalisé dans le RFC 8949) pour représenter des tableaux de données numériques, et des tableaux multidimensionnels.
Le format CBOR est en effet extensible par des étiquettes (tags) numériques qui indiquent le type de la donnée qui suit. Aux étiquettes définies dans la norme originale, le RFC 8949, ce nouveau RFC ajoute donc des étiquettes pour des types de tableaux plus avancés, en plus du type tableau de base de CBOR (qui a le type majeur 4, cf. RFC 8949, et dont les données ne sont pas forcément toutes de même type).
Le type de données « tableau de données numériques » est utile pour les calculs sur de grandes quantités de données numériques, et bénéficie de mises en œuvres adaptées puisque, opérant sur des données de même type, contrairement aux tableaux CBOR classiques, on peut optimiser la lecture des données. Pour comprendre l'utilité de ce type, on peut lire « TypedArray Objects » (la spécification de ces tableaux dans la norme ECMA de JavaScript, langage dont CBOR reprend le modèle de données) et « JavaScript typed arrays » (la mise en œuvre dans Firefox).
La section 2 spécifie ce type de tableaux dans CBOR. Un tableau typé (typed array) est composé de données numériques de même type. La représentation des nombres (par exemple entiers ou flottants) est indiquée par l'étiquette. En effet, il n'y a pas de représentation canonique des nombres dans un tableau typé (contrairement aux types numériques de CBOR, types majeurs 0, 1 et 7) puisque le but de ces tableaux est de permettre la lecture et l'écriture rapides de grandes quantités de données. En stockant les données sous diverses formes, on permet de se passer d'opérations de conversion.
Il n'y a pas moins de 24 étiquettes (désormais enregistrées dans
le
registre IANA des étiquettes CBOR) pour représenter toutes
les possibilités. (Ce nombre important, les étiquettes étant codées
en général sur un seul octet, a suscité des discussions dans le
groupe de travail, mais se justifie par le caractère très courant de
ces tableaux numériques. Voir la section 4 du RFC.) Par exemple,
l'étiquette 64 désigne un tableau typé d'octets (uint8
),
l'étiquette 70 un tableau typé d'entiers de 32 bits non
signés et petit-boutiens
(uint32
), l'étiquette 82 un tableau typé de
flottants IEEE 754 de 64 bits
gros-boutiens, etc. (CBOR est normalement gros-boutien, comme tous
les protocoles et formats Internet, cf. section 4 du RFC.) Les
étiquettes ne sont pas attribuées arbitrairement, chaque nombre
utilisé comme étiquette encode les différents choix possibles dans
les bits qui le composent. Par exemple, le quatrième bit de
l'étiquette indique si les nombres sont des entiers ou bien des
flottants (cf. section 2.1 du RFC pour les détails).
Le tableau typé est ensuite représenté par une simple chaîne d'octets CBOR (byte string, type majeur 2). Une mise en œuvre générique de CBOR peut ne pas connaitre ces nouvelles étiquettes, et considérera donc le tableau typé comme une bête suite d'octets.
La section 3 de notre RFC décrit ensuite les autres types de tableaux avancés. D'abord, les tableaux multidimensionnels (section 3.1). Ils sont représentés par un tableau qui contient deux tableaux unidimensionnels. Le premier indique les tailles des différentes dimensions du tableau multidimensionnel, le second contient les données. Deux étiquettes, 40 et 1040, sont réservées, pour différencier les tableaux en ligne d'abord ou en colonne d'abord. Par exemple, un tableau de deux lignes et trois colonnes, stocké en ligne d'abord, sera représenté par deux tableaux unidimensionnels, le premier comportant deux valeurs, 2 et 3, le second comportant les six valeurs, d'abord la première ligne, puis la seconde.
Les autres tableaux sont les tableaux homogènes (étiquette 41), en section 3.2. C'est le tableau unidimensionnel classique de CBOR, excepté que tous ses élements sont du même type, ce qui peut être pratique au décodage, pour les langages fortement typés. Mais attention : comme rappelé par la section 7 du RFC, consacrée à la sécurité, le décodeur doit être prudent avec des données inconnues, elles ont pu être produites par un programme malveillant ou bogué, et donc non conformes à la promesse d'homogénéité du tableau.
La section 5 de notre RFC donne les valeurs des nouvelles étiquettes dans le langage de schéma CDDL (RFC 8610).
Date de publication du RFC : Juillet 2020
Auteur(s) du RFC : C. Huitema (Private
Octopus)
Pour information
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 29 juillet 2020
Le but du protocole de cryptographie TLS est de protéger une session contre l'écoute par un indiscret, et contre une modification par un tiers non autorisé. Pour cela, TLS chiffre toute la session. Toute la session ? Non, certaines informations circulent en clair, car elles sont nécessaires pour la négociation des paramètres de chiffrement. Par exemple, le SNI (Server Name Indication) donne le nom du serveur qu'on veut contacter. Il est nécessaire de le donner en clair car la façon dont vont se faire le chiffrement et l'authentification dépendent de ce nom. Mais cela ouvre des possibilités aux surveillants (savoir quel serveur on contacte, parmi tous ceux qui écoutent sur la même adresse IP) et aux censeurs (couper sélectivement les connexions vers certains serveurs). Il est donc nécessaire de protéger ce SNI, ce qui n'est pas facile. Ce nouveau RFC décrit les objectifs, mais pas encore la solution. Celle-ci reposera sans doute sur la séparation entre un frontal général qui acceptera les connexions TLS, et le « vrai » service caché derrière.
C'est que le problème est difficile puisqu'on est face à un dilemme de l'œuf et de la poule. On a besoin du SNI pour chiffrer alors qu'on voudrait chiffrer le SNI. Le RFC note qu'il n'y aura sans doute pas de solution parfaite.
Cela fait longtemps que le SNI (normalisé il y a longtemps, dans
le RFC 3546, et aujourd'hui dans le RFC 6066) est connu pour les risques qu'il pose
pour la vie privée. Les autres canaux de
fuite d'information sont fermés petit à petit (par exemple le
DNS, avec les RFC 9156, RFC 7858 et RFC 8484), souvent en utilisant TLS (RFC 8446). Même l'adresse
IP de destination, qu'on ne peut pas cacher, perd de sa
signification lorsque de nombreux services sont hébergés derrière la
même adresse IP (c'est particulièrement vrai pour les gros
CDN). Mais
ces services comptent sur le SNI (Server Name
Indication), transporté en clair dans le message TLS
ClientHello
, pour le démultiplexage des
requêtes entrantes, et ce SNI tend donc à devenir le maillon faible
de la vie privée, l'information la plus significative qui reste en
clair. Voici, vu par tshark,
un exemple de ClientHello
, avec un SNI
(server_name
, affiche tshark) :
... Secure Sockets Layer TLSv1 Record Layer: Handshake Protocol: Client Hello Content Type: Handshake (22) Version: TLS 1.0 (0x0301) Length: 233 Handshake Protocol: Client Hello Handshake Type: Client Hello (1) Length: 229 Version: TLS 1.2 (0x0303) ... Extension: server_name (len=22) Type: server_name (0) Length: 22 Server Name Indication extension Server Name list length: 20 Server Name Type: host_name (0) Server Name length: 17 Server Name: doh.bortzmeyer.fr
Résultat, le SNI est souvent utilisé de manière intrusive, le RFC 8404 donnant de nombreux exemples. Par exemple,
il permet de censurer finement (si le SNI indique
site-interdit.example
, on jette les paquets,
s'il indique site-commercial.example
, on laisse
passer). Il permet de
shaper le trafic vers
certains sites, en violation de la neutralité du réseau. Il permet aussi de
laisser passer certains sites, lorsqu'on fait une attaque
de l'homme du milieu, par exemple une entreprise qui
fait de l'inspection HTTPS
systématique peut utiliser le SNI pour en exempter certaines sites
sensibles, comme ceux des banques ou des plate-formes médicales. Et,
bien sûr, le SNI peut être passivement observé à des fins de
surveillance (RFC 7258).
On peut s'amuser à noter que ces risques pour la vie privée
n'avaient pas été notés dans la section « Sécurité » des RFC de
normalisation de SNI, comme le RFC 6066. C'est
peut-être parce qu'à l'époque, les éventuels attaquants avaient des
moyens plus simples à leur disposition. (Je me souviens de
discussions à l'IETF « à quoi bon masquer le SNI puisque le
DNS révèle
tout ? » ; c'était avant le RFC 7626.) Il
restait un endroit où le nom du service était en clair, c'était le
certificat renvoyé par le serveur TLS
(subjectAltName
) mais, depuis TLS 1.3 (RFC 8446), lui aussi est chiffré. Aujourd'hui, les
progrès de la sécurité font que le moyen le plus simple de savoir à
quel service un internaute se connecte devient souvent le SNI.
Notez toutefois que tout le monde n'est pas d'accord sur la nécessité de chiffrer le SNI. Certains craignent que cette nécessité débouche sur une solution compliquée et fragile. D'autres ne veulent tout simplement pas priver surveillants et censeurs d'un mécanisme si pratique. En réponse à cette dernière objection (cf. RFC 8404), le RFC note que la méthode recommandée (section 2.3 du RFC), si on peut surveiller et filtrer, est de le faire dans les machines terminales. Après tout, si l'organisation contrôle ces machines terminales, elle peut les configurer (par exemple avec le certificat de l'intercepteur) et si elle ne les contrôle pas, elle n'a sans doute pas le droit d'intercepter les communications.
La solution est évidente, chiffrer le SNI. Ce ne sont pas les propositions qui ont manqué, depuis des années. Mais toutes avaient des inconvénients sérieux, liés en général à la difficulté du bootstrap. Avec quelle clé le chiffrer puisque c'est justement le SNI qui aide le serveur à choisir la bonne clé ?
La section 3 de notre RFC énumère les exigences auxquelles va devoir répondre la solution qui sera adoptée. (Rappelez-vous qu'il s'agit d'un travail en cours.) C'est que beaucoup de solutions ont déjà été proposées, mais toutes avaient de sérieux problèmes. Petit exercice avant de lire la suite : essayez de concevoir un protocole de chiffrement du SNI (par exemple « le SNI est chiffré avec la clé publique du serveur, récupérée via DANE » ou bien « le SNI est chiffré par une clé symétrique qui est le condensat du nom du serveur [vous pouvez ajouter du sel si vous voulez, mais n'oubliez pas d'indiquer où le trouver] ») et voyez ensuite si cette solution répond aux exigences suivantes.
Par exemple, pensez à la possibilité d'attaque par rejeu. Si le SNI est juste chiffré avec une clé publique du serveur, l'attaquant n'a qu'à observer l'échange, puis faire à son tour une connexion au serveur en rejouant le SNI chiffré et paf, dans la réponse du serveur, l'attaquant découvrira quel avait été le service utilisé. La solution choisie doit empêcher cela.
Évidemment, la solution ne doit pas imposer d'utiliser un secret partagé (entre le serveur et tous ses clients), un tel secret ne resterait pas caché longtemps.
Il faut aussi faire attention au risque d'attaque par déni de service, avec un méchant qui générerait plein de SNI soi-disant chiffrés pour forcer des déchiffrements inutiles. Certes, TLS permet déjà ce genre d'attaques mais le SNI peut être traité par une machine frontale n'ayant pas forcément les mêmes ressources que le vrai serveur.
Autre chose importante quand on parle de protéger la
vie privée : il ne faut pas se distinguer
(Do not stick out). Porter un masque du
Joker dans la rue pour protéger son anonymat
serait sans doute une mauvaise idée, risquant au contraire d'attirer
l'attention. La solution choisie ne doit donc pas permettre à, par
exemple, un état policier, de repérer facilement les gens qui sont
soucieux de leur vie privée. Une extension spécifique du
ClientHello
serait dangereuse, car triviale à
analyser automatiquement par un système de surveillance massive.
Il est évidemment souhaitable d'avoir une confidentialité persistante, c'est-à-dire que la compromission d'une clé privée ne doit pas permettre de découvrir, a posteriori, les serveurs auxquels le client s'était connecté. Simplement chiffrer le SNI avec la clé publique du serveur ne convient donc pas.
L'hébergement Web d'aujourd'hui est souvent compliqué, on peut avoir un frontal géré par une société d'hébergement, et des machines gérées par le client de l'hébergeur derrière, par exemple. Ou bien plusieurs clients de l'hébergeur sur la même machine physique, voire sur la même machine virtuelle avec certaines hébergements mutualisés. Cela peut être utile pour certaines solutions, par exemple le fronting où une machine frontale reçoit une demande de connexion TLS pour elle puis, une fois TLS démarré, reçoit le nom du vrai serveur, et relaie vers lui. Si la machine frontale relaie pour beaucoup de serveurs, cela fournit une assez bonne intimité. Mais cela nécessite de faire confiance à la machine frontale, et le risque d'attaque de l'homme du milieu (qui nous dit que ce frontal est le frontal légitime choisi par le serveur TLS ?) augmente. Or, plus la machine frontale protège des serveurs, plus elle est un objectif tentant pour la police ou les pirates.
Ah, et j'ai parlé du Web mais la solution doit évidemment fonctionner avec d'autres protocoles que HTTPS. Par exemple, le DNS sur TLS du RFC 7858 ou bien IMAP (RFC 8314) doivent fonctionner. Même pour HTTP, certaines particularités de HTTP peuvent poser problème, et il est donc important que la future solution de chiffrement de SNI soit agnostique, pour marcher avec tous les protocoles. Et elle doit également fonctionner avec tous les protocoles de transport, pas seulement TCP, mais aussi DTLS ou QUIC.
Le RFC note aussi qu'il serait bon d'avoir une solution de chiffrement de la négociation ALPN (RFC 7301). ALPN est moins indiscret que SNI mais il renseigne sur l'application utilisée.
Puisque j'ai parlé plus haut du fronting, cela
vaut la peine de parler du fronting HTTP car
c'est une technique courante, aussi bien pour échapper à la censure
que pour protéger la vie privée. La section 4 du RFC lui est
consacrée. Elle est décrite dans l'article de Fifield, D., Lan, C.,
Hynes, R., Wegmann, P., et V. Paxson, « Blocking-resistant
communication through domain fronting ». Le
principe est que le client TLS établit une connexion avec le système
de fronting puis, une fois le chiffrement TLS en
marche, le client demande la connexion avec le vrai serveur. Un
surveillant ne pourra voir que l'utilisation d'un service de
fronting, pas le nom du vrai service (le SNI en
clair dira, par exemple,
fronting.example.net
). En pratique, cela marche
bien avec HTTPS si le serveur de fronting et le
vrai serveur sont sur le même système : il suffit alors d'indiquer
le domaine du fronting dans le SNI et le vrai
domaine dans les en-têtes Host:
de HTTP. Cela
ne nécessite aucune modification du protocole TLS.
Le fronting a quelques limites :
À noter que les trames ORIGIN
du RFC 8336 peuvent être utiles en cas de
fronting, pour indiquer le contenu venant du
« vrai » serveur.
Voilà, vous connaissez maintenant le problème, l'IETF est en train
de travailler aux solutions, le brouillon le plus avancé est
draft-ietf-tls-esni
.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : D. Benjamin (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 24 février 2020
Voici un très court RFC, pour résoudre un petit problème d'interaction entre TLS 1.3 et HTTP/2. (Mais ne le lisez pas, il a depuis été intégré au RFC 9113.)
Les anciennes versions du protocole de sécurité TLS avaient un mécanisme de renégociation des paramètres de la session, permettant de changer certains paramètres même après que la session ait démarré. C'était parfois utilisé pour l'authentification du client TLS, par exempe lorsqu'un serveur HTTP décide de demander ou pas un certificat client en fonction de la requête dudit client. Or, dans la version 2 de HTTP, HTTP/2, normalisée à l'origine dans le RFC 7540, il peut y avoir plusieurs requêtes HTTP en parallèle. On ne peut donc plus corréler une requête HTTP avec une demande de certificat. Le RFC 7540 (section 9.2.1) interdit donc d'utiliser la renégociation.
Mais la nouvelle version de TLS, la 1.3, spécifiée dans le RFC 8446, a supprimé complètement le mécanisme de renégociation. À la place, un mécanisme d'authentification spécifique a été normalisé (section 4.6.2 du RFC 8446.) Or, ce mécanisme pose les mêmes problèmes que la rénégociation avec le parallélisme que permet HTTP/2. Il fallait donc l'exclure aussi, ce qui n'avait pas été remarqué tout de suite. C'est ce que fait notre RFC. Pas d'authentification après la poignée de main initiale, elle est incompatible avec HTTP/2 (ou avec le futur HTTP/3, d'ailleurs, qui permet également des requêtes en parallèle) et elle doit déclencher une erreur.
À noter que la renégociation était également utilisée pour dissimuler le vrai certificat serveur, par exemple pour contourner certaines solutions de censure. Comme TLS 1.3 chiffre désormais le certificat serveur, la renégociation n'est plus utile pour ce scénario. En outre, la renégociation avait posé quelques problèmes de sécurité (cf. une faille fameuse, et le RFC 7457.)
Outre la renégociation, il y a d'autres messages qui peuvent
survenir après la poignée de main initiale. C'est le cas des
KeyUpdate
(RFC 8446,
sections 4.6.3 et 7.2) mais ils concernent la session TLS entière,
pas juste une requête HTTP, donc ils sont compatibles avec le
parallélisme de HTTP/2. Quant aux
NewSessionTicket
(RFC 8446, section 4.6.1), ils dépendent, eux, de la requête
HTTP, mais leur interaction avec HTTP/2 est prévue et documentée
dans le RFC 8470, et ils sont donc
acceptés. De toute façon, depuis, le problème a été réglé par le
remplacement du RFC 7540 par le RFC 9113.
Date de publication du RFC : Mars 2020
Auteur(s) du RFC : Y. Sheffer
(Intuit), D. Lopez, O. Gonzalez de
Dios, A. Pastor Perales (Telefonica
I+D), T. Fossati (ARM)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF acme
Première rédaction de cet article le 12 mars 2020
Quand une Autorité de Certification (AC) émet un certificat numérique, une question de sécurité se pose : que se passe-t-il si un attaquant met la main sur la clé privée associée à ce certificat, et peut donc usurper l'identité du titulaire légitime ? La réponse traditionnelle était la révocation du certificat par l'AC dès qu'elle est prévenue. Pour diverses raisons, ce processus de révocation est peu fiable, ce qui laisse comme seule ligne de défense l'expiration du certificat. C'est le rôle du champ « Not After » dans un certificat. Pour la sécurité, on voudrait que la date d'expiration soit proche, pour ne pas laisser un éventuel attaquant profiter de son forfait trop longtemps. Mais ce n'est pas très pratique pour le titulaire que de renouveller son certificat très souvent, même avec un protocole comme ACME qui permet l'automatisation. Ce nouveau RFC propose une extension à ACME, qui autorise des certificats de très courte durée de vie (quelques jours seulement) mais renouvellés encore plus facilement qu'avec le ACME classique.
Petit rappel sur ACME : ce protocole, normalisé dans le RFC 8555, permet d'obtenir de manière automatique un certificat correspondant à une identité qui est, la plupart du temps, un nom de domaine. Comme ACME permet l'automatisation, il résout le problème de la révocation en utilisant des certificats dont la durée de vie se compte en mois et plus en années. Ainsi, l'AC Let's Encrypt émet des certificats qui durent trois mois. Mais même trois mois, ça peut être long, si quelqu'un a piqué votre clé privée et se sert de ce certificat. Si on souhaite des certificats durant quelques jours, peut-on utiliser ACME ? En théorie, oui, mais, en pratique, l'AC pourrait ne pas aimer cette charge supplémentaire, et puis que ferait le titulaire si l'AC était indisponible pendant 48 h et qu'on ne puisse pas renouveller le certificat ?
D'où l'idée des certificats STAR (Short-Term,
Automatically-Renewed), initialement décrits dans
l'article « Towards
Short-Lived Certificates », de Topalovic, E.,
Saeta, B., Huang, L., Jackson, C., et D. Boneh, puis dans
l'Internet-Draft
draft-nir-saag-star
. Les
certificats seront de très courte durée de vie, et publiés un peu à
l'avance par l'AC, sans demande explicite du client. Celui-ci pourra
par contre demander l'interruption de la série de certificats, si sa
clé privée a été compromise.
La section 2 de notre RFC explique le déroulement des
opérations. Le client (IdO, pour Identifier
Owner) demande à l'AC une série de certificats STAR, l'AC,
aux intervalles indiqués, crée et publie les certificats, à tout
moment, l'IdO peut arrêter la série. Commençons par le commencement,
le démarrage de la série. C'est du ACME classique (RFC 8555), avec ses défis (par exemple, l'IdO doit prouver
qu'il contrôle bien le nom de
domaine qui sert d'identité). L'IdO doit envoyer
l'extension ACME nommée auto-renewal
. L'AC
indique au client où seront publiés les certificats de la série.
Ensuite, la publication de la série. Tous les certificats de la série utilisent la même clé privée. (Par défaut, les clients ACME classiques créent une nouvelle clé pour chaque renouvellement du certificat.) Ces certificats sont publiés à l'URL annoncé à la fin de la phase précédente.
Lorsqu'il le souhaite, l'IdO peut demander à l'AC d'interrompre la publication de la série de certificats. Notez qu'on ne révoque jamais ces certificats STAR, puisque de toute façon ils expirent très vite.
Les détails du protocole figurent en section 3 du RFC. Ainsi,
l'objet auto-renewal
(désormais dans le registre des champs de l'objet Order) a
plusieurs champs intéressants, comme start-date
(début de la série), end-date
(fin de la série,
mais elle pourra se terminer plus tôt, en cas d'annulation
explicite), lifetime
(durée de vie des
certificats, notez que la valeur réelle dépendra de la politique de
l'AC, cf. section 6.2). Voici un exemple de cet objet, à ajouter aux requêtes de
demande de certificat :
"auto-renewal": { "start-date": "2019-01-10T00:00:00Z", "end-date": "2019-01-20T00:00:00Z", "lifetime": 345600, // 4 days "lifetime-adjust": 259200 // 3 days }
Les champs possibles dans un auto-renewal
sont
listés dans un registre IANA. D'autres
champs pourront être ajoutés dans le futur, en suivant la politique
« Spécification nécessaire » (RFC 8126.)
L'objet Order
(section 7.1.6 du RFC 8555 sera en état
ready
tant que la série des certificats
continuera.
L'AC annoncera sa capacité à faire du STAR (ici à la fin de son annonce) :
{ "new-nonce": "https://example.com/acme/new-nonce", "new-account": "https://example.com/acme/new-account", "new-order": "https://example.com/acme/new-order", ... "meta": { "terms-of-service": "https://example.com/acme/terms/2017-5-30", ... "auto-renewal": { "min-lifetime": 86400, "max-duration": 31536000, "allow-certificate-get": true } } }
Pour arrêter la série avant end-date
, le
client ACME mettra cet état à canceled
:
POST /acme/order/ogfr8EcolOT HTTP/1.1 Host: example.org Content-Type: application/jose+json { "protected": base64url({ "alg": "ES256", "kid": "https://example.com/acme/acct/gw06UNhKfOve", "nonce": "Alc00Ap6Rt7GMkEl3L1JX5", "url": "https://example.com/acme/order/ogfr8EcolOT" }), "payload": base64url({ "status": "canceled" }), "signature": "g454e3hdBlkT4AEw...nKePnUyZTjGtXZ6H" }
Le serveur ACME répondra alors 403 à toutes les requêtes de
récupération d'un certificat de la série annulée, de préférence en
ajoutant (RFC 7807)
urn:ietf:params:acme:error:autoRenewalCanceled
. (Cette
erreur, et quelques autres, ont été ajoutées au registre des erreurs ACME.)
Comme vous avez vu, la théorie est simple. Maintenant, il y a un certain nombre de détails opérationnels sur lesquels il faut se pencher, détaillés en section 4. D'abord, le problème des horloges. Les certificats X.509 utilisent partout des temps (la date limite de validité, par exemple) et le respect de ces temps dépend de l'horloge de la machine. Si votre ordinateur a deux mois d'avance, il considérera les certificats comme expirés alors qu'ils ne devraient pas l'être. C'est un problème général de la cryptographie, comme montré par l'article « Where the Wild Warnings Are: Root Causes of Chrome HTTPS Certificate Errors », qui signale que des déviations de plusieurs jours chez les clients ne sont pas rares. Mais c'est évidemment plus grave avec des certificats à très courte durée de vie. Si on a des certificats Let's Encrypt classiques, qui durent trois mois et qu'on renouvelle une semaine avant leur expiration, même si l'horloge du client déconne de plusieurs jours, ça passera. En revanche, avec les certificats STAR, la désynchronisation des horloges aura des conséquences dans bien plus de cas.
La décision d'utiliser STAR ou pas, et le choix de la durée de vie des certificats, va dépendre de la population d'utilisateurs qu'on attend. Le RFC note que les problèmes d'horloge sont bien plus fréquents sur Windows que sur Android, par exemple.
Autre risque avec STAR, la charge supplémentaire pour les journaux Certificate Transparency (RFC 9162). Si STAR devenait le principal mode d'émission de certificats (c'est peu probable), leur trafic serait multiplié par cent. Avant la publication de ce RFC, de nombreuses discussions avec le groupe de travail IETF trans et avec les opérateurs des principaux journaux ont montré qu'il n'y avait a priori pas de risque, ces journaux peuvent encaisser la charge supplémentaire.
Questions mises en œuvre de STAR, il y a eu une scission (non publique ?) de Boulder, le serveur de Let's Encrypt et du client certbot pour y ajouter STAR. Il y a également un client et serveur avec STAR dans Lurk.
La section 6 de notre RFC revient sur les questions de sécurité
liées à STAR. Ainsi, comme l'expiration remplace la révocation, on
ne peut plus exiger la suppression immédiate d'un certificat. (Mais,
on l'a dit, la révocation marche tellement mal en pratique que ce
n'est pas une grande perte.) En cas de compromission de la clé
privée, on peut demander l'arrêt de l'émission des certificats mais
(et cela ne semble pas mentionné par le RFC), si on perd son compte
ACME, ou simplement le numnique ACME, on
ne peut plus annuler cette émission, et on doit attendre
l'expiration de la séquence (indiquée par
end-date
.)
Date de publication du RFC : Février 2020
Auteur(s) du RFC : R. Shoemaker (ISRG)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF acme
Première rédaction de cet article le 1 mars 2020
Le protocole ACME, surtout connu via son utilisation par l'AC Let's Encrypt, permet de prouver la « possession » d'un nom de domaine, pour avoir un certificat comprenant ce nom. Ce court RFC spécifie une extension à ACME qui permet de prouver la « possession » d'une adresse IP, ce qui permettra d'obtenir via ACME des certificats utilisant une adresse.
Le protocole ACME est normalisé dans le RFC 8555. Son principe est qu'on demande un certificat pour un identificateur (à l'heure actuelle, forcément un nom de domaine) et que le serveur ACME va alors vous défier de prouver que vous contrôlez bien ce nom, par exemple en publiant une chaîne de caractères choisie par le serveur dans un serveur HTTP accessible via ce nom de domaine. Or, les identificateurs dans les certificats PKIX ne sont pas forcément des noms de domaine. Les adresses IP, par exemple, sont prévues. Examinons les certificats du résolveur DNS public Quad9 :
% openssl s_client -connect 9.9.9.9:853 -showcerts | openssl x509 -text ... X509v3 Subject Alternative Name: DNS:*.quad9.net, DNS:quad9.net, IP Address:9.9.9.9, IP Address:9.9.9.10, IP Address:9.9.9.11, IP Address:9.9.9.12, IP Address:9.9.9.13, IP Address:9.9.9.14, IP Address:9.9.9.15, IP Address:149.112.112.9, IP Address:149.112.112.10, IP Address:149.112.112.11, IP Address:149.112.112.12, IP Address:149.112.112.13, IP Address:149.112.112.14, IP Address:149.112.112.15, IP Address:149.112.112.112, IP Address:2620:FE:0:0:0:0:0:9, IP Address:2620:FE:0:0:0:0:0:10, IP Address:2620:FE:0:0:0:0:0:11, IP Address:2620:FE:0:0:0:0:0:12, IP Address:2620:FE:0:0:0:0:0:13, IP Address:2620:FE:0:0:0:0:0:14, IP Address:2620:FE:0:0:0:0:0:15, IP Address:2620:FE:0:0:0:0:0:FE, IP Address:2620:FE:0:0:0:0:FE:9, IP Address:2620:FE:0:0:0:0:FE:10, IP Address:2620:FE:0:0:0:0:FE:11, IP Address:2620:FE:0:0:0:0:FE:12, IP Address:2620:FE:0:0:0:0:FE:13, IP Address:2620:FE:0:0:0:0:FE:14, IP Address:2620:FE:0:0:0:0:FE:15 ...
On voit qu'outre des noms comme quad9.net
, ce
certificat inclut aussi des adresses IP comme
9.9.9.9
et
2620:fe::9
. Mais un tel certificat ne pouvait
pas s'obtenir automatiquement via ACME.
Notre RFC
résout ce problème en ajoutant un nouveau type d'identificateur
ACME, ip
(section 3 du RFC). Les types
d'identificateurs ACME sont décrits dans la section 9.7.7 du RFC 8555. Le nouveau type ip
a
été placé dans le
registre IANA des types d'identificateur. La valeur doit être
une adresse IP sous forme texte (normalisée très sommairement dans
la section 2.1 du RFC 1123 pour
IPv4, et dans la section 4 du RFC 5952 pour IPv6.)
Comme il s'agit d'authentifier des adresses IP, le défi ACME de
type dns-01
n'est pas pertinent et ne doit pas
être utilisé (section 7). Par contre, on peut (section 4 du RFC)
utiliser les défis http-01
(RFC 8555, section 8.3) et le récent
tls-alpn-01
(RFC 8737.)
Pour le défi HTTP, le serveur ACME va se connecter en
HTTP à l'adresse IP indiquée, en mettant
cette adresse dans le champ Host:
. Pour le défi
TLS avec ALPN, le certificat doit contenir un
subjectAltName
de type
iPAddress
. Un piège : contrairement au champ
Host:
de HTTP, l'adresse IP nue ne peut pas
être utilisée dans le SNI (RFC 6066, « Currently, the only server names
supported are DNS hostnames »). Il faut donc utiliser un
nom dérivé de l'adresse, en in-addr.arpa
ou
ip6.arpa
. Par exemple, si on veut un certificat
pour 2001:db8::1
, il faudra mettre
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa
dans le SNI.
Un défi utilisant la « résolution inverse » (via une requête DNS
dans in-addr.arpa
ou
ip6.arpa
) avait été envisagé mais n'a pas été
retenu (les domaines de la « résolution inverse » sont en général
mal maintenus et il est difficile d'obtenir des entrées dans ces
domaines.)
La section 9 de notre RFC étudie les conséquences de cette extension pour la sécurité. Le principal point à noter est que ce RFC ne spécifie qu'un mécanisme. L'AC a toute liberté pour définir une politique, elle peut par exemple refuser par principe les adresses IP dans les certificats, comme elle peut les accepter avec des restrictions ou des contrôles supplémentaires. Par exemple, il ne serait pas raisonnable d'allouer de tels certificats pour des adresses IP appartenant à des plages très dynamiques, pouvant changer d'utilisateur très souvent.
Côté mise en œuvre, pour le serveur Boulder (celui utilisé par Let's Encrypt), la discussion est ici.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : R. Shoemaker (ISRG)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF acme
Première rédaction de cet article le 1 mars 2020
Ce court RFC normalise un mécanisme d'authentification lors d'une session ACME, permettant de prouver, via TLS et ALPN, qu'on contrôle effectivement le domaine pour lequel on demande un certificat.
Le protocole ACME
(RFC 8555) laisse le choix entre plusieurs
mécanismes d'authentification pour répondre aux défis du serveur
ACME « prouvez-moi que vous contrôlez réellement le domaine pour lequel vous me
demandez un certificat ! ». Le RFC 8555 propose un défi fondé sur HTTP
(http-01
), dans sa section 8.3, et un défi
utilisant le DNS
(dns-01
), dans sa section 8.4. Notez que le
défi HTTP est fait en clair, sans HTTPS. Or, outre la sécurité
de TLS,
certains utilisateurs d'ACME auraient bien voulu une solution
purement TLS, notamment pour les cas où la terminaison de TLS et
celle de HTTP sont faites par deux machines différentes (CDN, répartiteurs de charge TLS,
etc.)
D'où le nouveau défi normalisé par ce RFC, tls-alpn-01
. Il
repose sur le mécanisme ALPN, qui avait été normalisé dans le RFC 7301. Déjà mis en œuvre dans des
AC comme Let's
Encrypt, il permet une vérification plus solide. Ce type
de défi figure maintenant dans le
registre des types de défis ACME. Notez qu'il existait déjà
un type utilisant TLS, tls-sni-01 / tls-sni-02
,
mais qui avait des
failles, autorisant un utilisateur d'un serveur TLS à obtenir
des certificats pour d'autres domaines du même
serveur. tls-sni
est aujourd'hui abandonné.
Les détails du mécanisme figurent dans la section 3 de notre
RFC. Le principe est que le serveur ACME se connectera en TLS au
nom de domaine indiqué
en envoyant l'extension ALPN avec le nom d'application
acme-tls/1
et vérifiera dans le
certificat la présence d'un
token, choisi aléatoirement par le serveur ACME,
token que le client ACME avait reçu sur le canal
ACME. (Ce nom d'application, acme-tls/1
est
désormais dans le
registre des applications ALPN.)
Bien sûr, c'est un peu plus compliqué que cela. Par exemple, comme le client ACME va devenir le serveur TLS lors du défi, il lui faut un certificat. La section 3 du RFC explique les règles auxquelles doit obéir ce certificat :
subjectAlternativeName
(RFC 5280) qui a comme valeur le nom de domaine à
valider,acmeIdentifier
(mise dans
le
registre des extensions aux certificats PKIX), qui doit
être marquée comme critique, pour éviter que des clients TLS
passant par là et n'ayant rien à voir avec ACME s'en servent, et
dont la valeur est l'autorisation ACME (RFC 8555, section 8.1).
Le client ACME doit ensuite configurer ce qu'il faut sur son serveur
TLS pour que ce soit ce certificat qui soit retourné lors d'une
connexion TLS où le SNI vaut le domaine à valider et où ALPN
vaut acme-tls/1
. Il annonce alors au serveur
ACME qu'il est prêt à répondre au défi. Le serveur ACME se connecte
au serveur TLS (créé par le client ACME) et fait les vérifications
nécessaires (nom de domaine dans le certificat, nom qui doit être un
A-label, donc en Punycode,
et extension du certificat acmeIdentifier
contenant la valeur indiquée par le serveur ACME lors du défi).
Une fois la vérification faite, le serveur ACME raccroche : ces
certificats ne sont pas conçus pour échanger de vraies données sur
la session TLS créée. D'ailleurs, les certificats auto-signés créés
pour le type de défi tls-alpn-01
ne permettent
pas d'authentification au sens du RFC 5280. Pour la même raison, le client TLS (créé par le
serveur ACME) n'est pas obligé d'aller jusqu'au bout de
l'établissement de la
session TLS.
La section 5 de notre RFC fait le point sur quelques suppositions
faites au sujet de la mise en œuvre de TLS, suppositions importantes
pour ACME. D'abord, si plusieurs organisations ou personnes
partagent la même adresse IP, ce qui est
courant en cas d'hébergement plus ou moins mutualisé, ACME compte
bien que leurs configurations TLS soient séparées, pour éviter
qu'une de ces entités puisse obtenir un certificat pour une autre,
hébergée au même endroit (cf. annexe A du RFC, qui décrit le
comportement surprenant de certains hébergeurs.) ACME espère
également que les serveurs TLS vont respecter le RFC 7301 en rejetant l'application
acme-tls/1
s'ils ne la connaissent
pas. (Certains programmeurs paresseux ont peut-être fait en sorte
que le serveur TLS accepte n'importe quelle application signalée en
ALPN.)
L'AC Let's Encrypt accepte déjà ce type de défi depuis juillet 2018. (Le RFC est en retard par rapport au déploiement effectif.) Le client ACME dehydrated permet d'utiliser le nouveau type de défi. Cet article utilise nginx côté serveur, avec son module SSL PreRead, qui permet d'aiguiller une requête en fonction de l'ALPN, mais, personnellement, je n'ai pas réussi (ça peut être un problème avec la gestion des modules dans le paquetage Debian de nginx, gestion quasiment pas documentée.)
Côté serveur, on a aussi ce qu'il faut dans Apache, avec le module
mod_md
(cf. plus précisement ce point
de la documentation.) Son utilisation est décrite
dans un article de Marc Framboisier (et sa
suite).
Côté client ACME, d'autres clients gèrent ce type de défi, mais pas encore certbot (cf. le ticket #6724 .)
Date de publication du RFC : Février 2020
Auteur(s) du RFC : A. Adamantiadis (libssh), S. Josefsson (SJD AB), M. Baushke (Juniper Networks)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 29 février 2020
Cela fait déjà pas mal de temps que des mises en œuvre du protocole SSH intègrent les courbes elliptiques « Bernstein », comme Curve25519. Ce RFC est donc juste une formalité, la normalisation officielle de cette utilisation.
SSH est normalisé dans le RFC 4251. C'est peut-être le protocole cryptographique de sécurisation d'un canal Internet le deuxième plus répandu, loin derrière TLS. Il permet de se connecter de manière sécurisée à une machine distante. En application du principe d'agilité cryptographique (RFC 7696), SSH n'est pas lié à un algorithme cryptographique particulier. Le protocole d'échange des clés, normalisé dans le RFC 4253, permet d'utiliser plusieurs algorithmes. Le RFC 5656 a étendu ces algorithmes aux courbes elliptiques.
Les courbes Curve25519 et
Curve448, créées par Daniel Bernstein, sont décrites dans le RFC 7748. Depuis des années, elles s'imposent
un peu partout, à la place des courbes
NIST comme P-256. La
libssh a ces courbes depuis des années,
sous l'ancien nom de
curve25519-sha256@libssh.org
. Notre
RFC ne fait qu'officialiser ces
algorithmes, sous les nouveaux noms de
curve25519-sha256
et
curve448-sha512
.
La section 3 du RFC décrit les détails de l'utilisation de ces algorithmes pour l'échange de clé. La méthode est proche de l'ECDH de la section 4 du RFC 5656.
Voici un exemple de session utilisant cet algorithme, avec OpenSSH 7.6 :
% ssh -v ... ... debug1: SSH2_MSG_KEXINIT sent debug1: SSH2_MSG_KEXINIT received debug1: kex: algorithm: curve25519-sha256
Date de publication du RFC : Février 2020
Auteur(s) du RFC : N. Brownlee (The University of Auckland), R. Hinden (Check Point Software)
Pour information
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
Si vous n'aimez pas les RFC de procédure, et que vous ne voulez que de la technique, rassurez-vous, ce document est court : il spécifie le rôle et les qualités de l'ISE (Independent Submission Editor), celui (ou celle) qui supervise les RFC qui ne passent pas par l'IETF. Il remplace le RFC 6548, remplacement nécessaire compte tenu de la nouvelle structure adminstrative de l'IETF, introduite par le RFC 8711.
Comment, ça existe, des RFC non-IETF ? Tous les RFC ne naissent pas au sein d'un des nombreux groupes de travail de l'IETF ? Eh oui, c'est souvent oublié, mais c'est le cas, il existe une voie indépendante qui permet à des individus de publier un RFC, quel que soit l'avis de l'IETF. Cette voie est décrite dans le RFC 4846. Autrefois, il n'existait qu'un seul RFC Editor pour examiner et préparer tous les RFC. Mais cette tâche est désormais répartie entre plusieurs éditeurs spécialisés et l'ISE est en charge de la voie indépendante.
Ce mécanisme de répartition des tâches pour les RFC est décrit dans le RFC 8728 (modèle du RFC Editor, « v2 ») et ce RFC 8730.
La section 2.1 décrit les qualifications attendues de l'ISE. Ce doit être quelqu'un d'expérimenté (senior), compétent techniquement dans les sujets traités à l'IETF (ce qui fait beaucoup !), connu et reconnu de la communauté IETF, sachant écrire et corriger, connaissant très bien l'anglais, et capable de travailler dans le milieu souvent agité de l'IETF.
Sa tâche principale (section 2.2) est de maintenir la qualité des RFC de la voie indépendante, par la relecture et l'approbation des documents, avant transmission au Producteur des RFC (RFC Production Center). Mais il doit aussi traiter les errata pour les RFC de la voie indépendante, contribuer à définir les évolutions de cette voie, interagir avec les autres acteurs des RFC, et fournir des statistiques à l'IAB et l'IETF LLC (cf. RFC 8711). Il peut se faire assister par un conseil consultatif, dont il nomme les membres « at the pleasure of the ISE », dit le RFC dans une jolie formule. Il y a aussi un conseil éditorial, également nommé par l'ISE (section 3 du RFC).
Le site officiel des RFC contient une page consacrée à la voie indépendante, si cela vous tente d'y publier.
L'ISE est nommé par l'IAB. Aujourd'hui, c'est Adrian Farrel, qui joue ce rôle.
Il y a peu de changements depuis le RFC précédent, le RFC 6548, essentiellement le remplacement de l'ancienne structure administrative de l'IETF (l'IAOC) par la nouvelle (l'IETF LLC).
Date de publication du RFC : Février 2020
Auteur(s) du RFC : R. Housley (Vigil Security), L. Daigle (Thinking Cat)
Pour information
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
Les RFC, qui incluent notamment les normes techniques de l'Internet, sont en général réalisés par l'IETF mais sont publiés par une entitée séparée, le RFC editor. Ce RFC documente les RFC, et le rôle du RFC Editor. Il remplace le RFC 4844, avec assez peu de changements, essentiellement l'adaptation à la nouvelle structure administrative de l'IETF, décrite dans le RFC 8711.
Comme pour beaucoup de choses dans l'Internet, la définition exacte des rôles des différents acteurs n'avait pas été formalisée car personne n'en ressentait le besoin. Au fur et à mesure que l'Internet devient une infrastructure essentielle, cette absence de formalisation est mal ressentie et la politique actuelle est de mettre par écrit tout ce qui était implicite auparavant.
C'est le cas du rôle du RFC Editor. Autrefois le titre d'une seule personne, Jon Postel, ce nom désigne désormais une organisation plus complexe. Relire les RFC avant publication, les corriger, leur donner un numéro et assurer leur distribution, tels sont les principaux rôles du RFC Editor.
Notre RFC décrit le but des RFC (section 2) : « documenter les normes de l'Internet, et diverses contributions ». Puis, il explique le rôle du RFC Editor, sa place dans le processus de publication et les fonctions qu'il doit assurer (le cahier des charges de l'IETF avait été publié dans le RFC 4714). Pour plus de détails sur le travail, on peut consulter le site Web du RFC Editor.
Il faut noter (section 5 du RFC) que le RFC editor reçoit des textes candidats par plusieurs voies. Si la plus connue est celle de l'IETF, il peut aussi publier des RFC d'autres origines notamment ceux des soumissions indépendantes (RFC 4846). Il est supervisé par l'IAB et par la nouvelle IETF LLC (RFC 8711.) Le fonctionnement détaillé du RFC Editor et son organisation figurent dans le RFC 8728.
Le RFC editor a une mission difficile puisqu'il doit agir avec prudence, voire avec conservatisme, pour assurer la qualité et la disponibilité des RFC pendant de très longues périodes.
Il y a peu de changements depuis le RFC précédent sur ce sujet (le RFC 4844), essentiellement le remplacement de l'ancienne IAOC par l'IETF LLC décrite dans le RFC 8711.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : O. Kolkman, J. Halpern (Ericsson), R. Hinden (Check Point Software)
Pour information
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
L'articulation compliquée entre l'IETF qui produit les normes TCP/IP et le RFC Editor qui les publie, n'a jamais cessé de faire couler de l'encre (ou d'agiter des électrons). Dans ce document, l'IAB décrit un modèle pour le RFC Editor, modèle où les fonctions de ce dernier sont éclatées en plusieurs fonctions logiques. Elles peuvent ainsi être réparties entre plusieurs groupes. Ce RFC remplace le RFC 6635, avec peu de changements : c'est toujours le modèle « v2 » du modèle RFC Editor. Il a depuis été remplacé par une version 3, assez différente, décrite dans le RFC 9280.
Le document de référence actuel est le RFC 8729. Comme le rappelle la section 1, il décrit les tâches du RFC Editor de manière globale, comme si c'était forcément une seule entité (c'était, à l'origine, une seule personne, Jon Postel). La section 2 note que la tâche de RFC Editor est actuellement une partie de l'IASA (dont la récente réforme, décrite dans le RFC 8711, a nécessité ce nouveau RFC) et financée par son budget.
La même section 2 s'attaque à la définition du rôle du RFC Editor sous forme de fonctions séparées. Elles sont ici légèrement modifiées, compte-tenu de l'expérience et des problèmes de communication rencontrés, problèmes de communication qui n'existaient évidemment pas lorsque Postel faisait tout lui-même. Le terme de RFC Editor désigne collectivement toutes ces fonctions. L'IAB voit maintenant trois fonctions (voir aussi le dessin 1 de notre RFC) :
Chaque section va ensuite détailler ces tâches. Il y a également des rôles de supervision, tenus par l'IAB (RFC 2850) et l'IETF LLC (RFC 8711).
L'Éditeur de la série des RFC (RFC Series Editor, poste actuellement vacant depuis le départ d'Heather Flanagan) est décrit en premier, en section 2.1. Il ou elle est responsable du travail à long terme, il doit définir les principes qui assurent la pérennité des RFC, réfléchir à la stratégie, développer le RFC 7322, le guide de style des RFC, de la langue des RFC, etc. Il sert aussi de « visage » aux RFC, vis-à-vis, par exemple, des journalistes. L'Éditeur est censé travailler avec la communauté IETF pour les décisions politiques. Désigné par l'IAB, il ou elle sera formellement un employé de l'IETF LLC, la structure administrative de l'IETF.
Le RFC 8728 évalue la charge de travail à un mi-temps, ce qui me semble très peu. Notre RFC décrit les compétences nécessaires (section 2.1.6) pour le poste d'Éditeur de la série des RFC, compétences en anglais et en Internet, capacité à travailler dans l'environnement... spécial de l'IETF, expérience des RFC en tant qu'auteur souhaitée, etc.
Le travail quotidien est, lui, assuré par le Producteur des RFC (RFC Production Center) dont parle la section 2.2. C'est un travail moins stratégique. Le Producteur reçoit les documents bruts, les corrige, en discute avec les auteurs, s'arrange avec l'IANA pour l'allocation des numéros de protocoles, attribue le numéro au RFC, etc.
Les RFC n'étant publiés que sous forme numérique, il n'y a pas d'imprimeur mais le numérique a aussi ses exigences de publication et il faut donc un Publieur des RFC (RFC Publisher), détaillé en section 2.3. Celui-ci s'occupe de... publier, de mettre le RFC dans le dépôt où on les trouve tous, d'annoncer sa disponibilité, de gérer l'interface permettant de soumettre les errata, de garder à jour ces errata, et de s'assurer que les RFC restent disponibles, parfois pendant de nombreuses années.
Chacune de ces fonctions pourra faire l'objet d'une attribution spécifique (à l'heure actuelle, les fonctions de Producteur et de Publieur sont assurées par le même groupe à l'AMS). La liste à jour peut être vue sur le site officiel.
Un comité joue un rôle de supervision et de contrôle du RFC Editor : le RSOC (RFC Series Oversight Committee) est décrit en section 3.
Combien cela coûte et qui choisit les titulaires des différentes fonctions ? La section 4 décrit ce rôle, dévolu à l'IETF LLC ( IETF Administration Limited Liability Company, cf. RFC 8711). Le budget est publié en sur le site de l'IETF LLC et on y trouvera aussi les futures évaluations du RFC Editor. On peut aussi trouver cette information, ainsi que plein d'autres sur le fonctionnement du RFC Editor, via le site officiel).
Comme indiqué plus haut, il n'y a pas de grand changement depuis le RFC 6635, juste le remplacement de l'ancienne IAOC par l'IETF LLC. La version 3, dans le RFC 9280, a apporté davantage de changements.
Date de publication du RFC : Novembre 2020
Auteur(s) du RFC : A. Farrel (Independent Submissions Editor)
Pour information
Première rédaction de cet article le 20 novembre 2020
Tous les RFC ne viennent pas de l'IETF. Certains sont publiés sur la voie indépendante, sous la responsabilité de l'ISE (Independent Submissions Editor). Certains de ces RFC « indépendants » créent ou modifient des registres IANA. Comment traiter ces demandes à l'IANA ?
Ce travail de l'ISE (Independent Submissions
Editor, actuellement Adrian Farrel, l'auteur de ce RFC, et
également des contes « Tales from the wood ») est
documenté dans le RFC 4846. Cet ancien RFC (il
date d'avant la création de l'ISE, qui avait été faite par le RFC 5620, puis précisée par le RFC 6548 puis enfin le RFC 8730) ne donne que peu de détails sur les relations avec
l'IANA, nécessaires si le RFC « indépendant »
demande la création d'un nouveau registre, ou la modification d'un
registre existant. Ces registres IANA sont en
, les politiques
possibles pour leur avitaillement sont dans le RFC 8126.https://www.iana.org/protocols
Pour les registres existants (section 2 du RFC), l'ISE est évidemment tenu par ce RFC 8126. Si un RFC de la voie indépendante ajoute une entrée à un registre IANA, il doit suivre la politique qui avait été définie pour ce registre. Cela va de soi. D'autre part, un RFC « indépendant » ne représente pas, par définition, l'IETF, et ne peut donc pas créer d'entrées dans un registre dont la politique est « Examen par l'IETF » ou « Action de normalisation ». Pour la même raison, un RFC de la voie indépendante ne peut pas changer la politique d'un registre qui n'avait pas été créé par un RFC de la voie indépendante (section 3).
Et la création de nouveaux registres (section 4 de notre RFC) ? En général, un RFC « indépendant » ne devrait pas le faire, puisqu'il s'agit de documents qui n'ont pas bénéficié du même examen que les RFC de la voie IETF. La seule exception est la possibilité de créer un sous-registre s'il existe un registre à la politique « ouverte » (« Spécification nécessaire », « Examen par un expert », « RFC nécessaire » ou « Premier Arrivé, Premier Servi ») et que le sous-registre correspond à une entrée ajoutée par le RFC indépendant. L'une des raisons de ce choix est d'éviter de donner trop de travail à l'IANA, en multipliant les registres.
Certaines politiques d'allocation dans un registre IANA nécessitent un expert. La section 5 de notre RFC précise que la voie indépendante ne nommera pas d'expert et que donc aucun des sous-registres éventuellement créés n'aura la politique « Examen par un expert ».
Enfin, la section 6 traite le cas du transfert du contrôle d'un registre. Il n'y aura jamais de transfert depuis la voie IETF vers la voie indépendante (une fois qu'un registre est « officiel », il le reste) mais l'inverse peut arriver, par exemple si un protocole initialement décrit dans un RFC « indépendant » passe finalement sur le chemin des normes.
Date de publication du RFC : Avril 2020
Auteur(s) du RFC : A. Minaburo (Acklio), L. Toutain (IMT-Atlantique), C. Gomez (Universitat Politecnica de Catalunya), D. Barthel (Orange Labs), JC. Zuniga (SIGFOX)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lpwan
Première rédaction de cet article le 16 avril 2020
Ce nouveau RFC décrit un mécanisme de compression des données, et de fragmentation des paquets, optimisé pour le cas des réseaux à longue distance lents connectant des objets contraints en ressources, ce qu'on nomme les LPWAN (Low-Power Wide Area Network, par exemple LoRaWAN.) L'un des buts est de permettre l'utilisation des protocoles Internet normaux (UDP et IPv6) sur ces LPWAN. SCHC utilise un contexte de compression et décompression statique : il n'évolue pas en fonction des données envoyées.
Le LPWAN (RFC 8376) pose en effet des défis particuliers. Il a une faible capacité, donc chaque bit compte. Il relie des objets ayant peu de ressources matérielles (par exemple un processeur très lent). Et la batterie n'a jamais assez de réserves, et émettre sur un lien radio coûte cher en énergie (1 bit reçu ou transmis = 1-1000 microjoules, alors qu'exécuter une instruction dans le processeur = 1-100 nanojoules.) . Le but de l'IETF est de pouvoir utiliser IPv6 sur ces LPWAN et la seule taille de l'en-tête IPv6 est un problème : 40 octets, dont plusieurs champs « inutiles » ou redondants, et qui auraient donc tout intérêt à être comprimés.
Trois choses importantes à retenir sur SCHC (Static Context Header Compression) :
Si les LPWAN posent des problèmes particuliers, vu le manque de ressources disponibles, ils ont en revanche deux propriétés qui facilitent les choses :
La section 5 de notre RFC expose le fonctionnement général de SCHC. SCHC est situé entre la couche 3 (qui sera typiquement IPv6) et la couche 2, qui sera une technologie LPWAN particulière, par exemple LoRaWAN (pour qui SCHC a déjà été mis en œuvre, et décrit dans le RFC 9011). Après compression, le paquet SCHC sera composé de l'identificateur de la règle (RuleID), du résultat de la compression de l'en-tête, puis de la charge utile du paquet. Compresseur et décompresseur doivent partager le même ensemble de règles, le contexte. Le contexte est défini de chaque côté (émetteur et récepteur) par des mécanismes non spécifiés, par exemple manuellement, ou bien par un protocole d'avitaillement privé. Chaque règle est identifiée par son RuleID (section 6 du RFC), identificateur dont la syntaxe exacte dépend d'un profil de SCHC donc en pratique du type de LPWAN. (Rappelez-vous que SCHC est un mécanisme générique, les détails concrets de syntaxe sont spécifiés dans le profil, pas dans SCHC lui-même. Par exemple, pour LoRaWAN, c'est dans le RFC 9011.)
Les règles sont expliquées dans la section 7. Chaque règle comporte plusieurs descriptions. Chaque description comprend notamment :
L'algorithme est donc : pour chaque description dans une règle, voir si elle correspond au champ visé, en utilisant l'opérateur de comparaison et la valeur cible. Si toutes les descriptions collent, appliquer les actions, et envoyer les données comprimées, précédées de l'identificateur de la règle. Un RuleID spécial est utilisé pour attraper tout le reste, les paquets qui ne seront pas comprimés car aucune règle ne correspondait.
Prenons un exemple : la règle de RuleID 1 a
deux descriptions, qui disent
que les champs X et Y de l'en-tête ont une valeur connue (indiquée
dans la valeur cible), mettons respectivement 42 et « foobar ». Dans
ce cas, les actions de compression (CDA, Compression
Decompression Action) peuvent être simplement « omets ces
champs » (not-sent
). Le décompresseur, à
l'autre bout, a la même règle (rappelez-vous que le contexte,
l'ensemble des règles, est statique). Lorsque qu'il voit passer un
paquet comprimé avec la règle 1, il crée simplement les deux champs,
avec les valeurs définies dans la règle (valeurs TV, Target
Value.)
Un exemple figure en section 10 du RFC, avec des règles pour
comprimer et décomprimer les en-têtes IPv6 et UDP. Ainsi, le champ
Version d'IPv6 vaut forcément 6. On met donc la valeur cible (TV) à
6, l'opérateur de comparaison (MO) à « ignorer » (on ne teste pas
l'égalité, on est sûr d'avoir 6, si le paquet est correct), et
l'action (CDA) à not-sent
(ne pas envoyer). Le
champ Longueur, par contre, n'a pas de valeur cible, l'opérateur de
comparaison est « ignorer », et l'action est
compute
(recalculer à partir des données).
Pour UDP, on peut également omettre les ports source et destination si, connaissant l'application, on sait qu'ils sont fixes et connus, et on peut également recalculer le champ Longueur, ce qui évite de le transmettre. La somme de contrôle est un peu plus compliquée. IPv6 en impose une (RFC 8200, section 8.1) mais autorise des exceptions. Ne pas l'envoyer peut exposer à des risques de corruption de données, donc il faut bien lire le RFC 6936 et le RFC 6282 avant de décider d'ignorer la somme de contrôle. Les règles complètes pour UDP et IPv6 sont rassemblées dans l'annexe A du RFC.
Outre la compression, SCHC permet également la fragmentation (section 8 du RFC). La norme IPv6 (RFC 8200) dit que tout lien qui fait passer de l'IPv6 doit pouvoir transmettre 1 280 octets. C'est énorme pour du LPWAN, où la MTU n'est parfois que de quelques dizaines d'octets. Il faut donc effectuer fragmentation et réassemblage dans la couche 2, ce que fait SCHC (mais, désolé, je n'ai pas creusé cette partie, pourtant certainement intéressante .)
La section 12 de notre RFC décrit les conséquences de SCHC sur la sécurité. Par exemple, un attaquant peut envoyer un paquet avec des données incorrectes, dans l'espoir de tromper le décompresseur, et, par exemple, de lui faire faire de longs calculs, ou bien de générer des paquets de grande taille, pour une attaque avec amplification. Comme toujours, le décompresseur doit donc se méfier et, entre autres, ne pas générer de paquets plus grands qu'une certaine taille. Question sécurité, on peut aussi noter que SCHC n'est pas vulnérable aux attaques comme CRIME ou BREACH, car il traite les différents champs de l'en-tête séparément.
La fragmentation et le réassemblage amènent leurs propres risques, qui sont bien connus sur l'Internet (d'innombrables failles ont déjà été trouvées dans les codes de réassemblage de paquets fragmentés.) Par exemple, une attaque par déni de service est possible en envoyant plein de fragments, sans jamais en envoyer la totalité, forçant le récepteur à consommer de la mémoire pour stocker les fragments en attente de réassemblage. Là encore, le récepteur doit être prudent, voire paranoïaque, dans son code. Par contre, les attaques utilisant la fragmentation pour se dissimuler d'un IDS ne marcheront sans doute pas, puisque SCHC n'est utilisé qu'entre machines directement connectées, avec probablement aucun IDS sur le lien.
Merci à Laurent Toutain pour avoir attrapé une sérieuse erreur dans cet article et à Dominique Barthel pour sa relecture très attentive.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : D. McPherson, O. Kolkman (ISOC), J.C. Klensin, G. Huston (APNIC), Internet Architecture Board
Pour information
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
Ce RFC officiel de l'IAB décrit le rôle de l'opérateur des registres des protocoles utilisé par l'IETF. Des tas de protocoles normalisés par cet organisme ont besoin de garder une trace des noms ou numéros réservés (par exemple les numéros de port de TCP ou UDP, les numéros d'options DHCP, etc.) C'est le rôle de l'opérateur du registre qui garde ces réservations (aujourd'hui, essentiellement l'IANA). Ce RFC remplace le RFC 6220, suite à la création de la nouvelle structure administrative de l'IETF, dans le RFC 8711.
L'IETF n'assure pas ce rôle d'opérateur du registre elle-même. Elle se focalise sur la production de normes (les RFC) et délègue la gestion du registre. Pourquoi les valeurs en question ne sont-elles pas directement mises dans les RFC ? Parce qu'elles évoluent plus vite que la norme elle-même. Ainsi, l'enregistrement d'un nouveau type d'enregistrement DNS est un processus bien plus souple que de modifier un RFC et la liste de tels types ne peut donc pas être figée dans un RFC (ceux-ci ne sont jamais modifiés, seulement remplacés, et cela n'arrive pas souvent).
Mais on ne peut pas non plus laisser chacun définir ses propres paramètres, car cela empêcherait toute interprétation commune. D'où cette idée d'un registre des paramètres. Les règles d'enregistrement dans ce registre, la politique suivie, sont décrites pour chaque norme dans la section IANA considerations du RFC, en utilisant le vocabulaire et les concepts du RFC 8126 (pour les types d'enregistrements DNS, cités plus haut, les détails sont dans le RFC 6895).
Plusieurs autres SDO suivent ce même principe de séparation entre la normalisation et l'enregistrement (en revanche, les groupes fermés d'industriels qui tentent d'imposer leur standard ne séparent pas en général ces deux fonctions). Par exemple, l'ISO définit, pour la plupart de ses normes, une Registration Authority ou bien une Maintenance Agency qui va tenir le registre. (Exemples : l'opérateur du registre de ISO 15924 est le consortium Unicode et celui du registre de ISO 639 est SIL. Contre-exemple : l'opérateur du registre de ISO 3166 est l'ISO elle-même.) Pourquoi cette séparation ? Les raisons sont multiples mais l'une des principales est la volonté de séparer la politique de base (définie dans la norme) et l'enregistrement effectif, pour gérer tout conflit d'intérêts éventuel. Un opérateur de registre séparé peut être plus indépendant, afin de ne pas retarder ou bloquer l'enregistrement d'un paramètre pour des raisons commerciales ou politiques. Notons aussi que bien d'autres fonctions liées à l'IETF sont également assurées à l'extérieur, comme la publication des RFC.
Contre-exemple, celui du W3C, qui utilise très peu de registres et
pas d'opérateur de registre officiel séparé. En pratique, c'est
l'IANA qui gère plusieurs registres Web, comme celui des
URI
bien
connus (RFC 8615), celui des types
de fichiers (comme application/pdf
ou
image/png
), celui des en-têtes
(utilisés notamment par HTTP), etc. En
dehors de l'IANA, le W3C a quelques registres gérés en interne
comme celui de
Xpointer. Pour le reste, la politique du W3C est plutôt
qu'un registre est un point de passage obligé et que de tels
points ne sont pas souhaitables.
Dans le cas de l'IETF, les documents existants sont le RFC 2026, qui décrit le processus de
normalisation mais pas celui d'enregistrement. Ce dernier est
traditionnellement connu sous le nom de « fonction IANA » (d'où la
section IANA considerations des RFC) même si,
en pratique, ce n'est pas toujours l'IANA qui l'effectue. (Les
registres de l'IANA sont en https://www.iana.org/protocols/
.)
La section 2 du RFC expose donc le rôle et les responsabilités
du(des) opérateur(s) de registres de paramètres. Celui(ceux)-ci,
nommés avec majuscules IETF Protocol Parameter Registry
Operator, seront choisis par l'IETF LLC
(RFC 8711). J'ai mis le pluriel car
l'IANA n'assure pas actuellement la totalité
du rôle : il existe d'autres opérateurs de registres, en général
sur des tâches très secondaires comme par exemple le RIPE-NCC pour
l'enregistrement en
e164.arpa
(ENUM, cf. RFC 6116). Dans le futur, on pourrait imaginer un rôle
moins exclusif pour l'IANA.
La section 2.1 est la (longue) liste des devoirs qu'implique le rôle d'opérateur de registre. Il y a bien sûr le tenue du registre à proprement parler mais ce n'est pas tout. En voici une partie :
https://www.iana.org/assignments/bootp-dhcp-parameters/
.Après cette description des devoirs de l'opérateur du registre, la section 2 continue avec les devoirs des autres acteurs. En section 2.2, ceux de l'IAB, qui supervise l'opérateur du registre : l'IAB procède à la délégation formelle du registre, après que l'IETF LLC ait identifié les candidats. L'IAB décide, l'IETF LLC gère la relation avec l'opérateur.
En section 2.3, le rôle de l'IESG : celui-ci s'occupe de la partie technique, vérifier que les RFC comportent une bonne section IANA considerations, identifier les experts techniques si le RFC précise que l'enregistrement est précédé d'une évaluation technique (exemple : le RFC 5646, où l'enregistrement d'une langue dans le registre des langues est précédé d'une telle évaluation par un linguiste), répondre aux questions de l'opérateur du registre si celui-ci a un problème pratique.
En section 2.4, le rôle de l'IETF Trust (RFC 4371). Il gère la propriété intellectuelle de l'IETF donc est « propriétaire » du contenu des registres. Enfin, en section 2.5, le rôle de l'IETF LLC, bras administratif de l'IETF, qui est de gérer au quotidien les relations avec l'opérateur du registre. (C'est la principale nouveauté de ce RFC, par rapport au RFC 6220, que le remplacement de l'ancienne structure par cette IETF LLC.)
Voilà, l'essentiel était là mais la section 3 rajoute quelques détails.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : J. Halpern (Ericsson)
Pour information
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
Une fois les droits de publication, et de modification, offerts par le(s) auteur(s) d'un RFC à l'IETF trust, quels droits ce dernier va-t-il transmettre aux lecteurs et lectrices d'un RFC ? Le RFC 5378 spécifie les droits « entrants » à l'IETF trust, et notre RFC 8721 spécifie les droits sortants : que puis-je faire avec un RFC ? Ai-je le droit de le lire ? De le redistribuer ? De le traduire ? Ce RFC est une légère modification du RFC 5377, qu'il remplace. Le but de cette modification était de s'adapter à la nouvelle structure administrative de l'IETF, décrite dans le RFC 8711. Notamment, l'ancien IAOC (RFC 4071) disparait.
La section 1 du RFC rappelle un point important : c'est
l'IETF trust qui décide. Le RFC 8721, publié par l'IETF, n'est qu'indicatif et ne fixe que des
grands principes. Le texte exact de la licence des RFC est écrit
par l'IETF trust (http://trustee.ietf.org/license-info/
et il existe aussi
une FAQ sur
ces textes.) La section 2 revient d'ailleurs sur les raisons de ce
choix (pouvoir s'adapter aux changements légaux aux ÉUA, pays de
l'IETF trust et de l'ISOC).
On pourra trouver ce texte standard, ce boilerplate, sur le site du Trust dans la Trust Legal Provisions.
La section 2 du RFC décrit les buts que suit l'IETF en publiant des RFC (cf. RFC 3935). Clairement, l'élaboration d'une licence doit se faire en gardant ces buts à l'esprit : faire fonctionner l'Internet le mieux possible, notamment en assurant l'interopérabilité des mises en œuvres des protocoles TCP/IP.
La section 3 explique l'articulation de ce RFC avec le RFC 5378 : les droits sortants (ceux que l'IETF trust accorde aux utilisateurs) doivent être inférieurs ou égaux aux droits entrants (ceux que l'auteur a accordé à l'IETF trust). Autrement dit, l'IETF ne peut pas donner de droits qu'elle n'a pas. Si l'auteur d'un RFC interdit toute modification à son texte, le RFC publié ne permettra pas de modifications (et ne pourra d'ailleurs pas être publié sur le chemin des normes).
La section 4 s'attaque aux droits que l'IETF trust devrait donner aux utilisateurs :
Comme indiqué plus haut, il n'y a pas de changements de fond depuis le RFC 5377, uniquement la suppression des références à l'ancien IAOC (IETF Administrative Oversight Committee).
Date de publication du RFC : Février 2020
Auteur(s) du RFC : R. Housley (Vigil Security), O. Kolkman (Internet Society)
Pour information
Première rédaction de cet article le 28 février 2020
Traditionnellement, le fonctionnement de la normalisation dans l'Internet sépare deux fonctions : l'IETF développe les normes techniques (qui sont publiées dans les RFC, documents immuables) et un opérateur de registres techniques, actuellement l'IANA, gère les nombreux registres qui stockent les paramètres des protocoles de l'IETF. Au contraire des RFC, ces registres changent tout le temps. Ce nouveau RFC décrit les principes de haut niveau sur lesquels repose le fonctionnement de ces registres. Il remplace le RFC 7500, sans changement crucial, juste en intégrant la nouvelle organisation administrative de l'IETF, qui voit le remplacement de l'IAOC par l'IETF LLC.
Ces registres sont cruciaux pour le bon fonctionnement de
l'Internet. Presque tous les protocoles Internet dépendent d'un ou
de plusieurs registres. Aujourd'hui, il existe plus de 2 000 registres à l'IANA (la liste complète est en https://www.iana.org/protocols
). Les valeurs stockées
peuvent être des nombres, des chaînes de caractères, des adresses,
etc. Leur allocation peut être centralisée
(tout est à l'IANA) ou décentralisée, avec
l'IANA déléguant à des registres qui à leur tour délèguent, comme
c'est le cas pour les noms de
domaine et pour les adresses IP.
Notons aussi que, pendant longtemps, la gestion des registres et
la publication des RFC étaient assurés par le même homme, Jon Postel à l'ISI. Aujourd'hui, les deux opérations sont
complètement séparées, https://www.iana.org/
et https://www.rfc-editor.org/
.
L'IANA n'a pas de pouvoirs de police : le respect des registres IANA dépend uniquement de la bonne volonté générale. Évidemment, la pression est forte pour respecter ces registres, puisque le bon fonctionnement de l'Internet en dépend. Ce RFC présente les principes sur lesquels ces registres reposent (sections 2 et 3 du RFC) :
.fr
ou
.org
, ou que
l'algorithme 13 de DNSSEC désigne bien ECDSA
pour tout le monde.La section 5 décrit le changement depuis le prédécesseur, le RFC 7500. Il ne s'agit que d'un changement bureaucratique ; l'ancienne IAOC (IETF Administrative Oversight Committee) a été, dans le RFC 8711, remplacée par une nouvelle structure, l'IETF LLC (Limited Liability Company), nécessitant la mise à jour du RFC 7500.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : S. Krishnan (Kaloom)
Réalisé dans le cadre du groupe de travail IETF mtgvenue
Première rédaction de cet article le 28 février 2020
Le groupe mtgvenue de l'IETF était chargé de définir une politique pour le choix des lieux pour les réunions physiques de l'IETF. Ce nouveau RFC pose le principe général d'une rotation entre divers continents. Le RFC 8718 décrit ensuite les détails.
L'IETF a trois réunions physiques par an. L'idée est d'avoir en moyenne (mais pas forcément pour chaque année) une réunion en Amérique du Nord, une en Europe et une en Asie (cf. cette présentation de la politique « 1-1-1 ».) Le but est de répartir les coûts et temps de voyage entre les différents participants. Cette politique était jusqu'à présent informelle, ce RFC la documente officiellement. Elle se base sur la répartition constatée des participants à l'IETF. Notez que les frontières exactes des continents sont volontairement laissées floues, pour ne pas se lier les mains dans certains cas (demandez-vous si le Mexique est en Amérique du Nord, ou si Chypre est en Europe ou en Asie…)
Certaines réunions, dites « exploratoires » peuvent se tenir dans d'autres continents. L'IETF a fait une fois une réunion à Buenos Aires, et une fois à Adelaide.
Sur la base de cette politique « 1-1-1 », les décisions effectives seront prises en suivant le RFC 8718. Et, bien sûr, la politique n'est pas 100 % rigide. L'Internet change, les participants à l'IETF également, et d'autres lieux deviendront envisageables.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : E. Lear (Cisco Systems)
Réalisé dans le cadre du groupe de travail IETF mtgvenue
Première rédaction de cet article le 28 février 2020
La question de la sélection du lieu pour les réunions physiques de l'IETF a toujours suscité des discussions passionnées, d'autant plus que chaque participant, et son chat, a un avis. Il y a de nombreux critères (coût, distance, agréments du lieu, goûts personnels…) Ce nouveau RFC décrit le nouveau mécanisme par lequel est sélectionné un lieu, et les critères à respecter.
En théorie, venir aux réunions physiques n'est pas indispensable. L'IETF, organisme de normalisation technique de l'Internet, travaille, étudie les propositions, les amende, les approuve ou les rejette entièrement en ligne (avec l'aide de quelques outils comme le DataTracker). Ceci dit, en pratique, venir aux trois réunions physiques par an aide certainement à faire avancer son sujet favori. Ces réunions rassemblent des centaines de participants IETF pendant une semaine et sont l'occasion de nombreuses discussions. À noter que, pendant la réunion, une liste de discussion permet également des bavardages généralisés, où les conseils sur les bons bars et restaurants rencontrent les avis sur la meilleure route vers l'aéroport, et des récriminations sur les ascenseurs, l'air conditionné, le manque de cookies, etc. Ce RFC sort d'ailleurs au moment exact où l'IETF se déchire sur l'opportunité ou non de maintenir la réunion de Vancouver malgré l'épidémie.
La reponsabilité du choix d'un lieu pour les réunions incombe à l'IASA (IETF Administrative Support Activity, RFC 8711). Ce RFC ne lui supprime pas cette responsabilité mais lui fournit des éléments à prendre en compte. L'IETF a trois rencontres physiques par an, s'inscrivant dans le cadre de ses missions, qui sont décrits dans le RFC 3935. (Notez que certains participants contestent ces rencontres physiques, considérant qu'une organisation de normalisation des protocoles Internet pourrait se « réunir » en ligne.) La section 2 du RFC décrit les principes fondamentaux à prendre en compte pour le choix du lieu de la réunion :
En revanche, la même section 2 cite des non-objectifs :
Compte-tenu de ces objectifs de haut niveau, la section 3 du RFC liste les critères. D'abord, ceux qui sont impératifs :
Il y a ensuite les critères importants. Évidemment, l'idéal serait de trouver un endroit qui satisfasse tous les critères mais, en général, ce n'est pas possible. Le choix sera toujours un compromis. Il faut donc déterminer quels critères on peut accepter de sacrifier, et c'est la raison pour laquelle il n'y a finalement que trois critères impératifs. Les critères importants mais non impératifs sont :
Il faut se rappeler que l'IETF est internationale, et que la notion de « lieu éloigné » dépend donc du participant, c'est le coût total de voyage qui compte, pas celui de telle ou telle personne, d'où la politique « 1-1-1 », une réunion en Amérique du Nord, puis une en Europe, puis une en Asie, politique qui vise à répartir les coûts sur les divers participants
Pour l'endroit exact de la réunion (en général un grand hôtel), il y a des critères supplémentaires :
Dans le cas typique, l'IETF se tient dans un grand hôtel pour hommes d'affaires riches. (La dernière (au moment de l'écriture de cet article) réunion, à Montréal, était au Fairmont.) Et les participants au budget voyage le plus important logent sur place. Il existe aussi des overflow hotels où logent les moins riches. Dans l'hôtel principal et, parfois, dans les overflow hotels, on dispose du réseau IETF même dans sa chambre. Ce réseau est installé par une équipe de volontaires de l'IETF avant la réunion (les réseaux des hôtels sont en général catastrophiques, pas d'IPv6, pas de DNSSEC, des ports filtrés, etc, une réunion à Paris avait battu les records). Beaucoup de participants vont chez Airbnb ou carrément en auberge de jeunesse et doivent alors se débrouiller pour le réseau.
Les ingénieurs et ingénieures sont des êtres humains et doivent donc manger. D'où des critères concernant nourriture et boisson :
Enfin, il y a des critères sur des points variés :
Même si tous ces critères ne sont pas impératifs, mis ensemble, ils sont très exigeants. Des gens demandent parfois pourquoi, par exemple, il n'y a jamais eu de réunion de l'IETF en Inde, pays certainement important pour l'Internet. C'est entre autres en raison de spécificités locales, par exemple la difficulté d'obtention des visas. (Cf. cette discussion.)
On notera que les critères sont finalement peu nombreux. De très nombreuses autres propositions avaient été faites, qui risquaient de mener à un catalogue de critères, très épais et, de toute façon, impossibles à satisfaire simultanément. Le rapport « droits humains » notait que certains critères pouvaient être incompatibles entre eux (une personne aveugle voudrait pouvoir être accompagnée de son chien guide, et une autre personne est allergique aux chiens, voire en a une phobie.)
C'est ainsi que des critères comme la disponibilité d'une crèche ont été retirés (notez que le RIPE le fait et que l'IETF va tenter le coup), de même que la demande de toilettes non genrées, qui était dans certaines versions du projet de RFC.
Comme vous vous en doutez, la discussion à l'IETF sur ce RFC n'a pas été évidente. De toute façon, la décision sur le choix d'un lieu reviendra à l'IASA, sauf pour les critères impératifs, qui nécessiteront une discussion générale.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : P. Resnick (Episteme Technology Consulting LLC), A. Farrel (Old Dog Consulting)
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
Les procédures de l'IETF en cas de harcèlement lors des réunions physiques ou en ligne sont décrites dans le RFC 7776. Ce RFC 7776 reste d'actualité, il n'est modifié que sur un point : le remplacement de l'ancienne IAOC (IETF Administrative Oversight Committee) par les institutions du projet « IASA version 2 », décrit dans le RFC 8711.
On peut résumer notre nouveau RFC 8716 simplement : partout où, dans le RFC 7776, il y a écrit IAOC, remplacer par IETF LLC (IETF Administration Limited Liability Company, cf. RFC 8711), et partout où il y a écrit IAD (IETF Administrative Director), remplacer par IETF Executive Director. C'est tout.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : J. Arkko (Ericsson)
Pour information
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
Le passage de la structure administrative de l'IETF vers « IASA 2.0 » (IASA = IETF Administrative Support Activity, cf. RFC 8711) a nécessité des changements dans la manière dont les membres de l'IETF Trust sont désignés. Ces changements sont décrits dans le RFC 8714, et expliqués brièvement dans ce court RFC.
Petit rappel : l'IETF Trust gère la
propriété intellectuelle de l'IETF, comme la
marque ou comme le nom de domaine ietf.org
et, bien sûr, comme les RFC, dont la licence dépend
formellement de cet IETF Trust. Cet IETF
Trust est enregistré
aux États-Unis. Au début, les membres de l'IAOC (IETF
Administrative Oversight Committee) étaient également
membres de l'IETF Trust. L'IAOC ayant été
supprimé par le RFC 8711, il fallait
donc changer les règles de désignation, ce qu'a fait le RFC 8714.
Lors des discussions sur la création de l'« IASA 2 » (IETF Administrative Support Activity, deuxième version), il avait été envisagé de fusionner l'IETF Trust avec la nouvelle structure, l'IETF LLC. Finalement, l'IETF Trust reste une organisation indépendante. Les raisons ? D'abord, une volonté de minimiser les changements liés au projet « IASA 2 », ensuite, l'observation du fait que le travail de l'IETF Trust est assez différent de celui de la IETF LLC (décrite dans le RFC 8711). L'IETF Trust a une activité calme, avec peu ou pas de problèmes urgents à résoudre, et les changements sont rares.
Mais comme il fallait bien tenir compte de la disparition de l'IAOC, le choix a été de réduire la taille de l'IETF Trust, plutôt que de créer un mécanisme alternatif à l'IAOC (puisque, comme indiqué plus haut, l'IETF Trust ne demande pas beaucoup de travail). Les cinq membres sont désignés par le comité de nomination de l'IETF (« NomCom »), par l'IESG et par l'ISOC. Pour ce dernier cas, le RFC note qu'on aurait pu utiliser l'IETF LLC et pas l'ISOC, mais que l'ISOC semblait plus adaptée, pour une tâche qui est assez politique (l'IETF LLC est normalement purement administrative).
Sinon, vous pouvez voir ici un appel à candidatures du NomCom pour l'IETF Trust.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : J. Arkko (Ericsson), T. Hardie
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
Un RFC purement bureaucratique, faisant partie de la série des RFC sur la nouvelle structure administrative de l'IETF, décrite dans le RFC 8711. Celui-ci change légèrement la manière dont sont désignés les membres de l'IETF Trust, la structure qui gère la propriété intellectuelle de l'IETF.
Cet IETF Trust avait été créé pour s'occuper de toutes les questions liées aux marques, noms de domaine, droits sur les RFC, etc. C'était aussi la seule solution pour récupérer partiellement des droits qui avaient été détournés par CNRI. Il était spécifié dans le RFC 4371 que les membres de l'IAOC (IETF Administrative Oversight Committee) étaient automatiquemement membres de l'IETF trust.
La section 3 de notre RFC contient le changement important : comme la nouvelle structure administrative de l'IETF, décrite dans le RFC 8711 supprime l'ancienne IAOC, les membres de l'IETF trust ne seront plus issus de l'IAOC. Les cinq membres de l'IETF Trust sont désignés, pour trois d'entre eux par le comité de nomination de l'IETF (RFC 8713), pour l'un par l'IESG et pour le dernier par l'ISOC. Leur mandat est de trois ans mais le RFC prévoit des mandats plus courts pour certains, pour lisser la transition.
Un exemple du processus de sélection des trois membres « NomCom » (comité de nomination) se trouve en ligne. Regardez l'appel aux nominations (ou bien celui-ci). Comme vous le voyez avec le premier exemple, le processus de remplacement de l'ancien IETF trust a été fait bien avant la publication formelle de ce RFC.
Les raisons de ce changement sont décrites plus longuement dans le RFC 8715.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : M. Kucherawy, R. Hinden (Check Point Software), J. Livingood (Comcast)
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
Voici un nouveau RFC « bureaucratique » autour des processus menant au choix et à la désignation des membres d'un certain nombre d'organismes de la galaxie IETF, comme l'IAB ou l'IESG. Ce RFC remplace le RFC 7437, mais il y a peu de changements ; les principaux portent sur les conséquences de la nouvelle structure administrative de l'IETF, « IASA 2 », décrite dans le RFC 8711.
Ce RFC concerne la désignation des membres de l'IAB, de l'IESG et de certains membres de la IETF LLC (voir la section 6.1 du RFC 8711) et de l'IETF Trust. Il ne concerne pas l'IRTF et ses comités propres. Il ne concerne pas non plus le fonctionnement quotidien de ces comités, juste la désignation de leurs membres.
Le processus tourne autour d'un comité nommé NomCom (pour Nominating Committee, comité de nomination).Comme expliqué en section 2, il faut bien différencier les nommés (nominee), les gens dont les noms ont été soumis au NomCom pour occuper un poste à l'IAB, l'IESG, à l'IETF LLC ou à l'IETF Trust, des candidats (candidate) qui sont les gens retenus par le NomCom. Le NomCom, comme son nom l'indique, n'a pas de pouvoir de désignation lui-même, celle-ci est décidée (on dit officiellement « confirmée ») par un organisme différent pour chaque comité (l'IAB pour l'IESG, l'ISOC pour l'IAB, l'IESG pour l'IETF Trust, etc). Une fois confirmé, le candidat devient... candidat confirmé (confirmed candidate). Et s'il n'est pas confirmé ? Dans ce cas, le NomCom doit se remettre au travail et proposer un ou une autre candidat·e.
La section 3 de notre RFC explique le processus général : il faut désigner le NomCom, le NomCom doit choisir les candidats, et ceux-ci doivent ensuite être confirmés. Cela peut sembler compliqué, mais le but est d'éviter qu'une seule personne ou une seule organisation puisse mettre la main sur l'IETF. Le processus oblige à travailler ensemble.
À première vue, on pourrait penser que le NomCom a un vaste pouvoir mais, en fait, au début du processus, il ne peut pas décider des postes vacants, et, à sa fin, il n'a pas le pouvoir de confirmation.
Un point important et souvent oublié est celui de la confidentialité (section 3.6 du RFC). En effet, l'IETF se vante souvent de sa transparence, tout doit être public afin que chacun puisse vérifier que tout le processus se déroule comme prévu. Mais le travail du NomCom fait exception. Toutes ses délibérations, toutes les informations qu'il manipule, sont confidentielles. Autrement, il serait difficile de demander aux personnes nommées de fournir des informations personnelles, et les personnes extérieures au NomCom qui sont interrogées hésiteraient à s'exprimer franchement sur tel ou tel candidat. Et la publicité des débats risquerait d'encourager des campagnes de soutien extérieures au NomCom, et du lobbying, toutes choses qui sont formellement interdites. La section 9, sur la sécurité, revient sur cette importance de la confidentialité : puisque le NomCom enquête littéralement sur les nommés, il peut récolter des informations sensibles et il doit donc faire attention à les garder pour lui.
Le résultat est annoncé publiquement. Voici, par exemple, l'annonce de la sélection des membres de l'IESG, début 2019.
Et le NomCom lui-même, comment est-il choisi (section 4) ? De ses quatorze membres, seuls dix ont le droit de vote. D'abord, les dix membres du NomCom votants doivent répondre à un certain nombre de critères (section 4.14) : ils doivent avoir été physiquement présents à trois des cinq précédentes réunions de l'IETF (c'est une des exceptions au principe comme quoi la participation à l'IETF n'impose pas de venir aux réunions physiques), et c'est vérifié par le secrétariat de l'IETF (chaque participant peut facilement voir sur sa page sur le Datatracker s'il est éligible ou pas.) Et ils doivent (évidemment), être très familiers avec les processus internes de l'IETF. Une fois qu'on a un ensemble (pool) de volontaires qui acceptent de participer au NomCom (voyez un appel à volontaires typique), comment choisit-on les dix membres de plein exercice ? Eh bien, c'est là que c'est amusant, ils sont choisis au hasard... Il n'existe en effet pas de critères consensuels sur la meilleure méthode de choix des membres du NomCom (rappelez-vous qu'à l'IETF, on ne peut pas voter, puisqu'il n'y a pas de notion de « membre » et donc pas de corps électoral rigoureusement défini). Le tirage au sort se fait selon la méthode, ouverte et publiquement vérifiable, spécifiée par le RFC 3797. Voici par exemple les sources de données aléatoires pour 2018 et un exemple de résultat.
Une fois sélectionnés, les membres du NomCom se mettent au travail (section 5 du RFC). Ils ne sont bien sûr pas éligibles pour les postes qu'il vont devoir pourvoir. Lorsqu'ils doivent prendre une décision, le NomCom vote (une procédure rare à l'IETF). Les nominations peuvent être faites par n'importe quel participant à l'IETF, y compris le nommé lui-même. La décision de retenir tel ou tel nommé comme candidat doit s'appuyer sur sa connaissance de l'IETF et ses qualifications pour le poste (qui ne sont pas forcément les mêmes pour tous les comités : par exemple, l'IETF LLC nécessite des compétences administratives qui sont moins importantes à l'IAB). L'IETF étant une organisation de grande taille, le NomCom ne connait pas forcément tout le monde, et peut donc aller à la « pêche aux informations » en consultant des gens extérieurs sur tel ou tel nommé.
Le « mandat » typique dure deux ans (trois à l'IETF LLC et au Trust). Il n'y a pas de limite au nombre de « mandats » mais le NomCom peut évidemment décider de donner la priorité aux nommés qui n'ont pas encore eu de mandat, ou pas encore effectué beaucoup de mandats.
Les informations récoltées par le NomCom, et ses discussions sont archivées (mais non publiques : voir plus haut au sujet de la confidentialité). Ces archives sont directement utiles s'il faut, par exemple, remplir un poste et qu'on ne veut pas recommencer le processus de zéro pour certains nommés.
Les humains étant ce qu'ils sont, il y aura des désaccords en interne. Comment le NomCom gère-t-il les contestations (section 6) ? Idéalement, le NomCom doit essayer de les régler tout seul (ne serait-ce que pour préserver la confidentialité déjà mentionnée). Si cela ne marche pas, le problème est transmis à l'ISOC, qui nomme un arbitre, dont les décisions sont définitives (pas d'appel).
J'ai parlé ici surtout de pourvoir des postes, mais il peut aussi y avoir révocation (recall, section 7) d'un membre d'un des comités concernés. Cette révocation peut être demandé par au moins vingt participants à l'IETF, qui doivent être éligibles au NomCom, à l'ISOC. Un Recall Committee est alors créé, et peut décider à la majorité des trois quarts d'une révocation, sur la base des griefs présentés par les signataires de la demande de révocation.
Bien des choses au sujet du NomCom ne sont pas écrites, et la tradition orale joue un rôle important dans son fonctionnement. L'annexe C rassemble plusieurs grains de sagesse issus de cette tradition. Par exemple, avoir été président d'un groupe de travail IETF est considéré comme une bonne préparation à l'IESG. Il y a aussi des considérations sur l'équilibre global entre les membres d'un comité. Il ne s'agit pas seulement que chaque membre soit individuellement bon, il faut aussi que le comité rassemble des gens de perspectives différentes (âge, expérience, région d'origine, monde académique vs. entreprises à but lucratif, etc). La tradition orale recommande aussi d'éviter qu'une même organisation n'occupe trop de postes dans un comité. Même si les gens de cette organisation ne forment pas un bloc, l'impression donnée serait mauvaise pour l'IETF.
L'annexe B de notre RFC contient les changements depuis le RFC 7437. Rien de crucial, mais on notera :
Date de publication du RFC : Février 2020
Auteur(s) du RFC : G. Camarillo (Ericsson), J. Livingood (Comcast)
Pour information
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
L'IETF, l'organisme qui écrit les normes techniques de l'Internet fait partie de la galaxie des nombreuses organisations qui ont un rôle plus ou moins formel dans le fonctionnement du réseau. Ce nouveau RFC décrit les relations de l'IETF avec un autre de ces organismes, l'Internet Society (ISOC). Ce RFC remplace le RFC 2031, notamment pour s'adapter à la nouvelle structuration de l'IETF, dite « IASA 2 » (IASA veut dire IETF Administrative Support Activity).
Historiquement, l'IETF n'était qu'un sigle et une idée. Il n'y avait pas d'organisation formelle de l'IETF. Au début, cela marchait bien comme cela, mais des inquiétudes se sont fait jour. Si l'IETF n'existe pas juridiquement, qui est responsable des RFC ? Si une entreprise mécontente d'un RFC fait un procès, chose courante aux États-Unis, à qui va-t-elle le faire ? Aux individus auteurs du RFC ? C'est entre autres en raison de ce genre de risques que l'IETF s'est mise sous un parapluie juridique, celui de l'Internet Society (ISOC), organisation créée pour donner une structure à l'Internet, permettant par exemple d'avoir un budget, d'empêcher des escrocs de déposer la marque « Internet », pour faire du lobbying, etc. C'est donc l'ISOC qui est la représentation juridique de l'IETF, mais aussi de l'IAB et de l'IRTF. (Cela date de 1995, via un groupe de travail qui se nommait Poised, qui a lancé l'effort de formalisation des processus IETF.) Si une entreprise, motivée par des juristes aussi méchants que dans un roman de Grisham, veut faire un procès en raison d'un RFC, elle doit faire un procès à l'ISOC (qui a ses propres bataillons de juristes).
La relation exacte entre l'IETF et l'ISOC était spécifiée dans le RFC 2031. Depuis, a émergé le concept d'« IASA » (IASA veut dire IETF Administrative Support Activity), une structuration plus forte des activités non techniques de l'IETF. Encore depuis, la création de « IASA 2 », dans le RFC 8711, a changé les choses, nécessitant le remplacement du RFC 2031. L'IASA version 2 créé une nouvelle structure, l'IETF LLC (LLC veut dire Limited Liability Company), qui est une filiale de l'ISOC.
Assez d'histoire, voyons d'abord, les principes (section 2 de notre RFC). L'ISOC a dans ses missions d'encourager et d'aider le développement de normes techniques ouvertes, ce qui correspond au rôle de l'IETF. Le but est donc d'être efficace, et de produire des bonnes normes, librement accessibles, et via un processus ouvert.
La section 3 précise la répartition des rôles : à l'IETF le développement des normes, à l'ISOC la partie juridique et financière. L'ISOC ne doit pas intervenir dans les choix techniques.
Cela ne veut pas dire que l'ISOC se contente de regarder. La section 4 du RFC rappelle qu'elle contribue au choix des membres du NomCom (qui fait les nominations à certains postes, cf. RFC 8713), de l'IAB, et qu'elle sert d'appel ultime en cas de conflit. D'autre part, l'ISOC est membre de certaines organisations très formelles, comme l'UIT, et sert donc de relais pour l'IETF auprès de cette organisation (RFC 6756).
L'ISOC a aussi un rôle de popularisation des technologies Internet et de sensibilisation à certains enjeux. Par exemple, elle a créé le programme Deploy 360, qui promeut notamment DNSSEC et IPv6.
Et en sens inverse (section 5 du RFC), l'IETF a une représentation au conseil d'administration de l'ISOC (RFC 3677).
Du point de vue juridique, rappelons que l'IETF LLC est rattachée à l'ISOC comme décrit dans l'accord entre l'IETF et l'ISOC d'août 2018 (oui, il a fallu du temps pour publier ce RFC…) Notez que le RFC qualifie la LLC de disregarded (ignorée, négligée) mais c'est en fait le sens fiscal de disregarded qui compte : la LLC ne paie pas d'impôts, l'ISOC le fait pour elle.
L'IETF a une structure pour gérer sa propriété intellectuelle, l'IETF Trust, créée par le RFC 5378, puis mise à jour dans les RFC 8715 et RFC 8714. C'est l'IETF Trust qui gère les marques, le copyright, etc. C'est officiellement cet IETF Trust qui vous donne le droit de lire et de distribuer les RFC. La nouvelle structure ne change pas le rôle de l'IETF Trust.
Enfin, le fric (qui fait tourner le monde). La section 7 du RFC rappelle que c'est souvent l'ISOC qui finance les activités de l'IETF. Les détails financiers sont en ligne.
Voilà, cela fait déjà beaucoup de choses à savoir, et c'est bien sûr encore pire si on inclut tous les RFC de la nouvelle structuration IASA mais il faut se rappeler que cette complexité est en partie volontaire. D'abord, il faut éviter la domination d'une organisation unique qui contrôlerait tout, et ensuite certaines organisations nécessitent des compétences spécifiques (par exemple, à l'IETF, il faut être pointu sur les questions techniques).
Date de publication du RFC : Février 2020
Auteur(s) du RFC : B. Haberman (Johns Hopkins University), J. Hall (CDT), J. Livingood (Comcast)
Réalisé dans le cadre du groupe de travail IETF iasa2
Première rédaction de cet article le 28 février 2020
Le sigle IASA (IETF Administrative Support Activity) désigne les structures qui font le travail administratif, non-technique, pour l'IETF. L'actvité de l'IETF est de produire des normes (sous forme de RFC) et cette activité nécessite toute une organisation et des moyens concrets, des employés, des logiciels, des déclarations pour le fisc, des serveurs, des réunions, et c'est tout cela que gère l'IASA. Dans sa première version, qui avait été spécifiée dans le RFC 4071 en 2005, l'IASA était gérée par l'ISOC, et était surtout formée par l'IAOC (IETF Administrative Oversight Committee). Dans cette « version 2 » de l'IASA, l'IAOC disparait, et l'essentiel des tâches revient à un nouvel organisme, une LLC (Limited Liability Company). Ce nouveau RFC remplace le RFC 4071 et décrit cette IETF Administration LLC et ses relations avec l'IETF.
L'IETF était à l'origine une structure informelle, des ingénieurs qui se réunissaient, discutaient, et écrivaient des RFC. Petit à petit, elle a grossi et s'est structurée (certains diraient « bureaucratisée »). La précédente structure était encore assez légère, reposant largement sur l'ISOC. L'IETF ayant continué à grossir, les enjeux ont continué à devenir de plus en plus importants pour la société dans son ensemble, il a été nécessaire de créer une organisation propre, la LLC (Limited Liability Company). Le conseil d'administration de cette LLC assurera les tâches qui étaient auparavant celles de l'IAOC. Les différents RFC qui mentionnaient l'IAOC ont été mis à jour pour cela (d'où la publication, entre autres, des RFC 8721, RFC 8714 ou RFC 8716). De même, l'ancienne fonction d'IAD IETF Administrative Director, la personne qui dirigeait le travail de l'IASA, est supprimée et ses tâches passent au directeur de la LLC, avec le titre de IETF Executive Director. C'était auparavant Portia Wenze-Danley mais Jay Daley lui a succédé le 24 octobre 2019.
C'est donc désormais cette « IETF LLC » qui est la structure légale de l'IETF mais aussi de l'IRTF, de l'IAB et, partiellement, du RFC Editor.
Cette réorganisation très importante ne change pourtant rien au processus de production des normes, qui, lui, reste couvert par le RFC 2026 (et ses nombreuses mises à jour, comme le RFC 6410). Cette écriture des normes techniques n'est pas pilotée par l'IASA mais par l'IESG et l'IAB (cf. RFC 2850). Même chose pour le travail de l'IRTF (RFC 2014). Si vous ne vous intéressez qu'à la technique, vous pouvez donc sauter le reste de cet article, qui ne parlera que de gouvernance et de droit.
Aucun changement non plus pour les procédures d'appels en cas de désaccord, ou pour les nominations (RFC 8713).
Comme la LLC n'a pas beaucoup de moyens, et que l'activité de l'IETF se déroule dans un environnement parfois compliqué, notamment du point de vue juridique (risque de procès), l'IETF continue de s'appuyer sur l'Internet Society (ISOC). L'accord entre LLC et ISOC est disponible en ligne. Voir aussi le RFC 8712.
La section 4 de notre RFC définit un certain nombre de termes, et pose les principes qui régissent la LLC. Parmi les définitions :
Le LLC va donc s'occuper :
Elle ne s'occupera pas du développement des normes (le travail central de l'IETF, qui ne relève pas de cette gestion administrative).
Les principes que lesquels va s'appuyer la LLC pour cela :
La section 5 du RFC expose la structure choisie pour mettre en pratique ces principes. Un directeur (aujourd'hui Jay Daley) pour les fonctions opérationnelles du quotidien, et plusieurs employés (traditionnellement, l'IETF fonctionnait avec une équipe très réduite, mais cela n'est plus possible aujourd'hui). Mais une spécificité de l'IETF est aussi son caractère associatif et fondé sur le volontariat. De nombreux participants à l'IETF donnent de leur temps pour effectuer des fonctions de support de l'IETF. C'est ainsi que, pendant longtemps, les outils logiciels de travail en groupe étaient tous développés et maintenus par des participants volontaires. De même, la gestion du réseau pendant les réunions, ou bien les activités de sensibilisation et d'éducation sont traditionnellement assurées en dehors des cadres organisés. Le but de la LLC n'est pas d'absorber tous ces efforts. Au contraire, le RFC insiste sur l'importance pour la LLC de travailler avec les volontaires, sans chercher à les remplacer.
Et qui est membre du conseil d'administration de la LLC ? La section 6 l'explique (cela avait été une des discussions les plus vives lors de la création de la LLC). Certains membres sont nommés par l'IESG, d'autres par l'ISOC. Vous pouvez voir sur le site Web de la LLC la liste des membres actuels.
L'argent étant une question évidemment cruciale, la section 7 discute le financement de la LLC. (Au fait, les informations financières sont elles aussi en ligne, pour la transparence. Et voici le plan pour le budget 2020.) Bien évidemment, le RFC précise que les éventuels dons, par exemple d'entreprises, ne donnent aucun droit particulier et ne permettent pas de donner des consignes à la LLC (du moins en théorie). L'argent ainsi récolté doit servir uniquement aux activités IETF, et proches (comme l'IRTF).
La LLC doit obéir à un certain nombre de règles, détaillées en section 8, et développées dans ses politiques publiques. Par exemple, la LLC doit avoir des politiques internes contre la corruption, ou en faveur de la protection des données personnelles. D'autre part, sa nature d'organisation officiellement enregistrée aux États-Unis l'oblige à appliquer certaines lois états-uniennes comme les sanctions contre ceux que le gouvernement de Washington va qualifier de terroristes. Pour l'instant, l'IETF n'a jamais pris de décision dans des domaines politiquement délicats comme celui-ci. Mais dans le futur ? Compte-tenu de l'actuelle crise entre le gouvernement Trump et Huawei, faudra-t-il un jour se demander si les employés de Huawei doivent être exclus des réunions ? Cela signifierait sans doute la fin de l'IETF.
Pour résumer les principaux changements entre l'« IASA 1 » (RFC 4071) et l'« IASA 2 » décrite par ce RFC : L'IAOC et l'IAD disparaissent, remplacés par la LLC, son conseil d'administration et son directeur.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : B. Harris, L. Velvindron (cyberstorm.mu)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 26 février 2020
Un très court RFC, juste pour ajouter au protocole SSH les algorithmes de signature Ed25519 et Ed448. Ces algorithmes sont déjà disponibles dans OpenSSH.
Ce protocole SSH est normalisé dans le RFC 4251, et a de nombreuses mises en œuvre, par exemple dans le logiciel libre OpenSSH. Pour authentifier le serveur, SSH dispose de plusieurs algorithmes de signature. Ce nouveau RFC en ajoute deux, dont Ed25519, qui avait été normalisé dans le RFC 8032. (En toute rigueur, l'algorithme se nomme EdDSA et Ed25519 est une des courbes elliptiques possibles avec cet algorithme. Mais je reprends la terminologie du RFC.) À noter que les courbes elliptiques sous-jacentes peuvent également être utilisées pour l'échange de clés de chiffrement, ce que décrit le RFC 8731.
La section 3 de notre RFC donne les détails techniques, suivant
le RFC 4253. L'algorithme se nomme
ssh-ed25519
. Son copain avec la courbe
elliptique Ed448 est ssh-ed448
. Ils sont tous
les deux enregistrés à l'IANA.
Le format de la clé publique est la chaîne "ssh-ed25519" suivie
de la clé, telle que décrite dans le RFC 8032,
section 5.1.5 (et 5.2.5 pour Ed448). Avec OpenSSH, vous pouvez la
voir dans ~/.ssh/id_ed25519.pub
. Les signatures
sont faites selon la technique du RFC 8032,
sections 5.1.6 et 5.2.6. Leur format est décrit en section 6, et la
vérification de ces signatures en section 7, en suivant la procédure
des sections 5.1.7 et 5.2.7 du RFC 8032.
La façon la plus courante de vérifier la clé publique du serveur SSH auquel on se connecte est le TOFU. Si on préfère une vérification plus sérieuse, on peut utiliser les clés SSH publiées dans le DNS, méthode décrite dans le RFC 4255, utilisant des enregistrements de type SSHFP. Cela fait longtemps que ces enregistrements peuvent utiliser Ed25519 (cf. RFC 7479) et notre RFC ajoute le cas de Ed448, par exemple :
example.net. IN SSHFP 6 2 ( a87f1b687ac0e57d2a081a2f282672334d90ed316d2b818ca9580ea384d924 01 )
(Il est enregistré à l'IANA.)
Ed25519 a été ajouté à OpenSSH en janvier 2014
(donc bien avant la publication de ce RFC.) C'est l'option
-t
de ssh-keygen
qui
permet de sélectionner cet algorithme :
% ssh-keygen -t ed25519 -f /tmp/ed25519 Generating public/private ed25519 key pair. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /tmp/ed25519. Your public key has been saved in /tmp/ed25519.pub. The key fingerprint is: SHA256:VEN6HVM0CXq+TIflAHWCOQ88tfR35WXQZ675mLIhIIs stephane@godin The key's randomart image is: +--[ED25519 256]--+ | o==O+*++| | oB* B.+*| | o o== oo=| | . . o.= .o| | . S + oo | | . o . o oo | | E . . + + | | ...o .| | .o | +----[SHA256]-----+
À noter que OpenSSH 7.6 n'a pas ed448. D'une manière générale, ed25519 a été beaucoup plus souvent mise en œuvre dans les clients et serveurs SSH.
Date de publication du RFC : Janvier 2020
Auteur(s) du RFC : D. Benjamin (Google)
Pour information
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 29 janvier 2020
Ce nouveau RFC s'attaque à un problème fréquent dans l'Internet : des programmeurs paresseux, incompétents ou pressés par les délais imposés mettent en œuvre un protocole normalisé (comme TLS) sans bien lire les normes, et notamment sans tenir compter des variations que la norme permet. Ils programment rapidement, testent avec une ou deux implémentations trouvées et, si ça marche, en déduisent que c'est bon. Mais dès qu'une autre implémentation introduit des variantes, par exemple un paramètre optionnel et qui n'était pas utilisé avant, la bogue se révèle. Le cas s'est produit de nombreuses fois. Notre RFC propose une solution disruptive : utiliser délibérément, et au hasard, plein de variantes d'un protocole, de façon à détecter rapidement les programmes écrits avec les pieds. C'est le principe de GREASE (Generate Random Extensions And Sustain Extensibility), la graisse qu'on va mettre dans les rouages de l'Internet pour que ça glisse mieux. Ce RFC 8701 commence par appliquer ce principe à TLS.
Le problème n'est évidemment pas spécifique à TLS, on l'a vu arriver aussi dans BGP lorsqu'on s'est aperçu que la simple annonce d'un attribut BGP inconnu pouvait planter les routeurs Cisco. Là aussi, le « graissage » (tester systématiquement des valeurs non allouées pour les différents paramètres, pour vérifier que cela ne plante rien) aurait bien aidé. D'où le projet « Use it or lose it », décrit dans le RFC 9170. dont GREASE est un cas particulier. Ce RFC analyse le problème des options non utilisées et recommande de les utiliser systématiquement, pour habituer les logiciels à voir ces options.
Le principe de GREASE (Generate Random Extensions And Sustain Extensibility) est donc de faire en sorte que clients et serveurs TLS (RFC 8446) annoncent, pour différents paramètres de la connexion, des valeurs qui ne correspondent à rien. Ainsi, les middleboxes boguées, installées au milieu de la communication parce que le commercial qui les vendait était convaincant, seront vite détectées, au lieu que le problème demeure dormant pendant des années et soit subitement révélé le jour où on essaie des valeurs légales mais nouvelles, comme dans le cas de l'attribut 99.
Qu'est-ce qui est variable dans TLS ? Beaucoup de choses, comme
la liste des algorithmes de cryptographie
ou comme les extensions. Dans TLS, le client annonce ce qu'il sait
faire, et le serveur sélectionne dans ce choix (RFC 8446, section 4.1.3). Voici un exemple vu par tshark, d'abord le message du client
(Client Hello
), puis la réponse du serveur
(Server Hello
) :
Secure Sockets Layer TLSv1 Record Layer: Handshake Protocol: Client Hello Content Type: Handshake (22) Handshake Protocol: Client Hello Version: TLS 1.2 (0x0303) Cipher Suites (28 suites) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) ... Extension: ec_point_formats (len=4) Type: ec_point_formats (11) EC point formats Length: 3 Elliptic curves point formats (3) EC point format: uncompressed (0) ... Extension: SessionTicket TLS (len=0) Type: SessionTicket TLS (35) Extension: encrypt_then_mac (len=0) Type: encrypt_then_mac (22) Extension: signature_algorithms (len=48) Type: signature_algorithms (13) Signature Hash Algorithms (23 algorithms) Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403) Signature Hash Algorithm Hash: SHA256 (4) Signature Hash Algorithm Signature: ECDSA (3) ... Secure Sockets Layer TLSv1.2 Record Layer: Handshake Protocol: Server Hello Content Type: Handshake (22) Version: TLS 1.2 (0x0303) Handshake Protocol: Server Hello Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) Compression Method: null (0) Extensions Length: 13 Extension: renegotiation_info (len=1) Type: renegotiation_info (65281) Length: 1 Renegotiation Info extension Renegotiation info extension length: 0 Extension: ec_point_formats (len=4) Type: ec_point_formats (11) ...
Le client propose de nombreux algorithmes de cryptographie, comme
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
, et
plusieurs extensions comme le format pour les courbes
elliptiques (normalisé dans le RFC 8422), les tickets du RFC 5077, le chiffrement
avant le MAC du RFC 7366 et des
algorithmes de signature. Le serveur choisit l'algorithme de
chiffrement
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
,
accepte l'extension sur le format des courbes elliptiques, et, puisque
le client était d'accord (via l'indication d'un algorithme de
chiffrement spécial), le serveur utilise l'extension de
renégociation.
Les valeurs inconnues (par exemple une nouvelle extension) doivent être ignorées (RFC 8446, section 4.1.2). Si ce n'était pas le cas, si une valeur inconnue plantait la partie située en face, il ne serait pas possible d'introduire de nouveaux algorithmes ou de nouvelles extensions, en raison des déploiements existants. Prenons les algorithmes de cryptographie, enregistrés à l'IANA. Si un nouvel algorithme apparait, et reçoit une valeur, comment vont réagir, non seulement les pairs avec qui on communique en TLS, mais également tous ces boitiers intermédiaires installés souvent sans raison sérieuse, et pour lesquels il n'existe pas de mécanisme pratique de remontée des problèmes ? Si leurs programmeurs avaient lu la norme, ils devraient ignorer ce nouvel algorithme, mais on constate qu'en pratique, ce n'est souvent pas le cas, ce qui rend difficile l'introduction de nouveaux algorithmes. Dans le pire des cas, le boitier intermédiaire jette les paquets portant les valeurs inconnues, sans aucun message, rendant le débogage très difficile.
D'où les métaphores mécaniques : dans l'Internet d'aujourd'hui, bien des équipements sur le réseau sont rouillés, et il faut les graisser, en faisant travailler les parties qui ne sont normalement pas testées. C'est le principe de GREASE que d'envoyer des valeurs inconnues pour certains paramètres, dans l'espoir de forcer les mises en œuvre de TLS, surtout celles dans les boitiers intermédiaires, à s'adapter. Une méthode darwinienne, en somme.
La section 2 de notre RFC indique les valeurs choisies pour ces annonces. C'est délibérement qu'elles ne sont pas contiguës, pour limiter le risque que des programmeurs paresseux ne testent simplement si une valeur est incluse dans tel intervalle. Il y a un jeu de valeurs pour les algorithmes de cryptographie et les identificateurs ALPN (RFC 7301), un pour les extensions, un pour les versions de TLS, etc. Toutes sont enregistrées à l'IANA, dans le registre respectif. Par exemple, pour les extensions TLS, (cf. leur liste), les valeurs, 2570, 6682, 10794 et plusieurs autres sont réservées pour le graissage. (Il fallait les réserver pour éviter qu'une future extension TLS ne reçoive le même numéro, ce qui aurait cassé la compatibilité avec les logiciels GREASE.)
Une fois ces valeurs réservées par notre RFC, le client TLS
peut, au hasard, ajouter ces valeurs dans, par exemple, la liste
des algorithmes de cryptographie qu'il gère, ou la liste des
extensions qu'il propose. Si jamais le serveur les accepte (dans
son ServerHello
), le client doit rejeter la
connexion ; le but de ces valeurs était de tester les logiciels,
elles ne doivent jamais être sélectionnées. Notez que c'est le
comportement normal d'un client TLS d'aujourd'hui de refuser
proprement les valeurs inconnues. De même, un serveur normal
d'aujourd'hui va ignorer ces valeurs inconnues (et donc ne jamais
les sélectionner). Si tout le monde suit la norme, l'introduction
des valeurs GREASE ne va rien changer. Les règles de la section 3
ne sont qu'un rappel de règles TLS qui ont toujours existé.
La section 4 de notre RFC traite le cas un peu plus difficile
où le serveur propose et le client accepte. C'est par exemple ce
qui arrive quand le serveur demande au client de s'authentifier,
en envoyant un CertificateRequest
. Le serveur
peut là aussi utiliser GREASE et indiquer des extensions ou des
algorithmes de signature inconnus, et le client doit les ignorer
(sinon, si le client sélectionne ces valeurs, le serveur doit
rejeter un tel choix).
La section 5 du RFC précise dans quels cas utiliser les valeurs GREASE et lesquelles. Le but étant de révéler les problèmes, le RFC recommande de choisir les valeurs aléatoirement. (Si un programme envoyait toujours la même valeur GREASE, il y aurait un risque que des programmes en face ignorent spécifiquement cette valeur, alors qu'il faut ignorer toutes les valeurs inconnues.) Par contre, pour un même partenaire TLS, il vaut mieux un certain déterminisme, sinon les problèmes seront difficiles à déboguer (parfois, ça marche, parfois, ça ne marche pas…)
Enfin, la section 7 du RFC discute des conséquences de l'absence de graissage sur la sécurité. Si certaines mises en œuvre de TLS résistent mal aux options inconnues, cela peut encourager le repli, c'est-à-dire à réessayer sans les options. Ainsi, un attaquant actif pourrait facilement forcer une machine à ne pas utiliser des options qui rendraient des attaques ultérieures plus compliquées. Les attaques par repli étant particulièrement dangereuses, il est important de ne pas se replier, et donc de s'assurer qu'on peut réellement utiliser toutes les possibilités du protocole. Cela veut dire entre autres que, si une machine TLS utilisant GREASE a du mal à se connecter, elle ne devrait pas essayer sans GREASE : cela annnulerait tous les bénéfices qu'on attend du graissage. Le principe de robustesse est mauvais pour la sécurité.
À noter que Chrome a déjà mis en œuvre ce principe, et que ça semble bien fonctionner. L'article « HTTPS : de SSL à TLS 1.3 », surtout consacré à la version 1.3 de TLS, montre à quoi ressemble les options GREASE pour Wireshark (« Unknown »).
À noter qu'un concept équivalent existe dans HTTP/3, Reserved Stream Types et Reserved Frame Types (RFC 9113, sections 6.2.3 et 7.2.9). Pour HTTP/2 (RFC 7540), où les trames de type inconnu devraient être ignorées, l'expérience a déjà prouvé qu'elles ne l'étaient pas toujours.
Date de publication du RFC : Décembre 2019
Auteur(s) du RFC : H. Flanagan (RFC Editor)
Pour information
Première rédaction de cet article le 24 décembre 2019
Ce nouveau RFC marque le cinquantième anniversaire des RFC. Le RFC 1 avait en effet été publié le 7 avril 1969. Ce RFC 8700, publié avec un certain retard, revient sur l'histoire de cette exceptionnelle série de documents.
Il y avait déjà eu des RFC faisant le bilan de la série, à l'occasion d'anniversaires, comme le RFC 2555 pour le trentième RFC, et le RFC 5540 pour le quarantième. La série a évidemment commencé avec le RFC 1, cinquante ans auparavant, et donc dans un monde très différent. À l'époque, les RFC méritaient leur nom, ils étaient été en effet des « appels à commentaires », prévus non pas comme des références stables, mais comme des étapes dans la discussion. En cinquante ans, les choses ont évidemment bien changé, et les RFC sont devenus des documents stables, intangibles, et archivés soigneusement. Logiquement, le processus de création des RFC a également évolué, notamment vers un plus grand formalisme (on peut même dire « bureaucratie »).
Plus de 8 500 RFC ont été publiés (il existe quelques trous dans la numérotation ; ainsi, le RFC 26 n'a jamais existé…) Les plus connus sont les normes techniques de l'Internet. La description précise de HTTP, BGP ou IP est dans un RFC. Mais d'autres RFC ne normalisent rien (c'est le cas du RFC 8700, sujet de cet article), ils documentent, expliquent, suggèrent… Tous les RFC ont en commun d'être publiés puis soigneusement gardés par le RFC Editor, une fonction assurée par plusieurs personnes, et aujourd'hui animée par Heather Flanagan, auteure de ce RFC 8700, mais qui a annoncé son départ.
Cette fonction a elle aussi une histoire : le premier RFC Editor était Jon Postel. À l'époque c'était une fonction informelle, tellement informelle que plus personne ne sait à partir de quand on a commencé à parler du (ou de la) RFC Editor (mais la première mention explicite est dans le RFC 902). Postel assurait cette fonction en plus de ses nombreuses autres tâches, sans que cela n'apparaisse sur aucune fiche de poste. Petit à petit, cette fonction s'est formalisée.
Les changements ont affecté bien des aspects de la série des RFC, pendant ces cinquante ans. Les premiers RFC étaient distribués par la poste ! Au fur et à mesure que le réseau (qui ne s'appelait pas encore Internet) se développait, ce mécanisme de distribution a été remplacé par le courrier électronique et le FTP anonyme. Autre changement, les instructions aux auteurs, données de manière purement orales, ont fini par être rédigées. Et l'équipe s'est étoffée : d'une personne au début, Postel seul, le RFC Editor a fini par être une tâche assurée par cinq à sept personnes. Autrefois, la fonction de RFC Editor était liée à celle de registre des noms et numéros, puis elle a été séparée (le registre étant désormais PTI). Puis la fonction de RFC Editor a été structurée, dans le RFC 4844, puis RFC 5620, enfin dans le RFC 6635. Et l'évolution continue, par exemple en ce moment avec le changement vers le nouveau format des documents (voir RFC 7990). Dans le futur, il y aura certainement d'autres changements, mais le RFC Editor affirme son engagement à toujours prioriser la stabilité de la formidable archive que représentent les RFC, et sa disponibilité sur le long terme (RFC 8153).
La section 2 du RFC rappelle les grands moments de l'histoire des RFC (je n'ai pas conservé toutes ces étapes dans la liste) :
Dans la section 3 de ce RFC, plusieurs personnes ayant vécu de l'intérieur l'aventure des RFC écrivent. Steve Crocker, dans la section 3.1, rappelle les origines des RFC (qu'il avait déjà décrites dans le RFC 1000). Il insiste sur le fait que les débuts étaient… peu organisés, et que la création de la série des RFC n'était certainement pas prévue dés le départ. Elle doit beaucoup aux circonstances. Le réseau qui, après bien des évolutions, donnera naissance à l'Internet a été conçu vers 1968 et a commencé à fonctionner en 1969. Quatre machines, en tout et pour tout, le constituaient, un Sigma 7, un SDS 940, un IBM 360/75 et un PDP-10. Le point important est qu'il s'agissait de machines radicalement différentes, un des points distinctifs de l'Internet, qui a toujours dû gérer l'hétérogénéité. Un byte n'avait pas la même taille sur toutes ces machines. (Le terme français octet est apparu bien plus tard, lorsque la taille de huit bits était devenue standard.)
Crocker décrit la première réunion de ce qui allait devenir le Network Working Group puis, très longtemps après l'IETF. Rien n'était précisement défini à part « il faut qu'on fasse un réseau d'ordinateurs » et persone ne savait trop comment le faire. La principale conclusion de la réunion avait été « il faudrait faire une autre réunion ». Dès le début, le réseau qui allait permettre de travailler à distance était donc un prétexte à des réunions AFK. (L'ironie continue aujourd'hui, où l'IETF réfléchit à avoir des réunions entièrement en ligne.)
L'espoir des étudiants comme Crocker était qu'un monsieur sérieux et expérimenté vienne expliquer ce qu'on allait devoir faire. Mais cet espoir ne s'est pas matérialisé et le futur Network Working Group a donc dû se débrouiller.
Parmi les idées les plus amusantes, le groupe avait réfléchi à la création d'un langage portable permettant d'envoyer du code sur une autre machine qui l'exécuterait. Ce lointain prédécesseur de JavaScript se nommait DEL (pour Decode-Encode Language) puis NIL (Network Interchange Language). Mais en attendant le travail matériel avançait, la société BBN ayant obtenu le contrat de construction des IMP (à peu près ce qu'on appelerait plus tard routeurs). La répartition des tâches entre le NWG et BBN n'était pas claire et le groupe a commencé de son côté à documenter ses réflexions, créant ainsi les RFC. Le nom de ces documents avait fait l'objet de longs débats. Le Network Working Group n'avait aucune autorité officielle, aucun droit, semblait-il, à édicter des « normes » ou des « références ». D'où ce titre modeste de Request for Comments ou « Appel à commentaires ». Cette modestie a beaucoup aidé au développement du futur Internet : personne ne se sentait intimidé par l'idée d'écrire des documents finaux puisque, après tout, ce n'était que des appels à commentaires. C'était d'autant plus important que certains des organismes de rattachement des participants avaient des règles bureaucratiques strictes sur les publications. Décréter les RFC comme de simples appels à commentaires permettait de contourner ces règles.
Le premier « méta-RFC » (RFC parlant des RFC) fut le RFC 3, qui formalisait cette absence de formalisme. De la même façon, il n'existait pas encore vraiment de RFC Editor, même si Crocker attribuait les numéros, et que le SRI gardait une archive non officielle. Mais deux principes cardinaux dominaient, et sont toujours vrais aujourd'hui : tout le monde peut écrire un RFC, nul besoin de travailler pour une grosse entreprise, ou d'avoir un diplôme ou un titre particulier, et tout le monde peut lire les RFC (ce qui n'a rien d'évident : en 2019, l'AFNOR ne distribue toujours pas librement ses normes.)
Dans la section 3.2, Vint Cerf décrit les changements ultérieurs. En 1971, Jon Postel est devenu RFC Editor (titre complètement informel à cette époque). Cette tâche était à l'époque mélée à celle d'attribution des numéros pour les protocoles, désormais séparée. Postel s'occupait à la fois du côté administratif du travail (donner un numéro aux RFC…) et de l'aspect technique (relecture et révision), tâche aujourd'hui répartie entre diverses organisations comme l'IESG pour les RFC qui sont des normes. C'est pendant cette « période Postel » que d'autres personnes sont venues rejoindre le RFC Editor comme Joyce Reynolds ou Bob Braden. Jon Postel est décédé en 1998 (cf. RFC 2468).
Leslie Daigle, dans la section 3.3 de notre RFC, rappelle la longue marche qu'a été la formalisation du rôle de RFC Editor, le passage de « bon, qui s'en occupe ? » à un travail spécifié par écrit, avec plein de règles et de processus. (Daigle était présidente de l'IAB au moment de la transition.) Le travail était devenu trop important en quantité, et trop critique, pour pouvoir être assuré par deux ou trois volontaires opérant « en douce » par rapport à leurs institutions. Une des questions importantes était évidemment la relation avec l'IETF. Aujourd'hui, beaucoup de gens croient que « les RFC, c'est l'IETF », mais c'est faux. Les RFC existaient bien avant l'IETF, et, aujourd'hui, tous les RFC ne sont pas issus de l'IETF.
Parmi les propositions qui circulaient à l'époque (début des années 2000) était celle d'une publication complètement automatique. Une fois le RFC approuvé par l'IESG, quelqu'un aurait cliqué sur Publish, et le RFC se serait retrouvé en ligne, avec un numéro attribué automatiquement. Cela aurait certainement fait des économies, mais cela ne réglait pas le cas des RFC non-IETF, et surtout cela niait le rôle actif du RFC Editor en matière de contenu du RFC. (Témoignage personnel : le RFC Editor a joué un rôle important et utile dans l'amélioration de mes RFC. C'est vrai même pour les RFC écrits par des anglophones : tous les ingénieurs ne sont pas des bons rédacteurs.) D'un autre côté, cela résolvait le problème des modifications faites de bonne foi par le RFC Editor mais qui changeaient le sens technique du texte.
La solution adoptée est décrite dans le RFC 4844, le premier à formaliser en détail le rôle du RFC Editor, et ses relations complexes avec les autres acteurs.
Nevil Brownlee, lui, était ISE, c'est-à-dire Independent Submissions Editor, la personne chargée de traiter les RFC de la voie indépendante (ceux qui ne viennent ni de l'IETF, ni de l'IAB, ni de l'IRTF.) Dans la section 3.4, il revient sur cette voie indépendante (d'abord décrite dans le RFC 4846). En huit ans, il a été responsable de la publication de 159 RFC… Avant, c'était le RFC Editor qui décidait quoi faire des soumissions indépendantes. Comme le rappelle Brownlee, le logiciel de gestion de cette voie indépendante était un simple fichier texte, tenu par Bob Braden.
Le principal travail de l'ISE est de coordonner les différents acteurs qui jouent un rôle dans ces RFC « indépendants ». Il faut trouver des relecteurs, voir avec l'IANA pour l'allocation éventuelle de numéros de protocoles, avec l'IESG pour s'assurer que ce futur RFC ne rentre pas en conflit avec un travail de l'IETF (cf. RFC 5742), etc. Ah, et c'est aussi l'ISE qui gère les RFC du premier avril.
Puis c'est la RFC Editor actuelle, Heather Flanagan qui, dans la section 3.5, parle de son expérience, d'abord comme simple employée. La charge de travail atteignait de tels pics à certains moments qu'il a fallu recruter des personnes temporaires (au nom de l'idée que la publication des RFC devait être un processus léger, ne nécessitant pas de ressources permanentes), ce qui a entrainé plusieurs accidents quand des textes ont été modifiés par des employés qui ne comprenaient pas le texte et introduisaient des erreurs. L'embauche d'employés permanents a résolu le problème.
Mais il a fallu aussi professionnaliser l'informatique. Le RFC Editor qui travaillait surtout avec du papier (et un classeur, le fameux « classeur noir ») et quelques outils bricolés (la file d'attente des RFC était un fichier HTML édité à la main), a fini par disposer de logiciels adaptés à la tâche. Finies, les machines de Rube Goldberg !
Dans le futur, bien sûr, les RFC vont continuer à changer ; le gros projet du moment est le changement de format canonique, du texte brut à XML. Si l'ancien format avait de gros avantages, notamment en terme de disponibilité sur le long terme (on peut toujours lire les anciens RFC, alors que les outils et formats à la mode au moment de leur écriture sont depuis longtemps oubliés), il avait aussi des inconvénients, comme l'impossibilité d'utiliser d'autres caractères que l'ASCII. Le RFC 7990 décrit le nouveau format, actuellement en cours de déploiement.
Autres lectures :
Date de publication du RFC : Novembre 2019
Auteur(s) du RFC : J. Fenton (Altmode Networks)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF uta
Première rédaction de cet article le 28 novembre 2019
Ah, la sécurité, c'est toujours compliqué. Pour le courrier électronique, par exemple, SMTP peut être fait sur TLS, pour garantir la confidentialité et l'intégrité du message. Mais TLS est optionnel. Cela entraine deux problèmes : si la politique du MTA est laxiste, le message risque de passer en clair à certains moments, et si elle est stricte, le message risque d'être rejeté alors que l'expéditeur aurait peut-être préféré qu'il passe en clair. Ce nouveau RFC fournit deux mécanismes, un pour exiger du TLS à toutes les étapes, un au contraire pour demander de la bienveillance et de la tolérance et d'accepter de prendre des risques.
SMTP
(RFC 5321) a une option nommée
STARTTLS
(normalisée dans le RFC 3207), qui permet, si le pair en face l'accepte, de passer
la session SMTP sur TLS, assurant ainsi sa
sécurité. STARTTLS
a plusieurs problèmes,
notamment son caractère optionnel. Même avec des sessions SMTP
entièrement TLS (sans STARTTLS
, cf. RFC 8314), le problème demeure. Que doit faire un
MTA s'il ne peut
pas lancer TLS, parce que le MTA en face ne l'accepte pas, ou parce
que son certificat est invalide (RFC 6125) ou
encore car DANE (RFC 7672) est
utilisé et que le certificat ne correspond pas ? Jusqu'à présent, la
décision était prise par chaque MTA et, comme SMTP repose sur le
principe du relayage, l'émetteur original ne pouvait pas exprimer
ses préférences, entre « je suis parano, j'ai lu le bouquin de Snowden, j'exige du TLS
tout le temps » et « je m'en fous, je suis inconscient, je crois que
je n'ai rien à cacher, je
veux que le message soit envoyé, même en clair ». La politique des
serveurs SMTP était typiquement de privilégier la distribution du
message plutôt que sa sécurité. Désormais, il possible pour
l'émetteur de donner ses préférences : l'option SMTP
REQUIRETLS
permet d'exiger du TLS tout le
temps, et l'en-tête TLS-Required:
(bien mal
nommé) permet d'indiquer qu'on préfère au contraire la délivrance du
message à sa sécurité.
En général, aujourd'hui, les MTA acceptent d'établir la session TLS, même si
le certificat est invalide. En effet, dans le
cas contraire, peu de messages seraient livrés, les certificats dans
le monde du courrier étant fréquemment
invalides, à l'opposé de ce qui se passe dans le monde du Web, où les navigateurs sont bien plus
stricts. Pour durcir cette politique par défaut, et aussi parce
qu'un attaquant actif peut retirer l'option
STARTTLS
et donc forcer un passage en clair, il existe
plusieurs mécanismes permettant de publier une politique, comme
DANE (RFC 7672) et
MTA-STS (RFC 8461). Mais elles sont contrôlées par le récepteur, et on
voudrait permettre à l'émetteur de donner son avis.
Commençons par REQUIRETLS
, l'extension
SMTP. (Désormais dans le registre IANA des
extensions SMTP.) Il s'agit pour l'émetteur d'indiquer qu'il
ne veut pas de laxisme : il faut du TLS du début à la fin, et,
évidemment, avec uniquement des certificats valides. En utilisant
cette extension, l'émetteur indique qu'il préfère que le message ne
soit pas distribué, plutôt que de l'être dans de mauvaises
conditions de sécurité. Cette extension peut être utilisée entre
deux MTA, mais aussi quand un MUA se connecte au premier MTA, pour une
soumission de message (RFC 6409). Voici un
exemple (« C: » = envoyé par le client, « S: » = envoyé par le
serveur) :
S: 220 mail.example.net ESMTP C: EHLO mail.example.org S: 250-mail.example.net Hello example.org [192.0.2.1] S: 250-SIZE 52428800 S: 250-8BITMIME S: 250-REQUIRETLS C: MAIL FROM:<roger@example.org> REQUIRETLS S: 250 OK C: RCPT TO:<editor@example.net> S: 250 Accepted C: DATA S: 354 Enter message, ending with "." on a line by itself (Le message) C: . S: 250 OK C: QUIT
(Le lecteur ou la lectrice astucieux aura remarqué qu'il y a un
piège, le risque qu'un attaquant actif ne retire le
REQUIRETLS
du client ou bien du serveur. Ce cas
est traité plus loin.)
Dans l'exemple ci-dessus, le serveur a annoncé qu'il savait faire
du REQUIRETLS
, et le client a demandé à ce que
l'envoi depuis roger@example.org
soit protégé
systématiquement par TLS. Cela implique que pour toutes les sessions
SMTP suivantes :
Puisque l'idée est d'avoir du TLS partout, cela veut dire qu'un MTA
qui reçoit un message marqué REQUIRETLS
doit
noter cette caractéristique dans sa base et s'en souvenir, puisqu'il
devra passer cette exigence au serveur suivant.
Si le serveur en face ne sait pas faire de
REQUIRETLS
(ou, pire, pas de TLS), l'émetteur
va créer une erreur
commençant par 5.7 (les erreurs SMTP étendues sont décrites dans le
RFC 5248) :
REQUIRETLS not supported by server: 5.7.30 REQUIRETLS needed
Et l'en-tête TLS-Required:
? (Ajouté dans le
registre IANA des en-têtes.) Il fait
l'inverse, il permet à l'émetteur de spécifier qu'il préfère la
distribution du message à la sécurité, et qu'il faut donc débrayer
les tests qu'on pourrait faire. Ce nom de
TLS-Required:
est mal choisi, car cet en-tête
ne peut prendre qu'une seule valeur, no
(non),
comme dans cet exemple amusant du RFC :
From: Roger Reporter <roger@example.org> To: Andy Admin <admin@example.com> Subject: Certificate problem? TLS-Required: No Date: Fri, 18 Jan 2019 10:26:55 -0800 Andy, there seems to be a problem with the TLS certificate on your mail server. Are you aware of this? Roger
Si l'en-tête est présent, le serveur doit être plus laxiste que
d'habitude et accepter d'envoyer le message même s'il y a des
problèmes TLS, même si la politique normale du serveur serait de
refuser. Bien sûr, TLS-Required: no
n'interdit
pas d'utiliser TLS, si possible, et l'émetteur doit quand même
essayer. Notez aussi que les MTA sont libres de leur politique et qu'on peut
parfaitement tomber sur un serveur SMTP qui refuse de tenir compte
de cette option, et qui impose TLS avec un certificat correct, même
en présence de TLS-Required: no
.
(Le lecteur ou la lectrice astucieux aura remarqué qu'il y a un
piège, le risque qu'un attaquant actif n'ajoute
TLS-Required: no
. Ce cas est traité plus
loin.)
Ah, et si on a les deux, REQUIRETLS
et
TLS-Required: no
? La section 4.1 du RFC couvre
ce cas, en disant que la priorité est à la sécurité (donc,
REQUIRETLS
).
La section 5 de notre RFC couvre le cas des messages d'erreur
générés par un MTA lorsqu'il ne peut pas ou ne veut pas envoyer le
message au MTA suivant (ou au MDA). Il
fabrique alors un message envoyé à l'expéditeur
(bounce, en anglais, ou message de non-distribution). Ce message contient
en général une bonne partie, voire la totalité du message
original. Sa confidentialité est donc aussi importante que celle du
message original. Si celui-ci était protégé par
REQUIRETLS
, le courrier d'erreur doit l'être
aussi. Le MTA qui génère ce courrier d'erreur doit donc lui-même
activer l'extension REQUIRETLS
. (Notez que,
comme le chemin que suivra cet avis de non-remise ne sera pas
forcément le même que celui suivi par le message originel, s'il y a
un serveur non-REQUIRETLS
sur le trajet, le
courrier d'erreur ne sera pas reçu.)
Si un logiciel ré-émet un message (par exemple un gestionnaire de
liste de diffusion transmettant aux membres
de la liste, cf. RFC 5598), il devrait,
idéalement, appliquer également le REQUIRETLS
sur le message redistribué. Le RFC ne l'impose pas car, en pratique,
cela risquerait d'empêcher la réception du message par beaucoup.
Notre RFC se termine par une longue
section 8 sur la sécurité, car les problèmes qu'essaie de résoudre
ces solutions sont complexes. Le cas des attaques
passives est facile : TLS protège presque parfaitement
contre elles. Mais les attaques actives
soulèvent d'autres questions. REQUIRETLS
mènera
à un refus des connexions SMTP sans TLS, protégeant ainsi contre
certaines attaques actives comme le SSL
stripping ou comme une attaque de
l'Homme du Milieu avec un mauvais
certificat. (Cette dernière attaque est facile aujourd'hui dans le
monde du courrier, où bien des serveurs SMTP
croient aveuglément tout certificat présenté.)
REQUIRETLS
protège également contre beaucoup
d'attaques via le DNS, en exigeant
DNSSEC (ou, sinon,
MTA-STS).
Par contre, REQUIRETLS
ne protège pas contre
un méchant MTA qui prétendrait gérer REQUIRETLS
mais en fait l'ignorerait. De toute façon, SMTP sur TLS n'a jamais
protégé des MTA intermédiaires, qui ont le texte du message en
clair. Si on veut se protéger contre un tel MTA, il faut utiliser
PGP (RFC 9580) ou
équivalent. (Par contre, le risque de l'ajout d'un
TLS-Required: no
par un MTA malveillant ne
semble pas traité dans le RFC ; PGP ne protège pas contre cela.)
Il peut y avoir un conflit entre TLS-Required:
no
et la politique du MTA, qui tient absolument à
vérifier les certificats des serveurs auxquels il se connecte, via
PKIX ou via DANE. Le RFC laisse
entendre que le dernier mot devrait revenir à l'expéditeur, au moins
si le message a été envoyé via TLS et donc pas modifié en route. (Le
cas d'un message reçu en clair - donc pas sécurisé - et demandant de
ne pas exiger TLS reste ouvert…)
Et pour finir, l'exemple de session SMTP où le serveur annonçait
qu'il gérait REQUIRETLS
(en disant
250-REQUIRETLS
) était simplifié. Si la session
commençait en clair, puis passait à TLS après, avec la commande
STARTTLS
, le client doit recommencer la session
une fois TLS activé, pour être sûr que ce qu'annonce le serveur est réel.
Bien qu'il y ait déjà des programmeurs ayant travaillé sur ce RFC, je ne trouve encore rien du tout dans le source de Postfix, le MTA que j'utilise, même dans la version expérimentale.
Date de publication du RFC : Décembre 2019
Auteur(s) du RFC : M. Nottingham
Pour information
Première rédaction de cet article le 5 décembre 2019
Ce nouveau RFC
définit une nouvelle préférence qu'un client HTTP peut envoyer au
serveur. « safe
» (sûr) indique que le client
ne souhaite pas recevoir du contenu qu'il trouve contestable.
Je vous arrête tout de suite : vous allez me demander « mais qui
définit du contenu contestable ? Ça dépend des gens » et, je vous
rassure, l'auteur du RFC a bien vu le
problème. La préférence safe
est juste une
possibilité technique, le RFC ne définit pas ce qui est sûr et ce
qui ne l'est pas, cela devra se faire dans un autre cadre, une
discussion à ce sujet serait bien trop casse-gueule pour
l'IETF. En pratique, le résultat de
l'utilisation de cette préférence dépendra de bien des choses, comme
la politique du serveur (le RFC dit « the cultural context
of the site »), et une éventuelle relation pré-existante
entre le serveur et un utilisateur particulier. Le RFC donne quand
même une indication : safe
peut vouloir dire
« adapté aux mineurs ».
Il y a manifestement une demande, puisque bien des
sites Web ont un mode « sûr », où on peut
sélectionner « je ne veux pas voir des choses que je n'aime
pas ». Notez que, dans ces cas, la définition de ce qui est sûr ou
pas dépend du site Web. S'ils est géré aux
États-Unis, « sûr » sera sans doute « aucune
nudité humaine », en Arabie saoudite,
« aucune femme visible », etc. Ce mode « sûr » des sites Web n'est
pas pratique pour l'utilisateurice, car il nécessite de sélectionner
l'option pour chaque site, et de se créer un compte, soit explicite,
soit implicite via les
cookies (RFC 6265). À moins que le mode « sûr » soit le mode par défaut
et, dans ce cas, ce sont les gens qui n'en voudront pas qui auront
du travail. D'où l'idée, très controversée à l'IETF, de
configurer cela dans le navigateur Web (ou
bien dans le système d'exploitation, pour que
tous les clients HTTP le fassent), qui va indiquer au serveur
les préférences (un peu comme le Do Not
Track, dont on sait qu'il est largement ignoré
par les sites Web). La technique utilisée est celle des préférences
HTTP, normalisées dans le RFC 7240,
préférences dont je rappelle que leur respect par le serveur est
optionnel. La préférence safe
envoyée par le
client est donc à prendre comme un appel à « faire au mieux » et
« je te fais confiance pour la définition de "sûr" », pas plus.
La section 2 de notre RFC est plus concrète, présentant la
syntaxe exacte de safe
. Voici un exemple de
requête HTTP exprimant cette préférence :
GET /foo.html HTTP/1.1 Host: www.example.org User-Agent: ExampleBrowser/1.0 Prefer: safe
La préférence est enregistrée
à l'IANA. Le RFC impose que les requêtes « sûres » soient
faites en HTTPS, pour éviter la surveillance spécifique
des gens qui demandent du safe
(et qui peuvent
être des enfants), et pour éviter qu'un intermédiaire ne bricole la
requête, ajoutant ou enlevant cette préférence. Une réponse possible
à la requête ci-dessus serait :
HTTP/1.1 200 OK Transfer-Encoding: chunked Content-Type: text/html Preference-Applied: safe Server: ExampleServer/2.0 Vary: Prefer
Le Vary:
(RFC 7231,
section 7.1.4) indique aux relais/caches intermédiaires qu'ils
doivent tenir compte de la valeur de Prefer:
avant de renvoyer la page mémorisée à un autre client.
Si vous voulez tester en vrai, la page
vous
renverra un contenu différent selon que vous envoyez l'en-tête
https://www.bortzmeyer.org/apps/porn
Prefer: safe
ou pas. Voici un exemple avec
curl :
% curl --header "Prefer: safe" https://www.bortzmeyer.org/apps/porn
Le code est du Python/WSGI et se résume à :
def porn(start_response, environ): # Apache/WSGI always give us one Prefer: header even if the client sent several. preferences = re.split("\s*,\s*", environ['HTTP_PREFER']) safe = False for pref in preferences: if pref.lower() == 'safe': safe = True break begin = """<html><head>...""" end = """</body></html>""" safe_content = """<p>Safe ...""" unsafe_content = """<p>Unsafe ...""" if safe: output = begin + safe_content + end else: output = begin + unsafe_content + end status = '200 OK' response_headers = [('Content-type', 'text/html'), ('Content-Length', str(len(output)))] start_response(status, response_headers) return [output]
Cette préférence semble répondre à une forte demande, puisqu'elle est déjà reconnue :
La section 3 du RFC rassemble quelques informations de sécurité :
safe
,safe
peut être
dangereux puisqu'il transmet une information supplémentaire au
serveur, aidant au
fingerprinting,safe
est
présente.Enfin, l'annexe A donne quelques conseils aux auteurs de
navigateurs quant à la mise en œuvre de cette
préférence. L'UI n'est pas évidente. Il est
crucial de ne pas donner à l'utilisateur ou l'utilisatrice
l'impression que cette préférence fournirait des garanties. Le RFC
suggère un texte plus prudent pour une case à
cocher « Demander du contenu "sûr" aux sites Web ». Et
l'annexe B a des conseils pour les gérants de sites Web comme, par
exemple, ne pas permettre aux utilisateurs de demander (via leur
profil, par exemple) d'ignorer la préférence
safe
puisqu'elle a pu être placée par un
logiciel de contrôle parental.
Ce RFC a le statut « pour information » et a été publié sur la voie indépendante (cf. RFC 5742) puisqu'il n'a pas fait l'objet d'un consensus à l'IETF (euphémisme…) Les principales objections étaient :
Date de publication du RFC : Novembre 2019
Auteur(s) du RFC : P. Hallam-Baker, R. Stradling (Sectigo), J. Hoffman-Andrews (Let's Encrypt)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lamps
Première rédaction de cet article le 22 novembre 2019
Ce RFC décrit un mécanisme pour renforcer un peu la sécurité des certificats. Il normalise les enregistrements CAA (Certification Authority Authorization), qui sont publiés dans le DNS, et indiquent quelles AC sont autorisées à émettre des certificats pour ce domaine. Le but est de corriger très partiellement une des plus grosses faiblesses de X.509, le fait que n'importe quelle AC peut émettre des certificats pour n'importe quel domaine, même si ce n'est pas un de ses clients. Ce RFC remplace l'ancienne définition de CAA, qui était dans le RFC 6844.
CAA est donc une technique très différente de DANE (RFC 6698), les seuls points communs étant l'utilisation du DNS pour sécuriser les certificats. DANE est déployé chez le client TLS, pour qu'il vérifie le certificat utilisé, CAA est surtout dans l'AC, pour limiter le risque d'émission d'un certificat malveillant (par exemple, CAA aurait peut-être empêché le faux certificat Gmail du ministère des finances.) Disons que CAA est un contrôle supplémentaire, parmi ceux que l'AC doit (devrait) faire. Les clients TLS ne sont pas censés le tester (ne serait-ce que parce que l'enregistrement CAA a pu changer depuis l'émission du certificat, la durée de vie de ceux-ci étant en général de plusieurs mois). CAA peut aussi servir à des auditeurs qui veulent vérifier les pratiques d'une AC (même avertissement : le certificat a pu être émis alors que l'enregistrement CAA était différent.)
La section 4 de notre RFC présente l'enregistrement CAA. Il a été ajouté au registre des types d'enregistrements sous le numéro 257. Il comprend une série d'options (flags) et une propriété qui est sous la forme {clé, valeur}. Un nom peut avoir plusieurs propriétés. Pour l'instant, une seule option est définie (un registre existe pour les options futures), « issuer critical » qui indique que cette propriété est cruciale : si on ne la comprend pas, le test doit être considéré comme ayant échoué et l'AC ne doit pas produire de certificat.
Les principales propriétés possibles sont (la liste complète est dans le registre IANA) :
issue
, la principale, qui indique une
AC autorisée à émettre des certificats pour ce domaine (l'AC est
indiquée par son nom de domaine),issuewild
, idem, mais avec en plus la
possibilité pour l'AC d'émettre des certificats incluants des
jokers,iodef
, qui indique où l'AC doit envoyer
d'éventuels rapports d'échec, pour que le titulaire du nom de
domaine puisse les corriger. Un URL est indiqué pour cela, et le rapport doit
être au format IODEF (RFC 7970).Voici par exemple quel était l'enregistrement CAA de mon domaine personnel :
% dig CAA bortzmeyer.org ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61450 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 3, AUTHORITY: 7, ADDITIONAL: 7 ... ;; ANSWER SECTION: bortzmeyer.org. 26786 IN CAA 0 issue "cacert.org" bortzmeyer.org. 26786 IN CAA 0 issuewild "\;" ...
Il indique que seule l'AC CAcert peut
créer un certificat pour ce domaine (et sans les jokers). Bon, c'est
un peu inutile car CAcert ne teste pas les enregistrements CAA, mais
c'était juste pour jouer. Je n'ai pas mis d'iodef
mais il aurait pu être :
bortzmeyer.org. CAA 0 iodef "mailto:security@bortzmeyer.org"
Et, dans ce cas, l'AC peut écrire à
security@bortzmeyer.org
, avec le rapport IODEF
en pièce jointe.
Attention, l'enregistrement CAA est valable pour tous les
sous-domaines (et ce n'est pas une option,contrairement à, par
exemple, HSTS du RFC 6797, avec
son includeSubDomains
). C'est pour cela que
j'avais dû retirer l'enregistrement ci-dessus, pour avoir des
certificats pour les sous-domaines, certificats faits par une autre
AC. (Depuis, j'ai mis deux enregistrements CAA, pour les deux AC
utilisées, les autorisations étant additives, cf. section 4.2 du RFC.)
Des paramètres peuvent être ajoutés, le RFC cite l'exemple d'un numéro de client :
example.com. CAA 0 issue "ca.example.net; account=230123"
Une fois les enregistrements CAA publiés, comment sont-ils utilisés (section 3) ? L'AC est censée interroger le DNS pour voir s'il y a un CAA (on note que DNSSEC est très recommandé, mais n'est pas obligatoire, ce qui réduit le service déjà faible qu'offre CAA). S'il n'y en a pas, l'AC continue avec ses procédures habituelles. S'il y a un CAA, deux cas : il indique que cette AC peut émettre un certificat pour le domaine, et dans ce cas-là, c'est bon, on continue avec les procédures habituelles. Second cas, le CAA ne nomme pas cette AC et elle doit donc renoncer à faire un certificat sauf s'il y a une exception configurée pour ce domaine (c'est la deuxième faille de CAA : une AC peut facilement passer outre et donc continuer émettre de « vrais/faux certificats »).
Notez que le RFC ne semble pas évoquer la possibilité d'imposer la présence d'un enregistrement CAA. C'est logique, vu le peu de déploiement de cette technique mais cela veut dire que « qui ne dit mot consent ». Pour la plupart des domaines, la vérification du CAA par l'AC ne changera rien.
Notez que, si aucun enregistrement CAA n'est trouvé, l'AC est
censé remonter l'arbre du DNS. (C'est pour cela que SSL [sic] Labs trouvait un
enregistrement CAA pour
mercredifiction.bortzmeyer.org
: il avait utilisé
le CAA du domaine parent, bortzmeyer.org
.) Si
example.com
n'a pas de CAA, l'AC va tester
.com
, demandant ainsi à
Verisign si son client peut avoir un
certificat et de qui. Cette erreur consistant à grimper sur l'arbre
avait déjà été dénoncée dans le RFC 1535, mais apparemment
la leçon n'a pas été retenue. Au moins, ce RFC corrige une grosse
erreur du RFC 6844, en limitant cette montée
de l'arbre au nom initialement cherché, et pas aux alias
(enregistrements DNS CNAME) éventuels.
Enfin, la section 5 du RFC analyse les différents problèmes de sécurité que peut poser CAA :
Le CA/Browser Forum avait décidé que le test des CAA serait obligatoire à partir du 8 septembre 2017. (Cf. la décision.) Comme exemple, parmi les enregistrements CAA dans la nature, on trouve celui de Google, qui autorise deux AC :
% dig CAA google.com ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55244 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ... ;; ANSWER SECTION: google.com. 86400 IN CAA 0 issue "pki.goog" google.com. 86400 IN CAA 0 issue "symantec.com" ...
(Le TLD
.goog
est apparemment utilisé par Google pour
son infrastructure, .google
étant plutôt pour
les choses publiques.) Notez que gmail.com
,
souvent détourné par des gouvernements et des entreprises qui
veulent surveiller le trafic, a également un enregistrement CAA. Le
célèbre SSL [sic] Labs
teste la présence d'un enregistrement CAA. S'il affiche
« DNS Certification Authority Authorization (CAA) Policy
found for this domain », c'est bon. Regardez le
cas de Google.
Quelques lectures et ressources pour finir :
urn:acme:error:caa
, « CAA record for
DOMAIN_NAME prevents issuance ». Le monde étant cruel,
et les logiciels souvent bogués, cela a d'ailleurs entrainé un
amusant problème.La section 7 de ce RFC décrit les changements depuis le RFC 6844. Sur la forme, le RFC a été profondément
réorganisé. Sur le fond, le principal changement est que la
procédure de montée dans l'arbre du DNS, très dangereuse, a été
légèrement sécurisée en la limitant au nom lui-même, et pas aux
alias. En effet, le RFC 6844 prévoyait que, si
on cherchait le CAA de something.example.com
,
qu'on ne le trouvait pas,
et que something.example.com
était en fait un
alias vers other.example.net
, on remonte
également l'arbre en partant de
example.net
. Cette idée a été heureusement
abandonnée (mais, pour something.example.com
,
on testera quand même example.com
et
.com
, donc le registre
de .com
garde la possibilité de mettre un CAA
qui s'appliquera à tous les sous-domaines…)
Autre changement, la section 6, sur les questions de déploiement, qui intègre l'expérience pratique obtenue depuis le RFC 6844. Notamment :
Le reste des changements depuis le RFC 6844
porte sur des points de détails comme une clarification de la
grammaire, ou bien des précisions sur la sémantique des
enregistrements CAA lorsque des propriétés comme
issue
sont absentes.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : T. Reddy (McAfee), A. Johnston (Villanova University), P. Matthews (Alcatel-Lucent, apparemment en fait Nokia, J. Rosenberg (jdrosen.net)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tram
Première rédaction de cet article le 23 février 2020
Le protocole TURN, que décrit notre RFC, est le dernier recours des applications coincées derrière un routeur NAT et qui souhaitent communiquer avec une application dans la même situation (cf. RFC 5128). Avec TURN, le serveur STUN ne se contente pas d'informer sur l'existence du NAT et ses caractéristiques, il relaie chaque paquet de données. Ce nouveau RFC remplace la définition originelle de TURN (dans le RFC 5766), et inclut plusieurs changements qui étaient jusqu'à présent spécifiés dans des RFC séparés, comme IPv6 ou DTLS, et ajoute la possibilité de relayer les messages ICMP.
Être situé derrière un routeur NAT n'est jamais une situation enviable. De nombreuses applications fonctionnent mal ou pas du tout dans ce contexte, nécessitant des mécanismes spécifique de traversée des NAT. Le socle de tous ces mécanismes de traversée est STUN (RFC 8489), où le client STUN (notre machine bloquée par le NAT) communique avec un serveur STUN situé dans le monde libre pour apprendre sa propre adresse IP externe. Outre cette tâche de base, des extensions à STUN permettent d'aider davantage le client, c'est par exemple le cas de TURN que normalise notre RFC.
L'idée de base est que deux machines Héloïse et Abélard, chacune peut-être située derrière un NAT, vont utiliser STUN pour découvrir s'il y a un NAT entre elles (sinon, la communication peut se faire normalement) et, s'il y a un NAT, s'il se comporte « bien » (tel que défini dans les RFC 4787 et RFC 5382). Dans ce dernier cas, STUN seul peut suffire, en informant les machines de leur adresse extérieure et en ouvrant, par effet de bord, un petit trou dans le routeur pour permettre aux paquets entrants de passer.
Mais, parfois, le NAT ne se comporte pas bien, par exemple parce qu'il a le comportement « address-dependent mapping » (RFC 4787, section 4.1). Dans ce cas, il n'existe aucune solution permettant le transport direct des données entre Héloïse et Abélard. La solution utilisée par tous les systèmes pair-à-pair, par exemple en téléphonie sur Internet, est de passer par un relais. Normalement, le serveur STUN ne sert qu'à un petit nombre de paquets, ceux de la signalisation et les données elles-mêmes (ce qui, en téléphonie ou en vidéo sur IP, peut représenter un très gros volume) vont directement entre Héloïse et Abélard. Avec TURN, le serveur STUN devient un relais, qui transmet les paquets de données.
TURN représente donc une charge beaucoup plus lourde pour le serveur, et c'est pour cela que cette option est restreinte au dernier recours, au cas où on ne peut pas faire autrement. Ainsi, un protocole comme ICE (RFC 8445) donnera systématiquement la préférence la plus basse à TURN. De même, le serveur TURN procédera toujours à une authentification de son client, car il ne peut pas accepter d'assurer un tel travail pour des inconnus (l'authentification - fondée sur celle de STUN, RFC 8489, section 9.2 - et ses raisons sont détaillées dans la section 5). Il n'y aura donc sans doute jamais de serveur TURN complètement public mais certains services sont quand même disponibles comme celui de Viagénie.
TURN est défini comme une extension de STUN, avec de nouvelles
méthodes et de nouveaux attributs (enregistrés à
l'IANA). Le client TURN envoie donc une requête STUN, avec
une méthode d'un type nouveau, Allocate
, pour
demander au serveur de se tenir prêt à relayer (les détails du
mécanisme d'allocation figurent dans les sections 6 et 7). Le client
est enregistré par son adresse de transport
(adresse IP publique et port). Par exemple, si Héloise a pour
adresse de transport locale 10.1.1.2:17240
(adresse IP du RFC 1918 et port n° 17240), et
que le NAT réécrit cela en 192.0.2.1:7000
, le
serveur TURN (mettons qu'il écoute en
192.0.2.15
) va, en réponse à la requête
Allocate
, lui allouer, par exemple,
192.0.2.15:9000
et c'est cette dernière adresse
qu'Héloïse va devoir transmettre à Abélard pour qu'il lui envoie des
paquets, par exemple RTP. Ces paquets arriveront donc au serveur
TURN, qui les renverra à 192.0.2.1:7000
, le
routeur NAT les transmettant ensuite à
10.1.1.2:17240
(la transmission des données
fait l'objet des sections 11 et 12). Pour apprendre l'adresse
externe du pair, on utilise ICE ou bien un protocole de
« rendez-vous » spécifique (RFC 5128.) Un
exemple très détaillé d'une connexion faite avec TURN figure dans la
section 20 du RFC.
Ah, et comment le serveur TURN a-t-il choisi le port 9000 ? La section 7.2 détaille les pièges à éviter, notamment pour limiter le risque de collision avec un autre processus sur la même machine, et pour éviter d'utiliser des numéros de port prévisibles par un éventuel attaquant (cf. RFC 6056).
Et pour trouver le serveur TURN à utiliser ? Il peut être marqué
en dur dans l'application, mais il y a aussi la solution de
découverte du RFC 8155. Pour noter l'adresse d'un serveur TURN,
on peut utiliser les plans d'URI
turn:
et turns:
du RFC 7065.
TURN fonctionne sur IPv4 et IPv6. Lorsqu'IPv4 et IPv6 sont possibles, le serveur TURN doit utiliser l'algorithme du RFC 8305, pour trouver le plus rapidement possible un chemin qui fonctionne.
TURN peut relayer de l'UDP, du TCP (section 2.1), du TLS ou du DTLS, mais le serveur TURN enverra toujours de l'UDP en sortie (une extension existe pour utiliser TCP en sortie, RFC 6062) La section 3.1 explique aussi pourquoi accepter du TCP en entrée quand seul UDP peut être transmis : la principale raison est l'existence de pare-feux qui ne laisseraient sortir que TCP. La meilleure solution, recommandée par le RFC, serait quand même qu'on laisse passer l'UDP, notamment pour WebRTC (RFC 7478, section 2.3.5.1 et RFC 8827.)
Ah, et à propos d'UDP, notre RFC recommande d'éviter la fragmentation, et donc d'envoyer vers le pair qui reçoit les données des paquets UDP suffisamment petits pour ne pas avoir besoin de la fragmentation, celle-ci n'étant pas toujours bien gérée par les middleboxes.
Les données peuvent circuler dans des messages STUN classiques
(nommés Send
et Data
,
cf. section 11) ou bien dans des canaux virtuels
(channels, sortes de sous-connexions, section 12)
qui permettent d'éviter de transmettre les en-têtes STUN à chaque
envoi de données.
Enfin, la section 21, très détaillée, note également que TURN ne peut pas être utilisé pour contourner la politique de sécurité : l'allocation ne se fait que pour une adresse IP d'un correspondant particulier (Abélard), TURN ne permet pas à Héloïse de faire tourner un serveur. Ce point permet de rassurer les administrateurs de pare-feux et de leur demander de ne pas bloquer TURN.
Autre point important de cette section sur la sécurité : comme certains messages ne sont pas authentifiés, un méchant peut toujours envoyer au client des messages qui semblent venir du serveur et réciproquement. Le problème existe, mais c'est un problème plus général d'IP. TURN ne le résout pas mais ne l'aggrave pas (section 21.1.4). Pour le cas du multimédia, on peut par exemple utiliser SRTP (RFC 3711) pour empêcher cette attaque.
Comme TURN relaie les paquets, au lieu de simplement les router, l'adresse IP source va identifier le serveur TURN et pas le vrai expéditeur. Un méchant pourrait donc être tenté d'utiliser TURN pour se cacher. Il peut donc être utile que le serveur TURN enregistre les données comme l'adresse IP et le port de ses clients, pour permettre des analyses a posteriori.
Une question intéressante est celle du traitement des en-têtes IP (voir la 3.6). TURN travaille dans la couche 7, il n'est pas un « routeur virtuel » mais un relais applicatif. En conséquence, il ne préserve pas forcément des en-têtes comme ECN ou comme le TTL. D'ailleurs, un serveur TURN tournant comme une application sans privilèges particuliers n'a pas forcément accès aux valeurs de l'en-tête IP. Tout cela permet son déploiement sur des systèmes d'exploitation quelconque, où il n'est pas forcément facile de changer ces en-têtes. Pour la même raison, la découverte traditionnelle de MTU (RFC 1191) à travers TURN ne marche donc pas.
À propos d'implémentations, il existe plusieurs mises en œuvre libres de TURN, comme turnserver, CoTurn, Restund ou Pion.
Les changements depuis les RFC précédents, notamment le RFC 5766, sont résumés dans les sections 24 et 25 :
REQUESTED-ADDRESS-FAMILY
et il y a de
nouveaux codes d'erreur comme
440 ou 443,turn:
et
turns:
(RFC 7065) ont
été intégrés,Date de publication du RFC : Août 2019
Auteur(s) du RFC : J. Borkenhagen (AT&T), R. Bush (IIJ & Arrcus), R. Bonica (Juniper Networks), S. Bayraktar (Cisco Systems)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF grow
Première rédaction de cet article le 3 novembre 2019
Le protocole d'annonces de routes BGP permet d'attacher aux annonces des étiquettes, les communautés, qui sont des métadonnées pour les routes. Certaines valeurs sont réservées pour des communautés « bien connues » qui sont censées donner le même résultat partout. Mais ce n'est pas vraiment le cas, comme l'explique ce RFC, qui demande qu'on améliore la situation.
Les communautés sont normalisées dans le RFC 1997, qui décrit par la même occasion le concept de communauté bien connue. Celles-ci sont enregistrées à l'IANA. Voici un exemple d'annonce BGP avec des communautés :
TIME: 11/03/19 09:14:47 TYPE: BGP4MP/MESSAGE/Update FROM: 89.149.178.10 AS3257 TO: 128.223.51.102 AS6447 ASPATH: 3257 8966 17557 136030 138368 NEXT_HOP: 89.149.178.10 COMMUNITY: 3257:4000 3257:8102 3257:50001 3257:50110 3257:54900 3257:54901 65535:65284 ANNOUNCE 103.131.214.0/24
Cette annonce du préfixe 103.131.214.0/24
contient sept communautés, dont une bien connue,
65535:65284
(0xFFFFFF04
en
hexadécimal), NOPEER
, normalisée dans le RFC 3765.
Le RFC estime que le RFC 1997 était un peu trop flou, et que cela explique partiellement les différences que nous observons aujourd'hui.
Ainsi, le changement d'une communauté par la politique
locale d'un AS. Un routeur
BGP qui reçoit une annonce avec des
communautés peut évidemment modifier ces communautés (typiquement en
ajouter, mais parfois aussi en enlever). Tous les modèles de
routeurs permettent donc de modifier les communautés, entre autres
en fournissant une commande, appelée
set
ou un nom de ce genre, qui remplace les communautés
par un autre ensemble de communautés. Toutes les communautés ? Non,
justement, c'est là qu'est le problème : sur certains routeurs, les
communautés bien connues sont épargnées par cette opération, mais pas
sur d'autres routeurs.
(Personnellement, cela me semble un problème d'interface utilisateur, qui ne concerne pas vraiment le protocole. Mais je cite l'opinion du RFC, qui trouve cette différence de comportement ennuyeuse, par exemple parce qu'elle peut créer des problèmes si un technicien, passant sur un nouveau type de routeur, suppose qu'une commande ayant le même nom va avoir la même sémantique.)
La section 4 du RFC liste les comportements constatés sur les routeurs :
community
set
remplace toutes les communautés, bien connues ou
pas,set community
a le même effet,community set
,replace
,set
community
remplace toutes les communautés
sauf certaines communautés bien connues,
comme NO_EXPORT
, ces communautés doivent être
retirées explicitement si on veut un grand remplacement ; la liste
des communautés ainsi préservées n'est même pas la liste enregistrée à l'IANA,set
community
ne supprime aucune des communautés
existantes, qu'elles soient bien connues ou pas.La section 5 de notre RFC souhaite donc que, pour les futures communautés spécifiées dans de futurs RFC, le comportement (remplaçable ou pas) soit précisé par l'IETF.
À destination, cette fois, des gens qui font le logiciel des routeurs, la section 6 suggère :
set
; que font-elles aux communautés bien
connues ?
Quant aux opérateurs réseau, le RFC leur rappelle qu'on ne peut
jamais être sûr de ce que vont faire les AS
avec qui on s'appaire, et qu'il vaut mieux vérifier avec eux ce
qu'ils font des NO_EXPORT
ou autres communautés
bien connues qu'on met dans les annonces qu'on leur envoie.
Date de publication du RFC : Juillet 2019
Auteur(s) du RFC : D. Reilly (Orolia USA), H. Stenn
(Network Time Foundation), D. Sibold
(PTB)
Réalisé dans le cadre du groupe de travail IETF ntp
Première rédaction de cet article le 27 octobre 2019
Le protocole NTP, qui sert à synchroniser les horloges sur l'Internet, est probablement un des plus vieux protocoles encore en service. Il est normalisé dans le RFC 5905. Ce nouveau RFC ne change pas la norme, il rassemble simplement un ensemble de bonnes pratiques pour l'utilisation de NTP. Sa lecture est donc très recommandée à toutes les personnes qui gèrent la synchronisation d'horloges dans leur organisation.
Bien sûr, tout dépend des niveaux d'exigence de cette organisation. Quel écart entre les horloges acceptez-vous ? Quel est le risque d'une attaque délibérée contre les serveurs de temps que vous utilisez ? À vous de lire ces bonnes pratiques et de les adapter à votre cas.
Le RFC commence par un conseil de sécurité réseau général (section 2). NTP est fondé sur UDP, ce qui signifie que le protocole de transport ne protège pas contre les usurpations d'adresses. Et comme les réponses NTP sont souvent bien plus grosses (en octets) que les questions, des attaques par réflexion et amplification sont possibles, et effectivement observées dans la nature. (Le RFC recommande également la lecture de « Technical Details Behind a 400Gbps NTP Amplification DDoS Attack », de « Taming the 800 Pound Gorilla: The Rise and Decline of NTP DDoS Attacks » et de « Attacking the Network Time Protocol ».) Il est donc nécessaire que les opérateurs réseau déploient les mesures « BCP 38 » contre les usurpations d'adresses IP.
Après ce conseil général, qui concerne également d'autres protocoles, comme DNS ou SNMP, la section 3 du RFC se penche sur les bonnes pratiques de configuration de NTP. Évidemment, il faut maintenir les logiciels à jour (ce qu'on oublie plus facilement lorsqu'il s'agit d'un protocole d'infrastructure, comme NTP).
Moins évidente, la nécessité d'avoir plusieurs sources de
temps. Une source donnée peut être malveillante, ou tout
simplement incorrecte. Or, NTP peut utiliser plusieurs sources, et
trouver le temps correct à partir de ces sources (les détails sont
dans le RFC 5905, et dans le livre de
D. Mills, « Computer network time synchronization: the
Network Time Protocol ».) Trois sources sont le minimum,
quatre sont recommandées, si, bien sûr, elles sont suffisamment
diverses, et dignes de confiance. (Au passage,
Renater gère une liste de
serveurs NTP en France mais elle ne semble pas à jour,
chronos.cru.fr
n'existe plus, il y manque le serveur de
l'AFNIC, ntp.nic.fr
, etc.)
Ce conseil de chercher plusieurs serveurs suppose évidemment
que ces serveurs sont indépendants : s'ils prennent tous le temps
depuis une même source et que celle-ci est déréglée ou
malveillante, avoir plusieurs serveurs ne suffira pas. Il est donc
également nécessaire de veiller à la diversité des horloges
sous-jacentes. Avoir plusieurs serveurs mais connectés à des
horloges du même vendeur fait courir le risque d'un problème
commun à toutes. La diversité doit aussi s'appliquer au choix du
type d'horloge : si on utilise plusieurs horloges, mais toutes
fondées sur des constellations de
satellites, une tache solaire va les perturber tous en même
temps. Autre risque : un problème DNS
supprimant le nom de
domaine, comme c'était arrivé à
usno.navy.mil
(l'USNO), en
décembre 2018 et surtout à ntp.org
en janvier
2017 (cf. cette
discussion ou bien celle-ci).
Autre question, les messages de contrôle de NTP. Introduits
dans l'annexe B du RFC 1305, qui normalisait
la version 3 de NTP, ils n'ont pas été conservés pour la version 4
(RFC 5905). (Un projet existe à
l'IETF pour les remettre,
cf. draft-ietf-ntp-mode-6-cmds
.) Utiles à l'administrateur
système pour la gestion de ses serveurs, ces messages
peuvent être dangereux, notamment en permettant des attaques par réflexion, avec
amplification. La bonne pratique est donc de ne pas les
ouvrir au monde extérieur, seulement à son réseau. Des exemples de
configurations restrictives figurent à la fin de cet article.
Il est évidemment nécessaire de superviser ses serveurs NTP, afin de s'assurer qu'ils sont en marche, et, surtout, du fait qu'ils soient bien synchronisés. Des exemples, utilisant Icinga, figurent à la fin de cet article.
Un serveur NTP qui sert des dizaines de milliers de clients
peut nécessiter beaucoup de ressources réseau. Il est donc
important de n'utiliser comme serveur que des serveurs qu'on est
autorisé à questionner (ce qui est le cas des serveurs publics
comme ntp.nic.fr
). Il existe hélas de
nombreux exemples d'abus de serveurs NTP, le
plus célèbre étant sans doute celui du serveur de
Poul-Henning Kamp par
D-Link.
Pour permettre à tous et toutes de synchroniser leurs horloges, le projet « NTP Pool » a été créé. De nombreux volontaires mettent à la disposition de tous leurs serveurs NTP. L'heure ainsi distribuée est en général de bonne qualité mais, évidemment, le projet ne peut fournir aucune garantie. Il convient bien pour les configurations par défaut distribuées avec les logiciels, ou pour des machines non critiques. Autrement, il faut utiliser des serveurs « de confiance ».
Pour l'utiliser, il faut regarder les
instructions (elles existent aussi en
français). En gros, on doit indiquer comme serveurs NTP des
noms pris sous pool.ntp.org
et ces noms
pointeront, au hasard, vers des machines différentes, de manière à
répartir la charge. Voici un exemple (avec un serveur français) :
% dig +short A 0.fr.pool.ntp.org 5.196.192.58 51.15.191.239 92.222.82.98 162.159.200.123
Mais quelque temps après, les adresses IP auront changé.
% dig +short A 0.fr.pool.ntp.org 80.74.64.2 212.83.154.33 94.23.99.153 37.187.5.167
Voici un
exemple de configuration avec le serveur NTP habituel, dans son
ntp.conf
:
pool 0.fr.pool.ntp.org iburst pool 1.fr.pool.ntp.org iburst pool 2.fr.pool.ntp.org iburst pool 3.fr.pool.ntp.org iburst
Ainsi, on aura quatre serveurs, pointant vers des adresses réparties dans le lot. Avec OpenNTPd, ce serait :
servers fr.pool.ntp.org
L'administrateur système ne pense pas
toujours à ajuster la configuration, donc beaucoup de fournisseurs
de logiciels ont un
sous-domaine de pool.ntp.org
, utilisé
dans les configurations livrées avec leurs serveurs NTP. Par
exemple, pour OpenWrt, le fichier de
configuration par défaut contiendra :
server 0.openwrt.pool.ntp.org iburst server 1.openwrt.pool.ntp.org iburst server 2.openwrt.pool.ntp.org iburst server 3.openwrt.pool.ntp.org iburst
Un problème récurrent des horloges sur
l'Internet est celui des
secondes intercalaires. La
Terre étant imparfaite, il faut de temps en
temps corriger le temps
universel avec ces secondes intercalaires. Jusqu'à
présent, on a toujours ajouté des secondes, puisque la Terre
ralentit, mais, en théorie, on pourrait avoir à en
retirer. Il existe un temps qui n'a pas ce problème,
TAI, mais, outre qu'il s'éloigne petit à
petit du temps astronomique, il n'a pas été retenu dans les normes
comme POSIX (ou NTP,
qui ne connait qu'UTC…) Il faut donc gérer les soubresauts d'UTC,
et c'est une source de bogues sans fin. Les secondes intercalaires
ne sont pas prévisibles longtemps à l'avance (je vous avait dit
que la Terre est imparfaite) et il faut donc lire
les bulletins de l'IERS (en l'occurrence le bulletin C)
pour se tenir au courant. Notez que ce bulletin n'est pas écrit
sous une forme structurée, lisible par un programme, donc on
pourra préférer le leap-seconds.list
,
disponible en plusieurs endroits. Pour un
serveur NTP, une autre solution est d'utiliser des horloges qui
distribuent de l'information sur les secondes intercalaires
prévues. C'est le cas du GPS ou de
DCF77. Dans ce cas, le serveur NTP peut se
préparer un peu à l'avance (ce qui n'évite pas les bogues…)
Autre problème amusant, noté par le RFC, le leap smearing, qui consiste à lisser l'arrivée d'une seconde intercalaire au lieu de brutalement décaler l'horloge d'une seconde. Lors de la seconde intercalaire de juin 2015, certains serveurs NTP faisaient du leap smearing et pas d'autres, ce qui semait la confusion chez les clients qui avaient un mélange de ces deux types de serveurs. Le leap smearing n'est pas conforme à la norme NTP et ne doit donc pas être utilisé sur des serveurs NTP publics, d'autant plus que le protocole ne permet pas de savoir si le serveur utilise ce smearing ou pas. Dans un environnement fermé, par contre, on fait évidemment ce qu'on veut.
Concernant ce leap smearing, le RFC note qu'il peut poser des problèmes juridiques : l'horloge de la machine ne sera pas en accord avec l'heure légale, ce qui peut créer des histoires en cas, par exemple, d'accès des autorités aux journaux.
Passons maintenant aux mécanismes de sécurité de NTP (section 4
du RFC). L'analyse des risques a été faite dans le RFC 7384, notre RFC rappelle les moyens de faire face à ces
risques. Il y a bien sûr les clés partagées (la directive
keys /etc/ntp/ntp.keys
dans le serveur NTP
classique). NTP n'a pas de mécanisme de distribution de ces clés,
il faut le faire en dehors de NTP (copier
/etc/ntp/ntp.keys
…), ce qui ne marche donc
pas pour des serveurs publics. (Comme il y a toujours des lecteurs
qui me disent « mais c'est pas pratique de recopier les clés à la
main sur toutes les machines », je rappelle l'existence
d'Ansible, et autres outils analogues.) À
noter que le seul algorithme de
condensation normalisé pour l'utilisation
de ces clés est MD5, clairement dangereux
(RFC 6151). Ceci dit, d'autres algorithmes
sont parfois acceptés par les mises en œuvre de NTP, cf. RFC 8573. (Opinion
personnelle : MD5 vaut mieux que pas de sécurité du tout.)
Et… c'est tout. Il n'existe pas actuellement d'autre mécanisme de sécurité pour NTP. Le système Autokey, normalisé dans le RFC 5906 a été abandonné, en raison de ses vulnérabilités. Un travail était en cours à l'époque pour lui concevoir un successeur, ce qui a donné le RFC 8915.
La section 5 de notre RFC résume les bonnes pratiques en matière de sécurité NTP :
broadcast
et
broadcastclient
dans le serveur NTP), ne
devrait se faire que si ledit réseau est de confiance et que les
informations sont authentifiées.peer
).Enfin, la section 6 du RFC couvre le cas particulier des systèmes embarqués. Par exemple, les objets connectés ont une fâcheuse tendance à rester en service des années sans mise à jour. S'ils ont été configurés en usine avec une liste de serveurs NTP, et que certains de ces serveurs disparaissent ensuite, l'objet risque de ne plus pouvoir se synchroniser ou, pire, il va matraquer une machine innocente qui a récupéré l'adresse d'un serveur NTP (cf. RFC 4085). Il est donc important que les clients NTP puissent mettre à jour la liste de leurs serveurs. D'autre part, la liste doit évidemment être correcte dès le début, et ne pas inclure des serveurs NTP, même publics, sans leur autorisation. Une solution simple est de passe par le le projet « NTP Pool ».
L'annexe A de notre RFC rassemble des conseils qui sont
spécifiques à une mise en œuvre de NTP, celle de la Network Time
Foundation, le « code NTP original »
(paquetage ntp
sur
Debian ou ArchLinux).
Pour obtenir une variété de sources, le
démon « ntpd » fourni a la directive
pool
, qui permet de désigner un ensemble de
serveurs :
pool 0.debian.pool.ntp.org iburst pool 1.debian.pool.ntp.org iburst pool 2.debian.pool.ntp.org iburst pool 3.debian.pool.ntp.org iburst
NTP a la possibilité de recevoir des messages de contrôle (annexe B du RFC 1305). Cela peut être dangereux, et il est recommandé d'en restreindre l'accès. Avec le serveur habituel ntpd, c'est bien documenté (mais cela reste complexe et pas intuitif du tout). Voici un exemple :
restrict default noquery nopeer nomodify notrap restrict ::1 restrict 2001:db8:b19:3bb0:: mask ffff:ffff:ffff:ffff:: notrust restrict 2001:db8:2fab:e9d0:d40b:5ff:fee8:a36b nomodify
Dans cet exemple, la politique par défaut (première ligne) est de
ne rien autoriser. Toute machine qui tenterait de parler au
serveur NTP serait ignorée. Ensuite, la machine locale
(::1
, deuxième ligne) a tous les droits
(aucune restriction). Entre les deux (troisième ligne), les
machines du réseau local
(2001:db8:b19:3bb0::/64
) ont le droit de
parler au serveur seulement si elles sont authentifiées
cryptographiquement. Enfin, la machine
2001:db8:2fab:e9d0:d40b:5ff:fee8:a36b
a le
droit de lire le temps chez nous mais pas de le modifier.
On avait parlé plus haut de l'importance de superviser ses services de temps. Voici une configuration avec Icinga pour faire cela :
apply Service "ntp-time" { import "generic-service" check_command = "ntp_time" assign where (host.address || host.address6) && host.vars.ntp } ... object Host "foobar" { ... vars.ntp = true
Avec cette configuration, la machine foobar
sera
supervisée. On peut tester depuis la ligne de commande que le
monitoring plugin arrive bien à lui parler :
% /usr/lib/nagios/plugins/check_ntp_time -H foobar NTP OK: Offset 7.331371307e-06 secs|offset=0.000007s;60.000000;120.000000;
Si la réponse avait été CRITICAL - Socket timeout after 10
seconds
, on aurait su que le serveur refuse de nous parler.
Ah, et puisqu'on a parlé de sécurité et de protéger (un peu) NTP par la cryptographie, voici un exemple (non, ce ne sont pas mes vrais clés, je vous rassure) :
% cat /etc/ntp/ntp.keys ... 13 SHA1 cc5b2e7c400e778287a99b273b19dc68369922b9 # SHA1 key % cat /etc/ntp.conf ... keys /etc/ntp/ntp.keys trustedkey 13
Avec cette configuration (le fichier ntp.keys
peut être généré avec la commande ntp-keygen
), le serveur NTP acceptera les messages
protégés par la clé numéro 13. Sur le client, la configuration sera :
keys /etc/ntp/ntp.keys server SERVERNAME key 13
Quelques petits trucs pour finir. Avec ntpd, comment voir l'état des pairs avec qui ont est connectés ? Ici, on a configuré l'utilisation du pool :
% ntpq -pn remote refid st t when poll reach delay offset jitter ============================================================================== 0.debian.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 1.debian.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 2.debian.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 3.debian.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 -162.159.200.123 10.19.11.58 3 u 25 128 377 1.381 -2.439 0.199 -164.132.45.112 43.13.124.203 3 u 8 128 377 5.507 -1.423 0.185 +212.83.145.32 193.200.43.147 2 u 4 128 377 1.047 -1.823 0.455 +5.196.160.139 145.238.203.14 2 u 12 128 377 5.000 -0.981 0.291 -163.172.61.210 145.238.203.14 2 u 8 128 377 1.037 -0.888 0.246 *82.64.45.50 .GPS. 1 u 71 128 377 11.116 -1.178 0.549 -178.249.167.0 193.190.230.65 2 u 3 128 377 6.233 -1.026 0.145 -194.57.169.1 145.238.203.14 2 u 2 128 377 10.660 -0.931 0.233 -151.80.124.104 193.204.114.232 2 u 16 128 377 4.888 -1.414 0.354
Autre commande utile, pour comparer l'heure locale avec les serveurs NTP :
% ntpdate -q ntp.nic.fr server 2001:67c:2218:2::4:12, stratum 2, offset 0.000520, delay 0.03194 server 2001:67c:2218:2::4:13, stratum 2, offset 0.000746, delay 0.03175 server 192.134.4.12, stratum 2, offset 0.000509, delay 0.03127 server 192.134.4.13, stratum 2, offset 0.000596, delay 0.04376 28 Oct 10:54:08 ntpdate[18996]: adjust time server 192.134.4.12 offset 0.000509 sec
Et avec un serveur NTP complètement différent ? Essayons avec OpenNTPD :
% cat /etc/ntpd.conf # No way to restrict per IP address :-( Use a firewall listen on * servers fr.pool.ntp.org sensor *
Avec cette configuration, la machine va se synchroniser au
pool, cequ'on pourra vérifier avec ntpctl
:
% sudo ntpctl -s all 4/4 peers valid, clock synced, stratum 4 peer wt tl st next poll offset delay jitter 162.159.200.123 from pool fr.pool.ntp.org 1 10 3 23s 30s -2.270ms 4.795ms 0.027ms 193.52.136.2 from pool fr.pool.ntp.org 1 10 2 32s 34s -1.904ms 18.058ms 1.788ms 91.121.96.146 from pool fr.pool.ntp.org * 1 10 3 0s 31s -1.147ms 1.872ms 0.069ms 62.210.213.21 from pool fr.pool.ntp.org 1 10 2 1s 34s -0.367ms 4.989ms 0.067ms
Date de publication du RFC : Juillet 2019
Auteur(s) du RFC : E. Wilde
Pour information
Première rédaction de cet article le 21 juillet 2019
Le Web, ce sont les pages auxquelles on accède depuis son navigateur, avec les textes à lire et les images à regarder. Mais ce sont aussi de nombreuses applications, avec une API, prévues pour être utilisées depuis un programme spécifique, pas depuis le navigateur Web. Ces Web services ont un ou plusieurs URL pour les appeler, et des ressources supplémentaires comme la documentation. Ce nouveau RFC décrit un type de liens hypertextes permettant de trouver l'URL de la documentation d'un service.
Normalement, on peut interagir avec un service Web sans connaitre les détails à l'avance. La négociation de contenu, par exemple (RFC 7231, sections 3.4 et 5.3) permet de choisir dynamiquement le type de données. En combinant les outils de l'architecture Web (URI, HTTP, etc), on peut créer des services plus simples que les anciennes méthodes compliquées, type CORBA. (Le terme de service REST est souvent utilisé pour ces services modernes et simples.) Mais cela ne dispense pas complètement de documentation et de description des services. (La documentation est du texte libre, conçue pour les humains, la description est sous un format structuré, et conçue pour les programmes.) Il faut donc, pour accéder à un service, trouver documentation et description. C'est ce que propose ce RFC, avec de nouveaux types de liens (les types de liens sont décrits dans le RFC 8288).
Notez bien que ce RFC ne dit pas comment doit être écrite la documentation, ou sous quel format structurer la description. Un format de description courant aujourd'hui est OpenAPI, fondé sur JSON. Mais il en existe d'autres comme RAML (fondé sur YAML) ou RSDL, si vous avez des expériences concrètes sur ces langages, ou des opinions sur leurs avantages et inconvénients, je suis intéressé. (Dans le passé, on utilisait parfois WSDL). Ce RFC fournit juste un moyen de trouver ces descriptions. (En prime, il permet également de trouver l'URL d'un service décrivant l'état actuel d'un service, permettant d'informer, par exemple, sur des pannes ou sur des opérations de maintenance.)
Parfois, documentation et description sont fusionnées en un seul ensemble de ressources. Dans ce cas, on n'est pas obligé d'utiliser notre RFC, on peut se contenter du type de lien décrit dans le RFC 5023.
Les quatre nouveaux types de liens (section 4 du RFC) sont :
service-doc
pour indiquer où se
trouve la documentation (écrite pour des
humains),service-desc
pour donner accès à la
description (conçue pour des
programmes),service-meta
pour
l'URI des
méta-informations diverses sur le service, comme des
informations à caractère juridique (politique « vie privée » du
service, par exemple),status
pour l'état actuel du service.Ces types sont notés dans le registre IANA des types de liens (section 6 du RFC).
Un exemple dans un document HTML serait, pour indiquer la documentation :
<link rel="service-doc" type="text/html" title="My documentation" href="https://api.example.org/documentation.html"/>
Et dans les en-têtes HTTP, ici pour indiquer la description :
Link: <https://api.example.org/v1/description.json> rel="service-desc"; type="application/json"
Si vous voulez voir un exemple réel, il y en a un dans le DNS Looking Glass. Les en-têtes HTTP, et le code HTML contiennent un lien vers la documentation.
La section 5 est consacrée à
status
, qui permet d'indiquer une ressource
sur le Web donnant des informations sur l'état du
service. On peut voir par exemple la page de Github ou
bien celle de
CloudFlare. (Évidemment, il est recommandé qu'elle soit hébergée sur
une infrastructure différente de celle du service dont elle
indique l'état de santé, pour éviter que le même problème
DNS, BGP ou autre ne
plante le service et son bulletin de santé en même temps. C'est ce
que ne fait pas la page
de Framasoft, qui utilise le même nom de domaine.) Aucune obligation sur le contenu
auquel mène le lien, cela peut être un texte conçu pour un humain
ou pour un programme.
Quelques considérations de sécurité pour finir (section 7 du RFC). D'abord, toute documentation peut être utilisée par les gentils utilisateurs, mais aussi par les méchants attaquants. Il peut donc être prudent de ne donner dans la documentation que ce qui est nécessaire à l'utilisation du service. D'autre part, la description (ce qui est en langage formel, analysable par un programme) peut permettre davantage d'automatisation. C'est bien son but, mais cela peut aider les attaquants à automatiser les attaques. Sans même parler d'attaque délibérée, le RFC note aussi que cette automatisation, utilisée par un programme client mal écrit, peut mener à une charge importante du service si, par exemple, le client se met à utiliser sans limitation toutes les options qu'il découvre.
Enfin, tout programmeur et toute programmeuse sait bien que les documentations ne sont pas toujours correctes. (Ou, plus charitablement, qu'elles ne sont pas toujours à jour.) Le programme client ne doit donc pas faire une confiance aveugle à la documentation ou à la description et doit se préparer à des comportements imprévus de la part du service.
À part le DNS Looking Glass, je n'ai pas encore trouvé de service Web qui utilise ces types de liens. Si vous en voyez un, vous me prévenez ?
Date de publication du RFC : Juin 2019
Auteur(s) du RFC : P. Wouters (Red Hat), O. Sury (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 2 septembre 2019
Quel algorithme de cryptographie choisir pour mes signatures DNSSEC, se demande l'ingénieur système. Lesquels doivent être reconnus par le logiciel que j'écris, s'interroge la programmeuse. Il y a bien un registre IANA des algorithmes normalisés mais c'est juste une liste non qualifiée, qui mêle des algorithmes de caractéristiques très différentes. Ce nouveau RFC vise à répondre à cette question en disant quels sont les algorithmes recommandés. Il remplace l'ancien RFC 6944, qui est modifié considérablement. Notamment, il marque l'avantage désormais donné aux courbes elliptiques par rapport à RSA.
La précédente liste d'algorithmes possibles datait donc du RFC 6944. D'autres algorithmes ont été ajoutés par la suite. Certains sont devenus populaires. Par exemple, ECDSA est maintenant suffisamment répandu pour qu'un résolveur validant ne puisse plus raisonnablement l'ignorer. D'autres algorithmes ont été peu à peu abandonnés, par exemple parce que les progrès de la cryptanalyse les menaçaient trop.
Aujourd'hui, le développeur qui écrit ou modifie un signeur (comme ldns, utilisé par OpenDNSSEC) ou un logiciel résolveur validant (comme Unbound ou Knot) doit donc se taper pas mal de RFC mais aussi pas mal de sagesse collective distillée dans plusieurs listes de diffusion pour se faire une bonne idée des algorithmes que son logiciel devrait gérer et de ceux qu'il peut laisser tomber sans trop gêner ses utilisateurs. Ce RFC vise à lui simplifier la tâche, en classant ces algorithmes selon plusieurs niveaux.
Notre RFC 8624 détermine pour chaque algorithme s'il est indispensable (MUST, nécessaire pour assurer l'interopérabilité), recommandé (RECOMMENDED, ce serait vraiment bien de l'avoir, sauf raison contraire impérieuse), facultatif (MAY, si vous n'avez rien d'autre à faire de vos soirées que de programmer) ou tout simplement déconseillé (NOT RECOMMENDED), voire à éviter (MUST NOT, pour le cas de faiblesses cryptographiques graves et avérées). Il y a deux catégorisations, une pour les signeurs (le cas de l'administratrice système cité au début), et une pour les résolveurs qui valideront. Par exemple, un signeur ne devrait plus utiliser RSA avec SHA-1, vu les faiblesses de SHA-1, mais un résolveur validant doit toujours le traiter, car des nombreux domaines sont ainsi signés. S'il ignorait cet algorithme, bien des zones seraient considérées comme non signées.
La liste qualifiée des algorithmes se trouve dans la section 3 : ECDSA avec la courbe P-256, et RSA avec SHA-256, sont les seuls indispensables pour les signeurs. ED25519 (RFC 8080) est recommandé (et sera probablement indispensable dans le prochain RFC). Plusieurs algorithmes sont à éviter, comme DSA, GOST R 34.10-2001 (RFC 5933) ou RSA avec MD5 (RFC 6151). Tous les autres sont facultatifs.
Pour les résolveurs validants, la liste des indispensables et des recommandés est un peu plus longue. Par exemple, ED448 (RFC 8080) est facultatif pour les signeurs mais recommandé pour les résolveurs.
La même section 3 justifie ces choix : RSA+SHA-1 est l'algorithme de référence, celui qui assure l'interopérabilité (tout logiciel compatible DNSSEC doit le mettre en œuvre) et c'est pour cela qu'il reste indispensable pour les résolveurs, malgré les faiblesses de SHA-1. RSA+SHA-256 est également indispensable car la racine et la plupart des TLD l'utilisent aujourd'hui. Un résolveur qui ne comprendrait pas ces algorithmes ne servirait pas à grand'chose. RSA+SHA-512 ne pose pas de problème de sécurité, mais a été peu utilisé, d'où son statut « non recommandé » pour les signeurs.
D'autre part, le RFC insiste sur le fait qu'on ne peut pas changer le statut d'un algorithme trop vite : il faut laisser aux ingénieurs système le temps de changer leurs zones DNS. Et les résolveurs sont forcément en retard sur les signeurs : même si les signeurs n'utilisent plus un algorithme dans leurs nouvelles versions, les résolveurs devront continuer à l'utiliser pour valider les zones pas encore migrées.
Depuis le RFC 6944, ECDSA a vu son utilisation augmenter nettement. Les courbes elliptiques sont clairement l'avenir, d'où leur statut mieux placé. Ainsi, une zone DNS qui n'était pas signée et qui va désormais l'être devrait choisir un algorithme à courbes elliptiques, comme ECDSA ou EdDSA (RFC 8032 et RFC 8080). Avec ECDSA, il est recommandé d'utiliser l'algorithme déterministe du RFC 6979 pour générer les signatures. Les zones actuellement signées avec RSA devraient migrer vers les courbes elliptiques. Une chose est sûre, la cryptographie évolue et ce RFC ne sera donc pas éternel.
Le RFC note d'ailleurs (section 5) que le remplacement d'un algorithme cryptographique par un autre (pas juste le remplacement d'une clé) est une opération complexe, à faire avec prudence et après avoir lu les RFC 6781 et RFC 7583.
Ah, et parmi les algorithmes à courbes elliptiques, GOST (RFC 5933) régresse
car l'ancien algorithme R 34.10-2001 a été remplacé par un nouveau qui n'est
pas, lui, normalisé pour DNSSEC. L'algorithme venant du GOST avait été
normalisé pour DNSSEC car les gérants du
.ru
disaient qu'ils ne
pouvaient pas signer avec un algorithme étranger mais, finalement,
ils ont utilisé RSA, ce qui diminue sérieusement l'intérêt des
algorithmes GOST.
Outre les signeurs et les résolveurs, le RFC prévoit le cas des registres, qui délèguent des zones signées, en mettant un enregistrement DS dans leur zone. Ces enregistrements DS sont des condensats de la clé publique de la zone fille, et, ici, SHA-1 est à éviter et SHA-256 est indispensable.
Aujourd'hui, les mises en œuvre courantes de DNSSEC sont en général compatibles avec ce que demande le RFC. Elles sont parfois trop « généreuses » (RSA+MD5 encore présent chez certains), parfois un peu trop en retard (ED448 pas encore présent partout).
Date de publication du RFC : Août 2019
Auteur(s) du RFC : N. Jenkins (FastMail), C. Newman
(Oracle)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF jmap
Première rédaction de cet article le 14 août 2019
Ce nouveau RFC décrit un remplaçant pour le traditionnel protocole IMAP, remplaçant fondé sur le cadre JMAP (JSON Meta Application Protocol, RFC 8620).
Le protocole décrit dans ce RFC fournit les mêmes services qu'IMAP (RFC 9051) : accéder aux boîtes aux lettres de courrier, chercher dans ces boîtes, gérer les messages (détruire les inutiles, par exemple), etc. Par rapport à IMAP, outre l'utilisation du format JSON, l'accent est mis sur la synchronisation rapide, l'optimisation pour les clients mobiles, et sur la possibilité de notifications. JMAP est sans état (pas besoin de connexion permanente). Ce « JMAP pour le courrier » s'appuie sur JMAP, normalisé dans le RFC 8620. JMAP est un protocole générique, qui peut servir à synchroniser bien des choses entre un client et un serveur (par exemple un agenda, ou bien une liste de contacts). Par abus de langage, je vais souvent dire « JMAP » dans cet article alors que je devrais normalement préciser « JMAP pour le courrier », premier « utilisateur » du JMAP générique.
JMAP manipule différents types d'objets. Le plus important est
sans doute Email
(section 4 du RFC), qui
modélise un message. Il s'agit d'une représentation de haut
niveau, le client JMAP n'a pas à connaitre tous les détails de
l'IMF (Internet Message Format, RFC 5322), de
MIME (RFC 2045),
etc. Un objet de type Email
a une liste
d'en-têtes et un corps, et JMAP fournit des méthodes pour accéder
aux différentes parties du corps. Il y a même plusieurs
représentations d'un message, pour s'adapter aux différents
clients. Par exemple, un message MIME est normalement un
arbre, de profondeur quelconque, mais un
client JMAP peut décider de demander une représentation aplatie,
avec juste une liste d'attachements. (La plupart des MUA présentent à
l'utilisateur une vue aplatie de l'objet MIME.) Voilà pourquoi
l'objet Email
a plusieurs propriétés, le
client choisissant à laquelle il accède :
bodyStructure
: l'arbre MIME, c'est
la représentation la plus « authentique »,textBody
: une
liste des parties MIME à afficher quand
on préfère du texte,htmlBody
: une liste des parties MIME
à afficher quand on préfère de l'HTML,attachments
: la liste des « pièces
jointes » (rappelez-vous que le concept de « pièces jointes » a
été créé pour l'interface avec l'utilisateur ; il n'a pas de
sens en MIME, qui ne connait qu'un arbre avec des feuilles de
différents types).Les en-têtes doivent pouvoir être internationaux (RFC 6532).
Un message a évidemment des métadonnées, parmi lesquelles :
id
, un identifiant du message (ce
n'est pas le Message-ID:
, c'est attribué
par le serveur JMAP), contrairement à IMAP, l'identificateur
d'un message ne change pas, même quand le message change de
boîte, et il peut apparaitre dans plusieurs boîtes à la
foisblobIf
, un identifiant du message
représenté sous la forme d'une suite d'octets, à analyser par le client,
par opposition à l'objet de haut niveau identifié par
id
,size
, la taille du message,keywords
, des mots-clés, parmi
lesquels certains, commençant par un
dollar, ont une signification
spéciale.
En IMAP, les mots-clés spéciaux sont précédés d'une
barre inverse. En JMAP, c'est le
dollar. Parmi ces mots-clés,
$seen
indique que le message a été lu,
$answered
, qu'on y a répondu,
$junk
, que le serveur l'a classé comme
spam, etc. Ces mots-clés sont dans un
registre IANA.
Et quelles opérations sont possibles avec les objets de type
Email
? Ce sont les opérations génériques de
JMAP (RFC 8620, section 5). Ainsi,
on peut récupérer un message avec
Email/get
. Cette requête :
[[ "Email/get", { "ids": [ "f123u456", "f123u457" ], "properties": [ "threadId", "mailboxIds", "from", "subject", "receivedAt", "header:List-POST:asURLs", "htmlBody", "bodyValues" ], "bodyProperties": [ "partId", "blobId", "size", "type" ], "fetchHTMLBodyValues": true, "maxBodyValueBytes": 256 }, "#1" ]]
peut récupérer, par exemple, cette valeur :
[[ "Email/get", { "accountId": "abc", "state": "41234123231", "list": [ { "id": "f123u457", "threadId": "ef1314a", "mailboxIds": { "f123": true }, "from": [{ "name": "Joe Bloggs", "email": "joe@example.com" }], "subject": "Dinner on Thursday?", "receivedAt": "2013-10-13T14:12:00Z", "header:List-POST:asURLs": [ "mailto:partytime@lists.example.com" ], "htmlBody": [{ "partId": "1", "blobId": "B841623871", "size": 283331, "type": "text/html" }, { "partId": "2", "blobId": "B319437193", "size": 10343, "type": "text/plain" }], "bodyValues": { "1": { "isTruncated": true, "value": "<html><body><p>Hello ..." }, "2": { "isTruncated": false, "value": "-- Sent by your friendly mailing list ..." } } } ], "notFound": [ "f123u456" ] }, "#1" ]]
Notez que le client a demandé deux messages, mais qu'un seul, le
f123u457
, a été trouvé.
Tout aussi indispensable, Email/query
permet de demander au serveur une recherche, selon de nombreux
critères comme la date, les mots-clés, ou bien le contenu du corps
du message.
Email/set
permet de modifier un message,
ou d'en créer un (qu'on pourra ensuite envoyer avec
EmailSubmission
, décrit plus loin). Notez qu'il n'y a pas de
Email/delete
. Pour détruire un message, on
utilise Email/set
en changeant la propriété
indiquant la boîte aux lettres, pour mettre la boîte aux lettres
spéciale qui sert de poubelle (rôle = trash
).
Comme IMAP, JMAP pour le courrier a la notion de boîte aux lettres (section 2 du RFC). Une boîte (vous pouvez appeler ça un dossier ou un label si vous voulez) est un ensemble de messages. Tout message est dans au moins une boîte. Les attributs importants d'une boîte :
inbox
identifie
la boîte où le courrier arrive par défaut. (Les rôles figurent
dans un
registre IANA créé par le RFC 8457.)Ensuite, on utilise les méthodes JMAP pour accéder aux boîtes
(révisez donc le RFC 8620, qui décrit
le JMAP générique). Ainsi, pour accéder à une boîte,, on utilise
la méthode JMAP Mailbox/get
, qui utilise le
/get
JMAP (RFC 8620, section 5.1). Le paramètre
ids
peut être nul, cela indique alors qu'on
veut récupérer tous les messages (c'est ce qu'on fait dans
l'exemple ci-dessous).
De même, pour effectuer une recherche sur le serveur, JMAP
normalise la méthode /query
(RFC 8620, section 5.5) et JMAP pour le courrier peut
utiliser Mailbox/query
.
Par exemple, si on veut voir toutes les boîtes existantes, le client JMAP envoie le JSON :
[[ "Mailbox/get", { "accountId": "u33084183", "ids": null }, "0" ]]
et reçoit une réponse du genre (on n'affiche que les deux premières boîtes) :
[[ "Mailbox/get", { "accountId": "u33084183","state": "78540", "state": "78540", "list": [{ "id": "MB23cfa8094c0f41e6", "name": "Boîte par défaut", "role": "inbox", "totalEmails": 1607, "unreadEmails": 15, "myRights": { "mayAddItems": true, ...}, { "id": "MB674cc24095db49ce", "name": "Personnel", ...
Notez que state
est l'identificateur d'un
état de la boîte. Si on veut ensuite récupérer les changements, on
pourra utiliser Mailbox/changes
avec comme
paramètre "sinceState": "88540"
.
Dans JMAP, les messages peuvent être regroupés en fils de discussion (threads, section 3 du RFC). Tout message est membre d'un fil (parfois membre unique). Le RFC n'impose pas de méthode unique pour constituer les fils mais suggère :
In-Reply-To:
ou
References:
indiquant le
Message-Id:
d'un autre message).On peut ensuite accéder aux fils. Le client envoie :
[[ "Thread/get", { "accountId": "acme", "ids": ["f123u4", "f41u44"] }, "#1" ]]
Et récupère les fils f123u4
et
f41u44
:
[[ "Thread/get", { "accountId": "acme", "state": "f6a7e214", "list": [ { "id": "f123u4", "emailIds": [ "eaa623", "f782cbb"] }, { "id": "f41u44", "emailIds": [ "82cf7bb" ] } ...
Un client qui vient de se connecter à un serveur JMAP va
typiquement faire un Email/query
sans
conditions particulières, pour recevoir la liste des messages (ou
alors en se limitant aux N messages les plus récents),
puis récupérer les fils de discussion correspondants avec
Thread/get
, récupérer les messages
eux-mêmes. Pour diminuer la latence, JMAP permet au client
d'envoyer toutes ces requêtes en une seule fois (batching), en disant pour
chaque requête qu'elle doit utiliser le résultat de la précédente
(backreference, membre JSON resultOf
).
JMAP permet également d'envoyer des messages. Un client JMAP
n'a donc besoin que d'un seul protocole, contrairement au cas
courant aujourd'hui
où il faut IMAP et
SMTP,
configurés séparement, avec, trop souvent, l'un qui marche et l'autre pas. Cela simplifie
nettement les choses pour l'utilisateur. Cela se fait avec le type
EmailSubmission
(section 7 du RFC). Deux
importantes propriétés d'un objet de type
EmailSubmission
sont
mailFrom
, l'expéditeur, et
rcptTo
, les destinataires. Rappel important
sur le courrier électronique : il y a les adresses indiquées dans
le message (champs To:
,
Cc:
, etc, cf. RFC 5322), et les adresses indiquées dans l'enveloppe
(commandes SMTP comme MAIL
FROM
et RCPT TO
, cf. RFC 5321). Ces adresses ne sont pas forcément
identiques. Lorsqu'on apprend le fonctionnement du courrier
électronique, la distinction entre ces deux catégories d'adresses
est vraiment cruciale.
Un EmailSubmission/set
va créer l'objet
EmailSubmission
, et
envoyer le message. Ici, on envoie à
john@example.com
et
jane@example.com
un message (qui avait été créé
par Email/set
et qui avait l'identificateur
M7f6ed5bcfd7e2604d1753f6c
) :
[[ "EmailSubmission/set", { "accountId": "ue411d190", "create": { "k1490": { "identityId": "I64588216", "emailId": "M7f6ed5bcfd7e2604d1753f6c", "envelope": { "mailFrom": { "email": "john@example.com", "parameters": null }, "rcptTo": [{ "email": "jane@example.com", "parameters": null }, ... ] } } }, "onSuccessUpdateEmail": { "#k1490": { "mailboxIds/7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e": null, "mailboxIds/73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6": true, "keywords/$draft": null } } }, "0" ]]
Anecdote sur l'envoi de courrier : les premières versions de
« JMAP pour le courrier » utilisaient une boîte aux lettres
spéciale, nommée Outbox
, où on mettait les
messages à envoyer (comme dans ActivityPub).
JMAP a d'autres types d'objets amusants, comme
VacationResponse
(section 8), qui permet de
faire envoyer un message automatiquement lorsqu'on est absent
(l'auto-répondeur du serveur doit évidemment suivre le RFC 3834, pour éviter de faire des bêtises comme
de répondre à une liste de diffusion). On
crée un objet avec VacationResponse/set
et
hop, l'auto-répondeur est amorcé.
Et je n'ai pas parlé de tout, par exemple JMAP permet de pousser des changements depuis le serveur vers le client, si la boîte aux lettres est modifiée par un autre processus (RFC 8620, section 7).
JMAP a le concept de
capacités (capabilities),
que le serveur annonce au client, dans un objet JSON (rappel :
JSON nomme « objets » les
dictionnaires), et sous la forme d'un URI. JMAP pour le courrier
ajoute trois capacités au registre des
capacités JMAP,
urn:ietf:params:jmap:mail
pour dire qu'on
sait gérer le courrier,
urn:ietf:params:jmap:submission
, pour dire
qu'on sait en envoyer (cf. RFC 6409, sur ce
concept de soumission d'un message), et
urn:ietf:params:jmap:vacationresponse
pour
dire qu'on sait gérer un auto-répondeur.
Le courrier électronique pose plein de problèmes de sécurité
intéressants. La section 9 de notre RFC les détaille. Par exemple,
les messages en HTML sont particulièrement dangereux. (Il
est toujours amusant de voir des entreprises de sécurité
informatique envoyer leur newsletter en HTML,
malgré les risques associés, qui sont aujourd'hui bien connus.) Le
RFC rappelle donc aux clients JMAP (mais c'est valable pour tous
les MUA) que du
JavaScript dans le message peut changer son
contenu, qu'un message en HTML peut récupérer du contenu sur
l'Internet (via par exemple un <img
src=…
), ce qui trahit le
lecteur et fait fuiter des données privées, que
CSS,
quoique moins dangereux que JavaScript, permet également des trucs
assez limites, que les liens en HTML ne pointent pas toujours vers
ce qui semble (<a
href="http://evil.example/">cliquez ici pour aller sur le site
de votre banque https://good-bank.example</a>
),
etc. Pour faire face à tous ces dangers du courrier en HTML, le
RFC suggère de nettoyer le HTML avant de l'envoyer au
client. Attention, outre que c'est une modification du contenu, ce
qui est toujours délicat politiquement, le faire proprement est
difficile, et le RFC recommande fortement d'utiliser une
bibliothèque bien testée, de ne pas le faire soi-même à la main
(il y a trop de pièges). Par exemple, en
Python, on peut utiliser lxml, et son module
Cleaner
, ici en mode extrémiste qui retire
tout ce qui peut être dangereux :
from lxml.html.clean import Cleaner ... cleaner = Cleaner(scripts=True, javascript=True, embedded=True, meta=True, page_structure=True, links=True, remove_unknown_tags=True, style=True)
Mais il est probablement impossible de complètement sécuriser HTML dans le courrier. Le RFC explique à juste titre que HTML augmente beaucoup la surface d'attaque. Une organisation soucieuse de sécurité ne devrait pas traiter le HTML dans le courrier.
La soumission du courrier (cf. RFC 6409) pose également des problèmes de sécurité. Imaginez un client JMAP piraté et qui serve ensuite à envoyer du spam de manière massive, utilisant le compte de l'utilisateur ignorant de ce piratage. Les MTA qui acceptent du courrier ont des mécanismes de défense (maximum N messages par heure, avec au plus M destinataires par message…) mais ces mécanismes marchent d'autant mieux que le serveur a davantage d'information. Si la soumission via JMAP est mise en œuvre par un simple relais vers un serveur SMTP de soumission, certaines informations sur le client peuvent être perdues. De tels relais doivent donc veiller à transmettre au serveur SMTP toute l'information disponible, par exemple via le mécanisme XCLIENT.
JMAP a été développé essentiellement au sein de FastMail, qui le met en œuvre sur ses serveurs. Il existe une page « officielle » présentant le protocole, qui explique entre autres les avantages de JMAP par rapport à IMAP. Vous y trouverez également des conseils pour les auteurs de clients, très bien faits et qui donnent une bonne idée de comment le protocole marche. Ce site Web est un passage recommandé.
On y trouve également une liste de mises en œuvre de JMAP. Ainsi, le serveur IMAP bien connu Cyrus a déjà JMAP en expérimental. Le MUA K-9 Mail a, quant à lui, commencé le travail.
Date de publication du RFC : Juillet 2019
Auteur(s) du RFC : N. Jenkins (Fastmail), C. Newman (Oracle)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF jmap
Première rédaction de cet article le 6 septembre 2019
Le protocole JMAP, JSON Meta Application Protocol, permet de bâtir des mécanismes d'accès à des objets distants (par exemple des boîtes aux lettres, ou des agendas), en envoyant et recevant du JSON au dessus de HTTPS. Son principal « client » est le protocole « JMAP for mail », un concurrent d'IMAP normalisé dans le RFC 8621.
Au début, JMAP était même conçu uniquement pour l'accès au courrier, comme l'indique son nom, qui évoque IMAP. Mais, dans le cadre de la normalisation de JMAP, est apparu le désir de séparer le protocole générique, adapté à toutes sortes d'objets distants, du protocole spécifique du courrier. D'où les deux RFC : ce RFC 8620 normalise le protocole générique, alors que le RFC 8621 normalise « JMAP for mail ». Dans le futur, d'autres protocoles fondés sur JMAP apparaitront peut-être, par exemple pour l'accès et la synchronisation d'un agenda (en concurrence avec le CalDAV du RFC 4791, donc).
Parmi les concepts techniques importants de JMAP, notons :
Du fait de l'utilisation de JSON, il est bon de réviser le vocabulaire JSON, notamment le fait qu'un objet (object) JSON est en fait un dictionnaire.
Outre les types de données de base de JSON, comme les booléens,
les chaînes de caractères
et les entiers, JMAP définit quelques types à lui, notamment le
type Id
, qui stocke l'identificateur d'un
objet. Syntaxiquement, c'est une chaîne de caractères
LDH (Letter-Digit-Hyphen, lettres ASCII, chiffres et trait d'union). Par exemple, dans le RFC 8621, la première boîte aux lettres mentionnée a comme
identificateur MB23cfa8094c0f41e6
. Notre RFC
crée aussi le type Date
, puisque JSON ne
normalise pas les dates. Ce type utilise le format du RFC 3339.
À ce stade, je peux avouer que j'ai fait un abus de langage. J'ai parlé de JSON mais en fait JMAP utilise un sous-ensemble de JSON, nommé I-JSON, et décrit dans le RFC 7493, afin d'éviter certaines ambiguités de JSON (section 8.4 de notre RFC). Tout le contenu échangé en JMAP doit être du I-JSON. D'ailleurs, si vous connaissez un logiciel libre qui vérifie qu'un texte JSON est du I-JSON, je suis preneur.
JMAP nécessite que le client se connecte au serveur (section 2 du RFC, sur la session). Pour cela, il lui faut l'URL du serveur (rappelez-vous que JMAP tourne sur HTTPS), qui peut être obtenu manuellement, ou par un processus de découverte décrit plus loin. Et il faut évidemment les moyens d'authentification (par exemple nom et phrase de passe), ceux-ci n'étant pas précisés dans notre RFC. Un mécanisme unique avait été prévu mais avait suscité trop de controverses à l'IETF. Finalement, les mécanismes utilisables sont ceux habituels de HTTPS (enregistrés à l'IANA). Lors de la connexion, le serveur va envoyer un objet JSON décrivant entre autres ses capacités, comme la taille maximale des objets téléversés, ou comme les extensions gérées (comme le « JMAP for mail » du RFC 8621). Voici un exemple (tiré du RFC, car je n'ai pas trouvé de serveur JMAP où je pouvais avoir facilement un compte pour tester, si vous en avez un, n'hésitez pas à m'écrire) :
{ "capabilities": { "urn:ietf:params:jmap:core": { "maxSizeUpload": 50000000, "maxSizeRequest": 10000000, ... "collationAlgorithms": [ "i;ascii-numeric", "i;ascii-casemap", "i;unicode-casemap" ] }, "urn:ietf:params:jmap:mail": {} "urn:ietf:params:jmap:contacts": {}, "https://example.com/apis/foobar": { "maxFoosFinangled": 42 } }, "accounts": { "A13824": { "name": "john@example.com", "isPersonal": true, "isReadOnly": false, "accountCapabilities": { "urn:ietf:params:jmap:mail": { "maxMailboxesPerEmail": null, "maxMailboxDepth": 10, ... }, "urn:ietf:params:jmap:contacts": { ... } ... "apiUrl": "https://jmap.example.com/api/", ...
Notez que ce serveur annonce qu'il sait faire du JMAP pour le
courrier (RFC 8621, cf. la ligne
urn:ietf:params:jmap:mail
) et qu'il a
également une extension privée,
https://example.com/apis/foobar
. Les
capacités publiques sont dans un registre
IANA. On peut ajouter des capacités par la procédure
(cf. RFC 8126) « Spécification nécessaire »
pour les capacités marquées « fréquentes »
(common), et par la procédure « Examen par un
expert » pour les autres.
La méthode standard pour découvrir le serveur JMAP, si on n'en
connait pas l'URL, est d'utiliser un enregistrement
SRV (RFC 2782, mais voir aussi
le RFC 6186) puis un URL bien
connu. Imaginons que le domaine soit
example.net
. On cherche le SRV pour
_jmap._tcp.example.net
. (jmap
a donc été ajouté au registre des
services.) On récupère alors le
nom du serveur et le port (a priori, ce
sera 443, le port standard de HTTPS). Et on n'a plus qu'à se
connecter à l'URL bien connu (RFC 8615), à
https://${hostname}[:${port}]/.well-known/jmap
.
jmap
figure à cet effet dans le registre des URL bien connus. (Notez
que l'étape SRV est facultative, certains clients iront
directement au /.well-known/jmap
.) Ainsi, si vous utilisez JMAP pour le courrier, et que votre
adresse est gerard@example.net
, vous partez
du domaine example.net
et vous suivez
l'algorithme ci-dessus. (Je ne sais pas pourquoi JMAP n'utilise
pas plutôt le WebFinger du RFC 7033.)
Puisqu'on utilise le DNS pour récupérer ces enregistrements SRV, il est évidemment recommandé de déployer DNSSEC.
Une fois qu'on a récupéré le premier objet JSON décrit plus
haut, on utilise la propriété (le membre, pour parler JSON) apiUrl
de cet
objet pour faire les requêtes suivantes (section 3 du RFC). On utilise la méthode
HTTP POST
, le type
MIME application/json
, et on
envoie des requêtes en JSON, qui seront suivies de réponses du
serveur, également en JSON. Les méthodes JMAP (à ne pas confondre
avec les méthodes HTTP comme GET
ou
POST
) sont écrites sous la forme
Catégorie/Méthode
. Il existe une catégorie
Core
pour les méthodes génériques de JMAP et
chaque protocole utilisant JMAP définit sa (ou ses) propre(s)
catégorie(s). Ainsi, le RFC 8621 définit les
catégories Mailbox
,
Email
(un message), etc. Comme
Core
définit une méthode
echo
(section 4, le ping de
JMAP), qui ne fait que renvoyer les données, sans les comprendre, un exemple de requête/réponse peut
être :
[[ "Core/echo", { "hello": true, "high": 5 }, "b3ff" ]] [[ "Core/echo", { "hello": true, "high": 5 }, "b3ff" ]]
(Oui, la réponse - le second paragraphe - est identique à la question.)
En cas d'erreur, le serveur devrait renvoyer un objet décrivant le problème, en utilisant la syntaxe du RFC 7807. Une liste des erreurs connues figure dans un registre IANA.
Il existe des noms de méthodes standard qu'on retrouve dans
toutes les catégories, comme get
. Si on a une
catégorie Foo
décrivant un certain type
d'objets, le client sait qu'il pourra récupérer les objets de ce
type avec la méthode Foo/get
, les modifier
avec Foo/set
et récupérer uniquement les
modifications incrémentales avec
Foo/changes
. La section 5 du RFC décrit ces
méthodes standard.
Une méthode particulièrement utile est
query
(section 5.5). Elle permet au client de demander au
serveur de faire une recherche et/ou un tri des objets. Au lieu de
tout télécharger et de faire recherche et tri soi-même, le client
peut donc sous-traiter cette opération potentiellement
coûteuse. Cette méthode est une de celles qui permet de dire que
JMAP est bien adapté aux machines clientes disposant de faibles
ressources matérielles, et pas très bien connectées. Le RFC cite
(section 5.7) un type (imaginaire) Todo
décrivant des tâches à accomplir, et l'exemple avec
query
permet d'illustrer le membre
filter
de la méthode, pour indiquer les
critères de sélection :
[[ "Todo/query", { "accountId": "x", "filter": { "operator": "OR", "conditions": [ { "hasKeyword": "music" }, { "hasKeyword": "video" } ] } } ]]
Comme beaucoup de méthodes JMAP, query
peut
imposer un travail important au serveur. Un client maladroit, ou
cherchant déliberement à créer une attaque par déni de
service pourrait planter un serveur trop léger. Les
serveurs JMAP doivent donc avoir des mécanismes de protection,
comme une limite de temps passé sur chaque requête.
On l'a dit, un des intérêts de JMAP est la possibilité d'obtenir des notifications du serveur, sans obliger le client à vider sa batterie en interrogeant périodiquement le serveur. La section 7 du RFC détaille ce mécanisme. Deux alternatives pour le client : garder la connexion HTTPS ouverte en permanence, pour y recevoir ces notifications, ou bien utiliser un service tiers comme celui de Google. Notons que ces notifications, par leur seule existence, même si le canal est chiffré, peuvent révéler des informations. Comme noté dans la revue du protocole par la direction Sécurité à l'IETF "I.e., if someone can see that wikileaks smtp server sends email to corporate smtp server, but the smtp traffic is encrypted so they do not know the recipient of the email, but then few seconds later see push event notification stream going to the Joe's laptop indicating something has happened in his mail box, they can find out the who the recipient was.".
Il existe une page « officielle » présentant le protocole et plusieurs mises en oeuvre (actuellement, la plupart sont, expérimentales et/ou en cours de développement). C'est une des raisons pour lesquelles je ne présente pas ici d'essais réels. Notez toutefois que Fastmail a du JMAP en production.
Date de publication du RFC : Septembre 2019
Auteur(s) du RFC : J. Dickinson (Sinodun), J. Hague (Sinodun), S. Dickinson (Sinodun), T. Manderson (ICANN), J. Bond (ICANN)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 4 septembre 2019
Lorsque l'opérateur d'un service DNS veut conserver les données de trafic, il peut demander au serveur d'enregistrer requêtes et réponses (mais, la plupart du temps, le serveur n'écrit qu'une petite partie des informations) ou bien écouter le trafic réseau et enregistrer le pcap. Le problème est que le format pcap prend trop de place, est de trop bas niveau (une connexion TCP, par exemple, va être éclatée en plusieurs paquets), et qu'il est difficile de retrouver les informations spécifiquement DNS à partir d'un pcap. D'où la conception de ce format de stockage, spécifique au DNS, et qui permet d'enregistrer la totalité de l'information, dans un format optimisé en taille et de plus haut niveau. C-DNS s'appuie sur CBOR pour cela.
Le DNS est un service d'infrastructure absolument critique. Il est donc nécessaire de bien le connaitre et de bien l'étudier. Cela passe par une récolte de données, en l'occurrence le trafic entrant et sortant des serveurs DNS, qu'ils soient des résolveurs ou bien des serveurs faisant autorité. Ce genre de récolte peut être coordonnée par l'OARC pour des projets comme DITL (« un jour dans la vie de l'Internet »). Un exemple d'une telle récolte, faite avec le classique tcpdump (qui, en dépit de son nom, ne fait pas que du TCP) :
% tcpdump -w /tmp/dns.pcap port 53
Le fichier produit (ici, sur un serveur faisant autorité pour
eu.org
), au format
pcap, contient les requêtes DNS et les
réponses, et peut être analysé avec des outils comme tcpdump
lui-même, ou comme Wireshark. Il y a aussi
des outils spécifiques au DNS comme PacketQ ou comme
dnscap. Ici,
avec tcpdump :
% tcpdump -n -r /tmp/dns.pcap 15:35:22.432746 IP6 2001:db8:aa:101::.40098 > 2400:8902::f03c:91ff:fe69:60d3.53: 41209% [1au] A? tracker.torrent.eu.org. (51) 15:35:22.432824 IP6 2400:8902::f03c:91ff:fe69:60d3.53 > 2001:db8:aa:101::.40098: 41209- 0/4/5 (428)
Au lieu des outils tous faits, on peut aussi développer ses propres programmes en utilisant les nombreuses bibliothèques qui permettent de traiter du pcap (attention si vous analysez du trafic Internet : beaucoup de paquets sont mal formés, par accident ou bien délibérément, et votre analyseur doit donc être robuste). C'est ce que font en général les chercheurs qui analysent les données DITL.
Le problème du format pcap (ou pcapng) est qu'il y a à la fois trop de données et pas assez. Il y a trop de données car il inclut des informations probablement rarement utiles, comme les adresses MAC et car il ne minimise pas les données. Et il n'y en a pas assez car il ne stocke pas les informations qui n'étaient pas visibles sur le réseau mais qui l'étaient uniquement dans la mémoire du serveur DNS. Ainsi, on ne sait pas si la réponse d'un résolveur avait été trouvée dans le cache ou pas. Ou bien si les données étaient dans le bailliage ou pas (cf. RFC 8499, section 7). Les captures DNS peuvent être de très grande taille (10 000 requêtes par seconde est banal, 100 000, ça arrive parfois) et on désire les optimiser autant que possible, pour permettre leur rapatriement depuis les serveurs éloignés, puis leur stockage parfois sur de longues périodes. (Les formats « texte » comme celui du RFC 8427 ne conviennent que pour un message isolé, ou un tout petit nombre de messages.)
Le cahier des charges du format C-DNS (Compacted DNS) est donc :
La section du RFC détaille les scénarios d'usage de C-DNS. En effet, la capture de données DNS peut être faite dans des circonstances très différentes. Le serveur peut être une machine physique, une virtuelle, voire un simple conteneur. La personne qui gère le capture peut avoir le contrôle des équipements réseau (un commutateur, par exemple, pour faire du port mirroring), le serveur peut être surdimensionné ou, au contraire, soumis à une attaque par déni de service qui lui laisse peu de ressources. Le réseau de collecte des données capturées peut être le même que le réseau de service ou bien un réseau différent, parfois avec une capacité plus faible. Bref, il y a beaucoup de cas. C-DNS est optimisé pour les cas où :
Donc, il est crucial de minimiser la taille des données récoltées. Mais il faut aussi faire attention à la charge que représente la collecte : le serveur de noms a pour rôle de répondre aux requêtes DNS, la collecte est secondaire, et ne doit donc pas consommer trop de ressources CPU.
Compte-tenu de ces contraintes, C-DNS a été conçu ainsi (section 4 du RFC) :
C-DNS repose sur CBOR (RFC 8949). La section 5 du RFC explique pourquoi :
Avec la section 6 du RFC commence la description du
format. Classiquement, un fichier C-DNS commence par un en-tête,
puis une série de blocs. Chaque bloc comprend un certain nombre de
tables (par exemple une table d'adresses IP pour les adresses
apparaissant dans le bloc), suivies des éléments R/R. Ceux-ci
référencent les tables. Ainsi, une requête de
2001:db8:1::cafe
à
2001:db8:ffff::beef
pour le nom
www.example.org
contiendra des pointeurs vers
les entrées 2001:db8:1::cafe
et
2001:db8:ffff::beef
de la table des adresses
IP, et un pointeur vers l'entrée
www.example.org
de la table des noms de
domaine. S'il n'y a qu'un seul élément R/R dans le bloc, ce serait
évidemment une complication inutile, mais l'idée est de factoriser
les données qui sont souvent répétées.
On l'a vu, dans C-DNS, plein de choses sont optionnelles, car deux dispositifs de capture différents ne récoltent pas forcément les mêmes données. Ainsi, par exemple, un système de capture situé dans le logiciel serveur n'a pas forcément accès à la couche IP et ne peut donc pas enregistrer le nombre maximal de sauts (hop limit). Cela veut dire que, quand on lit un fichier C-DNS :
C-DNS permet donc d'indiquer quels éléments des données initiales ont été délibérément ignorés. Cela permet, par exemple, à un programme de lecture de fichiers C-DNS de savoir tout de suite si le fichier contient les informations qu'il veut.
Ces indications contiennent aussi des informations sur un
éventuel échantillonnage (on n'a gardé que
X % des messages), sur une éventuelle normalisation (par exemple
tous les noms de domaine passés en caractères minuscules) ou sur
l'application de techniques de minimisation, qui permettent de
diminuer les risques pour la vie
privée. Par exemple, au lieu de stocker les adresses IP complètes, on peut ne
stocker qu'un préfixe (par exemple un /32 au lieu de l'adresse
complète), et il faut alors l'indiquer dans le fichier C-DNS
produit, pour que le lecteur comprenne bien que
2001:db8::
est un préfixe, pas une
adresse.
La section 7 du RFC contient ensuite le format détaillé. Quelques points sont à noter (mais, si vous écrivez un lecteur C-DNS, lisez bien tout le RFC, pas juste mon article !) Ainsi, toutes les clés des objets (maps) CBOR sont des entiers, jamais des chaînes de caractère, pour gagner de la place. Et ces entiers sont toujours inférieurs à 24, pour tenir sur un seul octet en CBOR (lisez le RFC 8949 si vous voulez savoir pourquoi 24). On peut aussi avoir des clés négatives, pour les extensions au format de base, et elles sont comprises entre -24 et -1.
La syntaxe complète, rédigée dans le format CDDL du RFC 8610, figure dans l'annexe A de notre RFC.
On peut reconstruire un fichier pcap à partir de C-DNS. Une des difficultés est qu'on n'a pas forcément toutes les informations, et il va donc falloir être créatif (section 9). Une autre raison fait qu'on ne pourra pas reconstruire au bit près le fichier pcap qui aurait été capturé par un outil comme tcpdump : les noms de domaines dans les messages DNS étaient peut-être comprimés (RFC 1035, section 4.1.4) et C-DNS n'a pas gardé d'information sur cette compression. (Voir l'annexe B pour une discussion détaillée sur la compression.) Pareil pour les informations de couche 3 : C-DNS ne mémorise pas si le paquet UDP était fragmenté, s'il était dans un ou plusieurs segments TCP, s'il y avait des messages ICMP liés au trafic DNS, etc.
Si vous voulez écrire un lecteur ou un producteur de C-DNS, la section 11 du RFC contient des informations utiles pour la programmeuse ou le programmeur. D'abord, lisez bien le RFC 8949 sur CBOR. C-DNS utilise CBOR, et il faut donc connaitre ce format. Notamment, la section 3.9 du RFC 7049 donne des conseils aux implémenteurs CBOR pour produire un CBOR « canonique ». Notre RFC en retient deux, représenter les entiers, et les types CBOR, par la forme la plus courte possible (CBOR en permet plusieurs), mais il en déconseille deux autres. En effet, la section 3.9 du RFC 7049 suggérait de trier les objets selon la valeur des clés, et d'utiliser les tableaux de taille définie (taille indiquée explicitement au début, plutôt que d'avoir un marqueur de fin). Ces deux conseils ne sont pas réalistes pour le cas de C-DNS. Par exemple, pour utiliser un tableau de taille définie, il faudrait tout garder en mémoire jusqu'au moment où on inscrit les valeurs, ce qui augmenterait la consommation mémoire du producteur de données C-DNS. (D'un autre côté, le problème des tableaux de taille indéfinie est qu'ils ont un marqueur de fin ; si le programme qui écrit du C-DNS plante et ne met pas le marqueur de fin, le fichier est du CBOR invalide.)
Le RFC a créé plusieurs registres IANA pour ce format, stockant notamment les valeurs possibles pour le transport utilisé, pour les options de stockage (anonymisé, échantillonné...), pour le type de réponse (issue de la mémoire du résolveur ou pas).
Bien sûr, récolter des données de trafic DNS soulève beaucoup de problèmes liés à la vie privée (cf. RFC 7626). Il est donc recommander de minimiser les données, comme imposé par des réglements comme le RGPD, ou comme demandé dans le rapport « Recommendations on Anonymization Processes for Source IP Addresses Submitted for Future Analysis ».
Les passionnés de questions liées aux formats regarderont l'annexe C, qui liste des formats alternatifs à CBOR, qui n'ont finalement pas été retenus :
Cette annexe décrit également le résultat de mesures sur la compression obtenue avec divers outils, sur les différents formats. C-DNS n'est pas toujours le meilleur, mais il est certainement, une fois comprimé, plus petit que pcap, et plus simple à mettre en œuvre qu'Avro ou Protocol Buffers.
Notez que j'ai travaillé sur ce format lors d'un hackathon de l'IETF, mais le format a pas mal changé depuis (entre autres en raison des problèmes identifiés lors du hackathon).
Voyons maintenant une mise en œuvre de ce format, avec l'outil DNS-STATS plus exactement son Compactor (source sur Github, et documentation). Je l'ai installé sur une machine Debian :
aptitude install libpcap-dev libboost1.67-all-dev liblzma-dev libtins-dev git clone https://github.com/dns-stats/compactor.git cd compactor sh autogen.sh autoconf automake ./configure make
Et après, on peut l'utiliser pour transformer du C-DNS en pcap et
réciproquement. J'ai créé un fichier pcap d'un million de paquets
avec tcpdump sur un serveur faisant autorité, avec tcpdump -w
dns.pcap -c 1000000 port 53
. Puis :
% ./compactor -o /tmp/dns.cdns /tmp/dns.pcap
Et en sens inverse (reconstituer le pcap) :
% ./inspector /tmp/dns.cdns
Cela nous donne :
% ls -lth /tmp/dns* -rw-r--r-- 1 stephane stephane 98M Jul 31 08:13 /tmp/dns.cdns.pcap -rw-r--r-- 1 stephane stephane 3.2K Jul 31 08:13 /tmp/dns.cdns.pcap.info -rw-r--r-- 1 stephane stephane 27M Jul 31 07:27 /tmp/dns.cdns -rw-r--r-- 1 root root 339M Jul 30 20:05 /tmp/dns.pcap
Notez que dns.cdns.pcap
est le pcap reconstitué, on
remarque qu'il est plus petit que le pcap original, certaines
informations ont été perdues, comme les adresses MAC. Mais il reste bien plus gros que la même
information stockée en C-DNS. Le
/tmp/dns.cdns.pcap.info
nous donne quelques informations :
% cat /tmp/dns.cdns.pcap.info CONFIGURATION: Query timeout : 5 seconds Skew timeout : 10 microseconds Snap length : 65535 Max block items : 5000 File rotation period : 14583 Promiscuous mode : Off Capture interfaces : Server addresses : VLAN IDs : Filter : Query options : Response options : Accept RR types : Ignore RR types : COLLECTOR: Collector ID : dns-stats-compactor 0.12.3 Collection host ID : ns1.example STATISTICS: Total Packets processed : 1000000 Matched DNS query/response pairs (C-DNS) : 484407 Unmatched DNS queries (C-DNS) : 98 Unmatched DNS responses (C-DNS) : 69 Malformed DNS packets : 68 Non-DNS packets : 0 Out-of-order DNS query/responses : 1 Dropped C-DNS items (overload) : 0 Dropped raw PCAP packets (overload) : 0 Dropped non-DNS packets (overload) : 0
Date de publication du RFC : Mai 2019
Auteur(s) du RFC : M. Nottingham
Chemin des normes
Première rédaction de cet article le 3 septembre 2019
Plusieurs normes du Web s'appuient sur l'existence d'un fichier à un
endroit bien connu d'un site. Les deux exemples les plus connus sont
robots.txt
et
favicon.ico
. Autrefois, ces
endroits « bien connus » étaient alloués sans schéma
central. Depuis le RFC 5785, c'est mieux
organisé, avec tous ces fichiers « sous »
/.well-known/
. Notre
RFC remplace le RFC 5785 (et le RFC 8307), avec peu de
changements significatifs.
Prenons l'exemple le plus connu,
robots.txt
, fichier
stockant la politique d'autorisation des robots qui fouillent le Web. Si un robot examine le site
Web http://www.example.org/
, il va tenter de
trouver ledit fichier en
http://www.example.org/robots.txt
. Même chose
pour, par exemple, sitemap.xml
ou P3P (section 1
du RFC). Ce système avait plusieurs inconvénients, notamment le
risque de collision entre deux noms (puisqu'il n'y avait pas de
registre de ces noms) et, pire, le risque de collision entre un de
ces noms et une ressource normale du site. D'où l'importance d'un
« rangement » de ces ressources bien connues. Elles doivent
dorénavant être préfixées de
/.well-known/
. Ainsi, si le protocole
d'autorisation des robots était normalisé aujourd'hui, on
récupérerait la politique d'autorisation en
http://www.example.org/.well-known/robots.txt
.
À noter que le RFC spécifie uniquement un
préfixe pour le chemin de la ressource,
/.well-known/
n'est pas forcément un
répertoire sur le disque du serveur (même si
c'est une mise en œuvre possible).
Le RFC 8615 note aussi qu'il existe déjà des mécanismes de récupération de métadonnées par ressource (comme les en-têtes de HTTP ou les propriétés de WebDAV) mais que ces mécanismes sont perçus comme trop lourds pour remplacer la ressource unique située en un endroit bien connu.
Le nom .well-known
avait été choisi
(cf. annexe A de notre RFC) car il
avait peu de chances de rentrer en conflit avec un nom existant
(traditionnellement, sur Unix,
système d'exploitation le plus utilisé sur
les serveurs Web, les fichiers dont le nom commencent par un point
ne sont pas affichés).
Bref, passons à la section 3 qui donne les détails
syntaxiques. Le préfixe est donc /.well-known/
,
les noms en « dessous » doivent être enregistrés (cf. section
5.1), et ils doivent se conformer à la production
segment-nz
du RFC 3986
(en clair, cela veut dire qu'ils doivent être une suite de
caractères ASCII imprimables, avec quelques
exclusions comme la barre oblique). Du
point de vue sémantique, ils doivent être précis, pour éviter
l'appropriation de termes génériques (par exemple, l'application
Toto qui veut stocker ses métadonnées
devrait utiliser toto-metadata
et pas juste
metadata
.) À noter que l'effet d'une requête
GET /.well-known/
(tout court, sans nom de
ressource après), est indéfini (sur mon blog, cela donne ça ; devrais-je le configurer pour
renvoyer autre chose ? Sur Mastodon, ça donne 404.)
Quelques conseils de sécurité pour le webmestre (section 4) : ces ressources « bien connues » s'appliquent à une origine (un « site Web ») entière, donc attention à contrôler qui peut les créer ou les modifier, et d'autre part, dans le contexte d'un navigateur Web, elles peuvent être modifiées par du contenu, par exemple JavaScript.
La section 5 décrit les conditions d'enregistrement des noms bien connus à l'IANA. Le registre contient par exemple les métadonnées du RFC 6415. Y mettre des noms supplémentaires nécessite un examen par un expert et une description publiée (pas forcément un RFC). Dans les termes du RFC 8126, ce sera Spécification Nécessaire et Examen par un Expert. Il y a un mini-formulaire à remplir (section 3.1 du RFC) et hop, le nom bien connu sera enregistré. Plusieurs existent désormais.
Notez qu'il est très difficile de savoir combien de sites ont
des ressources /.well-known
. Bien sûr,
Google
le sait, mais ne
donne pas accès à cette information (une requête
inurl:/.well-known
ou
inurl:"/.well-known"
ignore hélas le point
initial et trouve donc surtout des faux positifs). Si on n'a pas
accès à la base de Google, il faudrait donc faire soi-même une
mesure active avec un client HTTP qui aille visiter de nombreux
sites.
Les changements depuis le RFC 5785 sont résumés dans l'annexe B du RFC :
/.well-known/
n'est plus réservé au Web, il peut être utilisé pour d'autres
plans d'URI, ce qui a modifié le registre
des plans pour y ajouter une colonne indiquant s'ils
permettent ce préfixe (section 5.2),Date de publication du RFC : Mai 2019
Auteur(s) du RFC : A. Mortensen (Arbor Networks), T. Reddy (McAfee), R. Moskowitz (Huawei)
Pour information
Réalisé dans le cadre du groupe de travail IETF dots
Première rédaction de cet article le 22 août 2019
Les attaques par déni de service, et notamment les dDoS (distributed Denial of Service), sont une des principales plaies de l'Internet. Le projet DOTS (DDoS Open Threat Signaling) à l'IETF vise à développer un mécanisme de signalisation permettant à des acteurs de la lutte anti-dDoS d'échanger des informations et de se coordonner, même lorsque l'attaque fait rage. Par exemple, un mécanisme DOTS permettra à un client d'un service de traitement des attaques de demander à son fournisseur d'activer le filtrage anti-dDoS. Ce RFC est le premier du projet : il décrit le cahier des charges.
Ces attaques par déni de service (décrites dans le RFC 4732) peuvent être utilisées à des fins financières (racket), lors d'affrontements inter-étatiques (comme dans le cas estonien souvent cité), à des fins de censure contre des opposants politiques. Le risque est particulièrement élevé pour les « petits ». En effet, beaucoup d'attaques par déni de service reposent sur la taille : par exemple, l'attaquant envoie tellement d'octets qu'il sature la ou les connexions Internet de sa victime. La seule solution est alors de louer un tuyau plus gros, ce qui n'est pas toujours financièrement possible. Les attaques dDoS favorisent donc les plus riches. Aujourd'hui, par exemple, un petit hébergeur Web a le plus grand mal à faire face à d'éventuelles attaques, ce qui rend difficile l'hébergement associatif et/ou décentralisé. Les attaques par déni de service ont donc des conséquences bien au-delà des quelques heures d'indisponibilité du service : elles encouragent la centralisation des services, puisqu'il faut être gros pour encaisser le choc. C'est ainsi qu'aujourd'hui beaucoup d'organisations sont chez Cloudflare, dépendant de cette société privée étatsunienne pour leur « protection ». On est dans l'équivalent moderne de la relation féodale au Moyen-Âge : le paysan seul, ou même le village, est trop vulnérable, il doit chercher la protection d'un seigneur, en échange de sa soumission.
Il est très difficile de se protéger contre les attaques par déni de service. Et le projet DOTS ne va pas proposer de solution magique, uniquement des mécanismes de cooordination et d'échange en cas d'attaque. La réponse à une attaque dDoS consiste typiquement à examiner les paquets entrants, et à jeter ceux qui semblent faire partie de l'attaque. (Voir par exemple mon article sur le filtrage.) Il faut bien sûr le faire le plus tôt possible. Si vous êtes connecté à l'Internet par un lien de capacité 1 Gb/s, et que l'attaquant arrive à le saturer par les paquets qu'il envoie, trier les paquets de votre côté ne servira à rien, cela serait trop tard ; ils doivent être triés en amont, par exemple chez votre opérateur. Et, évidemment, trier n'est pas trivial, les paquets ne sont pas marqués comme participant à l'attaque (sauf si on utilise le RFC 3514, mais regardez sa date de publication). Il y aura donc toujours des faux positifs, des paquets innocents jetés. (Pour un exemple de solution anti-dDoS, voir le VAC d'OVH, et les nombreux articles qui lui ont été consacrés.) En 2019, beaucoup d'organisations ne font plus ce tri elles-mêmes (par manque de moyens financiers, et surtout humains) mais sous-traitent à un fournisseur spécialisé (comme Arbor, pour lequel travaille un des auteurs du RFC). On envoie le trafic vers ce fournisseur, par des astuces DNS ou BGP, il le trie, et vous renvoie ce qui lui semble inoffensif. Ce tri se nomme en anglais scrubbing. Ces fournisseurs sont donc un élement critique, par exemple parce qu'ils voient passer tout votre trafic. En général, on active ce service à la demande, et cette activation est un des scénarios d'utilisation de DOTS les plus cités dans le RFC.
Actuellement, l'activation du service de scrubbing se fait via des interfaces privatrices, fournies par le « protecteur », ce qui contribue à enfermer le client dans sa relation avec le fournisseur. Et puis, parfois, il faut que plusieurs acteurs participent à la réponse à attaque. D'où l'idée du projet DOTS (dDoS Open Threat Signaling) qui va développer une interface normalisée, au sein du groupe de travail du même nom à l'IETF.
La section 1.2 du RFC précise le terminologie employée : DOTS sera client/serveur, le client DOTS étant chez la victime, qui cherche une solution, et le serveur DOTS étant chez le protecteur (mitigator dans le RFC). Rappelez-vous que DOTS ne normalise pas les méthodes de protection (elles évoluent vite, même si le motif « tri puis poubellisation des paquets » reste dominant), mais uniquement la communication entre les acteurs impliqués. Les différents acteurs communiquent avec deux sortes de canaux, les canaux de signalisation et les canaux de données. Les premiers sont prévus pour des messages assez courts (« jette tous les paquets à destination du port NNN ») mais qui doivent arriver à tout prix, même en cas d'attaque intense ; ils sont le cœur du système DOTS, et privilégient la survivabilité. Les seconds, les canaux de données, sont prévus pour de grandes quantités de données, par exemple pour envoyer des informations de configuration, comme la liste des préfixes IP à protéger.
L'essentiel du RFC est la section 2, qui décrit les exigences auxquelles devra se soumettre le futur protocole DOTS. (Notez que le travail est déjà bien avancé. Depuis, un RFC d'architecture générale du systéme, le RFC 8811, a été publié, et les RFC 8782 et RFC 8783 ont normalisé le protocole.) Il s'agit d'exigences techniques : ce RFC ne spécifie pas d'exigences business ou de politique. Par exemple, il ne dit pas à partir de quand un client DOTS a le droit de demander une action au serveur, ni dans quels cas le client a le droit d'annuler une demande.
Le protocole DOTS a des exigences difficiles ; compte-tenu du caractère très sensible des échanges entre le client et le serveur, il faut absolument fournir authentification, intégrité, confidentialité et protection contre le rejeu par un tiers. Autrement, le protocole DOTS, comme la plupart des techniques de sécurité, pourrait en fait fournir un nouveau moyen d'attaque. Mais, d'un autre côté, le protocole doit être très robuste, puisqu'il est précisément conçu pour fonctionner face à un hostile, qui va essayer de perturber les communications. Combiner toutes ces demandes n'est pas trivial. DOTS fournira de la robustesse en utilisant des messages de petite taille (qui auront donc davantage de chances de passer), asynchrones, et qui pourront être répétés sans dommage (en cas de doute, les acteurs DOTS n'hésiteront pas à envoyer de nouveau un message).
Je ne vais pas répéter ici la longue liste des exigences, vous les trouverez dans la section 2. Elles sont réparties en plusieurs catégories. Il y a d'abord les exigences générales :
Il y a ensuite les exigences sur le canal de signalisation :
192.0.2.0/24
» ou « seulement les paquets à
destination de 2001:db8:a:b::/64
», voire
« seulement les paquets pour
www.example.com
», et le serveur DOTS doit
alors faire la résolution de nom).Il y a aussi des exigences pour l'autre canal, celui des données. Rappelons que, contrairement au canal de signalisation, il n'est pas indispensable qu'il puisse fonctionner pendant l'attaque. La principale exigence est la transmission fiable des données.
Vu le contexte de DOTS, il y a évidemment des exigences de sécurité :
La section 3 du RFC se penche sur le problème de la congestion. Le protocole DOTS ne doit pas ajouter à l'attaque en noyant tout le monde sous les données, alors qu'il utilisera sans doute un transport qui ne gère pas lui-même la congestion, UDP (au moins pour le canal de signalisation). Il faudra donc bien suivre les règles du RFC 8085.
À noter qu'Arbor a un brevet sur les mécanismes analogues à DOTS (brevet 20130055374, signalé à l'IETF ici.) Arbor promet des licences RF et RAND. Même les attaques créent du business…
Date de publication du RFC : Juin 2019
Auteur(s) du RFC : H. Birkholz (Fraunhofer SIT), C. Vigano (Universitaet Bremen), C. Bormann (Universitaet Bremen TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cbor
Première rédaction de cet article le 13 juin 2019
Le format de données binaire CBOR, normalisé dans le RFC 8949, commence à avoir un certain succès. Il lui manquait juste un langage de schéma, permettant de décrire les données acceptables (comme Relax NG ou XML Schema pour XML, ou comme le projet - abandonné - JCR (« JSON Content Rules ») pour JSON). C'est désormais fait dans ce RFC, qui normalise le langage CDDL, « Concise Data Definition Language ». (Il a par la suite été légèrement modifié dans le RFC 9682.)
La section 1 de notre RFC résume le cahier des charges : CDDL
doit permettre de décrire sans ambiguïté un fichier CBOR
acceptable pour un usage donné, il doit être lisible et rédigeable
par un humain, tout en étant analysable par un programme, et doit
permettre la validation automatique d'un fichier CBOR. Autrement
dit, étant donné une description en CDDL en
schema.cddl
et un fichier CBOR en
data.cbor
, il faut qu'on puisse développer un
outil validator
qui permettra de lancer la
commande validator data.cbor schema.cddl
et
qui dira si le fichier CBOR est conforme au schéma ou pas. (Un tel
outil existe effectivement, il est présenté à la fin de cet
article.) Comme CBOR utilise un modèle de données très proche de
celui de JSON, CDDL peut (même si ce n'est pas son but principal)
être utilisé pour décrire des fichiers JSON, ce que détaille
l'annexe E du RFC, consacrée à l'utilisation de CDDL avec JSON (il
y a quelques subtilités à respecter).
(Attention, il ne faut pas confondre notre CDDL avec la licence ayant le même acronyme.)
La section 2 de notre RFC explique les éléments de base d'un schéma CDDL. On y trouve les classiques nombres, booléens, chaînes de caractères, correspondant aux éléments identiques en CBOR. Pour les structures plus compliquées (tableaux et maps, c'est-à-dire dictionnaires, ce qu'on nomme objets en JSON), CDDL ne fournit qu'un seul mécanisme, le groupe. Un groupe est une liste de doublets {nom, valeur}, le nom pouvant être omis si on décrit un tableau. Avec ce concept de groupe, CDDL permet également de décrire ce que dans d'autres langages, on appelerait struct ou enregistrement.
La liste est encadrée par des parenthèses. Chaque donnée
décrite en CDDL a un type, par exemple bool
pour un booléen, uint
pour un entier non
signé ou tstr
pour une chaîne de
caractères. La définition indique également quel type majeur CBOR
(RFC 8949, section 3.1) va être utilisé pour
ce type CDDL. uint
est évidemment le type
majeur 0, bool
est le type majeur 7,
etc. (D'ailleurs, vous pouvez aussi créer des types en indiquant
le type majeur CBOR, ce qui donne une grande liberté, et la
possibilité d'influencer la sérialisation.) Une liste des types et
valeurs prédéfinies (comme false
et
true
) figure dans l'annexe D de notre
RFC.
Voici un groupe à qui on donne le nom
pii
:
pii = ( age: uint, name: tstr, employer: tstr )
Et ici une donnée person
est définie avec ce
groupe :
person = { pii }
Comme person
est défini avec des
accolades, ce sera un
dictionnaire (map). Le même groupe
pii
aurait pu être utilisé pour définir un
tableau, en mettant entre crochets (et, dans ce cas, les noms seraient ignorés, seule la
position compte).
On peut définir une donnée en utilisant un groupe et d'autres
informations, ici, person
et
dog
ont les attributs de
identity
et quelques uns en plus :
person = { identity, employer: tstr } dog = { identity, leash-length: float } identity = ( age: 0..120, ; Ou "uint" mais, ici, on utilise les intervalles name: tstr )
La syntaxe nom: valeur
est en fait un cas
particulier. La notation la plus générale est clé =>
valeur
. Comme CBOR (contrairement à JSON) permet
d'avoir des clés qui ne sont pas des chaînes de caractères, la
notation avec le deux-points est là pour ce
cas particulier, mais courant, où la clé est une chaîne de
caractères. (age: int
et "age"
=> int
sont donc équivalents.)
Un autre exemple permet d'illustrer le fait que l'encodage CBOR en tableau ou en dictionnaire va dépendre de la syntaxe utilisée en CDDL (avec en prime les commentaires, précédés d'un point-virgule) :
Geography = [ city : tstr, gpsCoordinates : GpsCoordinates, ] GpsCoordinates = { longitude : uint, ; multiplied by 10^7 latitude : uint, ; multiplied by 10^7 }
Dans le fichier CBOR, GpsCoordinates
sera un
dictionnaire (map) en raison de l'utilisation des
accolades, et Geography
sera un tableau (les
noms city
et
gpsCoordinates
seront donc ignorés).
Un champ d'un groupe peut être facultatif, en le faisant précéder d'un point d'interrogation, ou bien répété (avec une astérisque ou un plus) :
apartment = { kitchen: size, + bedroom: size, ? bathroom: size } size = float
Dans cet appartement, il y a exactement une cuisine, au moins une chambre et peut-être une salle de bains. Notez que l'outil cddl, présenté plus loin, ne créera pas d'appartements avec plusieurs chambres. C'est parce que CBOR, contrairement à JSON (mais pas à I-JSON, cf. RFC 7493, section 2.3), ne permet pas de clés répétées dans une map. On a ici un exemple du fait que CDDL peut décrire des cas qui ne pourront pas être sérialisés dans un format cible donné.
Revenons aux types. On a également le droit aux énumérations, les valeurs étant séparées par une barre oblique :
attire = "bow tie" / "necktie" / "Internet attire" protocol = 6 / 17
C'est d'ailleurs ainsi qu'est défini le type booléen (c'est prédéfini, vous n'avez pas à taper cela) :
bool = false / true
On peut aussi choisir entre groupes (et pas seulement entre types), avec deux barres obliques.
Et l'élement racine, on le reconnait comment ? C'est simplement le premier défini dans le schéma. À part cette règle, CDDL n'impose pas d'ordre aux définitions. Le RFC préfère partir des structures de plus haut niveau pour les détailler ensuite, mais on peut faire différemment, selon ses goûts personnels.
Pour les gens qui travaillent avec des protocoles réseau, il
est souvent nécessaire de pouvoir fixer exactement la
représentation des données. CDDL a la notion de contrôle, un
contrôle étant une directive donnée à CDDL. Elle commence par un
point. Ainsi, le contrôle .size
indique la
taille que doit prendre la donnée. Par exemple
(bstr
étant une chaîne d'octets) :
ip4 = bstr .size 4 ip6 = bstr .size 16
Un autre contrôle, .bits
, permet de placer
les bits exactement, ici pour
l'en-tête TCP :
tcpflagbytes = bstr .bits flags flags = &( fin: 8, syn: 9, rst: 10, psh: 11, ack: 12, urg: 13, ece: 14, cwr: 15, ns: 0, ) / (4..7) ; data offset bits
Les contrôles existants figurent dans un registre IANA, et d'autres pourront y être ajoutés, en échange d'une spécification écrite (cf. RFC 8126).
La section 3 du RFC décrit la syntaxe formelle de CDDL. L'ABNF (RFC 5234) complet est en annexe B. CDDL lui-même ressemble à ABNF, d'ailleurs, avec quelques changements comme l'autorisation du point dans les noms. Une originalité plus fondamentale, documentée dans l'annexe A, est que la grammaire utilise les PEG et pas le formalisme traditionnel des grammaires génératives.
La section 4 de notre RFC couvre les différents usages de CDDL. Il peut être utilisé essentiellement pour les humains, une sorte de documentation formelle de ce que doit contenir un fichier CBOR. Il peut aussi servir pour écrire des logiciels qui vont permettre une édition du fichier CBOR guidée par le schéma (empêchant de mettre des mauvaises valeurs, par exemple, mais je ne connais pas de tels outils, à l'heure actuelle). CDDL peut aussi servir à la validation automatique de fichiers CBOR. (Des exemples sont donnés plus loin, avec l'outil cddl.) Enfin, CDDL pourrait être utilisé pour automatiser une partie de la génération d'outils d'analyse de fichiers CBOR, si ce format continue à se répandre.
Un exemple réaliste d'utilisation de CDDL est donné dans
l'annexe H, qui met en œuvre les « reputons » du RFC 7071. Voici le schéma
CDDL. Un autre exemple en annexe H est de réécrire des
règles de l'ancien projet JCR (cf. Internet
draft draft-newton-json-content-rules
)
en CDDL.
Quels sont les RFC et futurs RFC qui se servent de CDDL ? CDDL est utilisé par le RFC 8007 (son annexe A), le RFC 8152 et le RFC 8428. Il est également utilisé dans des travaux en cours comme le format C-DNS (RFC 8618), sur lequel j'avais eu l'occasion de travailler lors d'un hackathon. Autre travail en cours, le système GRASP (RFC 8990) et dans OSCORE (RFC 8613). En dehors du monde IETF, CDDL est utilisé dans Web Authentication.
Un outil, décrit dans l'annexe F du RFC, a été développé pour générer des fichiers CBOR d'exemple suivant une définition CDDL, et pour vérifier des fichiers CBOR existants. Comme beaucoup d'outils modernes, il faut l'installer en utilisant les logiciels spécifiques d'un langage de programmation, ici Ruby :
% gem install cddl
Voici un exemple, pour valider un fichier JSON (il peut évidemment aussi valider du CBOR, rappelez-vous que c'est presque le même modèle de données, et que CDDL peut être utilisé pour les deux) :
% cddl person.cddl validate person.json %
Ici, c'est bon. Quand le fichier de données ne correspond pas au
schéma (ici, le membre foo
n'est pas prévu) :
% cat person.json {"age": 1198, "foo": "bar", "name": "tic", "employer": "tac"} % cddl person.cddl validate person.json CDDL validation failure (nil for {"age"=>1198, "foo"=>"bar", "name"=>"tic", "employer"=>"tac"}): ["tac", [:prim, 3], nil]
C'est surtout quand le schéma lui-même a une erreur que les messages d'erreur de l'outil cddl sont particulièrement mauvais. Ici, pour un peu d'espace en trop :
% cddl person.cddl generate *** Look for syntax problems around the %%% markers: %%%person = { age: int, name: tstr, employer: tstr,%%% } *** Parse error at 0 upto 69 of 93 (1439).
Et pour générer des fichiers de données d'exemple ?
% cat person.cddl person = { "age" => uint, ; Or 'age: uint' name: tstr, employer: tstr } % cddl person.cddl generate {"age": 3413, "name": "tic", "employer": "tac"}
Ce format est du JSON mais c'est en fait le profil « diagnostic »
de CBOR, décrit dans la section 8 du RFC 8949. (cddl person.cddl
json-generate
fabriquerait du JSON classique.)
On peut avoir du CBOR binaire après une conversion avec
les outils
d'accompagnement :
% json2cbor.rb person.json > person.cbor
CBOR étant un format binaire, on ne peut pas le regarder directement, donc on se sert d'un outil spécialisé (même dépôt que le précédent) :
% cbor2pretty.rb person.cbor a3 # map(3) 63 # text(3) 616765 # "age" 19 0d55 # unsigned(3413) 64 # text(4) 6e616d65 # "name" 63 # text(3) 746963 # "tic" 68 # text(8) 656d706c6f796572 # "employer" 63 # text(3) 746163 # "tac"
Et voilà, tout s'est bien passé, et le fichier CBOR est valide :
% cddl person.cddl validate person.cbor %
Date de publication du RFC : Mai 2019
Auteur(s) du RFC : S. Hollenbeck (Verisign Labs), R. Carney (GoDaddy)
Pour information
Première rédaction de cet article le 1 septembre 2019
Dernière mise à jour le 4 septembre 2019
Ce RFC décrit des extensions au format vCard afin d'ajouter dans les réponses RDAP deux informations exigées par l'ICANN. Il concerne donc surtout registres et utilisateurs des TLD ICANN.
Le protocole RDAP (RFC 9083) sert à récupérer des informations sur un objet
enregistré, par exemple un nom de
domaine. Une partie des informations, par exemple les
adresses et numéros de téléphone, est au format
vCard (RFC 6350). Mais l'ICANN a des exigences
supplémentaires, décrites dans la Specification
for gTLD Registration Data. Par exemple, l'ICANN
exige (cf. leur
politique) que, si les informations sur un contact ne sont
pas publiées (afin de préserver sa vie privée), le
registre fournisse au moins un URI indiquant un
moyen de contact (section 2.7.5.2 de la politique ICANN), par
exemple un formulaire Web (comme
). Cette
propriété https://www.afnic.fr/fr/resoudre-un-litige/actions-et-procedures/joindre-le-contact-administratif-d-un-domaine/
CONTACT-URI
est désormais dans
le
registre IANA. (Si vous voulez réviser les notions de
propriété et de paramètre en vCard, plus exactement
jCard, cf. RFC 7095.)
D'autre part, la norme vCard, le RFC 6350, précise dans sa section 6.3.1, que le nom du
pays doit être spécifié en langue
naturelle, alors que l'ICANN exige (section 1.4 de leur politique) un code à deux
lettres tiré de la norme ISO 3166. (Notez
qu'à l'heure actuelle, certains registres mettent le nom du pays,
d'autres le code à deux lettres…) Le
paramètre CC
, qui va indiquer le code, est désormais dans
le
registre IANA.
Ainsi, une réponse vCard suivant notre RFC pourrait indiquer (je ne peux pas vous montrer d'exemples réels d'un registre, aucun n'a apparemment déployé ces extensions, mais, si vous êtes curieux, lisez jusqu'à la fin) :
% curl -s https://rdap.nic.example/domain/foobar.example | jq . ... [ "contact-uri", <<< Nouveauté {}, "uri", "https://rds.nic.example/get-in-touch" ] ... [ "adr", {"cc": "US"}, <<< Nouveauté "text", ["", "", "123 Main Street", "Any Town", "CA", "91921-1234", "U.S.A."] ] ...
J'ai parlé jusqu'à présent des registres, mais l'ICANN impose
également RDAP aux bureaux d'enregistrement. Cela a en
effet un sens pour les registres minces, comme
.com
, où les données
sociales sont chez les bureaux en question. La liste
des bureaux d'enregistrement ICANN contient une colonne
indiquant leur serveur RDAP. Testons avec Blacknight, qui est
souvent à la pointe :
% curl https://rdap.blacknight.com/domain/blacknight.com | jq . ... [ "fn", {}, "text", "Blacknight Internet Solutions Ltd." ], [ "adr", { "cc": "IE" }, ... [ "contact-uri", {}, "uri", "https://whois.blacknight.com/contact.php?fqdn=blacknight.com&contact_type=owner" ]
On a bien un usage de ces extensions dans le monde réel (merci à Patrick Mevzek pour ses remarques et ajouts).
Date de publication du RFC : Mai 2019
Auteur(s) du RFC : M. Kucherawy
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dmarc
Première rédaction de cet article le 8 juin 2019
Il existe plusieurs techniques pour authentifier les courriers
électroniques. Certaines peuvent nécessiter des
calculs un peu compliqués et on voudrait souvent les centraliser
sur une machine de puissance raisonnable, dotée de tous les
logiciels nécessaires. Dans cette hypothèse, le MUA ne recevra qu'une
synthèse (« Ce message vient bien de
example.com
») et pourra alors prendre une
décision, basée sur cette synthèse. C'est le but de l'en-tête
Authentication-Results:
, normalisé originellement dans le RFC 5451 dix ans plus tôt, auquel ont succédés les RFC 7001 puis RFC 7601, que
ce nouveau RFC met légèrement à jour (il y a peu de
changements, le principal concernant l'ajout de la gestion du courrier
électronique internationalisé, avec davantage d'Unicode).
Avec des techniques d'authentification comme DKIM (RFC 6376) ou SPF (RFC 7208), les
calculs à faire pour déterminer si un message est authentique
peuvent être complexes (DKIM utilise la cryptographie) et
nécessiter la présence de bibliothèques non-standard. Les installer
et les maintenir à jour sur chaque machine, surtout en présence
d'éventuelles failles de sécurité qu'il faudra boucher en urgence,
peut être trop pénible pour l'administrateur système. L'idée de ce
RFC est donc de séparer l'opération en deux :
l'authentification est faite sur un
serveur, typiquement le premier MTA du site (cf. annexe C pour une discussion
de ce choix), celui-ci ajoute au message un en-tête indiquant le
résultat de ladite authentification et le MUA (ou bien le
MDA, voir la
section 1.5.4 pour un bon rappel sur ces concepts) peut ensuite,
par exemple par un langage de filtrage comme
procmail ou
Sieve, agir sur la base de ce
résultat. L'idée n'est donc pas de montrer la valeur de cet
en-tête à M. Michu (voir la section 4.1 pour quelques risques que
cela poserait), mais d'en faire une donnée pour un programme. Cet
en-tête marche pour tous les protocoles d'authentification et
surpasse donc les en-têtes spécifiques comme le
Received-SPF:
de SPF (section 1 du RFC). Le
filtrage des messages non authentifiés n'est
pas obligatoire (section 1.4) : agir - ou
pas - sur la base de l'en-tête Authentication-Results:
est une décision politique
locale.
J'ai utilisé le terme de « site » pour désigner un ensemble de
machines gérées par la même organisation mais le RFC a un terme
plus rigoureux, ADMD (ADministrative Management
Domain). La frontière d'un ADMD est la « frontière de
confiance » (trust boundary), définie en
section 1.2. Un domaine administratif de gestion est un groupe de
machines entre lesquelles il existe une relation de confiance,
notamment du fait que, à l'intérieur de l'ADMD, l'en-tête Authentication-Results:
ne
sera pas modifié ou ajouté à tort (section 1.6 : l'en-tête n'est
pas protégé, notamment il n'est pas signé). Il existe de
nombreuses variantes organisationnelles du concept d'ADMD. Un ADMD
inclus typiquement une organisation (ou un département de
celle-ci) et d'éventuels sous-traitants. Il a un nom,
l'authserv-id
, défini en section 2.2. Bien
sûr, la décision de faire confiance ou pas à telle entité, telle
machine ou tel ADMD est une décision locale, le RFC ne précise pas
comment elle est prise.
L'en-tête Authentication-Results:
lui-même est formellement défini en section
2. Il appartient à la catégorie des en-têtes de « trace » (RFC 5322, section 3.6.7 et RFC 5321, section 4.4) comme Received:
qui doivent être ajoutés en haut des en-têtes et jamais
modifiés. La syntaxe de Authentication-Results:
est en section 2.2. L'en-tête est
composé du authserv-id
, le nom de l'ADMD et
d'une série de doublets (méthode, résultat), chacun indiquant une
méthode d'authentification et le résultat obtenu. L'annexe B
fournit une série d'exemples. Elle commence (annexe B.1) par un
message sans Authentication-Results:
(eh oui, il n'est pas obligatoire). Puis (tiré
de l'annexe B.3), une authentification SPF réussie, au sein de
l'ADMD example.com
, donnera :
Authentication-Results: example.com; spf=pass smtp.mailfrom=example.net Received: from dialup-1-2-3-4.example.net (dialup-1-2-3-4.example.net [192.0.2.200]) by mail-router.example.com (8.11.6/8.11.6) with ESMTP id g1G0r1kA003489; Wed, Mar 14 2009 17:19:07 -0800 From: sender@example.net Date: Wed, Mar 14 2009 16:54:30 -0800 To: receiver@example.com
Rappelez-vous qu'il peut y avoir plusieurs authentifications. Voici un cas (annexe B.4) avec SPF et l'authentification SMTP du RFC 4954 :
Authentication-Results: example.com; auth=pass (cram-md5) smtp.auth=sender@example.net; spf=pass smtp.mailfrom=example.net Received: from dialup-1-2-3-4.example.net (8.11.6/8.11.6) (dialup-1-2-3-4.example.net [192.0.2.200]) by mail-router.example.com (8.11.6/8.11.6) with ESMTP id g1G0r1kA003489; Fri, Feb 15 2002 17:19:07 -0800 Date: Fri, Feb 15 2002 16:54:30 -0800 To: receiver@example.com From: sender@example.net
L'une des authentifications peut réussir et l'autre échouer. Un
exemple (annexe B.6) avec deux signatures DKIM, une bonne et une
qui était correcte au départ (regardez le premier Authentication-Results:
) mais plus
à l'arrivée, peut-être parce qu'un gestionnaire de liste de
diffusion a modifié le message :
Authentication-Results: example.com; dkim=pass reason="good signature" header.i=@mail-router.example.net; dkim=fail reason="bad signature" header.i=@newyork.example.com Received: from mail-router.example.net (mail-router.example.net [192.0.2.250]) by chicago.example.com (8.11.6/8.11.6) for <recipient@chicago.example.com> with ESMTP id i7PK0sH7021929; Fri, Feb 15 2002 17:19:22 -0800 DKIM-Signature: v=1; a=rsa-sha256; s=furble; d=mail-router.example.net; t=1188964198; c=relaxed/simple; h=From:Date:To:Message-Id:Subject:Authentication-Results; bh=ftA9J6GtX8OpwUECzHnCkRzKw1uk6FNiLfJl5Nmv49E=; b=oINEO8hgn/gnunsg ... 9n9ODSNFSDij3= Authentication-Results: example.net; dkim=pass (good signature) header.i=@newyork.example.com Received: from smtp.newyork.example.com (smtp.newyork.example.com [192.0.2.220]) by mail-router.example.net (8.11.6/8.11.6) with ESMTP id g1G0r1kA003489; Fri, Feb 15 2002 17:19:07 -0800 DKIM-Signature: v=1; a=rsa-sha256; s=gatsby; d=newyork.example.com; t=1188964191; c=simple/simple; h=From:Date:To:Message-Id:Subject; bh=sEu28nfs9fuZGD/pSr7ANysbY3jtdaQ3Xv9xPQtS0m7=; b=EToRSuvUfQVP3Bkz ... rTB0t0gYnBVCM= From: sender@newyork.example.com Date: Fri, Feb 15 2002 16:54:30 -0800 To: meetings@example.net
La liste complète des méthodes figure dans un registre IANA (section 6). De nouvelles méthodes peuvent être enregistrées en utilisant la procédure « Examen par un expert » du RFC 5226. Des méthodes sont parfois abandonnées comme la tentative de Microsoft d'imposer son Sender ID (RFC 4406.)
L'en-tête Authentication-Results:
inclut également les valeurs de certaines
propriétés (RFC 7410. Ainsi, dans :
Authentication-Results: example.com; auth=pass (cram-md5) smtp.auth=sender@example.net; spf=pass smtp.mailfrom=example.net
La propriété smtp.auth
(authentification
SMTP) a la valeur
sender@example.net
(l'identité qui a été validée).
La section 2.5 détaille
l'authserv-id
. C'est un texte qui identifie
le domaine, l'ADMD. Il doit donc être unique dans tout
l'Internet. En général, c'est un nom de
domaine comme laposte.net
. (Il
est possible d'être plus spécifique et d'indiquer le nom d'une
machine particulière mais cette même section du RFC explique
pourquoi c'est en général une mauvaise idée : comme les MUA du
domaine n'agissent que sur les Authentication-Results:
dont ils reconnaissent
l'authserv-id
, avoir un tel identificateur
qui soit lié au nom d'une machine, et qui change donc trop
souvent, complique l'administration système.)
La section 2.7 explique les résultats possibles pour les
méthodes d'authentification (en rappelant que la liste à jour des
méthodes et des résultats est dans le registre
IANA). Ainsi, DKIM (section 2.7.1) permet des résultats comme
pass
(authentification réussie) ou
temperror
(erreur temporaire au cours de
l'authentification, par exemple liée au DNS). Des résultats similaires sont
possibles pour SPF (section 2.7.2).
Notons la normalisation d'une méthode traditionnelle
d'authentification faible, le test DNS du chemin « adresse IP du
serveur -> nom » et retour. Baptisée iprev
,
cette méthode, bien que bâtie sur la pure superstition
(cf. section 7.11) est utilisée couramment. Très injuste (car les
arbres des résolutions inverses du
DNS, in-addr.arpa
et
ip6.arpa
, ne sont pas sous le contrôle du
domaine qui envoie le courrier), cette méthode discrimine les
petits FAI, ce qui est sans doute un avantage pour les gros, comme
AOL qui
l'utilisent. Attention aux implémenteurs : aussi bien la
résolution inverse d'adresse IP en nom que la résolution droite de
nom en adresse IP peuvent renvoyer plusieurs résultats et il faut
donc comparer des ensembles. (Cette méthode qui,
contrairement aux autres, n'avait jamais été exposée dans un RFC
avant le RFC 5451, est décrite en détail
dans la section 3, avec ses sérieuses limites.)
Autre méthode mentionnée, auth
(section
2.7.4) qui repose sur l'authentification SMTP du RFC 4954. Si un MTA (ou plutôt MSA) a authentifié un
utilisateur, il peut le noter ici.
Une fois le code d'authentification exécuté, où mettre le
Authentication-Results:
? La section 4 fournit tous les détails, indiquant
notamment que le MTA doit placer l'en-tête en haut du message, ce
qui facilite le repérage des Authentication-Results:
à qui on peut faire confiance
(en examinant les en-têtes Received:
; en
l'absence de signature, un Authentication-Results:
très ancien, situé au début du
trajet, donc en bas des en-têtes, ne signifie pas grand'chose). On
se fie a priori aux en-têtes mis par les MTA de l'ADMD, du domaine
de confiance. L'ordre est donc important. (La section 7 revient en
détail sur les en-têtes Authentication-Results:
usurpés.)
Ce n'est pas tout de mettre un Authentication-Results:
, encore faut-il
l'utiliser. La section 4.1 s'attaque à ce problème. Principe
essentiel pour le MUA : ne pas agir sur la base d'un Authentication-Results:
, même
si ce n'est que pour l'afficher, sans l'avoir validé un
minimum. Comme le Authentication-Results:
n'est pas signé, n'importe qui a pu en
insérer un sur le trajet. Le RFC précise donc que les MUA doivent,
par défaut, ne rien faire. Et qu'ils doivent ne regarder les Authentication-Results:
qu'après que cela ait été activé par l'administrateur de la
machine, qui indiquera quel authserv-id
est
acceptable.
Naturellement, le MTA d'entrée du domaine devrait supprimer les
Authentication-Results:
portant son propre authserv-id
qu'il
trouve dans les messages entrants : ils sont forcément frauduleux
(section 5). (Le RFC accepte aussi une solution plus simpliste,
qui est de supprimer tous les Authentication-Results:
des messages entrants, quel
que soit leur authserv-id
.)
Arrivé à ce stade de cet article, le lecteur doit normalement
se poser bien des questions sur la valeur du Authentication-Results:
. Quel poids lui
accorder alors que n'importe quel méchant sur le trajet a pu
ajouter des Authentication-Results:
bidons ? La section 7, consacrée à l'analyse
générale de la sécurité, répond à ces inquiétudes. 7.1 détaille le
cas des en-têtes usurpés. Les principales lignes de défense ici
sont le fait que le MUA ne doit faire confiance aux Authentication-Results:
que
s'ils portent le authserv-id
de son ADMD
et le fait que le MTA entrant doit filtrer
les Authentication-Results:
avec son authserv-id
. Comme
l'intérieur de l'ADMD, par définition, est sûr, cela garantit en
théorie contre les Authentication-Results:
usurpés. Le RFC liste néanmoins d'autres
méthodes possibles comme le fait de ne faire confiance qu'au
premier Authentication-Results:
(le plus récent), si on sait
que le MTA en ajoute systématiquement un (les éventuels Authentication-Results:
usurpés apparaîtront après ; mais certains serveurs les
réordonnent, cf. section 7.3). Pour l'instant, il n'y a pas de
méthode unique et universelle de vérification du Authentication-Results:
, le RFC
propose des pistes mais ne tranche pas.
Comme toujours en sécurité, il faut bien faire la différence
entre authentification et
autorisation. Un spammeur a pu insérer un Authentication-Results:
légitime
pour son
authserv-id
. Même authentifié, il ne doit pas
être considéré comme une autorisation (section 7.2).
De nombreuses mises en œuvre de ce système existent déjà comme dans MDaemon, sendmail (via sid-milter), Courier, OpenDKIM, etc. Des logiciels comme Zimbra permettent également de le faire :
Authentication-Results: zimbra.afnic.fr (amavisd-new); dkim=pass (2048-bit key) header.d=cfeditions.com header.b=hGHP51iK; dkim=pass (2048-bit key) header.d=cfeditions.com header.b=go30DQO8
Si on veut analyser les en-têtes Authentication-Results:
en Python, on a le module authres. Parmi
les grosses usines à courrier centralisées,
Gmail met systématiquement cet en-tête, par
exemple :
Authentication-Results: mx.google.com; spf=pass \ (google.com: domain of stephane@sources.org designates 217.70.190.232 \ as permitted sender) smtp.mail=stephane@sources.org
Outre Gmail, à la date de publication du RFC, des services comme
Yahoo et Outlook ajoutaient cet
en-tête. Évidemment, ces en-têtes ne sont pas toujours
corrects. Outlook ne met pas le authserv-id
et
n'affiche pas l'adresse IP dans les tests SPF :
authentication-results: spf=none (sender IP is ) smtp.mailfrom=abo@charliehebdo.fr;
Mon serveur de messagerie utilise
Postfix et j'y fais des tests
SPF, dont le résultat est affiché sous
forme d'un en-tête Authentication-Results:
. Pour cela, j'ai installé pypolicyd-spf via
le paquetage Debian :
% sudo aptitude install postfix-policyd-spf-python
Puis on configure pypolicyd-spf (dans
/etc/postfix-policyd-spf-python/policyd-spf.conf
,
la documentation est dans
/usr/share/doc/postfix-policyd-spf-python/policyd-spf.conf.commented.gz
). Par
défaut, pypolicyd-spf met l'ancien en-tête
Received-SPF:
. Pour avoir Authentication-Results:
, il faut
dire :
Header_Type = AR
Et ajouter l'authserv-id
(le nom de
l'ADMD) :
Authserv_Id = mail.bortzmeyer.org
Il reste à configurer Postfix, dans
master.cf
:
# SPF policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf
Et dans main.cf
:
smtpd_recipient_restrictions = [...] check_policy_service unix:private/policyd-spf
Postfix va alors mettre ceci dans les messages (ici, un test réussi) :
Authentication-Results: mail.bortzmeyer.org; spf=pass (mailfrom) smtp.mailfrom=nic.fr (client-ip=2001:67c:2218:2::4:12; helo=mx4.nic.fr; envelope-from=bortzmeyer@nic.fr; receiver=<UNKNOWN>)
Les changements depuis le RFC 7601 sont
peu nombreux (annexe D pour une liste complète). On y trouve notamment
l'ajout du courrier électronique internationalisé
(EAI, pour Email Address
Internationalization, voir RFC 6530, RFC 6531 et RFC 6532) et quelques petits détails de forme. Et le registre
IANA est légèrement modifié, entre autres
pour y ajouter deux possibilités DKIM, a
(algorithme utilisé) et
s
(sélecteur).
Date de publication du RFC : Mai 2019
Auteur(s) du RFC : E. Wilde
Pour information
Première rédaction de cet article le 22 mai 2019
Un nouvel en-tête HTTP (et un nouveau
type de lien) fait son
apparition avec ce RFC :
Sunset:
sert à indiquer la date où la
ressource Web cessera probablement d'être servie. Le but est,
lorsque le webmestre sait à l'avance qu'il retirera une ressource,
de prévenir les utilisateurs.
Normalement, bien sûr, cela ne devrait pas arriver. Les URL doivent être stables. Mais dans certains cas, il peut y avoir une raison légitime de retirer une ressource qui avait été publiée sur le Web. Et, si on le sait à l'avance, c'est plus gentil si on prévient les utilisateurs qui accèdent à cette ressource. Donc, en pratique, ce nouvel en-tête servira peu mais il sera utile dans des cas précis. Par exemple (ce sont les cas cités par le RFC, personnellement, je ne les trouve pas tous pertinents) :
Sunset:
ne permet pas de traiter ce cas,
cf. section 5 du RFC, mais le type de lien
sunset
permet d'indiquer une page Web
documentant l'API et ses changements.)Pour ces usages, ce RFC introduit
(section 3)
donc l'en-tête HTTP Sunset:
(coucher de
soleil). Il contient une seule valeur, la date et l'heure de la
suppression, au format HTTP classique de la section 7.1.1.1 du
RFC 7231. Par exemple, pour indiquer qu'une
ressource disparait à la fin de cette année (celle de parution du
RFC) :
Sunset: Tue, 31 Dec 2019 23:59:59 GMT
Et c'est tout. L'en-tête ne donne aucune information sur ce qui arrivera après (réponse 404, 410, redirection 3xx vers une autre ressource…) Cet en-tête figure désormais dans le registre IANA des en-têtes.
Notre RFC introduit, en plus de l'en-tête HTTP, un type de
lien (cf. RFC 8288), sunset
, qui peut être mis dans d'autres contextes que celui des
en-têtes HTTP, par exemple dans du HTML
(section 6 de notre RFC). Il
permet d'indiquer des détails sur la future suppression
de la ressource, à la page Web indiquée. Ainsi, en
HTML, cela donnerait :
<link rel="sunset" href="https://example.org/why-sunset-and-when">
Ce type de lien figure dans le registre IANA de ces types.
Le RFC ne précise pas ce que des applications comme les navigateurs doivent faire exactement avec cette information. C'est un choix des auteurs des applications. Ils peuvent choisir, par exemple, d'alerter l'utilisateur. Notez que la date indiquée n'est qu'une indication. Le serveur Web reste libre de garder la ressource plus longtemps, ou au contraire de la supprimer avant.
Quelques logiciels utilisent ou génèrent l'information sur le coucher de soleil :
Sunset:
dans les réponses,Sunset:
.Date de publication du RFC : Mai 2019
Auteur(s) du RFC : A. Tamas (OP3FT), B. Phister (OP3FT), J-E. Rodriguez (OP3FT)
Pour information
Première rédaction de cet article le 23 mai 2019
Ce nouveau RFC documente un nouveau plan
d'URI, leaptofrogans
,
qui permettra de construire des URI pour le système
Frogans, comme par exemple
leaptofrogans:example*test
.
Le système Frogans est un système, conçu il y a vingt ans, de publication de contenu sur l'Internet. Il ne semble pas avoir jamais décollé et je suis sceptique quant à ses chances. Mais l'enregistrement d'un plan d'URI (le plan est la première partie d'un URI, avant le deux-points, cf. RFC 3986) ne signifie pas approbation ou encouragement, il indique juste que les formes ont bien été respectées.
Le système Frogans (section 1 du RFC) contient plusieurs composants, un langage de
description des « sites Frogans »,
des adresses Frogans qu'on reconnait à l'astérisque (comme par exemple
example*test
), un logiciel
non-libre, le Frogans
Player, un registre des
adresses, une organisation qui pilote la
technologie, etc.
Pourquoi un nouveau plan d'URI ? (Section 2.) L'idée est de permettre au
navigateur, quand il voit un lien du genre
<a href="leaptofrogans:example*test">Contenu
intéressant</a>
de lancer le Frogans
Player lorsque ce lien est sélectionné par l'utilisateur.
Le nom un peu long du nouveau plan, leaptofrogans
, a
été choisi pour éviter des confusions avec les adresses Frogans,
qui commencent souvent par frogans
avant
l'astérisque (section 3 du RFC pour les détails.)
Quelques détails de syntaxe maintenant (section 4). Les
adresses Frogans peuvent utiliser tout
Unicode. Il faut donc utiliser le nouveau
plan leaptofrogans
dans des
IRI (RFC 3987) ou
bien encoder avec les pour-cent. Ainsi,
l'adresse Frogans
网络名*站名
sera l'IRI
leaptofrogans:网络名*站名
ou bien l'URI
leaptofrogans:%E7%BD%91%E7%BB%9C%E5%90%8D*%E7%AB%99%E5%90%8D
.
Les procédures du RFC 7595 ayant été
suivies, le plan leaptofrogans
est désormais dans
le
registre IANA (enregistrement permanent, un enregistrement
temporaire avait été envisagé à un moment).
Date de publication du RFC : Avril 2019
Auteur(s) du RFC : O. Garcia-Morchon (Philips IP&S), S. Kumar (Philips Research), M. Sethi (Ericsson)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF t2trg
Première rédaction de cet article le 16 août 2019
Une blague très courante dit que, dans IoT (Internet of Things, l'Internet des Objets), le S veut dire sécurité… C'est peu dire que la sécurité de l'Iot est mauvaise. Elle est en fait catastrophique, comme l'analyse bien Schneier dans son livre « Click here to kill everybody ». C'est en grande partie dû à des raisons politico-économiques (les fabriquants se moquent de la sécurité notamment, parce que les failles de sécurité n'ont aucune conséquence négative pour eux) et en petite partie aux réelles difficultés techniques qu'il y a à sécuriser des objets qui sont parfois contraints en ressources (énergie électrique limitée, par exemple.) Ce nouveau RFC du groupe de recherche T2T (Thing to Thing) de l'IRTF se penche sur la question et essaie d'identifier les questions à long terme. À lire absolument, si vous vous intéressez à la sécurité des objets connectés. Et à lire également si vous ne vous y intéressez pas car, que vous le vouliez ou non, la sécurité des objets connectés va vous toucher.
On ne part pas de zéro pourtant. Contrairement à ce que disent parfois les vendeurs d'objets connectés pour justifier l'insécurité abyssale de leurs produits, des solutions techniques ont été développées. (Voir par exemple cet article qui parle de la sécurité des sondes Atlas.) Il existe des protocoles adaptés aux objets, comme CoAP (RFC 7252), une alternative légère à HTTP, qu'on peut sécuriser avec DTLS (RFC 9147). Mais il reste à les déployer.
Notre RFC suit un plan classique : il étudie d'abord le cycle de vie des objets connectés (section 2 du RFC), examine ensuite les risques (section 3), l'état de l'art (section 4), puis les défis pour sécuriser les objets (section 5), et enfin les prochaines étapes du travail nécessaire (section 6).
Le terme d'Internet des Objets fait partie de ces termes pipeau qui ne veulent pas dire grand'chose. Les « objets » n'ont pas grand'chose à voir, allant d'un ordiphone plus puissant que certains ordinateurs, à des étiquettes RFID, en passant par des voitures connectées qui disposent d'électricité et de puissance de calcul considérables, et des capteurs industriels qui sont au contraire très contraints. Quant à leur connexion, elle se limite parfois au réseau local et, parfois, à envoyer toutes leurs données, aussi privées qu'elles soient, vers leur maitre dans le mythique cloud. C'est le consortium privé Auto-Id qui a popularisé ce terme à la fin des années 1990, pour de simples raisons marketing. À l'époque, c'était limité à des étiquettes RFID n'ayant qu'une connexion très limitée, sans rapport avec l'Internet. Certains ont suggéré de réserver le terme d'« Internet des Objets » aux objets connectés en IP mais ces appels à la rigueur terminologique n'ont en général que peu d'impact. Bref, chercher des solutions pour l'« Internet des Objets » en général n'a que peu de chances d'aboutir, vu la très grande variété de situations que ce terme recouvre.
Mais revenons au début, au cycle de vie de nos objets connectés (section 2 du RFC). Comme la variété des objets connectés est très grande, le RFC choisit de partir d'un exemple spécifique, un système de gestion de bâtiment. Ce système contrôle la climatisation, le chauffage, la ventilation, l'éclairage, la sécurité, etc. Un tel système représente de nombreux objets, dont certains, notamment les capteurs installés un peu partout, peuvent être très contraints en ressource (processeur lent, énergie fournie uniquement par des batteries, etc). Pire, du point de vue des protocoles réseau, certains de ces objets vont passer beaucoup de temps à dormir, pour économiser l'énergie, et ne répondront pas aux autres machines pendant ce temps. Et les objets seront sans doute fabriqués par des entreprises différentes, ce qui soulèvera des questions amusantes d'interopérabilité.
La figure 1 du RFC représente un cycle de vie simplifié. Je le simplifie encore ici. L'objet est successivement :
Les différents objets présents dans le bâtiment ne seront pas aux mêmes étapes au même moment.
Le RFC remarque que le cycle de vie ne commence pas forcément à la fabrication de l'objet physique, mais avant. Pour des objets comportant du logiciel, le cycle de vie commence en fait lorsque la première bibliothèque qui sera utilisée est écrite. Les logiciels des objets connectés ont une forte tendance à utiliser des versions anciennes et dépassées des bibliothèques, notamment de celles qui assurent des fonctions de sécurité. Les bogues ont donc une longue durée de vie.
La sécurité est une question cruciale pour les objets connectés, car ils sont en contact avec le monde physique et, si ce sont des actionneurs, ils agissent sur ce monde. Comme le note Schneier, une bogue sur un objet connecté, ce n'est plus seulement un programme qui plante ou un fichier qu'on perd, cela peut être des atteintes physiques aux humains. Et les objets sont nombreux : pirater une machine ne donne pas beaucoup de pouvoir à l'attaquant, mais s'il arrive à trouver une faille lui permettant de pirater via l'Internet tous les objets d'un vendeur donné, il peut se retrouver à la tête d'un botnet conséquent (c'est exactement ce qui était arrivé avec Mirai).
Quels sont les risques exactement ? La section 3 du RFC décrit les différentes menaces, une liste longue et un peu fourre-tout. Avant tout, le code peut être incorrect, bogué ou mal conçu. C'est le cas de tout logiciel (ne croyez pas un instant les commerciaux qui assurent, sans rien en savoir eux-mêmes, que « le logiciel est conforme à l'état de l'art et aux préconisations de [insérer ici le nom d'un organisme quelconque] »). Mais comme vu plus haut, les conséquences sont plus graves dans le cas des objets connectés. En outre, il y a deux problèmes logiciels qui sont davantage spécifiques aux objets connectés : les mises à jour, pour corriger les bogues, sont plus difficiles, et rarement faites, et le logiciel est globalement négligé, n'étant pas le « cœur de métier » de l'entreprise vendeuse. On voit ainsi des failles de sécurité énormes, qui n'arrivent plus dans l'informatique plus classique.
Autre raison pour laquelle la sécurité des objets connectés est difficile à assurer, le fait que l'attaquant peut avoir un accès physique aux objets (par exemple s'il s'agit d'un type d'objet vendu publiquement). Il peut le démonter, l'étudier, et acquérir ainsi des informations utiles pour le piratage d'autres objets du même type. Si, par exemple, tous les objets d'un même type partagent une clé cryptographique privée, elle pourrait être récupérée ainsi (l'objet connecté typique n'est pas un HSM). Un modèle de sécurité comme celui de la boite noire ne s'applique donc pas. Dans le futur, peut-être que les PUF seront une solution ?
La sécurité impose de mettre à jour le logiciel de l'objet régulièrement. Mais cette mise à jour ouvre elle-même des failles de sécurité. Si le processus de mise à jour n'est pas sécurisé (par exemple par une signature du logiciel), un malveillant pourra peut-être y glisser sa version du logiciel.
Ensuite, l'objet, même s'il fonctionne comme prévu, peut faire fuiter des informations privées. S'il envoie des informations à des tiers (c'est le cas de presque tous les objets conçus pour l'usage domestique) ou s'il transmet en clair, il permet la surveillance de son propriétaire. Le chiffrement est évidemment indispensable, mais il ne protège pas contre les extrémités de la communication (le sextoy connecté qui envoie les informations sur son usage au vendeur de l'objet) et, s'il n'est pas accompagné d'une authentification du partenaire avec qui on communique, ile ne protège pas contre l'homme du milieu. Une des difficultés de l'authentification est qu'il faut bien, avant la communication, avitailler l'objet en informations (par exemple les clés publiques de ses correspondants), un défi pour des objets fabriqués en masse. Avitailler une fois l'objet sur le terrain est tout aussi difficile : ces objets n'ont souvent pas d'interface utilisateur. Cela impose des solutions comme le TOFU (faire confiance la première fois, puis continuer avec le même correspondant) ou bien l'appairage (on approche deux objets, on appuie sur un bouton et ils sont désormais appairés, ils ont échangé leurs clés).
Les objets ont souvent une histoire compliquée, étant composée de l'assemblage de divers composants matériels et logiciels, parfois promenés sur de longues distances, entre beaucoup d'entreprises différentes. Une des attaques possibles est de s'insérer quelque part dans cette chaîne d'approvisionnement et d'y glisser du logiciel ou du matériel malveillant. Est-ce que quelqu'un sait vraiment ce que fait cette puce dont on a acheté des dizaines de milliers d'exemplaires à un revendeur ? Dans le cas extrême, c'est l'objet entier qui peut être remplacé par un objet apparemment identique, mais malveillant.
Les objets connectés sont souvent dans des lieux qui ne sont pas physiquement protégés. Par exemple, les capteurs sont placés un peu partout, et parfois accessibles à un attaquant. Une fois qu'on peut mettre la main sur un objet, il est difficile d'assurer sa sécurité. Des informations confidentielles, comme une clé privée, peuvent alors se retrouver entre les mains de l'attaquant. Transformer chaque objet connecté en un coffre-fort inviolable n'est évidemment pas réalistes.
Les objets communiquent entre eux, ou bien avec des passerelles les connectant à l'extérieur. Cela ouvre de nouvelles possibilités d'attaque via le routage. Les objets connectés se servent souvent de protocoles de routage non sécurisés, permettant au malveillant d'injecter de fausses routes, permettant ainsi, par exemple, de détourner le trafic vers une machine contrôlée par ce malveillant.
Enfin, il y a la menace des attaques par déni de service. Les objets sont souvent contraints en ressources et sont donc particulièrement vulnérables aux attaques par déni de service, même légères. Et les objets ne sont pas forcément victimes, ils peuvent être aussi devenir zombies et, recrutés par un logiciel malveillant comme Mirai, être complices d'attaques par déni de service comme cela avait été le cas en octobre 2016. (Un outil comme Shodan permet de trouver facilement des objets vulnérables et/ou piratés.)
Bon, ça, c'étaient les menaces. Mais on n'est pas resté les bras ballants, on a déjà des mécanismes possibles pour faire face à ces attaques. La section 4 de notre RFC décrit l'état de l'art en matière de connexion des objets connectés, et de leur sécurisation.
Déjà, il existe plusieurs protocoles pour les objets connectés, comme ZigBee, BACnet ou DALI. Mais l'IETF se focalise évidemment sur les objets qui utilisent IP, le seul cas où on puisse réellement parler d'« Internet des Objets ». IP tel qu'il est utilisé sur les ordinateurs classiques n'est pas forcément bien adapté, et des groupes ont développé des adaptations pour les réseaux d'objets (voir par exemple le RFC 4944). De même, il existe des normes pour faire tourner IP sur des tas de couches physiques différentes, comme Bluetooth (RFC 7668), DECT (RFC 8105) ou NFC (RFC pas encore publié). Au-dessus d'IP, le protocole CoAP (RFC 7252) fournit un protocole applicatif plus adapté aux objets que le HTTP classique.
Questions formats, on a également le choix. On a JSON (RFC 8259), mais aussi CBOR (RFC 8949) qui, lui, est un format binaire, sans doute plus adapté aux objets contraints. Tous les deux ont des solutions de sécurité, par exemple la famille JOSE pour signer et chiffrer les documents JSON, et son équivalent pour CBOR, CORE (RFC 8152).
Le problème de la sécurité de l'IoT est connu depuis longtemps, et ce ne sont pas les solutions techniques qui manquent, que ce soit pour protéger les objets connectés, ou pour protéger le reste de l'Internet contre ces objets. Certains de ces protocoles de sécurité ne sont pas spécifiques aux objets connectés, mais peuvent être utilisés par eux, c'est le cas de TLS (RFC 8446). Une excuse classique des fabricants d'objets connectés pour ne pas sécuriser les communications avec TLS est le caractère contraint de l'objet (manque de ressources matérielles, processeur, mémoire, énergie, etc). Cet argument peut jouer pour des objets vraiment contraints, des capteurs bon marché disséminés dans l'usine et ne fonctionnant que sur leur batterie mais beaucoup d'objets connectés ne sont pas dans ce cas, et ont largement les moyens de faire tourner TLS. Quand on entend des fabriquants de télévisions connectées ou de voitures connectées expliquer qu'ils ne peuvent pas utiliser TLS car ce protocole est trop coûteux en ressources, on rit ou on s'indigne car c'est vraiment un argument ridicule ; une télévision ou une voiture ont largement assez de ressources pour avoir un processeur qui fait tourner TLS. (Je n'utilise que TLS et SSH pour communiquer avec un Raspberry Pi 1, avec son processeur à 700 MHz et sa consommation électrique de 2 W.)
Outre les protocoles, la sécurité repose sur des règles à suivre. La section 4.3 liste les règles formalisées existantes. Ainsi, GSMA a publié les siennes, BITAG également, le DHS étatsunien s'y est mis, l'ENISA aussi et notre RFC liste de nombreux autres documents. Si les fabriquants d'objets connectés ne sont pas au courant, ce n'est pas faute d'information, c'est bien de la mauvaise volonté !
C'est d'autant plus grave que, comme l'a illustré le cas de Mirai, les objets connectés non-sécurisés ne sont pas un problème que pour le propriétaire de l'objet, mais peuvent également toucher tout l'Internet. Il est donc logique que beaucoup de voix s'élèvent pour dire qu'il faut arrêter de compter sur la bonne volonté des fabricants d'objets connectés, qui ont largement démontré leur irresponsabilité, et commencer à réguler plus sévèrement. (C'est par exemple une demande du régulateur étatsunien FCC.)
Cette disponibilité de très nombreuses solutions techniques ne veut pas dire que tous les problèmes sont résolus. La section 5 du RFC fait ainsi le point sur les défis qui nous restent, et sur lesquels chercheu·r·se·s et ingénieur·e·s devraient se pencher. D'abord, certains objets sont contraints en ressources (pas tous, on l'a vu), comme détaillé dans le RFC 7228. L'Internet est un monde très hétérogène, connectant des machines ayant des ressources très diverses, via des réseaux qui ont des capacités hautement variables. Pour ces objets contraints (qui sont une partie seulement des « objets », une caméra de vidéo-surveillance n'est pas un objet contraint), il est raisonnable de chercher à optimiser, par exemple la cryptographie. Ainsi, la cryptographie à courbes elliptiques (RFC 8446) demande en général moins de ressources que RSA.
Les attaques par déni de service sont un autre défi pour les objets connectés, qui disposent de peu de ressources pour y faire face. Des protocoles qui permettent de tester qu'il y a une voie de retour (return routability ou returnability) peuvent aider à éviter certaines attaques que des protocoles sans ce test (comme le DNS ou comme d'autres protocoles fondés sur UDP) rendent facile. C'est pour cela que DTLS (RFC 9147) ou HIP (RFC 7401) intègrent ce test de réversibilité. Évidemment, cela n'aide pas pour les cas de la diffusion, ou bien lorsque le routage est contrôlé par l'attaquant (ce qui est souvent le cas dans les réseaux « mesh ».) Autre protection, qui existe par exemple dans HIP : forcer l'initiateur d'une connexion à résoudre un problème, un « puzzle », afin d'éviter que les connexions soient « gratuites » pour l'initiateur. La principale limite de cette solution est qu'elle marche mal si les machines impliquées ont des capacités de calcul très différentes (un objet contraint contre un PC). Il y a également le cas, non mentionné par le RFC, où l'attaquant dispose d'un botnet et ne « paie » donc pas les calculs.
L'architecture actuelle de l'Internet n'aide pas au déploiement de certaines solutions de sécurité. Ainsi, un principe de base en sécurité est d'avoir une sécurité de bout en bout, afin de ne pas dépendre d'intermédiaires malveillants ou piratés, mais c'est rendu de plus en plus difficile par l'abus de middleboxes, qui interfèrent avec beaucoup de comunications. On est donc forcés d'adapter la sécurité à la présence de ces middleboxes, souvent en l'affaiblissant. Par exemple :
Une fois déployés, les objets connectés vont rester en fonctionnement des années, voire des décennies. Il est donc crucial d'assurer les mises à jour de leur logiciel, ne serait-ce que pour réparer les failles de sécurité qui ne manqueront pas d'être découvertes, qu'elles soient dans le code ou dans les algorithmes utilisés. Par exemple, si les promesses des ordinateurs quantiques se concrétisent un jour, il faudra jeter RSA et les courbes elliptiques (section 5.8 du RFC).
Mais assurer des mises à jour sûres n'est pas facile, comme le note Bruce Schneier. C'est que le processus de mise à jour, s'il est insuffisamment sécurisé, peut lui-même servir pour une attaque, par exemple en envoyant du code malveillant à un objet trop naïf. Et puis comment motiver les vendeurs à continuer à fournir des mises à jour logicielles des années après que le dernier exemplaire de ce modèle ait été vendu ? Capitalisme et sécurité ne vont pas bien ensemble. Et il se peut tout simplement que le vendeur ait disparu, que le code source de l'objet ne soit plus disponible, et qu'il soit donc impossible en pratique de développer une mise à jour. (D'où l'importance, même si le RFC ne le dit pas, du logiciel libre.) Enfin, si la mise à jour doit être effectuée manuellement, il est probable qu'elle ne sera pas faite systématiquement. (Un rapport de la FTC états-unienne détaille également ce problème.)
Mais les mises à jour automatiques posent également des tas de problèmes. Par exemple, pour des ampoules connectées (une idée stupide, mais le monde de l'IoT est plein d'idées stupides), il vaut mieux mettre à jour leur logiciel le jour que la nuit. Et il vaut mieux que toutes les ampoules ne soient pas mises à jour en même temps. Et les mises à jour supposent que le système ait été conçu pour cela. Par exemple, en cryptographie, il est souvent nécessaire de remplacer les algorithmes cryptographiques qui ont été cassés avec le temps, mais beaucoup d'objets connectés utilisent des systèmes cryptographiques mal conçus, qui n'ont pas d'agilité cryptographique. (Au passage, la section 5.8 du RFC traite le cas des possibles futurs ordinateurs quantiques, et des conséquences qu'ils auront pour la cryptographie. Les objets connectés peuvent rester actifs de nombreuses années, et il faut donc penser loin dans le futur.) Ces points, et beaucoup d'autres, avaient été traités dans un atelier de l'IAB, qui avait fait l'objet du RFC 8240. À l'IETF, le groupe de travail SUIT développe des mécanismes pour aider les mises à jour (mais qui ne traiteront qu'une petite partie du problème).
Rapidement dépassés, les objets connectés posent également des problèmes de gestion de la fin de vie. Au bout d'un moment, le vendeur va arrêter les différentes fonctions, comme les mises à jour du logiciel ou, plus radicalement, comme les serveurs dont dépend l'objet. Cet arrêt peut être volontaire (l'objet n'intéresse plus le vendeur, qui est passé à d'autres gadgets) ou involontaire (vendeur en faillite). Le RFC note qu'une des voies à explorer est la continuation de l'objet avec du logiciel tiers, qui ne dépend plus de l'infrastructure du vendeur. Bien des ordiphones ont ainsi vu leur vie prolongée par CyanogenMod, et bien des routeurs ont bénéficié d'OpenWrt. (D'où l'importance de pouvoir installer ce logiciel tiers, ce qu'interdisent beaucoup de vendeurs.)
Une autre question intéressante de sécurité posée par les objets connectés est la vérification de leurs capacités réelles et de leur comportement effectif. L'acheteur peut avoir l'impression qu'il est le propriétaire de l'objet acheté mais cet objet est suffisamment complexe pour que l'acheteur ne soit pas au courant de tout ce que l'objet fait dans son dos. Le vrai maitre de l'objet est alors le vendeur, qui continue à communiquer avec l'engin connecté. C'est ainsi que des malhonnêtes comme Lidl ou Google avaient installé des micros dans des objets qu'on installe chez soi, et évidemment sans le dire à l'acheteur. Et encore, un micro est un appareil physique, qu'un examen attentif (avez-vous vérifié tous les objets connectés chez vous ?) peut détecter. Mais savoir ce que raconte l'objet connecté à son maitre est plus difficile. Peu d'utilisateurs ont envie de configurer un routeur local, et d'y faire tourner tcpdump pour voir le trafic. Et encore, ce trafic peut être chiffré et l'acheteur (qui, rappelons-le, n'est pas le véritable propriétaire de l'objet, puisqu'il n'a quasiment aucun contrôle, aucune information) n'a pas les clés.
Le problème de fournir des informations à l'utilisateur n'est pas trivial techniquement. Beaucoup d'objets connectés n'ont pas d'interface utilisateur où afficher « je suis en train d'envoyer plein de données sur vous à mon maitre ». Une solution partielle serait une description des capacités de l'engin, et de ses communications, dans un fichier MUD (Manufacturer Usage Description, RFC 8520). Ceci dit, vu le niveau d'éthique dans le monde de l'IoT, gageons que ces fichiers MUD mentiront souvent, notamment par omission.
Puisqu'on a parlé de vie privée, c'est l'occasion de rappeler que l'IoT est une grave menace pour cette vie privée. Le RFC note que, dans le futur, nous serons peut-être entourés de centaines d'objets connectés. (Malheureusement, le RFC parle surtout des risques dus à des tiers qui observeraient le trafic, et très peu des risques dus aux vendeurs qui récoltent les données.) L'IoT permet une intensification considérable du capitalisme de surveillance.
Bref, la situation est mauvaise et, s'il y a en effet quelques progrès (on voit moins souvent des mots de passe identiques pour tous les objets d'un type), ils sont largement annulés par de nouveaux déploiements.
Date de publication du RFC : Avril 2019
Auteur(s) du RFC : H. Van de Sompel (Data Archiving and Networked Services), M. Nelson (Old Dominion University), G. Bilder (Crossref), J. Kunze (California Digital Library), S. Warner (Cornell University)
Pour information
Première rédaction de cet article le 18 avril 2019
Ce RFC décrit un nouveau type de liens hypertexte permettant d'indiquer l'URI sous lequel on préfère qu'une ressource soit accédée, à des fins de documentation ou de citation précise.
Imaginons que vous accédez à un article scientifique en ligne. Vous utilisez un URI qui identifie cet article. Vous voulez ensuite citer cet article dans un de vos textes. Allez-vous utiliser l'URI d'accès ? Ce n'est pas forcément le meilleur, par exemple ce n'est pas forcément le plus stable sur le long terme. Le lien « cite avec » permet au serveur d'indiquer l'URI le plus pertinent pour une citation.
Ce RFC s'appuie sur la formalisation du concept de lien faite dans le RFC 8288. « Contexte » et « cible » sont donc utilisés comme dans cette norme, le contexte d'un lien étant le point de départ et la cible l'arrivée. En prime, notre RFC 8574 définit deux termes, l'URI d'accès, celui utilisé pour accéder à une ressource (par exemple une page Web) et l'URI de référence, celui qu'il faudrait utiliser pour la citation.
La section 3 du RFC décrit quelques scénarios d'usage. Le premier est celui des identificateurs stables. Normalement, lorsque le ou la webmestre est compétent(e) et sérieux(se), les URI sont stables, comme précisé dans l'article « Cool URIs don't change. Mais, en pratique, beaucoup de webmestres sont incompétents ou paresseux. Cela a mené à des « solutions » fondées sur la redirection, où il apparait une différence entre URI d'accès et URI de référence. C'est le cas avec des techniques comme les DOI (« use the Crossref DOI URL as the permanent [reference] link »), PURL ou ARK. Dans les trois cas, au lieu de gérer proprement les URI, on utilise un redirecteur supposé plus stable (alors que rien ne le garantit) et on souhaite utiliser comme URI de référence l'URI du redirecteur (donnant ainsi des pouvoirs démesurés à des organisations privées comme l'IDF, qui matraque régulièrement l'importance de n'utiliser que leurs identificateurs).
Un autre scénario d'usage est celui des ressources
versionnées. C'est par exemple le cas de Wikipédia. La page Wikipédia sur
l'incendie de Notre-Dame de Paris change souvent en ce
moment. Comme toutes les pages Wikipédia, chaque version à un
identificateur, et on peut se référer à une version
particulier. Si
renvoie à la dernière version, sans cesse en mouvement,
https://fr.wikipedia.org/wiki/Incendie_de_Notre-Dame_de_Paris
renvoie uniquement à la toute première version, très sommaire et
https://fr.wikipedia.org/w/index.php?title=Incendie_de_Notre-Dame_de_Paris&oldid=158468007
à une version intermédiaire déjà très fournie.https://fr.wikipedia.org/w/index.php?title=Incendie_de_Notre-Dame_de_Paris&oldid=158478416
Souvent, quand on veut citer un article de Wikipédia, on veut se mettre à l'abri de changements ultérieurs, pas forcément positifs, et on souhaite donc citer exactement une version particulière. On clique donc sur « Lien permanent » (ou bien « Voir l'historique » puis sur la date la plus récente) pour avoir l'URI de la version qu'on vient de regarder. (Notez aussi le très utile lien « Citer cette page ».)
Troisième cas d'usage cité, celui des identifiants sur les
réseaux sociaux. M. Toutlemonde a
typiquement plusieurs pages le décrivant sur ces réseaux (dans mon
cas,
,
https://mastodon.gougere.fr/@bortzmeyer
,
https://twitter.com/bortzmeyer
, https://www.linkedin.com/in/sbortzmeyer/
et
sans doute bien d'autres que j'ai oubliés, et ceux que j'ai eu la
flemme de faire, comme FOAF). Or, on pourrait
souhaiter décider qu'un de ces profils est meilleur que les
autres, par exemple parce qu'il est plus directement contrôlé par
l'utilisateur, ou mieux maintenu. Il serait alors intéressant
d'indiquer lors de l'accès à chacun des autres profils quel est le
profil de référence. (Le RFC est très irréaliste là-dessus : je
vois mal un réseau « social » capitaliste permettre à ses
utilisateurs de dire « allez plutôt voir là ».)https://seenthis.net/people/stephane
Enfin, un dernier cas d'usage est celui d'une publication composée de plusieurs ressources (par exemple un livre où chaque chapitre est accessible séparement, avec son propre URI). On souhaite alors que l'accès à chaque ressource indique, à des fins de citation, l'URI de référence (par exemple la page d'accueil).
La section 4 du RFC présente la solution : un nouveau type de
lien, cite-as
, qui permet de dire quel est
l'URI de référence. (Le RFC recommande d'ailleurs que cet URI soit
un URL : le but est d'accéder à la
ressource !) Il est évidemment recommandé qu'il n'y ait qu'un seul
lien de type cite-as
. Ce lien n'interdit pas
d'utiliser d'autres URI, il indique seulement quel est l'URI
que le webmestre souhaite voir utilisé dans les références
webographiques. cite-as
est désormais dans
le
registre IANA des types de liens.
La section 6 du RFC donne des exemples concrets, puisque les
liens peuvent se représenter de plusieurs façons. Par exemple,
l'article de PLOS One auquel vous accédez en
pourrait contenir, en HTML, le lien avec
l'attribut https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0171057
rel="cite-as"
:
<link rel="cite-as" href="https://doi.org/10.1371/journal.pone.0171057" />
Cela indiquerait que les auteurs préfèrent être cités par le DOI (une mauvaise idée, mais c'est une autre histoire).
Autre exemple de syntaxe concrète pour les liens, imaginé pour
arXiv, pour des
articles avec versionnement, un lien dans un en-tête
HTTP pour
, qui pourrait indiquer
qu'on est en train de regarder la première version, « v1 » (il
existe une « v2 », essayez) :
https://arxiv.org/abs/1711.03787
HTTP/1.1 200 OK Date: Sun, 24 Dec 2017 16:12:43 GMT Content-Type: text/html; charset=utf-8 Link: <https://arxiv.org/abs/1711.03787v1> ; rel="cite-as"
Comme arXiv garde les différentes versions successives d'un article, cela permettrait de récupérer la version actuelle tout en sachant comment la référencer.
Revenons au HTML pour l'exemple des profils sur les réseaux sociaux, imaginons un utilisateur, John Doe, qui place dans le code HTML de sa page personnelle un lien vers son profil FOAF en Turtle :
<html> <head> ... <link rel="cite-as" href="http://johndoe.example.com/foaf" type="text/turtle"/> ... </head> <body> ...
Et un dernier exemple, celui d'une publication composée de
plusieurs ressources. Ici, l'exemple est Dryad une base de données scientifiques qui
permet l'accès à des fichiers individuels, mais où chaque jeu de données a un
identificateur canonique. En HTTP, cela donnerait, lorsqu'on accès
à
(un fichier CSV qui fait partie de cette
base de données) :
https://datadryad.org/bitstream/handle/10255/dryad.98509/PIPFIL_M_BCI.csv
HTTP/1.1 200 OK Date: Tue, 12 Jun 2018 19:19:22 GMT Last-Modified: Wed, 17 Feb 2016 18:37:02 GMT Content-Type: text/csv;charset=ISO-8859-1 Link: <https://doi.org/10.5061/dryad.5d23f> ; rel="cite-as"
Le fichier CSV est membre d'un jeu de données plus général, dont
l'URI de référence est
.https://doi.org/10.5061/dryad.5d23f
Ainsi, dans un monde idéal, un logiciel qui reçoit un lien
cite-as
pourrait :
D'autres solutions avaient été proposées pour résoudre ce
problème de l'URI de référence. La section 5 de notre RFC les
énumère. Il y avait notamment cinq autres types de liens qui
auraient peut-être pu convenir, alternate
,
duplicate
, related
, bookmark
et
canonical
.
Les trois premiers sont vite
éliminés. alternate
(RFC 4287) décrit une autre représentation de la même
ressource (par exemple la même vidéo mais encodée
différemment). duplicate
(RFC 6249) désigne une reproduction identique (et cela ne
traite donc pas, par exemple, le cas d'une publication composée de
plusieurs ressources). Quant à related
(RFC 4287), sa
sémantique est bien trop vague. Un article des auteurs du RFC
décrit
en détail les choix de conceptions et explique bien le
problème. (Je trouve cet article un peu gâché par les affirmations
sans preuves comme quoi les DOI seraient « permanents ». Si le
registre disparait ou fait n'importe quoi, il y aura le même
problème avec les DOI qu'avec n'importe quelle autre famille d'identificateurs.)
Le cas de bookmark
(normalisé par le W3C) est plus
compliqué. Il est certainement proche en sémantique de
cite-as
mais ne peut pas être présent dans
les en-têtes HTTP ou dans la tête d'une page HTML, ce qui en réduit
beaucoup l'intérêt. Le cas compliqué de
bookmark
est décrit dans
un autre article des auteurs du RFC.
Enfin, le cas de canonical
(RFC 6596). Ce dernier semble trop restreint
d'usage pour les utilisations prévues pour
cite-as
. Et il n'a pas vraiment la même
sémantique. Par exemple, pour les ressources versionnées,
canonical
indique la plus récente, exactement
le contraire de ce qu'on voudrait avec
cite-as
. Et c'est bien ainsi que l'utilise
Wikipédia. Si je récupère
:
https://fr.wikipedia.org/w/index.php?title=Incendie_de_Notre-Dame_de_Paris&oldid=158478416
<link rel="canonical" href="https://fr.wikipedia.org/wiki/Incendie_de_Notre-Dame_de_Paris"/>
On voit que canonical
renvoie à la dernière
version. Le cas de canonical
fait lui aussi
l'objet
d'un article détaillé.
Je n'ai pas mis de tels liens sur ce blog, ne voyant pas de cas où ils seraient utiles.
Date de publication du RFC : Juillet 2019
Auteur(s) du RFC : M. Mosko (PARC), I. Solis (LinkedIn), C. Wood (University of California Irvine)
Expérimental
Réalisé dans le cadre du groupe de recherche IRTF icnrg
Première rédaction de cet article le 16 juillet 2019
Certaines personnes trouvent une utilité au réseau centré sur le contenu, où adressage et nommage ne désignent que du contenu, des ressources numériques auxquelles on accède via le réseau. (Cette idée est souvent nommée ICN, pour Information-Centric Networking ou NDN pour Named Data Networking.) Le groupe de recherche ICNRG développe des spécifications pour normaliser certains aspects de ce réseau centré sur le contenu, un projet nommmé CCNx (pour Content Centric Networking). Ce nouveau RFC décrit les concepts de base de CCNx. CCNx est le premier protocole conçu par le groupe ICNRG.
Précisons tout de suite : ce point de vue comme quoi le socle du réseau devrait être l'accès au contenu est très contestable. Il évoque fortement le raccourci de certains journalistes, décideurs, et opérateurs de télécommunication traditionnels comme quoi le seul usage de l'Internet fait par M. Michu serait « accéder à du contenu ». Mais j'ai déjà développé ces critiques dans un autre article il y a huit ans, donc je ne les reprendrai pas ici.
Les acteurs du réseau centré sur le contenu sont notamment :
(Vous noterez qu'il n'y a pas de pair à pair dans ce réseau, ce qui limite certains usages.)
Le protocole CCnx repose sur deux types de messages :
Interest
, par lequel on signale qu'on
aimerait bien récupérer tel contenu, et Content
Object
, qui transporte un contenu. En général, la
machine de l'utilisateur, du consommateur demandeur, enverra un
Interest
et, si tout va bien, récupérera en
échange un ou plusieurs Content Object
. Si
tout va mal, on aura à la place un
InterestReturn
, qui signale un
problème. Comment sont désignés les contenus ? Par des noms
hiérarchiquement organisés, comme dans les
URL d'aujourd'hui (section 3 du RFC). Un nom est donc composé
d'une série de segments, et la correspondance entre un nom demandé
et une entrée de la table de routage est toujours faite de manière exacte, bit
à bit. (Pas
d'insensibilité à la casse, pas de
recherche floue.) Le nom est opaque. Il n'est donc pas forcément lisible par un
humain. Comme les noms sont hiérarchiques, un nom peut être exact
(le nom entier) ou être un préfixe (correspondant à plusieurs
noms). On dit aussi qu'un nom est complet quand il inclut un
condensat du contenu (comme dans les
magnets de
BitTorrent). Le condensat est expliqué plus
en détail dans les sections 5 et 7 du RFC. La syntaxe est décrite
dans l'Internet
Draft draft-mosko-icnrg-ccnxurischeme
, qui met
les noms CCNx sous forme d'URI. Par
exemple, un nom possible est
ccnx:/NAME=foo/APP:0=bar
. Il n'y a pas de
registre de tels noms pour l'instant. Si on veut trouver des noms,
le protocole lui-même ne le permet pas, il faudra bâtir des
solutions (par exemple un moteur de recherche) au-dessus de CCNx.
CCNx fonctionne en
relayant les messages (aussi bien Interest
que Content Object
) d'une machine à
l'autre. Du fait de ce modèle de relayage systématique, il faut
ajouter un troisième acteur au producteur et au consommateur, le
relayeur (forwarder), qui
est toute machine intermédiaire, un peu comme un
routeur sauf que le relayeur fait bien plus
de choses qu'un routeur. Par exemple, contrairement au routeur
IP, le relayeur a un état. Chaque demande
d'objet qui passe est mémorisée par le relayeur (dans une
structure de données nommée la PIT, pour Pending Interest
Table), qui saura donc où
renvoyer la réponse. CCNx est sourceless,
contrairement à IP : l'adresse source n'est pas indiquée dans la demande.
La FIB (Forwarding Information Base) est la table de routage de CCNx. Si elle contient une entrée pour le contenu convoité, cette entrée indique où envoyer la requête. Sinon, la requête ne peut aboutir. Notez que ce RFC ne décrit pas le protocole par lequel cette FIB sera construite. Il n'existe pas encore d'OSPF ou de BGP pour CCNx.
Comme le contenu peut être récupéré après un passage par pas mal d'intermédiaires, il est crucial de vérifier son intégrité. CCNx permet plusieurs méthodes, de la signature au HMAC. Ainsi, l'intégrité dans CCNx est une protection des objets (du contenu), pas uniquement du canal comme c'est le cas avec HTTPS. CCNX permet également de signer des listes d'objets (des manifestes), la liste contenant un SHA ou un CRC des objets, ce qui permet d'assurer l'intégrité de ceux-ci.
Ces concepts avaient été décrits dans les RFC 7476 et RFC 7927. Le vocabulaire est expliqué dans le RFC 8793. Maintenant, voyons les détails, sachant que le format précis des messages a été délégué à un autre RFC, le RFC 8609. La section 2 du RFC décrit précisement le protocole.
On a vu que le consommateur commençait l'échange, en envoyant
un message de type Interest
. Ce message
contient le nom de l'objet qui intéresse le consommateur, et
éventuellement des restrictions sur le contenu, par exemple qu'on ne veut que des objets signés, et avec tel
algorithme, ou bien ayant tel
condensat cryptographique de l'objet (le
tuple regroupant nom et restrictions se nomme le lien, cf. section
6). Un nombre maximal de
sauts restants est indiqué dans le message. Décrémenté par chaque
relayeur, il sert à empêcher les boucles (lorsqu'il atteint zéro,
le message est jeté). Le producteur, lui,
stocke le contenu, indexé par les noms, et signale ces noms sur le
réseau pour que les relayeurs peuplent leur FIB (on a vu que le
protocole permettant ce signalement n'était pas encore défini,
bien que plusieurs propositions existent). Enfin, le relayeur, le
troisième type d'acteur, fait suivre les
Interest
dans un sens (en consultant sa FIB)
et les Content Object
en sens inverse (en
consultant sa PIT).
Le relayeur a également une mémoire (un
cache), qui sert notamment à accélérer l'accès au contenu le
plus populaire (section 4 du RFC). Il existe des moyens de
contrôler l'utilisation de cette mémoire, par exemple deux champs
dans un Content Object
, la date d'expiration,
après laquelle il ne faut plus garder l'objet dans le cache, et la
date de fin d'intérêt, après laquelle il n'est sans doute plus
utile de garder l'objet (mais on peut quand même si on veut).
La validation des objets, leur vérification, est un composant crucial de CCNx. Elle est spécifiée dans la section 8 du RFC, avec ses trois catégories, la validation utilisant un simple condensat (pas forcément cryptographique), un HMAC ou bien une signature.
On a vu que le troisième type de message de CCNx, après
Interest
et Content
Object
, était
Interest Return
. Il est décrit en détail dans
la section 10 de notre RFC. Notez tout de suite qu'il peut ne pas
y avoir de réponse du tout, un relayeur n'étant pas forcé
d'envoyer un Interest Return
s'il ne peut
acheminer un Interest
. S'il y a un
Interest Return
, il indique l'erreur, par
exemple No Route (aucune entrée dans la FIB
pour ce nom), No Resources (le relayeur manque
de quelque chose, par exemple de place disque pour son cache),
Malformed Interest (un problème dans la demande),
Prohibited (le relayeur n'a pas envie de
relayer), etc.
Enfin, sur la question cruciale de la sécurité, la section 12 du RFC revient sur quelques points sensibles. Par exemple, j'ai dit plus haut que les objets pouvaient être validés par une signature numérique. Mais où trouve-t-on les clés publiques des producteurs, pour vérifier leur signature ? Eh bien ce point n'est pas actuellement traité. Notez que les relayeurs, eux, ne sont pas obligés de valider et un cache peut donc contenir des données invalides. Les RFC 7927 et RFC 7945 sont des bonnes ressources à lire sur la sécurité des réseaux centrés sur le contenu.
Il existait une version précédente du protocole CCNx,
identifiée « 0.x », et décrite dans « Networking Named
Content ». Dans 0.x, la correspondance entre le
nom demandé dans un Interest
et celui obtenu
était hiérarchique : le nom demandé pouvait être un préfixe du nom
obtenu. La version décrite dans ce RFC, « 1.0 », est plus
restrictive ; le nom obtenu doit être exactement le nom
demandé. Les fonctions de recherche ne sont pas dans CCNx, elles
doivent être dans un protocole de couche supérieure, un Google ou
Pirate Bay du réseau CCN. Un exemple d'un tel protocole est décrit
dans l'Internet Draft draft-mosko-icnrg-selectors
.
Et questions mise en œuvre du protocole CCNx ? Il en existe au moins deux, Community ICN, et CCN-Lite (cette dernière, tournant sur RIOT, visant plutôt l'Internet des objets).
Date de publication du RFC : Avril 2019
Auteur(s) du RFC : T. Hardie
Pour information
Première rédaction de cet article le 6 juin 2019
Ce nouveau RFC de l'IAB examine les signaux envoyés par un protocole de transport aux couches supérieures. Par exemple, la machine à états de TCP est observable de l'extérieur, on peut déduire son état de l'examen des paquets envoyés. Certains de ces signaux sont explicites, prévus pour être lus par les routeurs, d'autres sont implicites, déduits de certains comportements. Le RFC recommande de compter plutôt sur les signaux explicites, documentés et fiables. Attention, la tendance actuelle est, pour protéger la vie privée et pour limiter les interférences du réseau avec les communications, de limiter les signaux envoyés. Ainsi, QUIC envoie nettement moins de signaux que TCP.
Le principe de bout en bout dit que les éléments du réseau ne devraient pas avoir besoin de ces signaux du tout (RFC 1958). Ils devraient transporter les datagrammes, point. Mais en pratique, des raisons plus ou moins légitimes font que des équipements intermédiaires ont besoin d'accéder à des informations sur le transport. C'est par exemple le cas des routeurs NAT (RFC 3234).
Comme exemple de signaux implicites, on peut citer ceux de TCP (RFC 793). Les messages échangés (SYN, RST, FIN…) sont destinés aux extrémités, pas aux boitiers intermédiaires mais, comme ils sont visibles (sauf utilisation d'IPsec), le réseau peut être tenté de s'en servir comme signaux implicites. C'est ce que fait un pare-feu à état quand il utilise ces messages pour déterminer si la connexion a été demandée depuis l'intérieur (auquel cas elle est souvent autorisée) ou de l'extérieur (auquel cas elle est souvent interdite).
Cette observation des signaux implicites a souvent pour but une action (blocage des connexions entrantes, dans l'exemple ci-dessus, ou bien déni de service en envoyant des faux RST). Il est donc logique que les protocoles cherchent à se protéger en chiffrant la communication. TLS ou SSH ne chiffrent que l'application, et restent donc vulnérables aux attaques visant la couche 4. D'où le développement de protocoles comme QUIC, qui chiffrent l'essentiel de la machinerie de transport.
La section 2 de notre RFC liste les signaux qui peuvent être déduits de l'observation de la couche transport en action :
On le voit, les signaux implicites sont utilisés (pas forcément pour de bonnes raisons). Si on chiffre la couche transport, comme le fait QUIC, on perd certains de ces signaux. Que faut-il faire ? La section 3 du RFC liste, sans en recommander une particulière, plusieurs possibilités. La première est évidemment de ne rien faire. Si on chiffre, c'est justement pour assurer la confidentialité ! Le transport étant une fonction de bout en bout, les intermédiaires ne sont pas censés regarder son fonctionnement. Cette approche a quand même quelques inconvénients. Par exemple, un routeur NAT ne sait plus quand les connexions commencent et quand elles finissent, il peut donc être nécessaire d'ajouter du trafic « battement de cœur » pour maintenir l'état dans ce routeur.
On peut aussi se dire qu'on va remplacer les signaux implicites de la couche transport par des signaux explicites, conçus précisement pour une utilisation par des boitiers intermédiaires. C'est le cas du connection ID de QUIC, qui permet par exemple aux répartiteurs de charge d'envoyer tous les paquets d'une connexion QUIC donnée au même serveur. Ou du spin bit du même protocole, pour permettre certaines mesures par les intermédiaires (un bit qui a été très controversé dans la discussion à l'IETF). Le RFC note que ces signaux explicites pourraient être transportés par les en-têtes hop-by-hop d'IPv6 (RFC 7045) mais que leur capacité à être déployés sans perturber les équipements intermédiaires ne va pas de soi.
Ces signaux explicites pourraient être placés dans une mince couche intermédiaire entre UDP (qui sert de base à plusieurs protocoles de transport, comme QUIC ou comme SCTP désormais), et cette normalisation d'une couche intermédiaire avait, par exemple, été proposée dans le projet PLUS (Transport-Independent Path Layer State Management).
Après cette étude, quelles recommandations ? La section 4 du RFC recommande évidemment que les nouveaux protocoles fournissent de la confidentialité par défaut (TCP expose trop de choses), ce qui implique le chiffrement systématique. Les signaux implicites font fuiter de l'information et devraient être évités. L'approche de QUIC est donc la bonne. Par contre, comme il peut être utile d'envoyer certaines informations aux différents équipements intermédiaires situés sur le réseau, l'IAB recommande de mettre quelques signaux explicites.
Cela nécessite de suivre les principes suivants :
Notez que cette intégrité ne peut être vérifiée que par les machines terminales, les machines du réseau n'ayant pas le matériau cryptographique (les clés) nécessaires.
Reste enfin les questions de sécurité (section 6 du RFC). Le modèle de menace classique sur l'Internet est qu'on ne peut pas faire confiance aux intermédiaires : sur le trajet entre Alice et Bob, il est trop fréquent qu'au moins un des intermédiaires soit bogué, ou simplement malveillant. Tous les signaux envoyés implicitement sont dangereux, car ils peuvent donner de l'information à celui qui est peut-être un attaquant, lui facilitant certaines attaques. D'où l'importance de diminuer ces signaux implicites.
Naturellement, ce n'est pas une solution miracle ; les attaquants vont trouver d'autres méthodes et la lutte entre attaquant et défenseur ne sera donc jamais finie.
Publier des signaux explicites présente aussi des risques ; en voulant donner au réseau des informations qui peuvent lui être utiles, on peut menacer la vie privée. Ceci explique la vigueur des débats à l'IETF au sujet du spin bit de QUIC. Le spin bit n'a pas d'utilité pour les machines terminales, seulement pour les équipements intermédiaires. Ses partisans disaient qu'il était important que ces équipements puissent accéder à des informations sur le RTT. Ses adversaires (qui n'ont pas eu gain de cause complet, le spin bit est optionnel, on n'est pas forcé de l'envoyer) estimaient que faire fuiter volontairement de l'information, même assez inoffensive, ouvrait un risque potentiel.
Enfin, comme les signaux explicites sont déconnectés des messages échangés entre les deux machines qui communiquent, il faut se poser la question de leur authenticité. Un tiers peut les modifier pour tromper les machines suivantes sur le trajet. Les protections cryptographiques ne sont pas utilisables puisqu'il n'y a aucune chance que les équipements intermédiaires disposent des clés leur permettant de vérifier ces protections. Plus drôle, si un opérateur réseau agit sur la base de ces signaux explicites, et, par exemple, favorise certaines sessions au détriment d'autres, on pourrait voir des machines terminales décider de « tricher » en envoyant délibérement de faux signaux. (Ce qui n'est pas possible avec les signaux implicites, qui sont de véritables messages, interprétés par la machine située en face.)
Date de publication du RFC : Mars 2019
Auteur(s) du RFC : R. Barnes (Cisco), J. Hoffman-Andrews (EFF), D. McCarney (Let's Encrypt), J. Kasten (University of Michigan)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF acme
Première rédaction de cet article le 11 avril 2019
Une grande partie de la sécurité du Web, et d'ailleurs de plein d'autres chose sur l'Internet, repose sur des certificats où une Autorité de Certification (AC) garantit que vous êtes bien ce que vous prétendez être. Traditionnellement, l'émission d'un certificat se faisait selon un processus manuel, lent et coûteux, à part dans quelques AC automatisées et gratuites comme CAcert. Mais il n'existait pas de mécanisme standard pour cette automatisation. (Et CAcert n'a pas d'API, même non-standard.) Un tel mécanisme standard existe désormais, avec le protocole ACME, normalisé dans ce RFC. Son utilisateur le plus connu est l'AC Let's Encrypt.
Pour comprendre ACME, il faut d'abord revenir aux utilisations des certificats. La norme technique pour les certificats utilisés sur l'Internet se nomme PKIX et est normalisée dans le RFC 5280. PKIX est un profil (une restriction d'une norme beaucoup plus large - et bien trop large, comme le sont souvent les normes des organismes comme l'UIT ou l'ISO) de X.509. Un certificat PKIX comprend, entre autres :
On note que le certificat est public. N'importe qui peut récupérer
le certificat de, par exemple, un site Web. Voici un exemple avec
OpenSSL et
www.labanquepostale.fr
pour un certificat de
type EV :
% openssl s_client -connect www.labanquepostale.fr:443 -showcerts | openssl x509 -text Certificate: Data: Version: 3 (0x2) Serial Number: 0d:8f:ec:dd:d8:7b:83:b8:a0:1e:eb:c2:a0:2c:10:9b Signature Algorithm: sha256WithRSAEncryption Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 Extended Validation Server CA Validity Not Before: Sep 5 00:00:00 2018 GMT Not After : Sep 4 12:00:00 2020 GMT Subject: businessCategory = Private Organization, jurisdictionC = FR, serialNumber = 421 100 645, C = FR, L = PARIS, O = LA BANQUE POSTALE SA, OU = DISFE, CN = www.labanquepostale.fr ...
et un avec GnuTLS
pour un certificat DV (Domain Validation), mamot.fr
:
% gnutls-cli mamot.fr - subject `CN=mamot.fr', issuer `CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US', serial 0x035516154ab9120c186e9211d0da6296af62, RSA key 2048 bits, signed using RSA-SHA256, activated `2019-01-13 23:00:32 UTC', expires `2019-04-13 23:00:32 UTC', key-ID `sha256:ef62c4aae2a9a99c00c33f2bbac9c40b980c70400a056e2a8042885e501ce283' ...
D'ailleurs, des services comme Certificate
Transparency (RFC 6962), accessible entre autres en
, donnent accès facilement à
tous les certificats émis par les AC
participantes.https://crt.sh/
Du fait que seul le titulaire connait la clé privée, la capacité à signer des messages vérifiables avec la clé publique permet d'authentifier le partenaire avec lequel on communique. Grâce à la signature de l'AC, quiconque fait confiance à cette AC particulière peut être sûr que le certificat appartient bien au titulaire. Dans l'exemple avec OpenSSL, le certificat de la Banque Postale était signé par DigiCert, si on fait confiance à DigiCert, on sait qu'on est bien connecté à la Banque Postale.
Qui sont les AC ? Ce sont la plupart du temps des entreprises
commerciales qui sont payées par les titulaires de certificat, et
elles sont censées vérifier la sincérité de leurs clients. Cette
vérification peut être manuelle, partiellement ou totalement
automatique. Normalement, les certificats de type EV
(Extended Validation), comme celui de la Banque
Postale plus haut, font l'objet d'une vérification manuelle. Cela
permet de vérifier l'identité officielle (celle gérée par l'État)
du titulaire. Les certificats DV (Domain
Validation), comme celui de
mamot.fr
, eux, peuvent être validés
automatiquement, ils assurent uniquement le fait que le titulaire
contrôle bien le nom de
domaine utilisé comme sujet. (Pour avoir tous les
horribles détails, y compris les certificats OV - Organization Validated - dont je n'ai pas
parlé, on peut consulter les « Baseline
Requirements for the Issuance and Management of Publicly-Trusted
Certificates » du CA/Browser
Forum.) Ainsi, pour CAcert, on doit prouver le contrôle du
domaine en répondant aux courriers envoyés aux adresses publiques
de contact pour le domaine.
Les certificats peuvent servir à de nombreux protocoles de sécurité mais le plus connu est TLS (normalisé dans le RFC 8446). Comme il n'est pas le seul protocole pour lequel on a des certificats, il est erroné de parler de « certificats TLS » ou, pire encore, de « certificats SSL ». TLS est un protocole client/serveur, où le client authentifie le serveur mais où le serveur authentifie rarement le client. Il est à la base de la sécurisation d'un grand nombre de services sur l'Internet, à commencer par le Web avec HTTPS (RFC 2818). L'authentification du serveur par le client est faite en vérifiant (attention, je vais simplifier) :
Une fois cette authentification faite, TLS assure l'intégrité et la confidentialité de la communication.
Attention, on parle bien d'authentification, pas de
confiance. Malgré ce que vous pourrez lire dans les « La sécurité
pour les nuls », le fameux « cadenas vert » ne signifie pas du
tout que vous pouvez faire vos achats en ligne en toute
sécurité. Il indique seulement que le partenaire a bien le nom que
vous avez demandé, et qu'un tiers ne pourra pas écouter ou
modifier la conversation. Il n'indique pas
que le partenaire soit digne de confiance ; l'AC ne peut pas
vérifier cela ! Ainsi, dans l'exemple plus haut, TLS et
l'authentification par certificat garantissent bien qu'on se
connecte au serveur HTTPS de la Maison-Blanche,
www.whitehouse.gov
, mais pas que
Trump dise la vérité !
J'ai parlé du magasin où se trouvent les clés des AC à qui on fait confiance. Qui décide du contenu de ce magasin ? C'est une question complexe, il n'y a pas une liste d'AC faisant autorité. La plupart des systèmes d'exploitation ont une liste à eux, créée en suivant des critères plus ou moins opaques. Les applications (comme le navigateur Web) utilisent ce magasin global du système ou, parfois, ont leur propre magasin, ce qui aggrave encore la confusion. Les utilisateurs peuvent (c'est plus ou moins facile) ajouter des AC ou bien en retirer.
Et comment obtient-on un certificat ? Typiquement, on crée d'abord une demande de certificat (CSR pour Certificate Signing Request, cf. RFC 2986). Avec OpenSSL, cela peut se faire avec :
% openssl req -new -nodes -newkey rsa:2048 -keyout server.key -out server.csr
On se connecte ensuite au site Web de l'AC choisie, et on lui soumet le CSR. Ici, un exemple avec CAcert :
L'AC doit alors faire des vérifications, plus ou moins rigoureuses. Par exemple, l'AC fait une requête whois, note l'adresse de courrier du domaine, envoie un message contenant un défi et le client de l'AC doit y répondre pour prouver qu'il a bien accès à cette adresse et contrôle donc bien le domaine. L'AC crée ensuite le certificat qu'elle vous renvoie. Il faut alors l'installer sur le serveur (HTTPS, par exemple). L'opération est complexe, et beaucoup d'utilisateurs débutants cafouillent.
C'est ce processus non-standard et compliqué que le protocole ACME vise à normaliser et à automatiser. Ce RFC a une longue histoire mais est déjà déployé en production par au moins une AC.
Le principe d'ACME est simple : l'AC fait tourner un serveur
ACME, qui attend les requêtes des clients. Le client ACME (un
logiciel comme dehydrated ou
certbot) génère la
CSR, se connecte au serveur, et demande un certificat signé pour
un nom donné. Le serveur va alors renvoyer un
défi qui va permettre au client de prouver
qu'il contrôle bien le nom de domaine demandé. Il existe plusieurs
types de défis, mais le plus simple est un nom de fichier
que le serveur ACME choisit, demandant au client ACME de mettre un
fichier de ce nom sur son site Web. Si le nom de fichier était
Vyzs0Oqkfa4gn4skMwszORg6vJaO73dvMLN0uX38TDw
,
le serveur ACME va devenir client HTTP et chercher à récupérer
http://DOMAIN/.well-known/acme-challenge/Vyzs0Oqkfa4gn4skMwszORg6vJaO73dvMLN0uX38TDw
. S'il
y réussit, il considère que le client ACME contrôle bien le nom de
domaine, et il signe alors le certificat, qui est renvoyé au
client lorsque celui-ci soumet la CSR.
Le modèle idéal d'utilisation d'ACME est présenté dans la section 2. (En pratique, il n'est pas vraiment réalisé, notamment parce qu'il n'existe pratiquement qu'une seule AC utilisant ACME, Let's Encrypt. Il n'y a donc pas encore beaucoup de diversité.) L'espoir est qu'un jour, on puisse faire comme suit :
Ainsi, une procédure manuelle et pénible pourra devenir assez simple, encourageant une présence en ligne plus sécurisée. Cela pourrait devenir aussi simple que d'avoir un certificat auto-signé.
La section 4 du RFC expose de manière générale le protocole ACME (le RFC complet fait 94 pages, car il y a beaucoup de détails à spécifier). Rappelez-vous avant de la lire que, pour ACME, le client est celui qui demande le certificat (c'est donc typiquement un serveur Internet, par exemple un serveur HTTPS) et le serveur ACME est l'AC. Quand je dirais « client » ou « serveur » tout court, il s'agira du client et du serveur ACME.
ACME encode ses messages en JSON (RFC 8259). Le client doit d'abord avoir un compte auprès du serveur (avec Let's Encrypt, cela peut être fait automatiquement sans que l'utilisateur s'en rende compte). Par exemple, avec dehydrated, cela se fait ainsi :
% dehydrated --register --accept-terms + Generating account key... + Registering account key with ACME server... + Done!
Et on trouve dans le répertoire accounts/
la
clé privée du compte, et les informations du compte :
% cat accounts/aHR0cHM6Ly9...9yeQo/registration_info.json { "id": 5...1, "key": { "kty": "RSA", "n": "wv...HCk", "e": "AQAB" }, "contact": [], "initialIp": "2001:4b98:dc0:41:216:3eff:fe27:3d3f", "createdAt": "2019-03-12T19:32:20.018154799Z", "status": "valid" }
Pour certbot, on peut le faire tourner avec l'option
-v
pour avoir les mêmes informations. certbot
affiche également des messages d'ordre administratif comme :
Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): stephane+letsencrypt@bortzmeyer.org ... Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must agree in order to register with the ACME server at https://acme-v02.api.letsencrypt.org/directory - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (A)gree/(C)ancel: A JWS payload: b'{\n "contact": [\n "mailto:stephane+letsencrypt@bortzmeyer.org"\n ],\n "termsOfServiceAgreed": true,\n "resource": "new-reg"\n}' { "id": 53367492, "key": { ... "contact": [ "mailto:stephane+letsencrypt@bortzmeyer.org" ], "createdAt": "2019-03-15T16:07:58.29914038Z", "status": "valid" } Reporting to user: Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Would you be willing to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: No ...
Le compte sera authentifié en utilisant une biclé (clé privée et clé publique). Il y aura ensuite quatre étapes :
Mais comment transporte-t-on ces messages en JSON ? La section 6 du RFC répond à cette question : on utilise HTTPS. En prime, les messages sont signés avec JWS (RFC 7515), en utilisant la clé privée du client pour les requêtes. Voici par exemple la réponse d'un serveur ACME lorsqu'on l'interroge sur un défi en cours :
{ "type": "http-01", "status": "pending", "url": "https://acme-v02.api.letsencrypt.org/acme/challenge/7TAkQBMmFqm8Rhs6Sn8SFCne2MoZXoEHCz0Px7f0dpE/13683685175", "token": "mMXGXjEijKBZXl2RuL0rjlektPPpy-ozJpZ2vB4w6Dw" }
Les messages d'erreur utilisent le RFC 7807. En voici un exemple :
{ "type": "http-01", "status": "invalid", "error": { "type": "urn:acme:error:unauthorized", "detail": "Invalid response from http://mercredifiction.bortzmeyer.org/.well-known/acme-challenge/rE-rIfjjCfMlivxLfoJmMbRyspwmld97Xnxmy7K0-JA: \"\u003c!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"\u003e\n\u003chtml\u003e\u003chead\u003e\n\u003ctitle\u003e404 Not Found\u003c/title\u003e\n\u003c/head\u003e\u003cbody\u003e\n\u003ch1\u003eNot Found\u003c/h1\u003e\n\u003cp\"", "status": 403 ... [Le message d'erreur indique également typiquement l'URL demandé, et les adresses IP utilisées, ce qui est crucial si le serveur HTTP a plusieurs adresses IP, par exemple une en IPv4 et une en IPv6. Il faut donc bien lire tout le message d'erreur.]
Une liste des erreurs possibles est enregistrée à l'IANA. Voici par exemple une erreur CAA (RFC 8659) :
"error": { "type": "urn:acme:error:caa", "detail": "CAA record for mercredifiction.bortzmeyer.org prevents issuance", "status": 403 },
Comment un client ACME trouve-t-il les
URL pour les différentes opérations ? Il y
a un URL à connaitre, le répertoire
(directory). Une requête à cet URL (par exemple
curl
https://acme-v02.api.letsencrypt.org/directory
va renvoyer
un objet JSON qui contient la liste des autres URL (section 7, une
des plus cruciales du RFC). Voici un exemple chez Let's
Encrypt :
{ ... "meta": { "caaIdentities": [ "letsencrypt.org" ], "termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf", "website": "https://letsencrypt.org" }, "newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct", "newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce", "newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order", "revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert" }
On peut ensuite créer un compte (le champ
newAccount
dans l'exemple ci-dessus) puis demander
des certificats (champ newOrder
dans
l'exemple ci-dessus), ici (Let's Encrypt) en écrivant à
https://acme-v02.api.letsencrypt.org/acme/new-order
:
{ "payload": "ewogICJpZGVudGlmaWVycyI6IFsKICAgIHsKICAgICAgInR5cGUiOiAiZG5zIiwKICAgICAgInZhbHVlIjogInRlc3QtYWNtZS5ib3J0em1leWVyLmZyIgogICAgfQogIF0KfQ", "protected": "eyJhbGciOiAiUlMyNTYiLCAidXJsIjogImh0dHBzOi8vYWNtZS12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL25ldy1vcmRlciIsICJraWQiOiAiaHR0cHM6Ly9hY21lLXYwMi5hcGkubGV0c2VuY3J5cHQub3JnL2FjbWUvYWNjdC81MzE3NzA1MCIsICJub25jZSI6ICJyZXNXTXFtQkJYbVZrZ2JfSnVQU3VEcmlmQzhBbDZrTm1JeDZuNkVwRDFZIn0", "signature": "MP9rXTjX4t1Be-y6dhPOP7JE3B401wokydUlG8gJGWqibTM_gydkUph1smtrUZ5W4RXNTEnlmiFwoiU4eHLD-8MzN5a3G668VbgzKd0VN7Y1GxQBGtsj9fShx4VMjSGLzVq1f7bKCbdX3DYn0LaiRDApgNXiMfoEnPLltu5Ud7RBNOaWY8zE0yAV7e3NFlF9Wfaii5Ff9OT1ZCD8LusOHP-gA4VkimQ9ofYr32wZYgsUFu6G--QflP0tjc5eKYMe1cKlgpyKZsDtBurWwvKlj2cU_PUdOZvjXSBbHX18jVlwglzfFnu0xTaDGTTvOuMBfjnWJCWpr-oA7Ih48dL-Jg" }
Eh oui, tout est signé, en JWS (RFC 7515) et base64isé (cf. section 7.4 du RFC). Ici, le décodage Base64 nous dira que la requête était :
{ { "identifiers": [ { "type": "dns", "value": "test-acme.bortzmeyer.fr" } ] } , {"alg": "RS256", "url": "https://acme-v02.api.letsencrypt.org/acme/new-order", "kid": "https://acme-v02.api.letsencrypt.org/acme/acct/53177050", "nonce": "resWMqmBBXmVkgb_JuPSuDrifC8Al6kNmIx6n6EpD1Y"} }
Donc, une demande de certificat pour test-acme.bortzmeyer.fr
.
Les autres opérations possibles avec un serveur ACME sont enregistrées à l'IANA. Par exemple, on peut révoquer un certificat.
La réponse sera :
{ "status": "pending", "expires": "2019-03-19T19:50:41.434669372Z", "identifiers": [ { "type": "dns", "value": "test-acme.bortzmeyer.fr" } ], "authorizations": [ "https://acme-v02.api.letsencrypt.org/acme/authz/FVMFaHS_oWjqfR-rWd6eBKMlt1EWfIcf6i7D4wU_swM" ], "finalize": "https://acme-v02.api.letsencrypt.org/acme/finalize/53177050/352606317" }
Le client ACME va alors télécharger l'autorisation à l'URL
indiqué, récupérant ainsi les défis qu'il devra affronter (section
7.5 du RFC). Une fois qu'il a fait ce qui lui était demandé par le
serveur, il utilise l'URL donné dans le champ
finalize
pour indiquer au serveur que c'est
bon, que le serveur peut vérifier. La commande
certbot
avec l'option -v
vous permettra de voir tout ce dialogue.
Le protocole ACME peut être utilisé par d'autres AC que Let's
Encrypt. Avec le client dehydrated, il suffira, quand de telles AC
seront disponibles, de mettre CA=URL
dans le
fichier de configuration (l'URL par défaut est
https://acme-v02.api.letsencrypt.org/directory
). Un exemple d'autre AC utilisant ACME est BuyPass (pas testé).
Mais en quoi consistent exactement les
défis, dont j'ai déjà parlé plusieurs fois ?
La section 8 les explique. L'idée de base d'un défi ACME est de
permettre de prouver qu'on contrôle réellement un identificateur,
typiquement un nom de
domaine. ACME ne normalise pas un type de défi
particulier. Le cadre est ouvert, et de nouveaux défis pourront
être ajoutés dans le futur. Le principe est toujours le même :
demander au client ACME de faire quelque chose que seul le vrai
titulaire de l'identificateur pourrait faire. Un défi, tel
qu'envoyé par le serveur ACME, a un type (le plus utilisé
aujourd'hui, est de loin, est le défi
http-01
), un état (en attente ou bien, au
contraire, validé) et un texte d'erreur, au cas où la validation
ait échoué. Plusieurs défis, comme http-01
ont également un jeton, un cookie, un texte
généré par le serveur, et non prévisible par le client ou par le
reste du monde, et qu'il faudra placer quelque part où le serveur
pourra le vérifier. Le serveur ACME ne testera que lorsque le
client lui aura dit « c'est bon, je suis prêt, j'ai fait tout ce
que tu m'as défié de faire ». Le RFC demande également au serveur
de réessayer après cinq ou dix secondes, si la vérification ne
marche pas du premier coup, au cas où le client ait été trop
rapide à se dire prêt.
Le plus connu et le plus utilisé des défis, à l'heure actuelle,
est http-01
. Le client ACME doit configurer
un serveur HTTP où une page (oui, je sais,
le terme correct est « ressource ») a comme nom le contenu du
jeton. Le serveur ACME va devenir client HTTP pour récupérer cette
page et, s'il y arrive, cela prouvera que le client contrôlait
bien le nom de domaine qu'il avait indiqué. De manière
surprenante, et qui déroute beaucoup de débutants, le défi se fait
bien sur HTTP et pas HTTPS, parce que beaucoup d'hébergements Web
partagés ne donnent pas suffisamment de contrôle à l'hébergé.
Le jeton est une chaîne de caractères utilisant le jeu de caractères de Base64, pour passer partout. Voici un exemple de défi HTTP envoyé par le serveur :
{ "identifier": { "type": "dns", "value": "test-acme.bortzmeyer.fr" }, "status": "pending", "expires": "2019-03-19T19:50:41Z", "challenges": [ { "type": "http-01", "status": "pending", "url": "https://acme-v02.api.letsencrypt.org/acme/challenge/FVMFaHS_oWjqfR-rWd6eBKMlt1EWfIcf6i7D4wU_swM/13574068498", "token": "4kpeqw7DVMrY6MI3tw1-tTq9oySN2SeMudaD32IcxNM" } ...
L'URL qu'utilisera le serveur est
http://DOMAINE-DEMANDÉ/.well-known/acme-challenge/JETON
(ou, en syntaxe du RFC 6570,
http://{domain}/.well-known/acme-challenge/{token}
). Comme
expliqué plus haut, c'est bien http://
et pas
https://
. Les URL avec
.well-known
sont documentés dans le RFC 8615 et acme-challenge
est désormais dans
le registre.
Imaginons qu'on utilise le serveur HTTP Apache et qu'on veuille répondre à ce défi. Le plus simple est de configurer le serveur ainsi :
<VirtualHost *:80> Alias /.well-known/acme-challenge /var/lib/dehydrated/acme-challenges <Directory /var/lib/dehydrated/acme-challenges> Options None AllowOverride None ...
Cela indique à Apache que les réponses aux défis seront dans le
répertoire
/var/lib/dehydrated/acme-challenges
,
répertoire où le client ACME dehydrated va mettre ses
fichiers. Avec le serveur HTTP Nginx, le
principe est le même :
server { location ^~ /.well-known/acme-challenge { alias /var/lib/dehydrated/acme-challenges; } }
Bien sûr, de nombreuses autres solutions sont possibles. Le serveur HTTP peut intégrer le client ACME, par exemple. Autre exemple, le client ACME certbot inclut son propre serveur HTTP, et peut donc répondre aux défis tout seul, sans Apache.
Ensuite, on lance le client ACME, éventuellement en lui spécifiant où il doit écrire la réponse aux défis :
% certbot certonly --webroot -w /usr/share/nginx/html -d MONDOMAINE.eu.org
certbot va mettre le certificat généré et signé dans son
répertoire, typiquement
/etc/letsencrypt/live/MONDOMAINE.eu.org/fullchain.pem
. Et
on programme son système (par exemple avec
cron) pour relancer le client ACME tous les
jours (les clients ACME typique vérifient la date d'expiration du
certificat, et n'appellent l'AC que si cette date est proche.)
Notez bien qu'il est crucial de superviser l'expiration des
certificats. On voit fréquemment des sites Web utilisant
Let's Encrypt devenir inaccessibles parce que le certificat a été
expiré. Beaucoup d'administrateurs système
croient que parce que Let's Encrypt est « automatique », il n'y a
aucun risque. Mais ce n'est pas vrai : non seulement la commande
de renouvellement peut ne pas être exécutée, ou bien mal se passer
mais, même si le certificat est bien renouvellé, cela ne garantit
pas que le serveur HTTP soit rechargé.
Petite anecdote personnelle : pour le blog que vous êtes en train de lire, cela avait été un peu plus compliqué. En effet, le blog a deux copies, sur deux machines différentes. J'ai donc du rediriger les vérifications ACME sur une seule des deux machines. En Apache :
ProxyRequests Off ProxyPass /.well-known/acme-challenge/ http://MACHINE-DE-RÉFÉRENCE.bortzmeyer.org/.well-known/acme-challenge/ ProxyPreserveHost On
À noter qu'un serveur HTTP paresseux qui se contenterait de
répondre 200 (OK) à chaque requête sous
/.well-known/acme-challenge
n'arriverait pas à répondre
avec succès aux défis HTTP. En effet, le fichier doit non
seulement exister mais également contenir une chaîne de caractères
faite à partir d'éléments fournis par le serveur ACME (cf. section 8.3).
Un autre type de défi répandu est le défi
dns-01
, où le client doit mettre dans le
DNS un enregistrement
TXT
_acme-challenge.DOMAINE-DEMANDÉ
contenant le jeton. Cela nécessite donc un serveur
DNS faisant autorité qui permette les mises à jour dynamiques, via
le RFC 2136 ou bien via une
API. Notez que le RFC recommande (section
10.2) que l'AC fasse ses requêtes DNS via un résolveur qui valide
avec DNSSEC. (Le serveur ACME ne demande pas
directement aux serveurs faisant autorité, il passe par un
résolveur. Attention donc à la mémorisation par les résolveurs des
réponses, jusqu'au TTL.)
On peut utiliser le défi DNS avec des jokers (pour avoir un
certificat pour *.MONDOMAINE.fr
) mais c'est
un peu plus compliqué (section 7.1.3 si vous voulez vraiment les détails).
D'autres types de défis pourront être ajouté dans le futur. Un
registre
IANA en garde la liste. Notez que des types de défis
peuvent également être supprimés comme
tls-sni-01
et tls-sni-02
, jugés à l'usage pas assez sûrs.
Le but de ce RFC est la sécurité, donc toute faiblesse d'ACME dans ce domaine serait grave. La section 10 du RFC se penche donc sur la question. Elle rappelle les deux objectifs de sécurité essentiels :
Le RFC 3552 décrit le modèle de menace typique de l'Internet. ACME a deux canaux de communication, le canal ACME proprement dit, utilisant HTTPS, et le canal de validation, par lequel se vérifient les réponses aux défis. ACME doit pouvoir résister à des attaques passives et actives sur ces deux canaux.
ACME n'est qu'un protocole, il reçoit des demandes, envoie des
requêtes, traite des réponses, mais il ne sait pas ce qui se passe
à l'intérieur des machines. Les défis, par exemple, peuvent être
inutiles si la machine testée est mal gérée (section 10.2). Si, par exemple, le
serveur HTTP est sur un serveur avec plusieurs utilisateurs, et où
tout utilisateur peut bricoler la configuration HTTP, ou bien
écrire dans le répertoire .well-known
, alors
tout utilisateur sur ce serveur pourra avoir un certificat. Idem
évidemment si le serveur est piraté. Et, si on sous-traite le
serveur de son organisation à l'extérieur, le sous-traitant peut
également faire ce qu'il veut et obtenir des certificats pour son
client (« il n'y a pas de
cloud, il y a juste
l'ordinateur de quelqu'un d'autre »).
ACME permet d'obtenir des certificats DV et ceux-ci dépendent évidemment des noms de domaine et du DNS. Un attaquant qui peut faire une attaque Kaminsky, par exemple, peut envoyer les requêtes du serveur ACME chez lui. Plus simple, même si le RFC n'en parle guère (il se focalise sur les attaques DNS, pas sur celles portant sur les noms de domaine), un attaquant qui détourne le nom de domaine, comme vu fin 2018 au Moyen-Orient, peut évidemment obtenir les certificats qu'il veut, contrairement à la légende répandue comme quoi TLS protègerait des détournements.
Comment se protéger contre ces attaques ? Le RFC recommande d'utiliser un résolveur DNS validant (vérifiant les signatures DNSSEC) ce que peu d'AC font (Let's Encrypt est une exception), de questionner le DNS depuis plusieurs points de mesure, pour limiter l'efficacité d'attaques contre le routage (cf. celle contre MyEtherWallet en avril 2018), et pourquoi pas d'utiliser TCP plutôt qu'UDP pour les requêtes DNS (ce qui présente l'avantage supplémentaire de priver de certificat les domaines dont les serveurs de noms sont assez stupides pour bloquer TCP). Voir aussi la section 11.2, qui revient sur ces conseils pratiques. Par exemple, une AC ne doit évidemment pas utiliser le résolveur DNS de son opérateur Internet, encore moins un résolveur public.
ACME est un protocole, pas une politique. L'AC reste maitresse de sa poltique d'émission des certificats. ACME ne décrit donc pas les autres vérifications qu'une AC pourrait avoir envie de faire :
Les certificats DV (ceux faits avec ACME) sont sans doute moins fiables que les EV (les DV n'ont qu'une vérification automatique, avec peu de sécurité puisque, par exemple, DNSSEC n'est pas obligatoire) et il est donc prudent de limiter leur durée de validité. Let's Encrypt fait ainsi des certificats à courte durée de vie, seulement trois mois, mais ce n'est pas trop grave en pratique, puisque le renouvellement peut être complètement automatisé.
Quels sont les mises en œuvre disponibles d'ACME ? Comme le RFC
est publié longtemps après les premiers déploiements, il y en a
déjà pas mal. Let's Encrypt maintient une
liste de clients. Personnellement, j'ai pratiqué certbot et dehydrated
mais il en existe d'autres, comme acme-tiny, qui
semble simple et compréhensible. Un avantage que je trouve à
dehydrated est qu'il est bien plus simple de garde sa clé lors des
renouvellements, par exemple pour
DANE : il suffit de mettre
PRIVATE_KEY_RENEW="no"
dans le fichier de
configuration. En revanche, dehydrated est à la fois pas assez et
trop bavard. Pas assez car il n'a pas d'option permettant de voir
la totalité du dialogue en JSON avec le serveur (contrairement à
certbot) et trop car il affiche des messages même quand il n'a rien
fait (parce que le certificat était encore valide pour assez
longtemps). Pas moyen de le faire taire, et rediriger la sortie
standard ne marche pas car on veut savoir quand il y a eu
renouvellement effectif.
On dispose également de bibliothèques
permettant au programmeur ou à la programmeuse de développer plus
facilement un client ACME. (Par exemple
Protocol::ACME
(encore que j'ai l'impression qu'il n'est plus maintenu, un
programmeur Perl disponible pour évaluer ce
qui existe ?). Pour les programmeures
Python, il y a le module
acme
qui est celui utilisé par le client
certbot, mais qui est aussi distribué indépendamment. En
Go, il y a LeGo. Mais on peut aussi
mettre le client ACME dans le serveur HTTP, comme le permet
Apache
Et les serveurs ACME ? Évidemment, peu de gens monteront une AC mais, si vous voulez le faire, le serveur de Let's Encrypt, Boulder, est en logiciel libre.
Notez que ce RFC ne parle que de la validation de noms de domaines mais ACME pourra, dans le futur, être utilisé pour valider la « possession » d'une adresse IP, ou d'autres identifiants.
Et si vous voulez un résumé rapide d'ACME par ses auteurs, allez lire cet article sur le blog de l'IETF.
Date de publication du RFC : Mars 2019
Auteur(s) du RFC : D. Crocker (Brandenburg InternetWorking)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 19 avril 2019
Autrefois, de nombreux services et protocoles Internet avaient
« réservé » de manière informelle, et sans enregistrement de cette
réservation, des noms préfixés par un tiret
bas, comme _submission._tcp.example.net
(cf. RFC 6186 pour cet exemple). Comme le RFC 8552 a mis fin à cette activité en créant un
registre officiel des noms préfixés, il fallait réviser les normes
existantes pour s'aligner sur les nouvelles règles. C'est le but
de ce RFC 8553 qui modifie pas moins de trente-trois
RFC !
Dans le nouveau registre, les
entrées sont indexées par un couple {type d'enregistrement DNS,
nom}. Par exemple, {TXT, _dmarc
} pour
DMARC (RFC 7489).
Les enregistrements SRV (RFC 2782) et URI (RFC 7553) posent un problème supplémentaire puisqu'ils utilisent un autre registre de noms, celui des noms de protocoles et services (dit aussi registre des numéros de ports) décrit dans le RFC 6335.
La section 2 du RFC décrit les usages actuels des noms préfixés par le tiret bas. Les enregistrements de type TXT, par exemple, sont utilisés dans sept RFC différents, comme le RFC 5518. Et les SRV dans bien davantage.
Enfin la section 3 du RFC contient le texte des changements qui est fait aux différentes spécifications utilisant les noms préfixés. (Il s'agit essentiellement de faire référence au nouveau registre du RFC 8552, il n'y a pas de changement technique.)
Date de publication du RFC : Mars 2019
Auteur(s) du RFC : D. Crocker (Brandenburg InternetWorking)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 19 avril 2019
Une convention répandue pour les noms de domaine est de préfixer les services
par un tiret bas, par exemple
_xmpp-client._tcp.jabber.ietf.org
. Cette
pratique n'avait jamais été documentée mais c'est désormais
fait. Et il existe désormais un registre
IANA des noms ainsi préfixés.
Bien sûr, on peut mettre des ressources sous n'importe quel
nom. Le DNS
n'impose aucune restriction pour cela, et vous pouvez décider que
le service X sera sous le nom $X%.example.com
(si vous ne me croyez pas, relisez le RFC 1035 et RFC 2181). Mais les
humains aiment les conventions, par exemple pour les machines,
comme www
comme préfixe d'un serveur
Web (préfixe
d'ailleurs contesté, souvent
pour de mauvaises
raisons) ou
mail
pour un serveur de messagerie. Ce ne sont
que des conventions, le DNS s'en moque, et on peut mettre un
serveur Web en mail.example.com
si on veut,
cela ne perturbera que les humains. D'autant plus qu'on peut
utiliser n'importe quel type de données avec n'importe quel nom
(par exemple un enregistrement MX pour
www.example.org
).
La convention du tiret bas initial est répandue, notamment parce qu'elle évite toute confusion avec les noms de machines, qui ne peuvent pas comporter ce caractère (RFC 952). Elle est donc très commune en pratique. Cette convention permet de restreindre explicitement une partie de l'arbre des noms de domaine pour certains usages. Comme ce RFC ne fait que documenter une convention, il ne nécessite aucun changement dans les logiciels.
Une
alternative au tiret bas serait d'utiliser un type de données
spécifique. Quant aux types « généralistes » comme
TXT, ils ont l'inconvénient qu'on récupère,
lors de la résolution DNS, des informations inutiles, par exemple
les TXT des autres services. Bref, vous créez un nouveau service,
mettons X, vous avez le choix, pour le cas du domaine parent
example.org
, entre :
TYPEX
(en pratique, c'est
long et compliqué, et sans déploiement garanti, cf. RFC 5507),x.example.org
,_x.example.org
), l'objet de ce RFC 8552.
Un exemple d'un service réel utilisant la convention avec le tiret
bas est DKIM (RFC 6376), avec le préfixe _domainkey
:
% dig +short TXT mail._domainkey.gendarmerie.interieur.gouv.fr "v=DKIM1; k=rsa; t=y; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgwhYZeeZgM94IofX9uaGAwQ+tynFX7rYs/igs+d1afqrrjMaoKay11IMprRyqhcVurQtJwGfk7PAWrXRjB+KpdySH9lqzvbisB3GrSs1Sf4uWscAbZ+saapw3/QD8YFyfYbsXunc6HROJQuQHM+U56OOcoiAiPHpfepAmuQyFQIDAQAB"
Comme beaucoup de choses, la convention « tiret bas » s'entend
mal avec les jokers du DNS. D'abord, on ne peut pas utiliser les
jokers entre le préfixe et le reste du nom
(_x.*.example.net
ne marche pas), ensuite, un
joker couvre également les noms avec tiret bas donc
*.example.net
va répondre positivement pour
_x.example.net
même si on ne le voulait
pas.
La section 1.5 de notre RFC détaille l'histoire de la convention « tiret bas au début ». Beaucoup de services utilisaient cette convention mais sans coordination, et sans qu'il existe une liste complète. Du fait de l'existence de plusieurs choix possibles (énumérés plus haut), ce RFC n'a pas obtenu de consensus immédiatement et les débats ont été longs et compliqués.
La section 2 du RFC explique comment remplir le nouveau
registre des noms à tiret bas. On ne met dans ce registre que le
nom le plus proche de la racine du DNS. Si un service mène à des
noms comme _foo._bar.example.org
, seul le
_bar
sera mis dans le registre. C'est
particulièrement important pour le cas des
enregistrements SRV qui ont souvent deux niveaux de
noms préfixés (par exemple _sip._tcp.cisco.com
). Seul
le nom le plus proche de la racine, ici _tcp
,
est enregistré (ici, _sip
est quand même
enregistré car il peut en théorie être utilisé sans le
_tcp
mais il me semble que c'est rare en pratique).
Les règles
pour les noms plus spécifiques sous le _bar
(ou _tcp
)
sont spécifiées lors de la description du service en question. Par
exemple, pour DKIM, le RFC 6376 précise que que sous le nom
_domainkey
, on trouve un sélecteur dont
l'identificateur apparait dans le courrier signé. Donc, pour un
message envoyé avec s=mail
et
d=gendarmerie.interieur.gouv.fr
, on cherche
les informations DKIM en mail._domainkey.gendarmerie.interieur.gouv.fr
.
Le formulaire pour demander l'enregistrement d'un nouveau nom préfixé par un tiret bas figure en section 3 du RFC. Il faut indiquer le type de données DNS (un enregistrement n'est valable que pour un certain type, donc la clé du registre est un couple {type, nom}), le nom et la référence du document décrivant le service en question. Le registre est décrit en section 4 du RFC. L'ajout dans ce registre se fait selon la politique « examen par un expert » (RFC 8126, section 4.5). La section 5 de notre RFC donne quelques indications à l'IANA sur cet examen.
Un ensemble d'entrées à ajouter pour initialiser ce nouveau
registre est indiqué. On y trouve par exemple {TXT,
_domainkey
} pour DKIM, {TLSA,
_tcp
} pour DANE (RFC 6698), {TXT,
_acme-challenge
} pour
ACME (RFC 8555),
etc. Deux cas particuliers : le nom _example
est réservé pour tous les types d'enregistrement, lorsqu'on a
besoin de donner un exemple, sans spécifier un cas réel, et le nom
_ta
, qui sert au mécanisme de signalement des
clés DNSSEC du RFC 8145, désigne en fait tous les noms commençant par _ta
.
Date de publication du RFC : Mai 2019
Auteur(s) du RFC : A. Bittau (Google), D. Giffin
(Stanford University), M. Handley (University College
London), D. Mazieres (Stanford
University), Q. Slack
(Sourcegraph), E. Smith (Kestrel
Institute)
Expérimental
Réalisé dans le cadre du groupe de travail IETF tcpinc
Première rédaction de cet article le 23 mai 2019
Aujourd'hui, il n'est plus acceptable d'avoir des communications non chiffrées. États, entreprises et délinquants surveillent massivement les réseaux publics et toute communication effectuée en clair peut être espionnée, voire modifiée. Il est donc nécessaire de continuer les efforts pour chiffrer ce qui ne l'est pas encore. Ce RFC décrit un mécanisme expérimental pour TCP, nommé tcpcrypt, permettant de chiffrer les communications sans participation de l'application située au-dessus, sans authentification obligatoire. (Mais le projet semble mal en point donc je ne suis pas optimiste quant à son déploiement.)
La section 2 du RFC est le cahier des charges de « tcpcrypt », ce nouveau mécanisme de protection de TCP :
tcpcrypt est très différent des classiques TLS et SSH. Il est conçu pour ne pas impliquer l'application, qui peut ignorer qu'on chiffre dans les couches inférieures. tcpcrypt est prévu pour être une solution simple, ne nécessitant pas de modifier protocoles ou applications, changeant le moins de chose possible pour être déployable. Il est également intéressant de voir le « non-cahier des charges », ce qui n'est pas obligatoire dans tcpcrypt :
Ces deux points rendent tcpcrypt vulnérable aux attaquants actifs.
Pendant la longue et douloureuse gestation de ce protocole, TLS avait été envisagé comme alternative. Après tout, pourquoi inventer un nouveau protocole de cryptographie, activité longue et délicate (les failles de sécurité sont vite arrivées) ? Il y avait deux propositions sur la table à l'IETF, le futur tcpcrypt, et une solution fondée sur TLS. C'est pour essayer de faire fonctionner les deux solutions que la négociation des paramètres avait été traitée à part (option ENO, RFC 8547). Mais, depuis, la proposition TLS a été de facto abandonnée, en partie parce que la communauté TLS était occupée par le travail sur la version 1.3.
tcpcrypt s'appuie sur l'option TCP ENO (Encryption Negotiation Option) pour la négociation de l'utilisation du chiffrement. Ce RFC 8548 décrit comment chiffrer, une fois les paramètres négociés.
La section 3 décrit le protocole en détail. Je ne vais pas la reprendre ici (la cryptographie n'est pas mon point fort). On est dans le classique, de toute façon, avec cryptographie asymétrique pour se mettre d'accord sur une clé et cryptographie symétrique pour chiffrer ; tcpcrypt utilise trois types d'algorithmes cryptographiques :
La fonction d'extraction et la fonction pseudo-aléatoire sont celles de HKDF (RFC 5869), elle-même fondée sur HMAC (RFC 2104). Une fois qu'on a la clé, on chiffre avec un algorithme de chiffrement intègre.
Comme vous avez vu, les clés publiques utilisées dans le protocole tcpcrypt sont temporaires, jamais écrites sur disque et renouvellées fréquemment. Ce ne sont pas des clés permanentes, indiquant l'identité de la machine comme c'est le cas pour SSH. tcpcrypt n'authentifie pas la machine en face (la section 8 détaille ce point).
La négociation du protocole, pour que les deux parties qui font du TCP ensemble se mettent d'accord pour chiffrer, est faite avec l'option TCP ENO (Encryption Negotiation Option), décrite dans le RFC 8547. La négociation complète peut nécessiter un aller-retour supplémentaire par rapport à du TCP habituel, ce qui augmente la latence d'établissement de connexion. Un mécanisme de reprise des sessions permet de se passer de négociation, si les deux machines ont déjà communiqué, et gardé l'information nécessaire.
Une fois la négociation terminée, et le chiffrement en route, tcpcrypt génère (de manière imprévisible, par exemple en condensant les paramètres de la session avec un secret) un session ID, qui identifie de manière unique cette session tcpcrypt particulière. Ce session ID est mis à la disposition de l'application via une API, qui reste à définir et l'application peut, si elle le souhaite, ajouter son mécanisme d'authentification et lier une authentification réussie au session ID. Dans ce cas, et dans ce cas seulement, tcpcrypt est protégé contre l'Homme du Milieu.
Notez que seule la charge utile TCP est chiffrée, et donc protégée. L'en-tête TCP ne l'est pas (pour pouvoir passer à travers des boitiers intermédiaires qui tripotent cet en-tête, et tcpcrypt ne protège donc pas contre certaines attaques comme les faux paquets RST (terminaison de connexion, les détails figurent en section 8). Toutefois, certains champs de l'en-tête (mais pas RST) sont inclus dans la partie chiffrée (cf. section 4.2). C'est par exemple le cas de FIN, pour éviter qu'une troncation des données passe inaperçue.
L'option TCP ENO (RFC 8547) crée le
concept de TEP (TCP Encryption Protocol). Un
TEP est un mécanisme cryptographique particulier choisi par les
deux machines qui communiquent. Chaque utilisation de l'option ENO
doit spécifier son ou ses TEP. Pour tcpcrypt, c'est fait dans la
section 7 de notre RFC, et ces TEP sont placés dans un
registre IANA. On y trouve, par exemple,
TCPCRYPT_ECDHE_Curve25519
(le seul qui soit
obligatoire pour toutes les mises en œuvre de tcpcrypt, cf. RFC 7696) qui veut dire « création des clés avec
du Diffie-Hellman sur courbes
elliptiques avec la courbe
Curve25519 ». Pour le chiffrement lui-même,
on a vu qu'il ne fallait utiliser que du chiffrement intègre, et le seul algorithme
obligatoire pour tcpcrypt est
AEAD_AES_128_GCM
(« AES en mode
GCM »). Les autres sont également dans
un
registre IANA.
Le but de tcpcrypt est la sécurité donc la section 8, consacrée à l'analyse de la sécurité du protocole, est très détaillée. D'abord, tcpcrypt hérite des propriétés de sécurité de l'option ENO (RFC 8547). Ainsi, il ne protège pas contre un attaquant actif, qui peut s'insérer dans le réseau, intercepter les paquets, les modifier, etc. Un tel attaquant peut retirer l'option ENO des paquets et il n'y a alors plus grand'chose à faire (à part peut-être épingler la connaissance du fait qu'une machine donnée parlait tcpcrypt la dernière fois qu'on a échangé, et qu'il est bizarre qu'elle ne le fasse plus ?) Si l'application a son propre mécanisme d'autentification, situé au-dessus de tcpcrypt, et qui lie l'authentification au session ID, alors, on est protégé contre les attaques actives. Sinon, seul l'attaquant passif (qui ne fait qu'observer) est bloqué par tcpcrypt. Une analyse plus détaillée figure dans l'article fondateur du projet tcpcrypt, « The case for ubiquitous transport-level encryption », par Bittau, A., Hamburg, M., Handley, M., Mazieres, D., et D. Boneh.. tcpcrypt fait donc du chiffrement opportuniste (RFC 7435).
tcpcrypt ne protège pas la plus grande partie des en-têtes TCP. Donc une attaque active comme l'injection de faux RST (RFC 793, et aussi RFC 5961) reste possible.
Comme la plupart des techniques cryptographiques, tcpcrypt dépend fortement de la qualité du générateur de nombres pseudo-aléatoires utilisé. C'est d'autant plus crucial qu'un des cas d'usage prévus pour tcpcrypt est les objets contraints, disposant de ressources matérielles insuffisantes. Bref, il faut relire le RFC 4086 quand on met en œuvre tcpcrypt. Et ne pas envoyer l'option ENO avant d'être sûr que le générateur a acquis assez d'entropie.
On a dit que tcpcrypt ne protégeait pas les « métadonnées » de la connexion TCP. Ainsi, les keepalives (RFC 1122) ne sont pas cryptographiquement vérifiables. Une solution alternative est le mécanisme de renouvellement des clés de tcpcrypt, décrit dans la section 3.9 de notre RFC.
Ce RFC 8548 est marqué comme « Expérimental ». On n'a en effet que peu de recul sur l'utilisation massive de tcpcrypt. La section 9 liste les points qui vont devoir être surveillés pendant cette phase expérimentale : que deux machines puissent toujours se connecter, même en présence de boitiers intermédiaires bogués et agressifs (tcpcrypt va certainement gêner la DPI, c'est son but, et cela peut offenser certains boitiers noirs), et que l'implémentation dans le noyau ne soulève pas de problèmes insurmontables (comme le chiffrement change la taille des données, le mécanisme de gestion des tampons va devoir s'adapter et, dans le noyau, la gestion de la mémoire n'est pas de la tarte). C'est d'autant plus important qu'il semble qu'après l'intérêt initial, l'élan en faveur de ce nouveau protocole se soit sérieusement refroidi (pas de commit depuis des années dans le dépôt initial).
Et les mises en œuvre de tcpcrypt (et de l'option ENO, qui lui est nécessaire) ? Outre celle de référence citée plus haut, qui est en espace utilisateur, et qui met en œuvre ENO et tcpcrypt, il y a plusieurs projets, donc aucun ne semble prêt pour la production :
TCP_CRYPT_SESSID
, donc du code
PHP, par exemple, peut lire
$_SERVER['TCP_CRYPT_SESSID']
).
Il y avait un site « officiel » pour le projet,
mais qui semble désormais
cassé.http://tcpcrypt.org/
Date de publication du RFC : Mai 2019
Auteur(s) du RFC : A. Bittau (Google), D. Giffin (Stanford University), M. Handley (University College London), D. Mazieres (Stanford University), E. Smith (Kestrel Institute)
Expérimental
Réalisé dans le cadre du groupe de travail IETF tcpinc
Première rédaction de cet article le 23 mai 2019
Ce RFC, tout juste sorti des presses, décrit une extension de TCP nommée ENO, pour Encryption Negotiation Option. Elle permet d'indiquer qu'on souhaite chiffrer la communication avec son partenaire TCP, et de négocier les options. Elle sert au protocole tcpcrypt, décrit, lui, dans le RFC 8548.
Malgré le caractère massif de la surveillance exercée sur les
communications Internet, il y a encore des
connexions TCP dont le contenu n'est pas chiffré. Cela peut être
parce que le protocole applicatif ne fournit pas de moyen (genre une
commande STARTTLS
) pour indiquer le passage
en mode chiffré, ou simplement parce que les applications ne sont
plus guère maintenues et que personne n'a envie de faire le
travail pour, par exemple, utiliser TLS. Pensons à
whois (RFC 3912), par
exemple. La nouvelle option ENO va permettre de chiffrer ces
protocoles et ces applications, en agissant uniquement dans la
couche transport, au niveau
TCP.
Le but de cette nouvelle option TCP est de permettre aux deux pairs TCP de se mettre d'accord sur le fait d'utiliser le chiffrement, et quel chiffrement. Ensuite, tcpcrypt (RFC 8548) ou un autre protocole utilisera cet accord pour chiffrer la communication. En cas de désaccord, on se rabattra sur du TCP « normal », en clair.
Le gros de la spécification est dans la section 4 du RFC. ENO est une option TCP (cf. RFC 793, section 3.1). Elle porte le numéro 69 (qui avait déjà été utilisé par des protocoles analogues mais qui étaient restés encore plus expérimentaux) et figure dans le registre des options TCP. (0x454E a été gardé pour des expériences, cf. RFC 6994.) Le fait d'envoyer cette option indique qu'on veut du chiffrement. Chaque possibilité de chiffrement, les TEP (TCP Encryption Protocol) est dans une sous-option de l'option ENO (section 4.1 pour les détails de format). Si la négociation a été un succès, un TEP est choisi. Les TEP sont décrits dans d'autres RFC (par exemple, le RFC 8548, sur tcpcrypt, en décrit quatre). Les TEP sont enregistrés à l'IANA.
À noter que TCP est symétrique : il n'y a pas de « client » ou de « serveur », les deux pairs peuvent entamer la connexion simultanément (BGP, par exemple, utilise beaucoup cette possibilité). ENO, par contre, voit une asymétrie : les deux machines qui communiquent sont nommées A et B et ont des rôles différents.
A priori, c'est A qui enverra un SYN (message de demande d'établissement de connexion). Ce SYN inclura l'option ENO, et ce sera de même pour les trois messages de la triple poignée de mains TCP. La section 6 du RFC donne quelques exemples. Ainsi :
Cet échange mènera à un chiffrement fait avec le TEP Y, le seul que A et B avaient en commun. Par contre, si B est un vieux TCP qui ne connait pas ENO :
Ne voyant pas de ENO dans le SYN+ACK, A renonce au chiffrement. La connexion TCP ne sera pas protégée.
Et les TEP (TCP Encryption Protocol), qu'est-ce qu'ils doivent définir ? La section 5 détaille les exigences pour ces protocols. Notamment :
Si, à ce stade, vous vous posez des questions sur les choix faits par les concepteurs d'ENO, et que vous vous demandez pourquoi diable ont-ils décidé ceci ou cela, il est temps de lire la section 8, qui explique certains choix de conception. D'abord, une décision importante était qu'en cas de problème lors de la négociation, la connexion devait se replier sur du TCP classique, non chiffré, et surtout ne pas échouer. En effet, si un problème de négociation empêchait la connexion de s'établir, personne n'essayerait d'utiliser ENO. Donc, si on n'a pas d'option ENO dans les paquets qu'on reçoit, on n'insiste pas, on repasse en TCP classique. Et ceci, que les options n'aient jamais été mises, ou bien qu'elles aient été retirées par un intermédiaire trop zélé. On trouve de tout dans ces machines intermédiaires, y compris les comportements les plus délirants. Le RFC note ainsi que certains répartiteurs de charge renvoient à l'expéditeur les options TCP inconnues. L'émetteur pourrait alors croire à tort que son correspondant accepte ENO même quand ce n'est pas vrai. Un bit nommé b, mis à 0 par la machine A et à 1 par la machine B, permet de détecter ce problème, et de ne pas tenter de chiffrer avec un correspondant qui ne sait pas faire.
Cette asymétrie (une machine met le bit b à 1 mais pas l'autre) est un peu ennuyeuse, car TCP est normalement symétrique (deux machines peuvent participer à une ouverture simultanée de connexion, cf. RFC 793, section 3.4). Mais aucune meilleure solution n'a été trouvée, d'autant plus qu'une machine ne sait qu'il y a eu ouverture simultanée qu'après avoir envoyé son SYN (et si le message SYN de l'autre machine est perdu, elle ne saura jamais qu'une ouverture simultanée a été tentée).
Les protocoles utilisant ENO, comme tcpcrypt, sont conçus pour fonctionner sans la participation de l'application. Mais si celle-ci le souhaite, elle peut s'informer de l'état de sécurisation de la connexion TCP, par exemple pour débrayer un chiffrement au niveau applicatif, qui n'est plus nécessaire. Le bit a dans l'option ENO sert à cela. Mis à 1 par une application, il sert à informer l'application en face qu'on peut tenir compte du chiffrement, par exemple pour activer des services qui ont besoin d'une connexion sécurisée. (Notez qu'il n'existe pas d'API standard pour lire et modifier le bit a, ce qui limite les possibilités.)
La section 7 de notre RFC explique quelques développements futurs qui pourraient avoir lieu si des améliorations futures à TCP se répandent. Ainsi, si de nouvelles API, plus perfectionnées que celles du RFC 3493, permettent à TCP de connaitre non seulement l'adresse IP de la machine où on veut se connecter mais également son nom, on pourrait imaginer une authentification fondée sur le nom, par exemple avec DANE (RFC 6394). On pourrait aussi imaginer qu'ENO permette de sélectionner et de démarrer TLS même sans que l'application soit au courant.
Dans l'Internet très ossifié d'aujourd'hui, il est difficile de déployer quelque chose de nouveau, comme l'option ENO (d'où le statut expérimental de ce RFC.) On risque toujours de tomber sur un intermédiaire qui se croit autorisé à modifier ou jeter des paquets dont la tête ne lui revient pas. La section 9 du RFC analyse deux risques :
Le premier risque n'est pas trop sérieux, ENO était prévu pour du déploiement incrémental, de toute façon (on ne peut pas espérer que toutes les machines adoptent ENO en même temps.) Le deuxième est plus grave et, s'il s'avère trop commun, il faudra des heuristiques du genre « si pas de réponse en N millisecondes, réessayer sans ENO ».
Outre ces risques, il est toujours possible, lorsqu'on touche à un protocole aussi crucial que TCP, que d'autres choses aillent mal, et il est donc nécessaire d'expérimenter. Il y a aussi des inconnues du genre « les applications vont-elles tirer profit d'ENO ? » (ce qui n'est pas nécessaire mais pourrait être utile).
La section 10 du RFC étudie les questions de sécurité soulevées par ENO. Comme ENO vise à permettre, entre autres, le chiffrement opportuniste (on chiffre si on peut, sinon on passe en clair, et on n'impose pas d'authentification, cf. RFC 7435), il faut être bien conscient des limites de ce modèle. Le chiffrement opportuniste protège bien contre un surveillant purement passif, mais pas contre un attaquant actif qui pourrait, par exemple, supprimer toutes les options ENO des paquets TCP, ou bien se mettre en position de terminaison TCP, avant de relayer vers le vrai destinataire, agissant ainsi en homme du milieu. Il ne faudrait donc pas prétendre à l'utilisateur que sa connexion est sûre.
Une solution est l'authentification, et c'est bien à cela que sert le session ID. Si l'application peut authentifier, elle doit lier cette authentification au session ID, pour être bien sûr qu'un attaquant ne va pas profiter d'une authentification réussie dans une session pour abuser d'une autre. Par exemple, si l'authentification est faite par une méthode analogue à celle du RFC 7616, le session ID peut être ajouté aux éléments qui seront condensés. Et si la méthode d'authentification ressemble à SCRAM (RFC 5802), le session ID peut être utilisé comme channel binding.
ENO n'est pas lié à un algorithme cryptographique particulier, en application du principe d'agilité (RFC 7696). Mais cela implique qu'un algorithme faible peut affaiblir la sécurité de tout le système. Les mises en œuvre d'ENO doivent donc faire attention à ne pas accepter des algorithmes cryprographiques faibles.
Pour les mises en œuvre d'ENO, voir la fin de mon article sur le RFC 8548 ; pour l'instant, ce sont les mêmes que celles de tcpcrypt.
Date de publication du RFC : Avril 2019
Auteur(s) du RFC : B. Trammell, M. Kuehlewind (ETH Zurich)
Pour information
Première rédaction de cet article le 29 avril 2019
Ce nouveau RFC de l'IAB décrit le très important concept de vue depuis le réseau (wire image), une abstraction servant à modéliser ce que voit, sur le réseau, une entité qui ne participe pas à un protocole, mais peut en observer les effets. Cela peut être un routeur, un boitier de surveillance, etc. Le concept n'était pas nécessaire autrefois, où tout le trafic était en clair. Maintenant qu'une grande partie est (heureusement) chiffrée, il est important d'étudier ce qui reste visible à ces entités extérieures.
Un protocole de communication, c'est un ensemble de règles que les participants doivent respecter, le format des messages, qui doit envoyer quoi et comment y répondre, etc. Par exemple, si on prend le protocole HTTP, il y a au moins deux participants, le client et le serveur, parfois davantage s'il y a des relais. Ces participants (par exemple le navigateur Web et le serveur HTTP) connaissent le protocole, et le suivent. (Du moins on peut l'espérer.) Mais, en plus des participants, d'autres entités peuvent observer le trafic. Cela va des couches basses de la machine (TCP, IP, Ethernet) aux équipements intermédiaires. Même si le routeur ne connait pas HTTP, et n'en a pas besoin pour faire son travail, il voit passer les bits et peut techniquement les décoder, en suivant le RFC. C'est ainsi que des applications comme Wireshark peuvent nous afficher une compréhension d'un dialogue auxquelles elles ne participent pas.
Cette fuite d'informations vers d'autres entités n'est pas explicite dans la spécification d'un protocole. Autrefois, avec le trafic en clair, elle allait de soi (« bien sûr que le routeur voit tout passer ! »). Aujourd'hui, avec le chiffrement, il faut se pencher sur la question « qu'est-ce que ces entités voient et comprennent du trafic ? » C'est la vue depuis le réseau qui compte, pas la spécification du protocole, qui ne mentionne pas les fuites implicites.
Prenons l'exemple de TLS (RFC 8446). TLS chiffre le contenu de la connexion TCP. Mais il reste des informations visibles : les couches inférieures (un observateur tiers voit le protocole TCP en action, les retransmissions, le RTT, etc), les informations sur la taille (TLS ne fait pas de remplissage, par défaut, ce qui permet, par exemple, d'identifier la page Web regardée), la dynamique des paquets (délai entre requête et réponse, par exemple). Tout ceci représente la vue depuis le réseau.
Le RFC prend un autre exemple, le protocole QUIC. Cette fois, la mécanique du protocole de transport est largement cachée par le chiffrement. QUIC a donc une « vue depuis le réseau » réduite. C'est le premier protocole IETF qui essaie délibérement de réduire cette vue, de diminuer le « rayonnement informationnel ». Cela a d'ailleurs entrainé de chaudes discussions, comme celles autour du spin bit, un seul bit d'information laissé délibérement en clair pour informer les couches extérieures sur le RTT. En effet, diminuer la taille de la vue depuis le réseau protège la vie privée mais donne moins d'informations aux opérateurs réseau (c'est bien le but) et ceux-ci peuvent être frustrés de cette décision. Le conflit dans ce domaine, entre sécurité et visibilité, ne va pas cesser de si tôt.
Après cette introduction, la section 2 du RFC décrit formellement cette notion de « vue depuis le réseau ». La vue depuis le réseau (wire image) est ce que voit une entité qui ne participe pas aux protocoles en question. C'est la suite des paquets transmis, y compris les métadonnées (comme l'heure de passage du paquet).
La section 3 de notre RFC discute ensuite en détail les propriétés de cette vue. D'abord, elle ne se réduit pas aux « bits non chiffrés ». On l'a vu, elle inclut les métadonnées comme la taille des paquets ou l'intervalle entre paquets. Ces métadonnées peuvent révéler bien des choses sur le trafic. Si vous utilisez OpenVPN pour chiffrer, et que vous faites ensuite par dessus du SSH ou du DNS, ces deux protocoles présentent une vue très différente, même si tout est chiffré. Mais un protocole chiffré, contrairement aux protocoles en clair (où la vue est maximale) peut être conçu pour changer volontairement la vue qu'il offre (la section 4 approfondira cette idée).
La cryptographie peut aussi servir à garantir l'intégrité de la vue (empêcher les modifications), même si on ne chiffre pas. En revanche, toutes les parties de la vue qui n'utilisent pas la cryptographie peuvent être non seulement observées mais encore changées par des intermédiaires. Ainsi, un FAI sans scrupules peut changer les en-têtes TCP pour ralentir certains types de trafic. (Beaucoup de FAI ne respectent pas le principe de neutralité.)
Notez que la vue depuis le réseau dépend aussi de
l'observateur. S'il ne capture qu'un seul paquet, il aura une vue
réduite. S'il observe plusieurs paquets, il a accès à des
informations supplémentaires, et pas seulement celles contenues
dans ces paquets, mais également celles liées à l'intervalle entre
paquets. De même, si l'observateur ne voit que les paquets d'un
seul protocole, il aura une vue limitée de ce qui se passe alors
que, s'il peut croiser plusieurs protocoles, sa vue s'élargit. Un
exemple typique est celui du DNS : très
majoritairement non chiffré, contrairement à la plupart des
protocoles applicatifs, et indispensable à la très grande majorité
des transactions Internet, il contribue beaucoup à la vue depuis le
réseau (RFC 7626). Si vous voyez une requête DNS
pour imap.example.net
juste avant un soudain
trafic, il est facile de suspecter que le protocole utilisé était
IMAP. Élargissons encore la perspective :
outre le trafic observé, le surveillant peut disposer d'autres
informations (le résultat d'une reconnaissance faite avec
nmap, par exemple), et cela augmente encore
les possibilités d'analyse de la vue dont il dispose.
Puisqu'on parle de vue (image), le RFC note également que le terme n'est pas uniquement une métaphore, et qu'on pourrait utiliser les techniques de reconnaissance d'images pour analyser ces vues.
Notez que, du point de vue de l'IETF, l'Internet commence à la couche 3. Les couches 1 et 2 contribuent également à la vue depuis le réseau, mais sont plus difficiles à protéger, puisqu'elles n'opèrent pas de bout en bout.
Pour un protocole, il est difficile de réduire la vue qu'il offre au réseau. On ne peut pas rendre les paquets plus petits, ni diminuer l'intervalle entre deux paquets. Une des solutions est d'envoyer volontairement des informations fausses, pour « noyer » les vraies. (Voir le livre de Finn Brunton et Helen Nissenbaum, « Obfuscation », chez C&F Éditions en français.) On ne peut pas réduire les paquets, mais on peut les remplir, par exemple. Ou bien on peut ajouter de faux paquets pour brouiller les pistes. Mais il n'y a pas de miracle, ces méthodes diminueront la capacité utile du réseau, ou ralentiront les communications. (Par exemple, utiliser le Web via Tor est bien plus lent.) Bref, ces méthodes ne sont vraiment acceptables que pour des applications qui ne sont pas trop exigeantes en performance.
J'ai dit plus haut qu'on pouvait assurer l'intégrité de certains champs du protocole, sans les chiffrer. Cela permet d'avoir des informations fiables, non modifiables, mais visibles, ce qui peut être utile pour certains équipements intermédiaires. Notez que cette protection a ses limites : on ne peut protéger que des bits, pas des données implicites comme l'écart entre deux paquets. Et la protection est forcément par paquet puisque, dans un réseau à commutation de paquets, comme l'Internet, on ne peut pas garantir l'arrivée de tous les paquets, ou leur ordre.
Enfin, la dernière section de notre RFC, la section 4, explore les moyens par lesquels un protocole peut tromper un éventuel surveillant, en modifiant la vue qu'il offre au réseau. Une fois qu'on a ce concept de vue depuis le réseau, on peut bâtir des choses utiles sur ce concept. Par exemple, il aide à comprendre des questions d'ossification (la difficulté à déployer de nouveaux services ou protocoles, et qui rend, par exemple, nécessaire de faire passer même le DNS sur HTTPS, comme spécifié dans le RFC 8484). En effet, tout ce qui est visible sera regardé, tout ce qui n'est pas protégé sera modifié. Les boitiers intermédiaires, ou plutôt les entreprises et les États qui les conçoivent et les déploient, n'ont aucun scrupule et ne connaissent aucune restriction. Cela veut dire que si un protocole laisse une information visible, celle-ci sera utilisée par les boitiers intermédiaires et donc il sera difficile de changer sa sémantique par la suite, même si toutes les machines terminales sont d'accord.
Prenons l'exemple de TCP (normalisé dans le RFC 793). TCP envoie un certain nombre de signaux involontaires et implicites. Par exemple, l'observation des numéros de séquence permet de mesurer le RTT. Et TCP permet également de modifier ces signaux. Comme l'explique le RFC 8558, des équipements sont vendus aujourd'hui avec des fonctions de surveillance et tripotage des en-têtes TCP. Le RFC fournit deux exemples d'utilisation de cette surveillance :
Dans le cas de TCP, cette exposition d'information est « involontaire ». Le but de TCP n'est pas que tout le monde sur le trajet puisse regarder, et encore moins modifier, ces informations. Mais c'est quand même ce qui arrive. Avec un protocole qui réduit consciemment la vue, comme QUIC, ne serait-ce pas une bonne idée que de donner un peu à manger aux équipements intermédiaires, afin qu'ils puissent optimiser leurs décisions ? Ce fut tout le débat dans le groupe de travail QUIC à l'IETF sur le spin bit, un bit uniquement conçu pour agrandir un peu la vue dont disposent les équipements du réseau, mais qui était un peu en conflit avec le principe d'en dire le moins possible, et ossifiait un peu le protocole (une fois QUIC déployé avec le spin bit, on ne peut plus le supprimer, sous peine de mettre en colère les middleboxes.)
Les informations accessibles dans la vue sont en pratique difficiles à changer puisque les boitiers intermédiaires vont s'habituer à compter dessus. Au moins, on pourrait les rendre explicites plutôt qu'implicites, et documenter clairement ces invariants, ces informations présentes dans la vue et que les concepteurs du protocole s'engagent à garder dans les évolutions futures. Typiquement, les invariants sont des données stables, et simples. Pour un protocole qui a la notion de version, et de négociation de version, cette négociation a intérêt à être déclarée invariante (RFC 8999 pour le cas de QUIC). Mais attention : une fois qu'on a figé certaines parties de la vue, en les déclarant invariantes, il ne faut pas s'imaginer que les équipements du réseau vont s'arrêter là : ils vont sans doute utiliser d'autres parties de la vue pour prendre leur décision, et ces autres parties risquent donc de devenir des invariants de fait. Le RFC recommande donc que toutes les parties qui ne sont pas explicitement listées comme invariantes soient chiffrées, pas pour la confidentialité, mais pour éviter qu'elles ne deviennent invariantes du fait de leur utilisation par les intermédiaires.
Enfin, le RFC rappelle que les équipements intermédiaires ne peuvent pas savoir ce que les deux parties qui communiquent ont décidé entre elles, et que la véracité de la vue depuis le réseau n'est jamais garantie.
Date de publication du RFC : Avril 2019
Auteur(s) du RFC : L. Zhou (CNNIC), N. Kong (Consultant), J. Wei, J. Yao (CNNIC), J. Gould (Verisign)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 12 avril 2019
Le RFC 8543 étendait le format utilisé par le protocole d'avitaillement EPP, afin d'ajouter le concept d'« organisation », une entreprise, association ou agence qui joue un rôle dans la création et gestion d'objets enregistrés, notamment les noms de domaine. Ce RFC 8544 ajoute une extension au protocole EPP pour affecter ces organisations à un domaine, contact ou autre objet enregistré.
Prenons l'exemple le plus connu (quoique EPP puisse servir à d'autres), celui de l'industrie des noms de domaine. Souvent, le registre reçoit des demandes d'un BE, via le protocole EPP. Mais d'autres organisations peuvent jouer un rôle, en plus du BE. Il y a par exemple l'hébergeur DNS (qui n'est pas forcément le BE) ou bien un revendeur du BE, ou bien un « anonymisateur » qui, pour protéger la vie privée des participants, est placé entre le participant et le monde extérieur. Ces différents acteurs (cf. RFC 8499, section 9, pour la terminologie) sont décrits par les nouvelles classes d'objets du RFC 8543. Notre RFC 8544 permet d'utiliser ces classes. Une fois les objets « organisation » créés au registre, on peut les attacher à un nom de domaine ou à un contact, par exemple pour dire « ce nom de domaine a été acheté via le revendeur X ».
L'espace de noms
XML est
urn:ietf:params:xml:ns:epp:orgext-1.0
(et est
enregistré dans le
registre IANA). L'extension à EPP est notée dans le
registre des extensions EPP. Dans les exemples qui suivent,
l'espace de noms est abrégé orgext
. Les
organisations ont un identificateur (le
<org:id>
du RFC 8543), et cet identificateur sera un attribut
<orgext:id>
des objets comme par
exemple le domaine. Pour chaque rôle (revendeur, hébergeur DNS,
etc, cf. RFC 8543, section 7.3), le domaine
a au plus un attribut identifiant une organisation.
La section 4 du RFC décrit les ajouts aux commandes et réponses EPP. Par
exemple, pour <info>
, la commande ne
change pas mais la réponse a désormais en plus des attributs
<orgext:id>
. Voici un exemple :
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <response> <result code="1000"> <msg lang="en-US">Command completed successfully</msg> </result> <resData> <domain:infData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> <domain:name>example.com</domain:name> ... </resData> <extension> <orgext:infData xmlns:orgext="urn:ietf:params:xml:ns:epp:orgext-1.0"> <orgext:id role="reseller">reseller1523</orgext:id> <orgext:id role="privacyproxy">proxy2935</orgext:id> </orgext:infData> </extension> <trID> <clTRID>ngcl-IvJjzMZc</clTRID> <svTRID>test142AWQONJZ</svTRID> </trID> </response> </epp>
Ici, le domaine a été avitaillé via le revendeur « reseller1523 » et est protégé par l'« anonymisateur » « proxy2935 ».
Bien sûr, la commande EPP <create>
est également modifiée, pour pourvoir créer un domaine avec les
organisations associées. Ici, le revendeur « reseller1523 » crée
un domaine :
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <command> <create> <domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> <domain:name>example.com</domain:name> <domain:period unit="y">3</domain:period> ... </create> <extension> <orgext:create xmlns:orgext="urn:ietf:params:xml:ns:epp:orgext-1.0"> <orgext:id role="reseller">reseller1523</orgext:id> </orgext:create> </extension> </command> </epp>
De le même façon, on peut mettre à jour les organisations
associées à un objet, avec
<update>
. Ici, on ajoute un « anonymiseur » :
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <command> <update> <domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> <domain:name>example.com</domain:name> </domain:update> </update> <extension> <orgext:update xmlns:orgext="urn:ietf:params:xml:ns:epp:orgext-1.0"> <orgext:add> <orgext:id role="privacyproxy">proxy2935</orgext:id> </orgext:add> </orgext:update> </extension> </command> </epp>
Et ici on retire le revendeur (pas besoin d'indiquer son identificateur, rappelez-vous qu'il ne peut y avoir qu'une seule organisation par rôle) :
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <command> <update> <domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> <domain:name>example.com</domain:name> </domain:update> </update> <extension> <orgext:update xmlns:orgext="urn:ietf:params:xml:ns:epp:orgext-1.0"> <orgext:rem> <orgext:id role="reseller"/> </orgext:rem> </orgext:update> </extension> </command> </epp>
La syntaxe complète (au format XML Schema) figure dans la section 5 du RFC.
Question mise en œuvre, cette extension est dans le SDK de Verisign, accessible avec leurs autres logiciels pour registres. CNNIC a également inclus cette extension, dans leur code interne.
Date de publication du RFC : Mars 2019
Auteur(s) du RFC : L. Zhou (CNNIC), N. Kong (Consultant), G. Zhou, J. Yao (CNNIC), J. Gould (Verisign)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 12 avril 2019
L'industrie des noms de domaine est d'une grande complexité. Les utilisateurs s'y perdent facilement entre registres, bureaux d'enregistrement, hébergeurs DNS, revendeurs divers, sociétés qui développent des sites Web, prête-noms pour protéger la vie privée, etc. Cette complexité fait qu'il est difficile de savoir qui est responsable de quoi. Dans le contexte d'EPP, protocole d'avitaillement de noms de domaine (création, modification, suppression de noms), il n'y avait jusqu'à présent pas de moyen de décrire ces acteurs. Par exemple, l'ajout d'un enregistrement DS dépend d'actions de l'hébergeur DNS, qui n'est pas forcément le BE. Mais ces hébergeurs DNS n'étaient pas définis et connus. Désormais, avec ce nouveau RFC, on peut utiliser EPP pour l'avitaillement d'objets « organisation ».
EPP (RFC 5730) est le protocole standard d'avitaillement de noms de domaine, permettant à un client (en général le BE) de créer des objets dans un registre, en parlant au serveur EPP. EPP permettait déjà des objets de type « contact » RFC 5733, identifiant les personnes ou les organisations qui assuraient certaines fonctions pour un nom de domaine. Par exemple, le contact technique était la personne ou l'organisation à contacter en cas de problème technique avec le nom de domaine.
Désormais, avec notre nouveau RFC 8543, une nouvelle catégorie (mapping) d'objets est créée, les organisations. On peut ainsi utiliser EPP pour enregistrer l'hébergeur DNS d'un domaine (qui peut être le titulaire du domaine, mais ce n'est pas toujours le cas, ou qui peut être le BE, mais ce n'est pas systématique). Ce nouveau RFC décrit donc une extension à EPP, qui figure désormais dans le registre des extensions (cf. RFC 7451).
EPP utilise XML et tout ici va donc être spécifié en
XML, avec un nouvel espace de
noms XML,
urn:ietf:params:xml:ns:epp:org-1.0
, abrégé en
org
dans le RFC (mais rappelez-vous que le
vrai identificateur d'un espace de noms XML est l'URI, pas
l'abréviation). Le nouvel espace de noms est désormais dans
le registre des espaces de noms.
La section 3 de notre RFC décrit les attributs d'une organisation (notez que le vocabulaire est trompeur : ils s'appellent attributs mais ne sont pas des attributs XML). Mais commençons par un exemple, décrivant le BE nommé « Example Registrar Inc. » :
<org:infData xmlns:org="urn:ietf:params:xml:ns:epp:org-1.0"> <org:id>registrar1362</org:id> <org:roid>registrar1362-REP</org:roid> <org:role> <org:type>registrar</org:type> <org:status>ok</org:status> <org:status>linked</org:status> <org:roleID>1362</org:roleID> </org:role> <org:status>ok</org:status> <org:postalInfo type="int"> <org:name>Example Registrar Inc.</org:name> <org:addr> <org:street>123 Example Dr.</org:street> <org:city>Dulles</org:city> <org:sp>VA</org:sp> <org:cc>US</org:cc> </org:addr> </org:postalInfo> <org:voice x="1234">+1.7035555555</org:voice> <org:email>contact@organization.example</org:email> <org:url>https://organization.example</org:url> <org:contact type="admin">sh8013</org:contact> <org:contact type="billing">sh8013</org:contact> <org:contact type="custom" typeName="legal">sh8013</org:contact> <org:crID>ClientX</org:crID> <org:crDate>1999-04-03T22:00:00.0Z</org:crDate> <org:upID>ClientX</org:upID> <org:upDate>1999-12-03T09:00:00.0Z</org:upDate> </org:infData>
Voyons maintenant quelques-uns des attributs possibles.
Une organisation a un identificateur,
indiqué par l'élément XML <org:id>
,
attribué par le registre (c'est registrar1362
dans l'exemple). Il a aussi un ou plusieurs
rôles, dans l'élement XML
<org:role>
. Un même acteur peut avoir
plusieurs rôles (par exemple il est fréquent que les BE soient
également hébergeurs DNS). Le rôle inclut un
type, qui peut valoir :
registrar
: BE, comme dans le cas
ci-dessus,reseller
: revendeur, par exemple
l'organisation à laquelle le titulaire du nom de domaine achète
un domaine n'est pas toujours un « vrai » BE, ce peut être un
revendeur,privacyproxy
: un prête-nom qui, en
se mettant devant l'utilisateur, permet de protéger sa
vie privée,dns-operator
, l'hébergeur DNS.D'autres types pourront apparaitre dans le futur, ils sont indiqués dans un registre IANA, des nouveaux types seront ajoutés en suivant la procédure « Examen par un expert » du RFC 8126.
Notez qu'au début du travail à l'IETF sur cette extension, seul le cas des revendeurs était prévu. Après des discussions sur l'importance relative des différents acteurs, il a été décidé de prévoir d'autres types que les seuls revendeurs.
Il y a aussi dans l'objet un ou plusieurs état(s),
<org:status>
, qui peut valoir notamment :
ok
, l'état normal, celui du BE dans
l'exemple ci-dessus,terminated
, quand l'organisation va
être retirée de la base et ne peut plus être utilisée (c'est le
cas d'un BE qui n'est plus accrédité),linked
, qui indique que cette
organisation est liée à d'autres objets, et ne doit donc pas
être supprimée.
Il existe également un attribut
<org:parent>
, qui indique une relation
avec une autre organisation. Par exemple, un revendeur aura une
relation <org:parent>
vers le BE dont
il est revendeur. (Dans l'exemple plus haut, il n'y a pas de <org:parent>
.)
La section 4 du RFC présente ensuite les commandes EPP qui
peuvent être appliquées à ces objets
« organisation ». <check>
permet au
client EPP de
savoir s'il pourra créer un objet,
<info>
lui donnera les moyens de
s'informer sur une oranisation (l'exemple en XML ci-dessus était
le résultat d'une commande EPP
<info>
) et bien sûr une commande
<create>
et une
<delete>
. Voici
<create>
en action, pour créer un objet
de rôle « revendeur » (notez que, cette fois, il a un parent) :
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <command> <create> <org:create xmlns:org="urn:ietf:params:xml:ns:epp:org-1.0"> <org:id>res1523</org:id> <org:role> <org:type>reseller</org:type> </org:role> <org:parentId>1523res</org:parentId> <org:postalInfo type="int"> <org:name>Example Organization Inc.</org:name> <org:addr> <org:street>123 Example Dr.</org:street> <org:city>Dulles</org:city> <org:sp>VA</org:sp> <org:cc>US</org:cc> </org:addr> </org:postalInfo> <org:voice x="1234">+1.7035555555</org:voice> <org:email>contact@organization.example</org:email> <org:url>https://organization.example</org:url> <org:contact type="admin">sh8013</org:contact> <org:contact type="billing">sh8013</org:contact> </org:create> </create> </command> </epp>
Le schéma complet, en syntaxe XML Schema, figure dans la section 5 du RFC.
Question mise en œuvre de cette extension EPP, Verisign l'a ajouté dans son SDK, disponible en ligne. CNNIC a une implémentation, mais non publique.
Date de publication du RFC : Février 2019
Auteur(s) du RFC : A. Olson, P. Eggert (UCLA), K. Murchison (FastMail)
Chemin des normes
Première rédaction de cet article le 13 février 2019
Ce nouveau RFC (depuis remplacé par le RFC 9636) 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
.
Ce format existe depuis bien trente ans (et a pas mal évolué pendant ce temps) mais n'avait apparemment jamais fait l'objet d'une normalisation formelle (notre RFC 8536 a depuis été remplacé par le RFC 9636). 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.
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 3. 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 et 3 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 cette 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 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 ...
Un exemple détaillé et commenté de fichier TZif figure 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é 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 :
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 8 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 horaire 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.
La section 6 du RFC donne quelques conseils de sécurité :
Une bonne liste de logiciels traitant ce format figure à l'IANA.
Date de publication du RFC : Février 2019
Auteur(s) du RFC : M. Stubbig
Pour information
Première rédaction de cet article le 6 février 2019
Avec plusieurs systèmes de routage, et notamment avec le protocole standard de l'Internet, BGP, un routeur donné n'a qu'une vue partielle du réseau. Ce que voit votre routeur n'est pas forcément ce que verront les autres routeurs. Pour déboguer les problèmes de routage, il est donc souvent utile de disposer d'une vue sur les routeurs des autres acteurs. C'est fait par le biais de looking glasses qui sont des mécanismes permettant de voir l'état d'un routeur distant, même géré par un autre acteur. Il n'y a aucun standard pour ces mécanismes, il faut donc tout réapprendre pour chaque looking glass, et il est difficile d'automatiser la collecte d'informations. Ce RFC propose une solution (surtout théorique aujourd'hui) : une norme des commandes à exécuter par un looking glass, utilisant les concepts REST.
Voyons d'abord deux exemples de looking glass. Le premier, au France-IX, utilise une interface Web :
Le second, chez Route Views, utilise telnet :
% telnet route-views3.routeviews.org ... route-views3.routeviews.org> show bgp 2001:678:c::1 BGP routing table entry for 2001:678:c::/48 Paths: (8 available, best #4, table Default-IP-Routing-Table) Not advertised to any peer 39351 2484 2a03:1b20:1:ff01::5 from 2a03:1b20:1:ff01::5 (193.138.216.164) Origin IGP, localpref 100, valid, external AddPath ID: RX 0, TX 179257267 Last update: Mon Jan 28 15:22:29 2019 46450 6939 2484 2606:3580:fc00:102::1 from 2606:3580:fc00:102::1 (158.106.197.135) Origin IGP, localpref 100, valid, external AddPath ID: RX 0, TX 173243076 Last update: Sat Jan 26 08:26:39 2019 ...
Notez que le premier looking glass n'affichait que les routes locales
au point d'échange alors que le second voit
toute la DFZ. Les looking glasses jouent un rôle
essentiel dans l'Internet, en permettant d'analyser les problèmes
réseau chez un autre opérateur. (On peut avoir une liste, très
partielle et pas à jour, des looking glasses existants en
.)http://traceroute.org/#Looking%20Glass
La section 2 de notre RFC décrit le fonctionnement de ce looking glass normalisé. Il y a trois acteurs, le client, le serveur et le routeur. Le client est le logiciel utilisé par l'administrateur réseaux qui veut des informations (par exemple curl), le serveur est le looking glass, le routeur est la machine qui possède les informations de routage. (Le RFC l'appelle « routeur » mais ce n'est pas forcément un routeur, cela peut être une machine spécialisée qui récolte les informations en parlant BGP avec les routeurs.) Entre le client et le serveur, on parle HTTP ou, plus exactement, HTTPS. Le client ne parle pas directement au routeur. La communication entre le serveur et le routeur se fait avec le protocole de leur choix, il n'est pas normalisé (cela peut être SSH, NETCONF, etc).
La requête se fera toujours avec la méthode HTTP
GET
, puisqu'on ne modifie rien sur le
serveur. Si le serveur est lg.op.example
, la
requête sera vers l'URL
https://lg.op.example/.well-known/looking-glass/v1
(le préfixe bien connu est décrit dans le RFC 8615, et ce looking-glass
figure
désormais dans le registre
IANA). L'URL est complété avec le nom de la commande
effectuée sur le routeur. Évidemment, on ne peut pas exécuter de
commande arbitraire sur le routeur, on est limité au jeu défini
dans ce RFC, où on trouve les grands classiques comme ping ou bien l'affichage de la
table de routage. La commande peut être suivie de détails, comme
l'adresse IP visée, et de paramètres. Parmi ces paramètres :
protocol
qui indique notamment si on
va utiliser IPv4 ou
IPv6,router
qui va indiquer l'identité du
routeur qui exécutera la commande, pour le cas, fréquent, où un
même serveur looking glass donne accès à plusieurs routeurs.vrf
(Virtual Routing and
Forwarding), une table de routage spécifique, pour le
cas où le routeur en ait plusieurs,format
, qui indique le format de sortie
souhaité sous forme d'un type MIME ; par
défaut, c'est text/plain
. Attention, c'est
le format des données envoyées par le routeur, pas le format de
l'ensemble de la réponse, qui est forcément en JSON.Le code de retour est un code HTTP classique (RFC 7231, section 6), la réponse est de type
application/json
, suivant le profil
JSend, rappelé dans l'annexe A. Ce profil
impose la présence d'un champ success
, ainsi
que d'un champ data
en cas de succès. Voici un
exemple où tout s'est bien passé :
HTTP/1.1 200 OK Content-Type: application/json { "status" : "success", "data" : { "router" : "route-server.lg.op.example" "performed_at" : "2019-01-29T17:13:11Z", "runtime" : 2.63, "output" : [ "Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd 2001:67c:16c8:18d1::1 4 206479 80966 79935 0 0 0 01w2d14h 2 2401:da80::2 4 63927 12113237 79918 0 0 0 07w6d13h 62878 2405:3200:0:23:: 4 17639 0 0 0 0 0 never Connect" ], "format" : "text/plain" } }
JSend impose un champ status
qui, ici,
indique le succès. Le champ data
contient la
réponse. output
est la sortie littérale du routeur, non
structurée (le type est
text/plain
). router
est
le nom du routeur utilisé (rappelez-vous qu'un serveur looking glass peut
donner accès à plusieurs routeurs).
Ici, par contre, on a eu une erreur (une erreur sur le routeur, le serveur, lui, a bien fonctionné, d'où le code de retour 200, cf. section 4 sur les conséquences de ce choix) :
HTTP/2.0 200 OK Content-Type: application/json { "status" : "fail", "data" : { "performed_at" : "2019-01-29T17:14:51Z", "runtime" : 10.37, "output" : [ "Sending 5, 100-byte ICMP Echos to 2001:db8:fa3::fb56:18e", ".....", "Success rate is 0 percent (0/5)" ], "format" : "text/plain", "router" : "route-server.lg.op.example" } }
Le fail
indique que la commande exécutée sur
le routeur a donné un résultat négatif, mais, autrement, tout
allait bien. Si le serveur looking glass ne peut même pas faire exécuter la
commande par le routeur, il faut utiliser
error
et mettre un code de retour HTTP
approprié :
HTTP/1.1 400 Bad Request { "status" : "error", "message" : "Unrecognized host or address." }
La section 3 du RFC donne la liste des commandes
possibles (un looking glass peut toujours en ajouter d'autres). La syntaxe
suivie pour les présenter est celle des gabarits d'URL du RFC 6570. La première est évidemment ce brave
ping
, avec le gabarit https://lg.op.example/.well-known/looking-glass/v1/ping/{host}
:
GET /.well-known/looking-glass/v1/ping/2001:db8::35?protocol=2 Host: lg.op.example HTTP/1.1 200 OK { "status" : "success", "data" : { "min" : 40, "avg" : 41, "max" : 44, "rate" : 100, "output" : [ "Sending 5, 100-byte ICMP Echos to 2001:db8::35", "!!!!!", "Success rate is 100 percent (5/5)" ], "format" : "text/plain", "performed_at" : "2019-01-29T17:28:02Z", "runtime" : 0.77, "router" : "c2951.lab.lg.op.example" } }
Notez que le RFC ne décrit pas les champs possibles (comme
min
ou avg
), ni les
unités utilisées (probablement la milliseconde).
Autre commande traditionnelle,
traceroute
:
GET /.well-known/looking-glass/v1/traceroute/192.0.2.8 Host: lg.op.example HTTP/1.1 200 OK { "status": "success", "data": { "output": [ "Tracing the route to 192.0.2.8", "", " 1 198.51.100.77 28 msec 28 msec 20 msec", " 2 203.0.113.130 52 msec 40 msec 40 msec", " 3 192.0.2.8 72 msec 76 msec 68 msec" ], "format": "text/plain", "performed_at": "2018-06-10T12:09:31Z", "runtime": 4.21, "router": "c7206.lab.lg.op.example" } }
Notez une des conséquences du format non structuré de
output
: ce sera au client de l'analyser. Le
RFC permet d'utiliser des formats structurés (par exemple, pour
traceroute, on peut penser à celui du RFC 5388) mais ce n'est pas obligatoire car on ne peut pas
forcément exiger du serveur qu'il sache traiter les formats de
sortie de tous les routeurs qu'il permet d'utiliser. L'analyse
automatique des résultats reste donc difficile.
D'autres commandes permettent d'explorer la table de
routage. Ainsi, show route
affiche le route
vers un préfixe donné (ici, le routeur est un
Cisco et affiche donc les données au format Cisco) :
GET /.well-known/looking-glass/v1/show/route/2001:db8::/48?protocol=2,1 Host: lg.op.example HTTP/1.1 200 OK { "status": "success", "data": { "output": [ "S 2001:DB8::/48 [1/0]", " via FE80::C007:CFF:FED9:17, FastEthernet0/0" ], "format": "text/plain", "performed_at": "2018-06-11T17:13:39Z", "runtime": 1.39, "router": "c2951.lab.lg.op.example" } }
Une autre commande, show bgp
, affiche les
informations BGP :
GET /.well-known/looking-glass/v1/show/bgp/192.0.2.0/24 Host: lg.op.example HTTP/1.1 200 OK { "status": "success", "data": { "output": [ "BGP routing table entry for 192.0.2.0/24, version 2", "Paths: (2 available, best #2, table default)", " Advertised to update-groups:", " 1", " Refresh Epoch 1", " Local", " 192.0.2.226 from 192.0.2.226 (192.0.2.226)", " Origin IGP, metric 0, localpref 100, valid, internal", "[...]" ], "format": "text/plain", "performed_at": "2018-06-11T21:47:17Z", "runtime": 2.03, "router": "c2951.lab.lg.op.example" } }
Les deux précédentes commandes avaient un argument, le préfixe
IP sur lequel on cherche des informations (avec la syntaxe des
gabarits, cela s'écrit
https://lg.op.example/.well-known/looking-glass/v1/show/bgp/{addr}
). Mais
certaines commandes n'ont pas d'argument. Par exemple,
show bgp summary
affiche la liste des pairs
BGP :
GET /.well-known/looking-glass/v1/show/bgp/summary?protocol=2&routerindex=3 Host: lg.op.example HTTP/1.1 200 OK { "status": "success", "data": { "output": [ "BGP router identifier 192.0.2.18, local AS number 64501", "BGP table version is 85298, main routing table version 85298", "50440 network entries using 867568 bytes of memory", "[...]", "Neighbor V AS MsgRcvd MsgSent TblVer Up/Down", "2001:DB8:91::24 4 64500 481098 919095 85298 41w5d" ], "format": "text/plain", "performed_at": "2018-06-11T21:59:21Z", "runtime": 1.91, "router": "c2951.lab.lg.op.example" } }
D'autres commandes (« organisationnelles ») sont destinées à aider le client à formuler
sa question. C'est le cas de show router
list
, qui affiche la liste des routeurs auquel ce
serveur looking glass donne accés :
GET /.well-known/looking-glass/v1/routers ... { "status" : "success", "data" : { "routers" : [ "route-server.lg.op.example", "customer-edge.lg.op.example", "provider-edge.lg.op.example" ], "performed_at" : "2018-10-19T12:07:23Z", "runtime" : 0.73 } }
Autre exemple de commande organisationnelle,
cmd
, qui permet d'obtenir la liste des
commandes acceptées par ce serveur (curieusement, ici, la syntaxe
des gabarits du RFC 6570 n'est plus utilisée
intégralement) :
GET /.well-known/looking-glass/v1/cmd ... { "status" : "success", "data" : { "commands" : [ { "href" : "https://lg.op.example/.well-known/looking-glass/v1/show/route", "arguments" : "{addr}", "description" : "Print records from IP routing table", "command" : "show route" }, { "href" : "https://lg.op.example/.well-known/looking-glass/v1/traceroute", "arguments" : "{addr}", "description" : "Trace route to destination host", "command" : "traceroute" } ] } }
La liste des commandes données dans ce RFC n'est pas exhaustive, un looking glass peut toujours en ajouter (par exemple pour donner de l'information sur les routes OSPF en plus des BGP).
La section 6 du RFC décrit les problèmes de sécurité envisageables. Par exemple, un client malveillant ou maladroit peut abuser de ce service et il est donc prudent de prévoir une limitation de trafic. D'autre part, les informations distribuées par un looking glass peuvent être trop détaillées, révéler trop de détail sur le réseau et ce qu'en connaissent les routeurs, et le RFC note qu'un serveur peut donc limiter les informations données.
Et question mises en œuvre de ce RFC ? Il existe Beagle mais qui, à l'heure où ce RFC est publié, ne semble pas à jour et concerne encore une version antérieure de l'API. Peut-être RANCID va t-il fournir une API compatible pour son looking glass. Periscope, lui, est un projet différent, avec une autre API.
Date de publication du RFC : Novembre 2018
Auteur(s) du RFC : S. Hollenbeck (Verisign Labs), A. Newton (ARIN)
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 23 novembre 2022
Contrairement à son lointain prédécesseur
whois, le protocole d'accès aux informations
des registres RDAP a un mécanisme standard de
découverte du serveur faisant autorité. Ce mécanisme est prévu pour
des données organisées de manière
arborescente comme les noms de domaine ou les adresses IP. Pour des objets qui n'ont
pas cette organisation, comment faire ? Ce RFC fournit une solution pour des objets
quelconques, s'ils obéissent à une convention de nommage
simple. C'est le cas des handles, ces
identificateurs d'entités (titulaires de noms de domaine, contacts
pour un préfixe IP, etc) ; s'ils sont de la forme
QUELQUECHOSE-REGISTRE
, on pourra trouver le
serveur responsable.
Le mécanisme habituel de découverte du serveur est normalisé dans
le RFC 9224. En gros, l'IANA garde un
registre des serveurs, indexé par un nom de domaine ou par un
préfixe IP. Ce registre est au format JSON. Un client
RDAP est juste censé télécharger ce fichier,
trouver le serveur faisant autorité, puis le contacter. Pour pouvoir
faire la même chose avec des objets non structurés, il faut leur
imposer une convention de nommage. Si on interroge avec RDAP (ou
whois) l'adresse IP 88.198.21.14
, on voit que
son contact technique est HOAC1-RIPE
. Cette
convention de nommage est « d'abord un identifiant unique au sein du
registre, puis un tiret, puis le nom du registre, ici le RIPE-NCC ». L'IANA a juste à garder un registre de ces
registres qui nous dira, par exemple, que
RIPE
a comme serveur RDAP
https://rdap.db.ripe.net/
. On peut ensuite
l'interroger en RDAP :
% curl https://rdap.db.ripe.net/entity/HOAC1-RIPE "vcardArray" : [ "vcard", [ [ "version", { }, "text", "4.0" ], [ "fn", { }, "text", "Hetzner Online GmbH - Contact Role" ], [ "kind", { }, "text", "group" ], [ "adr", { "label" : "Hetzner Online GmbH\nIndustriestrasse 25\nD-91710 Gunzenhausen\nGermany" }, "text", [ "", "", "", "", "", "", "" ] ], [ "tel", { "type" : "voice" }, "text", "+49 9831 505-0" ], [ "tel", { ...
Petit piège, la partie du début peut elle-même comporter des tirets donc lorsqu'on coupe en deux un handle, il faut le faire sur le dernier tiret (section 2 du RFC). Pourquoi le tiret, d'ailleurs ? Parce qu'il est couramment utilisé comme séparateur, et parce que son utilisation dans un URL ne pose pas de problème particulier (RDAP utilise HTTPS et donc des URL).
Le registre des serveurs RDAP est géré à l'IANA comporte un tableau JSON des services, chaque entrée étant composée de trois tableaux, la liste des adresses de courrier du responsable, la liste des suffixes (en général, un seul par registre) et la liste des serveurs RDAP. Par exemple, pour l'ARIN, on aura :
[ [ "andy@arin.net" ], [ "ARIN" ], [ "https://rdap.arin.net/registry/", "http://rdap.arin.net/registry/" ] ]
Les entrées dans le registre sont ajoutées selon la politique « Premier Arrivé, Premier Servi » du RFC 8126.
Comme ce RFC décrit une
extension à RDAP, le serveur RDAP doit ajouter à sa réponse,
dans l'objet rdapConformance
,
rdap_objectTag_level_0
. (Apparemment, personne
ne le fait.)
Vous voulez du code ? La bibliothèque
ianardap.py
, qui est disponible
en ligne, peut récupérer la base et l'analyser. Cela permet
ensuite de faire des requêtes pour des objets :
% ./dump-object.py F11598-FRNIC | jq . ... [ "", "", "Association Framasoft c/o Locaux Motiv", "Lyon", "", "69007", "FR" ] ...
Le programme dump-object.py
étant simplement :
#!/usr/bin/env python3 import ianardap import requests import sys rdap = ianardap.IanaRDAPDatabase(category="objects") for object in sys.argv[1:]: url = rdap.find(object) if url is None: print("No RDAP server for %s" % object, file=sys.stderr) continue response = requests.get("%sentity/%s" % (url[0], object)) print(response.text)
Date de publication du RFC : Février 2019
Auteur(s) du RFC : D. Dolson, J. Snellman, M. Boucadair, C. Jacquenet (Orange)
Pour information
Première rédaction de cet article le 26 mai 2019
À l'IETF, ou, d'une manière générale, chez les gens qui défendent un réseau neutre, les boitiers intermédiaires, les middleboxes, sont mal vus. Ces boitiers contrarient le modèle de bout en bout et empêchent souvent deux machines consentantes de communiquer comme elles le veulent. Des simples routeurs, OK, qui ne font que transmettre les paquets, mais pas de middleboxes s'arrogeant des fonctions supplémentaires. Au contraire, ce RFC écrit par des employés d'Orange, défend l'idée de middlebox et explique leurs avantages pour un opérateur. Ce n'est pas par hasard qu'il est publié alors que les discussions font toujours rage à l'IETF autour du protocole QUIC, qui obscurcira délibérement une partie de son fonctionnement, pour éviter ces interférences par les middleboxes.
Le terme de middlebox est défini dans le RFC 3234. Au sens littéral, un routeur IP est une middlebox (il est situé entre les deux machines qui communiquent, et tout le trafic passe par lui) mais, en pratique, ce terme est utilisé uniquement pour les boitiers qui assurent des fonctions en plus de la transmission de paquets IP. Ces fonctions sont par exemple le pare-feu, la traduction d'adresses, la surveillance, etc. (Le RFC parle de advanced service functions, ce qui est de la pure publicité.) Ces boitiers intermédiaires sont en général situés là où on peut observer et contrôler tout le trafic donc en général à la connexion d'un réseau local avec l'Internet, par exemple dans la box qu'imposent certains FAI, ou bien, dans les réseaux pour mobiles, là où le GGSN se connecte au PDN (RFC 6459, section 3.1.) Mais, parfois, ces boitiers sont en plein milieu du réseau de l'opérateur.
L'Internet suit normalement un modèle de bout en bout (RFC 1958.) Ce modèle dit que les équipements intermédiaires entre deux machines qui communiquent ne doivent faire que le strict minimum, laissant aux deux machines terminales l'essentiel des fonctions. Cela assure la capacité de changement (il est plus facile de modifier les extrémités que le réseau), ainsi le Web a pu être déployé sans avoir besoin de demander la permission aux opérateurs, contrairement à ce qui se passe dans le monde des télécommunications traditionnelles. Et, politiquement, ce modèle de bout en bout permet la neutralité du réseau. Il n'est donc pas étonnant que les opérateurs de télécommunication traditionnels, dont les PDG ne ratent jamais une occasion de critiquer cette neutralité, n'aiment pas ce modèle. Truffer l'Internet de middleboxes de plus en plus intrusives est une tentative de revenir à un réseau de télécommunications comme avant, où tout était contrôlé par l'opérateur. Comme le RFC 8404, ce RFC est donc très politique.
Et, comme le RFC 8404, cela se manifeste par des affirmations outrageusement publicitaires, comme de prétendre que les opérateurs réseaux sont « les premiers appelés quand il y a un problème applicatif ». Faites l'expérience : si vlc a du mal à afficher une vidéo distante, appelez votre FAI et regardez si vous aurez réellement de l'aide… C'est pourtant ce que prétend ce RFC, qui affirme que l'opérateur veut accéder aux informations applicatives « pour aider ».
Le RFC note aussi que certaines des fonctions assurées par les boitiers intermédiaires ne sont pas réellement du choix de l'opérateur : contraintes légales (« boîtes noires » imposées par l'État) ou bien réalités de l'Internet (manque d'adresses IPv4 - cf. RFC 6269, fonctions liées aux attaques par déni de service, par exemple).
Les middleboxes travaillent parfois avec les informations de la couche 7 (ce qu'on nomme typiquement le DPI) mais le RFC se limite au travail sur les fonctions de la couche 4, une sorte de « DPI léger ».
Voyons maintenant ces utilisations des middleboxes, et commençons par les mesures (section 2 du RFC), activité passive et qui ne modifie pas les paquets, donc n'entre pas forcément en conflit avec le principe de neutralité. Par exemple, mesurer le taux de perte de paquets (RFC 7680) est certainement quelque chose d'intéressant : s'il augmente, il peut indiquer un engorgement quelque part sur le trajet (pas forcément chez l'opérateur qui mesure). Chez l'opérateur, qui ne contrôle pas les machines terminales, on peut mesurer ce taux en observant les réémissions TCP (ce qui indique une perte en aval du point d'observation), les trous dans les numéros de séquence (ce qui indique une perte en amont), ou bien les options SACK (RFC 2018). Cela marche avec TCP, moins bien avec QUIC, où ces informations sont typiquement chiffrées. Comme l'observation des options ECN (RFC 3168), le taux de pertes permet de détecter la congestion.
Et le RTT (RFC 2681) ? Il donne accès à la latence, une information certainement intéressante. L'opérateur peut le mesurer par exemple en regardant le délai avant le passage d'un accusé de réception TCP. Plusieurs autres mesures sont possibles et utiles en étant « au milieu » et le RFC les détaille. Arrêtons-nous un moment sur celles liées à la sécurité : l'observation du réseau peut permettre de détecter certaines attaques, mais le RFC note (et déplore) que l'évolution des protocoles réseau tend à rendre cela plus difficile, puisque les protocoles annoncent de moins en moins d'information au réseau (en terme du RFC 8546, ils réduisent la vue depuis le réseau). Cela se fait par la diminution de l'entropie pour éviter le fingerprinting et par le chiffrement (cf. RFC 8404, qui critiquait déjà le chiffrement de ce point de vue). Évidemment, les utilisateurs diront que c'est fait exprès : on souhaite en effet réduire les possibilités de surveillance. Mais tout le monde n'a pas les mêmes intérêts.
Le RFC parle également des mesures effectuées au niveau applicatif. Ainsi, il note que les opérateurs peuvent tirer des conclusions de l'analyse des temps de réponse DNS.
Il y a bien sûr plein d'autres choses que les boitiers intermédiaires peuvent faire, à part mesurer. La section 3 du RFC les examine, et c'est le gros de ce RFC. L'une des plus connues est la traduction d'adresses. Celle-ci est souvent un grave obstacle sur le chemin des communications, malgré les recommandations (pas toujours suivies) des RFC 4787 et RFC 7857.
Après la traduction d'adresses, la fonction « pare-feu » est sans doute la plus connue des fonctions assurées par les middleboxes. Par définition, elle est intrusive : il s'agit de bloquer des communications jugées non souhaitées, voire dangereuses. La question de fond est évidemment « qui décide de la politique du pare-feu ? » Du point de vue technique, le RFC note qu'il est très fréquent de différencier les communications initiées depuis l'intérieur de celles initiées depuis l'extérieur. Cela nécessite d'observer la totalité du trafic pour détecter, par exemple, quel paquet avait commencé la session.
Les autres fonctions des middleboxes citées par ce RFC sont moins connues. Il y a le « nettoyage » (dDoS scrubbing) qui consiste à classifier les paquets en fonction de s'ils font partie d'une attaque par déni de service ou pas, et de les jeter si c'est le cas (cf. par exemple RFC 8811). Le RFC explique que c'est une action positive, puisque personne (à part l'attaquant) n'a intérêt à ce que le réseau soit ralenti, voire rendu inutilisable, par une telle attaque. Le cas est compliqué, comme souvent en sécurité. Bien sûr, personne ne va défendre l'idée que le principe de neutralité va jusqu'à laisser les attaques se dérouler tranquillement. Mais, d'un autre côté, toutes les classifications (un préalable indispensable au nettoyage) ont des faux positifs, et la sécurité peut donc avoir des conséquences néfastes (le RFC regrette que la protection de la vie privée a pour conséquence qu'il est plus difficile de reconnaitre les « paquets honnêtes »). La vraie question, ici comme ailleurs est « qui va décider ? ».
Autre utilisation, cette fois franchement problématique, l'identification implicite. Il s'agit d'identifier un utilisateur donné sans qu'il ait d'action explicite à faire, par exemple en ajoutant à ses requêtes HTTP un élément d'identification (comme expliqué dans un article fameux) ou bien en jouant avec les options TCP (RFC 7974). Il s'agit là clairement de prise de contrôle par le boitier intermédiaire, qui se permet non seulement de modifier les données pendant qu'elles circulent, mais également prétend gérer l'identification des utilisateurs.
Autre fonction des boitiers intermédiaires, l'amélioration des performances en faisant assurer par ces middleboxes des fonctions qui étaient normalement assurées par les machines terminales, mais où l'opérateur estime qu'il est mieux placé pour le faire. Ces PEP (Performance-Enhancing Proxies) sont notamment courants dans les réseaux pour mobiles (cf. RFC 3135, notamment sa section 2.1.1 ou bien cet exposé). Cette fonction nécessite de pouvoir tripoter les en-têtes TCP.
Bien sûr, comme ce RFC exprime le point de vue des gros intermédiaires, il reprend l'élément de langage courant comme quoi il est nécessaire de prioriser certains types de trafic par rapport à d'autres. On aurait une voie rapide pour certains et des lentes pour les autres. Tout le monde est d'accord que la prioritisation est utile (la vidéo YouTube est moins importante que mon courrier professionnel) mais la question est encore « qui décide de ce qu'on priorise, et donc de ce qu'on ralentit ? » Ici, le RFC dit sans hésiter que c'est aux middleboxes de décider, après examen du trafic. L'exemple donné est amusant « on peut ainsi décider de donner la priorité aux jeux en ligne sur les mises à jour de logiciels ». Les gens de la sécurité, qui essaient toujours d'obtenir que les mises à jour de sécurité soient déployées plus rapidement, apprécieront…
On peut prioriser sans avoir accès à toutes les données (par exemple, en se basant uniquement sur les adresses IP) mais le RFC estime que, sans cet accès à tout, les décisions seront forcément moins efficaces.
Les auteurs du RFC sont manifestement conscients que beaucoup de leurs propositions vont énerver les utilisateurs. Alors, ils tentent de temps en temps d'expliquer que c'est pour leur bien qu'on viole la neutralité du réseau. Ainsi, le RFC cite l'exemple d'un réseau qui ralentirait le téléchargement d'une vidéo, pour épargner à l'abonné l'épuisement de son « forfait » « illimité ». Après tout, la vidéo ne sera peut-être pas regardée en entier, donc il n'est pas nécessaire de la charger tout de suite…
Voilà, nous sommes arrivés au bout de la liste des fonctions assurées par les boitiers intermédiaires (je ne les ai pas toutes citées dans ce court article). Il reste à voir les conséquences de ces fonctions pour la sécurité, et c'est le rôle de la section 5 du RFC. Elle estime d'abord que les fonctions décrites ne violent pas forcément la vie privée (RFC 6973) mais note quand même que même les champs « purement techniques » comme l'en-tête TCP, peuvent poser des risques pour la confidentialité des communications.
Et cette section 5 note aussi que l'information observée dans les en-têtes de couche 4 peuvent rendre certaines attaques plus faciles, par exemple en fabriquant un paquet TCP qui sera accepté par la machine terminale. On peut alors mener une attaque par déni de service en envoyant un faux RST (qui coupe la connexion, une attaque que les opérateurs ont déjà pratiquée.) La solution citée est l'utilisation du RFC 5925, qui protège l'intégrité de la connexion TCP mais pas sa confidentialité…
Date de publication du RFC : Décembre 2018
Auteur(s) du RFC : G. Huston, J. Damas (APNIC), W. Kumari (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 14 avril 2019
On le sait, le premier changement de la clé DNSSEC de la racine du DNS s'est déroulé sans encombre le 11 octobre dernier. Ce qu'on sait moins est que ce changement a été précédé par des nombreuses études, pour tenter de prévoir les conséquences du changement. Une question importante était « combien de résolveurs DNS n'ont pas vu la nouvelle clé, depuis sa publication, et vont donc avoir des problèmes lorsque l'ancienne sera retirée du service ? » Ce RFC décrit une des techniques qui avaient été développées pour répondre à cette question, technique qui continuera à être utile pour les discussions actuellement en cours sur une éventuelle systématisation et accélération du rythme des changements de clé.
La question de cette systématisation a fait par exemple l'objet
d'un débat à la dernière réunion IETF à
Prague le 28
mars. L'idée est de voir si on peut passer de changements
exceptionnels et rares à des changements réguliers,
banalisés. Pour cela, il faut avoir une idée de ce que voient les
résolveurs DNS,
du moins ceux qui valident avec DNSSEC, technique dont
l'importance avait encore été démontré par les attaques récentes. Mais comment
savoir ce que voient les résolveurs et, notamment, quelle(s)
clé(s) de départ de la validation (trust
anchor) ils utilisent ? La solution de la
sentinelle, spécifiée dans ce nouveau
RFC, peut
permettre de répondre à cette question. L'idée est que les
résolveurs reconnaitront une requête « spéciale », dont le nom
commence par root-key-sentinel-is-ta
ou
root-key-sentinel-not-ta
, nom qui sera suivi
de l'identificateur (key tag) de la clé (ta =
Trust Anchor). La réponse du résolveur dépendra
de s'il utilise cette clé ou pas comme point de départ de la
validation. Un logiciel client pourra alors tester si son
résolveur a bien vu le changement de clé en cours, et est
prêt. L'utilisateur peut alors savoir si ce résolveur fonctionnera
lors du changement. (Cela peut aussi aider
l'administrateurice système mais celui-ci
ou celle-là a d'autres moyens pour cela, comme d'afficher le
fichier contenant la clé de départ de la validation. Par contre,
les sentinelles sont utiles pour les chercheurs qui récoltent des
données automatiquement, comme dans l'article de Huston cité à la
fin.)
Ce mécanisme de sentinelle est complémentaire de celui du RFC 8145, où le résolveur signale aux serveurs faisant autorité les clés qu'il utilise comme trust anchor. Ici, la cible est le client du résolveur, pas les serveurs faisant autorité que contacte le résolveur. Ce mécanisme de sentinelle permet à tout utilisateur de savoir facilement la(es) clé(s) utilisée(s) par son résolveur DNS.
Petit rappel sur DNSSEC d'abord : comme le DNS, DNSSEC est arborescent. La validation part en général de la racine, et, via les enregistrements DS, arrive à la zone qu'on veut valider. Les clés sont identifiées par un key tag qui est un condensat de la clé. La clé qui est le point de départ de la validation se nomme le trust anchor et est stockée par le résolveur. Si on a la mauvaise clé, la validation échouera. Le trust anchor est géré manuellement par l'administrateur système en charge du résolveur, ou peut être mise à jour automatiquement en suivant la technique du RFC 5011. Aujourd'hui, le résolveur sur la machine où j'écris cet article utilise la trust anchor ayant le key tag 20326, la clé publique de la racine IANA (le résolveur est un Unbound) :
% cat /var/lib/unbound/root.key ... . 172800 IN DNSKEY 257 3 8 AwEAAaz/tAm8y...1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b} ;;state=2 [ VALID ] ;;count=0 ;;lastchange=1502474052 ;;Fri Aug 11 19:54:12 2017
Le id = 20326
indique l'identificateur de la
clé.
La section 2 expose le cœur du RFC. Un résolveur DNS, validant
avec DNSSEC (RFC 4035) et qui met en œuvre
ce RFC, doit reconnaitre comme spéciaux, les noms de domaine
demandés qui commencent
par root-key-sentinel-is-ta-NNNNN
et
root-key-sentinel-not-ta-NNNNN
où NNNNN est
un identificateur de clé. Voyons un exemple avec un domaine de
test, dans lequel on trouve
root-key-sentinel-is-ta-20326.ksk-test.net
et
root-key-sentinel-not-ta-20326.ksk-test.net
(20326 est l'identificateur de la clé actuelle de la racine). Tout
résolveur validant qui utilise la clé 20326 va pouvoir récupérer
et valider le premier nom :
% dig root-key-sentinel-is-ta-20326.ksk-test.net ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23817 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 1 ... ;; ANSWER SECTION: root-key-sentinel-is-ta-20326.ksk-test.net. 30 IN A 204.194.23.4
Ici, le ad
dans les résultats indique que
l'enregistrement a bien été validé (AD = Authentic
Data). Si le résolveur ne valide pas, on n'aura
évidemment pas le ad
. Maintenant, que se
passe-t-il avec le second nom, celui avec
not-ta
? Un résolveur validant, mais qui ne
met pas en œuvre notre RFC 8509 va récupérer et valider
ce nom :
% dig root-key-sentinel-not-ta-20326.ksk-test.net ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20409 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 1 ... ;; ANSWER SECTION: root-key-sentinel-not-ta-20326.ksk-test.net. 30 IN A 204.194.23.4
En revanche, un résolveur validant et qui met en œuvre ce RFC 8509 (ici, le résolveur Knot) va renvoyer un SERVFAIL (Server Failure) :
% dig root-key-sentinel-not-ta-20326.ksk-test.net ... ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 37396 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
On voit comment on peut utiliser le mécanisme de ce RFC pour découvrir si le résolveur utilise ou pas la clé 20326. Avec un client DNS, et une zone qui dispose des sentinelles :
root-key-sentinel-not-ta-20326
et qu'on n'a
pas le bit ad
, c'est que le résolveur ne
valide pas,root-key-sentinel-not-ta-20326
, c'est que
le résolveur valide mais ne gère pas le mécanisme des
sentinelles,root-key-sentinel-is-ta-20326
mais pas root-key-sentinel-not-ta-20326
, c'est que
le résolveur valide, gère le mécanisme des
sentinelles, et utilise la clé 20326,root-key-sentinel-not-ta-20326
mais pas root-key-sentinel-is-ta-20326
, c'est que
le résolveur valide, gère le mécanisme des
sentinelles, et n'utilise pas la clé 20326. (Ce test ne marche
pas forcément, cela dépend de comment est configurée la zone de test.)L'annexe A de notre RFC détaille comment on utilise le mécanisme de sentinelle.
La beauté du mécanisme est qu'on peut l'utiliser sans client
DNS, depuis une page Web. On crée une page qui essaie de charger
trois images, une depuis invalid.ZONEDETEST
(enregistrement mal signé), une depuis
root-key-sentinel-is-ta-20326.ZONEDETEST
et une depuis
root-key-sentinel-not-ta-20326.ZONEDETEST
. On teste en
JavaScript si les images se sont chargées :
invalid
) ne charge
pas mais que les deux autres chargent, c'est que le résolveur ne
connait pas les sentinelles, on ne peut donc pas savoir
quelle(s) clé(s) il utilise,invalid
) et la
troisième (root-key-sentinel-not-ta-20326
)
ne se chargent
pas mais que la deuxième
(root-key-sentinel-is-ta-20326
) se charge, c'est que le résolveur
connait les sentinelles, et utilise la clé 20326.invalid
) et la
deuxième (root-key-sentinel-is-ta-20326
)
ne se chargent
pas mais que la troisième
(root-key-sentinel-not-ta-20326
) se charge, c'est que le résolveur
connait les sentinelles, et n'utilise pas la clé 20326. Soit il
utilise une autre racine que celle de
l'ICANN, soit il a gardé une ancienne clé
et aura des problèmes lors du remplacement.Notez que le choix des préfixes avait été chaudement discuté à
l'IETF. À un moment, l'accord s'était fait
sur les préfixes _is-ta
ou _not-ta
, le tiret
bas convenant bien à ces noms spéciaux. Mais comme ce
même tiret bas rendait ces noms ilégaux comme noms de machine, cela rendait
difficile certains tests. Autant _is-ta
ou _not-ta
étaient utilisables depuis
dig, autant on ne pouvait pas les tester
depuis un navigateur Web, ce qui rendait
difficile des tests semi-automatisés, sous formes d'images qu'on
charge depuis une page Web et de JavaScript
qui regarde le résultat. D'où les noms root-key-sentinel-is-ta-NNNNN
et
root-key-sentinel-not-ta-NNNNN
. Ce n'est pas
une solution satisfaisante que de prendre des noms ne commençant
pas par un tiret bas, mais cela a semblé le meilleur compromis
possible. Le nom est suffisamment long et alambiqué pour que le
risque de collisions avec des noms existants soit faible.
Donc, que doit faire le résolveur quand on l'interroge pour un
nom commençant par root-key-sentinel-is-ta-NNNNN
ou
root-key-sentinel-not-ta-NNNNN
? Si et
seulement si le type demandé est A (adresse IPv4) ou AAAA (adresse
IPv6), et que la réponse est validée par DNSSEC, le résolveur doit
extraire le key tag du nom et déterminer s'il
corresponde à une des clés de départ de la validation pour la
racine. (Les serveurs faisant autorité, eux, n'ont rien de
particulier à faire, ils fonctionnent comme aujourd'hui.)
root-key-sentinel-is-ta
et que
l'identificateur de clé est celui d'une trust
anchor, alors on renvoie la réponse originale,root-key-sentinel-is-ta
et que
l'identificateur n'est pas celui d'une trust
anchor, alors on renvoie le code d'erreur
SERVFAIL,root-key-sentinel-not-ta
et que
l'identificateur est celui d'une trust
anchor, alors on renvoie le code d'erreur
SERVFAIL,root-key-sentinel-not-ta
et que
l'identificateur n'est pas celui d'une trust
anchor, alors on renvoie la réponse originale.Le principe est simple, les sections suivantes du RFC décrivent comment déduire
l'état du résolveur avec ce principe. Par exemple, la section 3
décrit le cas où on n'a qu'un seul résolveur. Si on veut connaitre
la situation de la clé 12345, et que les noms nécessaires sont
présents dans le domaine xxx.example, que le domaine broken.xxx.example
est
cassé, question DNSSEC, on cherche à résoudre les
noms root-key-sentinel-is-ta-12345.xxx.example
,
root-key-sentinel-not-ta-12345.xxx.example
et
broken.xxx.example
. Pour un résolveur validant, et
qui met en œuvre ce RFC, seule la première requête doit
réussir. Si la troisième réussit, c'est que le résolveur ne valide
pas. Si la deuxième réussit, c'est qu'il ne connait pas le système
sentinelle de notre RFC 8509. (L'algorithme détaillé est
dans la section 3 du RFC, il y a quelques cas vicieux.)
Notez bien qu'on n'a pas besoin d'un client DNS complet pour ces tests. Une résolution de nom en adresse normale suffit, ce qui permet de faire ces tests depuis un navigateur Web, ce qui était bien le but. Par exemple en HTML :
<ul> <li><a href="http://invalid.ksk-test.net/invalid.gif">"http://invalid.ksk-test.net/invalid.gif"</a></li> <li><a href="http://root-key-sentinel-is-ta-20326.ksk-test.net/is-ta.gif">"http://root-key-sentinel-is-ta-20326.ksk-test.net/is-ta.gif"</a></li> <li><a href="http://root-key-sentinel-not-ta-20326.ksk-test.net/not-ta.gif">"http://root-key-sentinel-not-ta-20326.ksk-test.net/not-ta.gif"</a></li> </ul>
Et avec du JavaScript pour vérifier :
if (img_invalid.height === 0) {invalid = false;} if (img_is_ta.height === 0) {is_ta = false;} if (img_not_ta.height === 0) {not_ta = false;} switch (true) { case invalid === true: result="You are not DNSSEC validating, and so will be fine!"; break; case (is_ta === true && not_ta === true): result="You are using a legacy resolver (or updated resolvers, with some new and some old), we cannot determine your fate!"; break; case (not_ta === true): result="WARNING!: Your resolvers do not have the new KSK. Your Internet access will break!"; break; case (is_ta === true): result="Congratulations, you have the new key. You will be fine."; break; }
Si la machine a plusieurs résolveurs, le cas est plus compliqué par la bibliothèque qui fait la résolution de noms va typiquement passer au résolveur suivant en cas de réponse SERVFAIL. La section 4 décrit un algorithme qui peut permettre, sinon de déterminer la situation exacte de chacun des résolveurs utilisés, en tout cas de voir si une clé donnée a des chances de fonctionner avec cet ensemble de résolveurs.
Quels résolveurs ont ce mécanisme de sentinelle ? BIND l'a depuis la version 9.13, Knot a également ce mécanisme, activé par défaut, depuis la version 2.0 (et le documente), Unbound en dispose depuis la version 1.7.1 et c'est activé par défaut. Parmi les résolveurs DNS publics, Cloudflare et Quad9 ne gèrent apparemment pas les sentinelles de notre RFC 8509.
Côté client, on peut tester son résolveur avec dig, ou
bien avec les
services Web
, http://www.ksk-test.net
,
http://test.kskroll.dnssec.lab.nic.cl/
(le code
derrière ce service est
publié, notez que les résultats sont présentés en utilisant
des codes spécifiques au RFC, pas très lisibles) ou http://sentinel.research.icann.org/
(utilise le
domaine http://www.bellis.me.uk/sentinel/
ksk-test.net
). La lecture des sources
de ces pages est recommandée.
On peut aussi regarder, parmi les sondes RIPE Atlas, lesquelles utilisent un résolveur avec sentinelles :
% blaeu-resolve --displayvalidation --type A --requested 100 --dnssec root-key-sentinel-not-ta-20326.ksk-test.net [ (Authentic Data flag) 204.194.23.4] : 30 occurrences [ERROR: SERVFAIL] : 12 occurrences [ (Authentic Data flag) (TRUNCATED - EDNS buffer size was 4096 ) 204.194.23.4] : 1 occurrences [204.194.23.4] : 52 occurrences Test #20692175 done at 2019-04-13T16:32:15Z % blaeu-resolve --displayvalidation --type A --requested 100 --old_measurement 20692175 --dnssec root-key-sentinel-is-ta-20326.ksk-test.net [ERROR: SERVFAIL] : 1 occurrences [ (Authentic Data flag) 204.194.23.4] : 38 occurrences [204.194.23.4] : 56 occurrences Test #20692176 done at 2019-04-13T16:33:56Z
Le premier test montre 52 sondes utilisant un résolveur non validant, 30 utilisant un validant sans sentinelles (ou bien utilisant une autre clé que 20326), et 12 utilisant un résolveur validant avec sentinelles. Le second test, effectué avec exactement les mêmes sondes, montre que les sondes utilisant un résolveur validant à sentinelles utilisent bien la clé 20326 (sauf une, qui a un SERVFAIL). Notez que les nombres ne coïncident pas parfaitement (30 + 12 - 1 ne fait pas 38), sans doute parce que certaines sondes ont plusieurs résolveurs DNS configurés, ce qui complique sérieusement l'analyse.
Un exemple d'utilisation de ce mécanisme de sentinelles figure dans cet article de Geoff Huston et cet autre.
Date de publication du RFC : Janvier 2019
Auteur(s) du RFC : T. Chown (Jisc), J. Loughney (Intel), T. Winters (UNH-IOL)
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 31 janvier 2019
Il existe des tas de RFC qui concernent IPv6 et le programmeur qui met en œuvre ce protocole dans une machine risque fort d'en rater certains, ou bien d'implémenter certains qu'il aurait pu éviter. Ce RFC est donc un méta-RFC, chargé de dresser la liste de ce qui est indispensable dans une machine IPv6. Pas de changements spectaculaires par rapport au précédent RFC mais beaucoup de détails.
Ce document remplace son prédécesseur, le RFC 6434. Il vise le même but, servir de carte au développeur qui veut doter un système de capacités IPv6 et qui se demande s'il doit vraiment tout faire (réponse : non). Ce RFC explique clairement quels sont les points d'IPv6 qu'on ne peut pas négliger, sous peine de ne pas pouvoir interagir avec les autres machines IPv6. Le reste est laissé à l'appréciation du développeur, ou à celle de profils spécifiques à un usage ou à une organisation (comme dans le cas du « Profile for IPv6 in the U.S. Government »). La section 1 résume ce but.
Ce RFC s'applique à tous les nœuds IPv6, aussi bien les routeurs (ceux qui transmettent des paquets IPv6 reçus qui ne leur étaient pas destinés) que les machines terminales (toutes les autres).
Bien, maintenant, voyons les obligations d'une machine IPv6, dans l'ordre. D'abord, la couche 2 (section 4 du RFC). Comme IPv4, IPv6 peut tourner sur des tas de couches de liaison différentes et d'autres apparaîtront certainement dans le futur. En attendant, on en a déjà beaucoup, Ethernet (RFC 2464), 802.16 (RFC 5121), les réseaux pour objets connectés LowPAN 802.15.4 (RFC 4944), PPP (RFC 5072) et bien d'autres, sans compter les tunnels.
Ensuite, la couche 3 (section 5 du RFC) qui est évidemment le gros morceau puisque c'est la couche d'IP. Le cœur d'IPv6 est normalisé dans le RFC 8200 et ce dernier RFC doit donc être intégralement implémenté.
Comme IPv6, contrairement à IPv4, ne permet pas aux routeurs intermédiaires de fragmenter les paquets, la découverte de la MTU du chemin est particulièrement cruciale. La mise en œuvre du RFC 8201 est donc recommandée. Seules les machines ayant des ressources très limitées (du genre où tout doit tenir dans la ROM de démarrage) sont dispensées. Mais le RFC se doit de rappeler que la détection de la MTU du chemin est malheureusement peu fiable dans l'Internet actuel, en raison du grand nombre de pare-feux configurés avec les pieds et qui bloquent tout l'ICMP. Il peut être donc nécessaire de se rabattre sur les techniques du RFC 4821. Le RFC recommande également de ne pas trop fragmenter les paquets, pour ne pas tenter le diable, un certain nombre de machines intermédiaires bloquant les fragments. Cela concerne notamment les protocoles utilisant UDP, comme le DNS, qui n'ont pas de négociation de la taille des messages, contrairement à TCP.
En parlant de fragmentation, notre RFC rappelle également qu'on ne doit pas générer de fragments atomiques (un datagramme marqué comme fragmenté alors que toutes les données sont dans un seul datagramme), pour les raisons expliquées dans le RFC 8021. Et il ne faut pas utiliser d'identificateurs de fragment prévisibles (RFC 7739), afin d'empêcher certaines attaques comme l'attaque Shulman sur le DNS.
Le RFC 8200 décrit le format des paquets et leur traitement. Les adresses y sont simplement mentionnées comme des champs de 128 bits de long. Leur architecture est normalisée dans le RFC 4291, qui est obligatoire. (Une tentative de mise à jour de ce RFC 4291 avait eu lieu mais a été abandonnée sous les protestations.) La règle actuelle (RFC 7421) est que le réseau local a une longueur de préfixe de 64 bits (mais il y a au moins une exception, cf. RFC 6164.)
Également indispensable à toute machine IPv6, l'autoconfiguration sans état du RFC 4862 (SLAAC, StateLess Address AutoConfiguration), ainsi que ses protocoles auxiliaires comme la détection d'une adresse déjà utilisée. D'autre part, il est recommandé d'accepter l'allocation d'un préfixe entier à une machine (RFC 8273).
Les en-têtes d'extension (RFC 8200, section 4) sont un élément de base d'IPv6 et doivent donc être acceptés et traités par toutes les machines terminales. Notez que la création de nouveaux en-têtes est désormais déconseillée donc qu'une mise en œuvre d'IPv6 a des chances de ne pas voir apparaitre de nouveaux en-têtes. Même si c'était le cas, ils devront suivre le format générique du RFC 6564, ce qui devrait faciliter leur traitement.
ICMP (RFC 4443) est évidemment obligatoire, c'est le protocole de signalisation d'IP (une des erreurs les plus courantes des administrateurs réseaux incompétents est de bloquer ICMP sur les pare-feux).
Dernier protocole obligatoire, la sélection de l'adresse source selon les règles du RFC 6724, pour le cas où la machine aurait le choix entre plusieurs adresses (ce qui est plus fréquent en IPv6 qu'en IPv4).
Le reste n'est en général pas absolument obligatoire mais recommandé (le terme a un sens précis dans les RFC, définie dans le RFC 2119 : ce qui est marqué d'un SHOULD doit être mis œuvre, sauf si on a une bonne raison explicite et qu'on sait exactement ce qu'on fait). Par exemple, la découverte des voisins (NDP, RFC 4861) est recommandée. Toutes les machines IPv6 en ont besoin, sauf si elles utilisent les liens ne permettant pas la diffusion.
Moins générale, la sélection d'une route par défaut s'il en existe plusieurs, telle que la normalise le RFC 4191. Elle est particulièrement importante pour les environnements SOHO (RFC 7084). Et si la machine IPv6 a plusieurs adresses IP, et qu'il y a plusieurs routeurs, elle doit choisir le routeur en fonction de l'adresse source (RFC 8028).
On a vu que l'autoconfiguration sans état (sans qu'un serveur doive se souvenir de qui a quelle adresse) était obligatoire. DHCP (RFC 8415), lui, n'est que recommandé. Notez que DHCP ne permet pas d'indiquer le routeur à utiliser, cette information ne peut être obtenue qu'en écoutant les RA (Router Advertisements).
Une extension utile (mais pas obligatoire) d'IP est celle des adresses IP temporaires du RFC 8981, permettant de résoudre certains problèmes de protection de la vie privée. Évidemment, elle n'a pas de sens pour toutes les machines (par exemple, un serveur dans son rack n'en a typiquement pas besoin). Elle est par contre recommandée pour les autres. Le RFC 7721 fait un tour d'horizon plus général de la sécurité d'IPv6.
Encore moins d'usage général, la sécurisation des annonces de route (et des résolutions d'adresses des voisins) avec le protocole SEND (RFC 3971). Le déploiement effectif de SEND est très faible et le RFC ne peut donc pas recommander cette technique pour laquelle on n'a pas d'expérience, et qui reste simplement optionnelle.
L'une des grandes questions que se pose l'administrateur réseaux avec IPv6 a toujours été « autoconfiguration SLAAC - StateLess Address AutoConfiguration - / RA - Router Advertisement - ou bien DHCP ? » C'est l'un des gros points de ce RFC et la section 8.4 le discute en détail. Au début d'IPv6, DHCP n'existait pas encore pour IPv6 et les RA utilisés par SLAAC ne permettaient pas encore de transmettre des informations pourtant indispensables comme les adresses des résolveurs DNS (c'est maintenant possible, cf. RFC 8106). Aujourd'hui, les deux protocoles ont à peu près des capacités équivalentes. RA a l'avantage d'être sans état, DHCP a l'avantage de permettre des options de configuration différentes par machine. Mais DHCP ne permet pas d'indiquer le routeur à utiliser. Alors, quel protocole choisir ? Le problème de l'IETF est que si on en normalise deux, en laissant les administrateurs du réseau choisir, on court le risque de se trouver dans des situations où le réseau a choisi DHCP alors que la machine attend du RA ou bien le contraire. Bref, on n'aurait pas d'interopérabilité, ce qui est le but premier des normes Internet. Lorsque l'environnement est très fermé (un seul fournisseur, machines toutes choisies par l'administrateur réseaux), ce n'est pas un gros problème. Mais dans un environnement ouvert, par exemple un campus universitaire ou un hotspot Wifi, que faire ? Comme l'indiquent les sections 6.3 et 6.5, seul RA est obligatoire, DHCP ne l'est pas. RA est donc toujours la méthode recommandée si on doit n'en choisir qu'une, c'est la seule qui garantit l'interopérabilité.
Continuons à grimper vers les couches hautes. La section 7 est
consacrée aux questions DNS. Une machine IPv6 devrait pouvoir suivre le
RFC 3596 et donc avoir la possibilité de
gérer des enregistrements DNS de type AAAA (les adresses IPv6) et
la résolution d'adresses en noms grâce à des enregistrements PTR dans
ip6.arpa
.
À noter qu'une machine IPv6, aujourd'hui, a de fortes chances de se retrouver dans un environnement où il y aura une majorité du trafic en IPv4 (c'est certainement le cas si cet environnement est l'Internet). La section 10 rappelle donc qu'une machine IPv6 peut avoir intérêt à avoir également IPv4 et à déployer des techniques de transition comme la double-pile du RFC 4213.
Encore juste une étape et nous en sommes à la couche 7, à laquelle la section 11 est consacrée. Si elle parle de certains détails de présentation des adresses (RFC 5952), elle est surtout consacrée à la question des API. En 2019, on peut dire que la très grande majorité des machines a IPv6, côté couche 3 (ce qui ne veut pas dire que c'est activé, ni que le réseau le route). Mais les applications sont souvent en retard et beaucoup ne peuvent tout simplement pas communiquer avec une autre machine en IPv6. L'IETF ne normalise pas traditionnellement les API donc il n'y a pas d'API recommandée ou officielle dans ce RFC, juste de l'insistance sur le fait qu'il faut fournir une API IPv6 aux applications, si on veut qu'elles utilisent cette version d'IP, et qu'il en existe déjà, dans les RFC 3493 (fonctions de base) et RFC 3542 (fonctions avancées).
La sécurité d'IPv6 a fait bouger beaucoup d'électrons, longtemps avant que le protocole ne soit suffisamment déployé pour qu'on puisse avoir des retours d'expérience. Le RFC note donc bien que la sécurité est un processus complexe, qui ne dépend certainement pas que d'une technique magique et qu'aucun clair gagnant n'émerge de la liste des solutions de sécurité (IPsec, TLS, SSH, etc). D'autant plus qu'IPv6 vise à être déployé dans des contextes comme « l'Internet des Trucs » où beaucoup de machines n'auront pas forcément les ressources nécessaires pour faire de l'IPsec.
Toutes les règles et recommandations précédentes étaient pour tous les nœuds IPv6. La section 14 expose les règles spécifiques aux routeurs. Ils doivent évidemment router les paquets (cf. RFC 7608). Ils doivent aussi être capables d'envoyer les Router Advertisement et de répondre aux Router Solicitation du RFC 4861 et on suggère aux routeurs SOHO d'envisager sérieusement d'inclure un serveur DHCP (RFC 7084) et aux routeurs de réseaux locaux de permettre le relayage des requêtes DHCP.
Nouveauté de ce RFC, la section 15 se penche sur le cas des objets contraints, ces tout petits ordinateurs avec peu de ressources, pour lesquels il a parfois été dit qu'IPv6 n'était pas réaliste. Le groupe de travail de l'IETF 6LowPAN a beaucoup travaillé pour faire en sorte qu'IPv6 tourne bien sur ces objets contraints (RFC 4919). Pour la question de la consommation énergétique, voir le RFC 7772.
Enfin, la section 16 se penche sur la gestion des réseaux IPv6 en notant qu'au moins deux MIB sont recommandées, celle du RFC 4292 sur la table de routage, et celle du RFC 4293 sur IP en général. SNMP n'est plus le seul protocole cité, Netconf (RFC 6241) et Restconf (RFC 8040) apparaissent.
Les changements par rapport au RFC 6434 sont résumés dans l'annexe A. Pas mal de changements pendant les sept ans écoulés depuis le RFC 6434 mais aucun n'est dramatique :
Date de publication du RFC : Novembre 2018
Auteur(s) du RFC : L. Howard (Retavia)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 28 janvier 2019
Le DNS permet d'avoir des
enregistrements de type PTR, dans un sous-arbre spécifique sous
.arpa
, permettant
d'associer un nom à
une adresse IP. Les
FAI et autres fournisseurs peuplent souvent
ce sous-arbre afin que tous les clients aient un PTR, et donc que
la « résolution inverse » (mal nommée, mais tant pis) rende un
résultat. Avec IPv4, c'est facile. Si on
gère un préfixe /22, on a 1 024 PTR à mettre, ce qui fait encore
une assez petite zone DNS. Mais en IPv6 ?
La zone ainsi pré-peuplée serait de taille colossale, bien au-delà
des capacités des serveurs de noms. Il faut donc trouver d'autres
solutions, et c'est ce que décrit ce
RFC.
Deux exemples d'enregistrement PTR en IPv6, pour commencer, vus avec dig :
% dig +short -x 2a00:1450:4007:816::2005 par10s33-in-x05.1e100.net.
(1E100 = Google). Et un autre, avec tous les détails :
% dig -x 2001:67c:2218:e::51:41 ... ;; QUESTION SECTION: ;1.4.0.0.1.5.0.0.0.0.0.0.0.0.0.0.e.0.0.0.8.1.2.2.c.7.6.0.1.0.0.2.ip6.arpa. IN PTR ;; ANSWER SECTION: 1.4.0.0.1.5.0.0.0.0.0.0.0.0.0.0.e.0.0.0.8.1.2.2.c.7.6.0.1.0.0.2.ip6.arpa. 600 IN PTR epp.nic.fr.
Et les mêmes, vus avec le DNS looking
glass :
et https://dns.bortzmeyer.org/2a00:1450:4007:816::2005?reverse=1
.https://dns.bortzmeyer.org/2001:67c:2218:e::51:41?reverse=1
Pourquoi mettre ces enregistrements PTR ? Le RFC 1912, section 2.1, dit que ce serait bien de le faire (« For every IP address, there should be a matching PTR record in the in-addr.arpa domain ») mais ne donne pas de raison, à part que certains services risquent de vous refuser si vous n'avez pas ce PTR. En fait, il y a deux sortes de vérification que peut faire un service distant auquel vous vous connectez :
reject_unknown_client_hostname
et
reject_unknown_reverse_client_hostname
. Pour
OpenSSH, c'est l'option
UseDNS
qui décide des conséquences du
test. La deuxième vérification peut se faire ainsi (en pseudo-code sur le serveur) :
if is_IPv4(client_IP_address) then arpa_domain = inverse(client_IP_address) + '.in-addr.arpa' else arpa_domain = inverse(client_IP_address) + '.ip6.arpa' end if names = PTR_of(arpa_domain) # Bien se rappeler qu'il peut y avoir plusieurs PTR for name in names loop if is_IPv4(IP_address) then addresses = A_of(name) # Bien se rappeler qu'il peut y avoir plusieurs A ou AAAA else addresses = AAAA_of(name) end if if client_IP_address in addresses then return True end if end loop return False
Est-ce une bonne idée d'exiger un enregistrement PTR ? Le RFC 1912 le fait, mais il est vieux de 22 ans et
est nettement dépassé sur ce point. Cette question des
enregistrements PTR, lorsqu'elle est soulevée dans les forums
d'opérateurs ou bien à l'IETF, entraine toujours des débats
passionnés. Un projet de RFC avait été développé (draft-ietf-dnsop-reverse-mapping-considerations
)
mais n'avait jamais abouti. Mais ce qui est sûr est que ces PTR
sont parfois exigés par des serveurs Internet, et qu'il faut donc
réfléchir à la meilleure façon de les fournir.
En IPv4, comme indiqué dans le
paragraphe introductif, c'est simple. On écrit un petit programme
qui génère les PTR, on les met dans un fichier de zone, et on
recharge le serveur DNS faisant
autorité. Si le FAI
example.net
gère le préfixe IP
203.0.113.0/24
, la zone DNS correspondante
est 113.0.203.in-addr.arpa
, et on met dans le
fichier de zone quelque chose comme :
1 IN PTR 1.north.city.example.net. 2 IN PTR 2.north.city.example.net. 3 IN PTR 3.north.city.example.net. 4 IN PTR 4.north.city.example.net. ...
Et, si on est un FAI sérieux, on vérifie qu'on a bien les enregistrements d'adresse dans la
zone example.net
:
1.north.city IN A 203.0.113.1 2.north.city IN A 203.0.113.2 3.north.city IN A 203.0.113.3 4.north.city IN A 203.0.113.4 ...
Mais en IPv6 ? Un PTR a cette forme (dans
le fichier de zone de
a.b.b.a.2.4.0.0.8.b.d.0.1.0.0.2.ip6.arpa
) :
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 IN PTR 1.north.city.example.net.
Et l'enregistrement d'adresse :
1.north.city IN AAAA 2001:db8:42:abba::1
Mais il faudrait autant de PTR que d'adresses possibles. Générer automatiquement la multitude de PTR qu'il faudrait, même pour un simple /64, est inenvisageable. Même si un programme arrivait à le faire (comme le note le RFC, en écrivant 1 000 entrées par seconde, il faudrait presque 600 millions d'années pour un pauvre /64), le serveur de noms ne pourrait pas charger une zone aussi grosse.
La section 2, le gros du RFC, décrit les solutions possibles. Elles n'ont pas les mêmes propriétés en terme de passage à l'échelle, conformité aux normes et disponibilité dans les logiciels existants, et, disons-le tout de suite, aucune n'est parfaite.
Première solution, la plus brutale, répondre NXDOMAIN (« ce nom n'existe pas »). C'est simple à faire et ça marche bien, quelle que soit la taille du préfixe. Il suffit d'un fichier de zone vide (juste les enregistrements SOA et NS) pour mettre en œuvre cette solution.
Après tout, c'est la réponse la plus honnête : pas d'information disponible. (Attention, on parle bien de répondre NXDOMAIN, et surtout pas de ne pas répondre. Ne pas répondre va laisser le client DNS attendre, peut-être très longtemps, alors qu'un NXDOMAIN va tout de suite lui dire qu'il n'y a pas de réponse.) Cela ne suit pas le RFC 1912, mais dont on a vu qu'il est très contestable sur ce point. Par contre, certains services seront refusés (à tort, je pense, mais la légende « s'il n'a pas de PTR, c'est un méchant » est très répandue) au client qui n'a pas de PTR.
Deuxième solution, profiter des jokers du DNS (RFC 4592) pour avoir une réponse identique pour toutes les adresses IP (cf. RFC 4472). C'est simple à faire et ça passe bien à l'échelle. Le fichier de zone va être :
* IN PTR one-host.foo.bar.example.
Le principal inconvénient est que la requête en sens inverse (du
nom - ici one-host.foo.bar.example
- vers
l'adresse IP) ne donnera pas un bon résultat, puisqu'il n'y a
qu'un seul nom, ce qui plantera les programmes qui vérifient la
cohérence des PTR et des A/AAAA.
Troisième solution, laisser les machines mettre à jour elle-mêmes le DNS, pour y mettre le PTR. La machine qui vient d'obtenir une adresse IP (que ce soit par SLAAC, DHCP ou un autre moyen), va mettre à jour enregistrements AAAA et PTR dans un serveur DNS dynamique. Cette fois, c'est compliqué à réaliser. Pour un gros FAI, il y aura plusieurs serveurs DNS, à des fins de redondance, il faudra que les machines clientes s'adressent au bon (il n'y a pas de mécanisme standard pour cela), s'il y a beaucoup de clients DNS, le serveur va souffrir, authentifier ces requêtes de mise à jour du DNS va être compliqué… Sans compter le risque qu'un utilisateur facétieux ne mette un PTR… « créatif » ou tout simplement un nom déjà utilisé.
Notez que, à la maison ou dans au autre petit réseau local, ce n'est pas forcément la machine terminale qui pourrait faire la mise à jour DNS. Cela pourrait se centraliser au niveau du routeur CPE (la box).
Dans ce cas, on pourrait même imaginer que le FAI délègue deux
noms de domaine à ce routeur CPE, un nom pour les requêtes
d'adresses (mettons
customer-7359.city.example.net
) et un pour
les requêtes inverses (mettons
6.a.2.1.6.a.a.b.8.b.d.0.1.0.0.2.ip6.arpa
). Dans
ce cas, ce serait de la responsabilité du routeur CPE de répondre
pour ces noms, d'une manière configurable par l'utilisateur. Si le
FAI fournit la box, comme c'est courant en
France, il peut s'assurer qu'elle est capable de fournir ce
service. Autrement, il faut ne faire cette délégation que si
l'utilisateur a cliqué sur « oui, j'ai un serveur DNS bien
configuré, déléguez-moi », ou bien en utilisant une heuristique
(si un client demande une délégation de préfixe IP, on peut
estimer qu'il s'agit d'un routeur et pas d'une machine terminale,
et elle a peut-être un serveur DNS inclus). Notez que le RFC 6092 dit qu'un routeur CPE ne doit pas avoir,
par défaut, de serveur DNS accessible de l'extérieur, mais cet
avis était pour un résolveur, pas pour un serveur faisant
autorité.
Enfin, la mise à jour dynamique du DNS peut aussi être faite par le serveur DHCP du FAI, par exemple en utilisant le nom demandé par le client (RFC 4704). Ou par le serveur RADIUS.
Quatrième solution, déjà partiellement évoquée plus haut, déléguer le DNS à l'utilisateur. Cela ne marche pas en général avec l'utilisateur individuel typique, qui n'a pas de serveur DNS, ni le temps ou l'envie d'en installer un. Une solution possible serait d'avoir une solution toute prête dans des offres de boitiers faisant tout comme le Turris Omnia.
Enfin, cinquième et dernière possibilité, générer des réponses
à la volée, lors de la requête DNS. Un exemple d'algorithme
serait, lors de la réception d'une requête DNS de type PTR, de
fabriquer un nom (mettons
host-56651.customers.example.net
), de le
renvoyer et de créer une entrée de type AAAA
pour les requêtes portant sur ce nom. Ces deux entrées pourraient
être gardées un certain temps, puis nettoyées. Notez que cela ne
permet pas à l'utilisateur de choisir son nom. Et que peu de
serveurs DNS savent faire cela à l'heure actuelle. (Merci à Alarig
Le Lay pour m'avoir signalé le
module qui va bien dans Knot.)
Cette technique a deux autres inconvénients. Si on utilise DNSSEC, elle impose de générer également les signatures dynamiquement (ou de ne pas signer cette zone, ce qui est raisonnable pour de l'information non-critique). Et l'algorithme utilisé doit être déterministe, pour donner le même résultat sur tous les serveurs de la zone.
La section 3 de notre RFC discute la possibilité d'un avitaillement du DNS par l'utilisateur final, via une interface dédiée, par exemple une page Web (évidemment authentifiée). Voici par exemple l'interface de Linode :
Notez qu'on ne peut définir un PTR qu'après avoir testé que la recherche d'adresses IP pour ce nom fonctionne, et renvoie une des adresses de la machine. Chez Gandi, on ne peut plus changer ces enregistrements depuis le passage à la version 5 de l'interface, hélas. Mais ça fonctionne encore tant que la version 4 reste en service :
L'interface
de Free (qui ne gère qu'IPv4) est boguée : elle affiche le nom
choisi mais une requête PTR donne quand même le nom automatique
(du genre lns-bzn-x-y-z.adsl.proxad.net
,
d'autant plus drôle qu'il concerne un abonnement via la
fibre).
La section 4 tente de synthétiser le problème et les solutions. Considérant qu'il y a six utilisations typiques des enregistrements PTR :
2001:db8:42::1
peut, via son contrôle de
2.4.0.0.8.b.d.0.1.0.0.2.ip6.arpa
, mettre un
PTR indiquant www.google.com
),Considérant ces usages, la meilleure solution serait la délégation du DNS à l'utilisateur, pour qu'il ait un contrôle complet, mais comme c'est actuellement irréaliste la plupart du temps, le NXDOMAIN est parfois la solution la plus propre. Évidemment, comme toujours, « cela dépend » et chaque opérateur doit faire sa propre analyse (rappelez-vous qu'il n'y a pas de consensus sur ces enregistrements PTR). Personnellement, je pense que tout opérateur devrait fournir à ses utilisateurs un moyen (formulaire Web ou API) pour mettre un enregistrement PTR.
Et pour terminer, la section 5 de notre RFC décrit les questions de sécurité et de vie privée liées à ces questions de résolution « inverse ». Parmi elles :
le-ministre-machin-est-un-voleur.example.net
,
ce qui peut valoir des ennuis juridiques au FAI).Et si vous préférez lire en espagnol, Hugo Salgado l'a fait ici.
Date de publication du RFC : Janvier 2019
Auteur(s) du RFC : P. Hoffman (ICANN), A. Sullivan, K. Fujiwara (JPRS)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 3 janvier 2019
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 8499, successeur du RFC 7719, 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. Il a depuis été remplacé par le RFC 9499, mais avec peu de changements.
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 un toilettage complet. Mais personne n'a encore osé faire pareil pour le DNS. Au moins, ce RFC 8499, comme son prédécesseur RFC 7719, 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 8499 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é de ce RFC, il a suscité bien des débats. Il y a en effet 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
).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 ceux
d'Akamai, répondent 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 désormais 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 ».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 son prédécesseur le RFC 7719). 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 7719, alors qu'il y a quelques modifications substantielles. Parmi les gros changements :
Date de publication du RFC : Octobre 2018
Auteur(s) du RFC : J. Kunze (California Digital
Library), J. Littman (Stanford
Libraries), E. Madden (Library of
Congress), J. Scancella, C. Adams (Library of Congress)
Pour information
Première rédaction de cet article le 20 décembre 2018
Le format BagIt, très utilisé dans le monde des bibliothèques (monde d'où sont issus les auteurs de ce RFC), décrit une série de conventions pour un ensemble de fichiers décrivant un contenu numérique quelconque. En fait, BagIt n'est pas vraiment un format (on ne peut pas le comparer à tar ou à zip), il définit juste les fichiers qui doivent être présents dans l'archive.
Une archive BagIt est appelée un sac (bag). Elle est composée des fichiers de contenu, qui sont d'un format quelconque, et des fichiers de métadonnées, qui décrivent le contenu (ces fichiers de métadonnées se nomment tags). BagTit met l'accent sur le contrôle de l'intégrité des données (les tags contiennent un condensat cryptographique des données) et sur la facilité d'accès à un fichier donné (les fichiers de données ne sont pas sérialisés dans un seul grand fichier, comme avec tar ou zip, ils restent sous la forme d'une arborescence).
La section 2 du RFC décrit la structure d'un sac :
bagit.txt
qui indique le numéro de version
BagIt, et manifest-HASHALGO.txt
qui contient
les condensats.data/
sous lequel se
trouvent les fichiers de données.
Voici un exemple d'un sac, contenant deux fichiers de données,
Makefile
et
bortzmeyer-ripe-atlas-lapaz.tex
:
% find /tmp/RIPE-Atlas-Bolivia /tmp/RIPE-Atlas-Bolivia /tmp/RIPE-Atlas-Bolivia/manifest-sha512.txt /tmp/RIPE-Atlas-Bolivia/data /tmp/RIPE-Atlas-Bolivia/data/Makefile /tmp/RIPE-Atlas-Bolivia/data/bortzmeyer-ripe-atlas-lapaz.tex /tmp/RIPE-Atlas-Bolivia/manifest-sha256.txt /tmp/RIPE-Atlas-Bolivia/tagmanifest-sha256.txt /tmp/RIPE-Atlas-Bolivia/bag-info.txt /tmp/RIPE-Atlas-Bolivia/tagmanifest-sha512.txt /tmp/RIPE-Atlas-Bolivia/bagit.txt % cat /tmp/RIPE-Atlas-Bolivia/bagit.txt BagIt-Version: 0.97 Tag-File-Character-Encoding: UTF-8 % cat /tmp/RIPE-Atlas-Bolivia/bag-info.txt Bag-Software-Agent: bagit.py v1.7.0 <https://github.com/LibraryOfCongress/bagit-python> Bagging-Date: 2018-12-20 Contact-Email: stephane+atlas@bortzmeyer.org Contact-Name: Stéphane Bortzmeyer Payload-Oxum: 6376.2 % cat /tmp/RIPE-Atlas-Bolivia/manifest-sha256.txt 6467957fa9c06d30c1a72b62d13a224a3cdb570e5f550ea1d292c09f2293b35d data/Makefile 3d65d66d6abcf1313ff7af7f94b7f591d2ad2c039bf7931701a936f1305ac728 data/bortzmeyer-ripe-atlas-lapaz.t
Les condensats ont été faits avec SHA-256.
Le fichier obligatoire bagit.txt
doit
indiquer le numéro de version et
l'encodage. Le RFC décrit la version 1.0
mais, comme vous pouvez le voir plus haut, j'ai utilisé un outil un
peu ancien pour fabriquer le sac. Le répertoire
data/
contient les fichiers de données, non
modifiés (BagIt les traite comme du contenu binaire, copié au bit
près). manifest-sha256.txt
contient une ligne
par fichier de données, indiquant le condensat. Notez qu'il peut y
avoir plusieurs manifestes, avec des
algorithmes différents. Cela permet, si un nouvel algorithme de
condensation plus
solide apparait, d'ajouter le manifeste au sac. Les noms
d'algorithmes de condensation sont tirés du registre
IANA du RFC 6920. Quant aux fichiers
(non obligatoires) dont le nom commence par
tagmanifest
, ils indiquent les condensats des
fichiers de métadonnées :
% cat /tmp/RIPE-Atlas-Bolivia/tagmanifest-sha256.txt 16ed27c2c457038ca57536956a4431de4ac2079a7ec8042bab994696eb017b90 manifest-sha512.txt b7e3c4230ebd4d3b878f6ab6879a90067ef791f1a5cb9ffc8a9cb1f66a744313 manifest-sha256.txt 91ca8ae505de9266e37a0017379592eb44ff0a2b33b240e0b6e4f2e266688a98 bag-info.txt e91f941be5973ff71f1dccbdd1a32d598881893a7f21be516aca743da38b1689 bagit.txt
Enfin, le facultatif bag-info.txt
contient des
métadonnées qui ne sont typiquement prévues que pour les humains,
pas pour être analysées automatiquement. La syntaxe est la classique
Nom: Valeur
. Certains des noms sont
officiellement réservés (Contact-Name
,
Bagging-Date
…) et on peut en ajouter d'autres à
volonté.
Le sac est un répertoire, pas un fichier, et ne peut donc pas être transporté simplement, par exemple avec le protocole HTTP. On peut utiliser rsync, ou bien le sérialiser, par exemple en zip.
Il est amusant de noter qu'un sac peut être incomplet : des
fichiers de données peuvent être stockés à l'extérieur, et récupérés
dynamiquement lorsqu'on vérifie l'intégrité du sac. Dans ce cas, le
condensat dans le manifeste permettra de vérifier qu'on a bien
récupéré le contenu attendu. Les URL où
récupérer ce contenu supplémentaire seront dans un fichier
fetch.txt
.
Un sac peut être complet (ou pas) et ensuite
valide (ou pas). La section 3 de notre RFC
définit ces termes : un sac est complet s'il contient tous les
fichiers obligatoires, et que tous les fichiers dans les manifestes
sont présents, et que les fichiers comme
bagit.txt
ont une syntaxe correcte. Un sac est
valide s'il est complet et que tous les
condensats sont corrects.
La section 5 du RFC détaille quelques questions de sécurité liées à BagIt :
/../../../
. (Une suite
de tests existe, avec plusieurs sacs… intéressants,
permettant de tester la robustesse d'une mise en œuvre de BagIt.)Sans que cela soit forcément un problème de sécurité, d'autres différences entre systèmes d'exploitation peuvent créer des surprises (section 6 du RFC), par exemple l'insensibilité à la casse de certains systèmes de fichiers, ou bien la normalisation Unicode. La section 6 est d'ailleurs une lecture intéressante sur les systèmes de fichiers, et leurs comportements variés.
Passons maintenant aux programmes disponibles. Il en existe en plusieurs langages de programmation. Le plus répandu semble bagit-python, en Python. Il a été développé à la bibliothèque du Congrès, un gros utilisateur et promoteur de BagIt. La documentation est simple et lisible. On installe d'abord :
% pip3 install bagit Collecting bagit Downloading https://files.pythonhosted.org/packages/ee/11/7a7fa81c0d43fb4d449d418eba57fc6c77959754c5c2259a215152810555/bagit-1.7.0.tar.gz Building wheels for collected packages: bagit Running setup.py bdist_wheel for bagit ... done Stored in directory: /home/bortzmeyer/.cache/pip/wheels/8d/77/f7/8f91043ef3c99bbab558f578d19ce5938896e37e57609f9786 Successfully built bagit Installing collected packages: bagit Successfully installed bagit-1.7.0
On peut ensuite utiliser cette bibliothèque depuis Python :
% python3 Python 3.5.3 (default, Sep 27 2018, 17:25:39) [GCC 6.3.0 20170516] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import bagit >>> bag = bagit.make_bag('/tmp/toto', {'Contact-Name': 'Ed Summers'}) >>>
Le répertoire /tmp/toto
aura été transformé en
sac.
Cette bibliothèque vient aussi avec un outil en ligne de commande. J'ai créé le premier sac d'exemple de cet article avec :
% bagit.py --contact-name 'Stéphane Bortzmeyer' --contact-email 'stephane+atlas@bortzmeyer.org' \ /tmp/RIPE-Atlas-Bolivia
Et ce même outil permet de vérifier qu'un sac est valide :
% bagit.py --validate /tmp/RIPE-Atlas-Bolivia 2018-12-20 16:16:09,700 - INFO - Verifying checksum for file /tmp/RIPE-Atlas-Bolivia/data/bortzmeyer-ripe-atlas-lapaz.tex 2018-12-20 16:16:09,700 - INFO - Verifying checksum for file /tmp/RIPE-Atlas-Bolivia/data/Makefile 2018-12-20 16:16:09,701 - INFO - Verifying checksum for file /tmp/RIPE-Atlas-Bolivia/manifest-sha256.txt 2018-12-20 16:16:09,701 - INFO - Verifying checksum for file /tmp/RIPE-Atlas-Bolivia/manifest-sha512.txt 2018-12-20 16:16:09,701 - INFO - Verifying checksum for file /tmp/RIPE-Atlas-Bolivia/bagit.txt 2018-12-20 16:16:09,701 - INFO - Verifying checksum for file /tmp/RIPE-Atlas-Bolivia/bag-info.txt 2018-12-20 16:16:09,702 - INFO - /tmp/RIPE-Atlas-Bolivia is valid
Il existe d'autres mises en œuvre comme bagit ou bagins en Go. L'article « Using BagIt in 2018 » donne des informations utiles dans d'autres langages.
Date de publication du RFC : Février 2019
Auteur(s) du RFC : D. Harkins (HP Enterprise)
Pour information
Première rédaction de cet article le 22 février 2019
Ce nouveau RFC décrit un mécanisme permettant une authentification lors de l'utilisation de TLS sans utiliser de cryptographie à clé publique, mais avec un mot de passe.
Au passage, l'auteur réhabilite la notion de mot de passe, souvent considérée comme une méthode d'authentification dépassée. Il estime que « un mot de passe est plus naturel qu'un certificat » car « depuis l'enfance, nous avons appris la sémantique d'un secret partagé ».
Experts en sécurité, ne jetez pas tout de suite ce RFC à la poubelle. Rassurez-vous, le mot de passe n'est pas utilisé tel quel, mais via un protocole nommé TLS-PWD, dont la principale partie s'appelle Dragonfly. Dragonfly était déjà utilisé dans le RFC 5931.
D'abord, pourquoi ce nouveau mécanisme (section 1 du RFC) ? TLS (RFC 5246) utilise traditionnellement de la cryptographie à clé publique pour l'authentification. La machine qui veut être authentifiée (en général, c'est le serveur, mais le client peut le faire aussi) présente un certificat, contenant la clé publique et diverses métadonnées plus ou moins utiles (date d'expiration, signature d'une AC). Le protocole vérifie ensuite que la machine peut signer des données avec la clé privée correspondante, prouvant ainsi qu'elle connait cette clé privée. Ce mécanisme est bien connu mais, estime le RFC, a quelques défauts. Notamment, obtenir un certificat n'est pas trivial, et certainement bien plus compliqué que de configurer un mot de passe, opération qui est familière (le RFC dit « naturelle », ce qui est exagéré) à beaucoup. La quantité de certificats auto-signés (les plus simples à obtenir) qu'on trouve (surtout sur les MTA) en est la preuve. (Sans compter les autres problèmes comme les certificats expirés, qui montrent bien que l'avitaillement en certificats est un problème non résolu.) Et puis s'authentifier pour obtenir le premier certificat est un problème d'œuf et de poule qu'on pourrait résoudre avec ce nouveau protocole, authentifiant simplement un échange, quitte à obtenir le certificat par la suite, sur le lien sécurisé (méthode décrite dans le RFC 7030). Autre scénario d'usage pour ce nouveau protocole, le cas d'un lecteur qui transmet le PIN à la carte à puce en clair : il serait trop compliqué d'utiliser un certificat dans ce cas, alors qu'une authentification par mot de passe serait plus simple.
Les solutions sans certificat ont des vulnérabilités comme la possibilité d'attaques par dictionnaire. Un attaquant essaie plein de mots de passe possibles (par exemple, le recrutement de Mirai fonctionne ainsi) et tombe forcément juste de temps en temps. Une solution souvent proposée est d'avoir un mot de passe à forte entropie, qui a peu de chances d'être dans les essais effectués par l'attaquant. Par exemple un mot de passe choisi aléatoirement oblige l'attaquant à essayer toutes les possibilités (puisque toutes les combinaisons sont également possibles). Si le mot de passe fait N bits, cela imposera à l'attaquant 2^(N-1) essais en moyenne.
Mais la faiblesse de ce raisonnement est qu'il ne marche que si l'attaquant peut faire autant d'essais qu'il veut, sans conséquences négatives pour lui (par exemple parce qu'il a mis la main sur un fichier de mots de passe condensés, et qu'il peut à loisir tester hors-ligne s'il en trouve un). D'autres solutions sont pourtant possibles. Ainsi, le PIN d'une carte Visa ne fait que quatre chiffres, ce qui est très peu (5 000 essais suffisent, en moyenne) sauf que la carte se bloque au bout de trois essais échoués. (Notez que sur les très anciennes cartes, il était parfois possible de remettre le compteur d'essais à zéro, annulant cette sécurité.) Ce qui compte, ce ne sont pas les 10 000 possibilités théoriques, ce sont les trois essais. Le RFC généralise cette observation : « la résistance à une attaque par dictionnaire doit être une fonction du nombre d'interactions avec un participant honnête, pas une fonction de la quantité de calculs effectués ». En effet, le nombre d'interactions avec le participant honnête ne croîtra guère dans le futur (l'attaquant est détecté avant) alors que la quantité de calculs réalistiquement possibles croîtra à coup sûr. Comme dans le cas du code PIN, il n'est pas forcément nécessaire d'avoir un mot de passe à forte entropie, il faut juste empêcher l'adversaire d'essayer à volonté, et pour cela le forcer à des attaques actives, interagissant réellement avec sa victime. La section 7 du RFC détaille cette analyse, notant que ce protocole peut se contenter de mots de passe relativement « faibles ». Ainsi, note le RFC, un mot de passe de quatre lettres ASCII peut être suffisant (sans les consignes « votre mot de passe doit comporter au moins douze caractères, dont une lettre minuscule, une lettre majuscule, un chiffre, un emoji et un hiéroglyphe ») puisqu'il offre 459 976 possibilités. Un attaquant qui tenterait la force brute devrait essayer des dizaines de milliers de fois, ce qui sera certainement détecté (par exemple par des logiciels du genre de fail2ban, ou par des IDS).
Désolé, mais je ne vais pas vous exposer le protocole Dragonfly. Il dépasse mes maigres connaissances en cryptographie. La section 3 du RFC expose le minimum qu'il faut savoir en cryptographie pour comprendre la suite (vous aurez besoin de réviser la cryptographie sur courbes elliptiques, par exemple via le RFC 6090 et ça vaut aussi la peine de relire le RFC 8422 sur les extensions TLS spécifiques aux courbes elliptiques). La section 3 rappelle aussi qu'il faut saler les mots de passe avant de les stocker, côté serveur, au cas où le fichier des mots de passe se fasse voler (voir aussi la section 7, qui détaille ce risque et ses conséquences). Le résultat se nomme la base. Plus précisement, la base est le résultat de l'application de HMAC-SHA256 à un sel et à la concaténation du nom de l'utilisateur et du mot de passe. Par exemple, si vous voulez le faire avec OpenSSL, que l'utilisateur est "fred" et le mot de passe "barney", avec un sel (tiré aléatoirement) "57ff4d5abdfe2ff3d849fb44848b42f2c41fd995", on fera :
% echo -n fredbarney | openssl dgst -sha256 -hmac "57ff4d5abdfe2ff3d849fb44848b42f2c41fd995" -binary \ | openssl enc -base64 4m9bv5kWK6t3jUFkF8qk96IuixhG+C6CY7NxsOGrlPw=
Comme le mot de passe peut inclure de l'Unicode, il doit être traité selon les règles du RFC 8265. Le serveur va stocker le nom de l'utilisateur, le sel et la base.
C'est la base qui est présentée par le client (après que le serveur lui ait envoyé le sel), pas le mot de passe, et le fichier des bases est donc aussi sensible qu'un fichier de mots de passe en clair (cf. section 7).
La section 4 du RFC décrit le protocole. L'échange de clés utilisé, Dragonfly, est décrit dans le RFC 7664. Utiliser Dragonfly en clair serait sûr du point de vue de l'authentification, mais pas du point de vue de la vie privée puisque le nom d'utilisateur passerait en clair. TLS-PWD, le protocole complet (dont Dragonfly n'est qu'une partie), offre un mécanisme pour protéger contre l'écoute de ce nom, une paire de clés cryptographiques étant générée et utilisée juste pour chiffrer ce bout de la session.
Le protocole TLS-PWD nécessite l'utilisation d'extensions
TLS (RFC 5246,
notamment la section 7.4.1.2). Elles sont au nombre de trois, notamment
PWD_protect
(protection du nom) et
PWD_clear
(nom d'utilisateur en clair). Elles
figurent désormais dans le registre IANA. Une
fois le nom reçu et trouvé dans le fichier côté serveur, le client
peut s'authentifier avec le mot de passe (c'est plus compliqué que
cela : voir la section 4 pour tous les détails).
Dans TLS, la suite complète des algorithmes utilisés dans une
session est indiquée dans une variable nommée cipher
suite (RFC 5246, sections 7.4.1.2, 9 et annexe
C). Les nouvelles suites permettant l'authentification par mot de
passe sont listées dans la section 5 : ce sont
TLS_ECCPWD_WITH_AES_128_GCM_SHA256
,
TLS_ECCPWD_WITH_AES_256_GCM_SHA384
,
TLS_ECCPWD_WITH_AES_128_CCM_SHA256
et
TLS_ECCPWD_WITH_AES_256_CCM_SHA384
. Elles
sont indiquées dans le registre IANA.
La section 7 du RFC contient de très nombreux détails sur la sécurité du protocole. Elle note par exemple qu'un attaquant qui essaierait tous les mots de passe d'un même utilisateur serait facilement détecté mais qu'un attaquant pourrait aussi essayer un seul mot de passe avec beaucoup d'utilisateurs, échappant ainsi à la détection. Le RFC recommande donc de compter toutes les tentatives de connexion ensemble, quel que soit l'utilisateur. (Cela traite aussi partiellement le cas d'un IDS extérieur, qui ne verrait pas le nom d'utilisateur, celui-ci étant protégé. L'IDS pourrait néanmoins compter les problèmes de connexion et donner l'alarme.) D'autre part, les mesures contre les attaques par dictionnaire (forcer l'attaquant à interagir avec le serveur, et donc à se signaler) ne sont évidemment pas efficaces si un utilisateur a le même mot de passe sur plusieurs serveurs et que l'un est compromis. La consigne d'utiliser des mots de passe différents par service reste donc valable. (Et, même si le RFC n'en parle pas, il ne faut évidemment pas noter ces mots de passe sur un post-it collé sous le clavier. La relativisation de la règle des mots de passe compliqués permet d'utiliser des mots de passe courts et mémorisables, donc plus d'excuses pour noter sur le post-it.) Sinon, les fanas de cryptographie qui voudraient étudier la sécurité du protocole Dragonfly sont invités à lire l'article de Lancrenon, J. et M. Skrobot, « On the Provable Security of the Dragonfly Protocol ».
Une curiosité : ce RFC est apparemment le premier à avoir une section « Droits Humains » (Human Rights Considerations, section 8). Une telle section, analysant les conséquences du protocole décrit dans le RFC sur les droits humains était suggérée (mais non imposée) par le RFC 8280. Mais, en l'occurrence, celle-ci se réduit à une défense des armes à feu, sans trop de rapport avec le sujet du RFC. On note également une vision très personnelle et très états-unienne des droits humains « le premier des droits est celui de se protéger », ce qui ne figure dans aucun des textes fondateurs des droits humains.
Date de publication du RFC : Mars 2019
Auteur(s) du RFC : R. Bellis (ISC), S. Cheshire (Apple), J. Dickinson (Sinodun), S. Dickinson (Sinodun), T. Lemon (Nibbhaya Consulting), T. Pusateri
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 18 mars 2019
Autrefois, le DNS était toujours cité comme exemple d'un protocole sans état. On envoie une requête, on reçoit une réponse, et le client et le serveur oublient aussitôt qu'ils ont échangé, ils ne gardent pas de trace de cette communication. Mais, dans certains cas, maintenir un état sur une durée plus longue qu'un simple échange requête/réponse peut être utile. Ce nouveau RFC propose un mécanisme pour des sessions DNS, le mécanisme DSO (DNS Stateful Operations). Il introduit donc une nouvelle notion dans le DNS, la persistance des sessions.
Ne pas avoir d'état a de nombreux avantages : cela simplifie les programmes, cela augmente les performances considérablement (pas besoin de chercher dans une table l'état actuel d'un dialogue, le contenu de la requête est suffisant pour donner une réponse, on peut répondre à la vitesse de l'éclair) et cela permet de résister aux DoS, qui réussissent souvent lorsqu'elles arrivent à épuiser le système qui dépend d'un état. (C'est pour cela que c'est souvent une mauvaise idée de mettre un pare-feu à état devant un serveur Internet, et c'est même franchement absurde quand il s'agit d'un serveur DNS.) Le DNS « habituel », tournant sur UDP et sans maintenir d'état, doit une partie de son succès à son caractère sans état.
Mais ne pas avoir d'état a aussi des inconvénients : toutes les options, tous les choix doivent être répétés dans chaque requête. Et cela rend impossible de négocier des paramètres entre les deux parties, par exemple dans le cas d'une session cryptographiquement protégée. Bref, dans certains cas, on aimerait bien avoir une vraie session, de durée relativement longue (plusieurs secondes, voire plusieurs minutes). Le DNS a un mécanisme de connexion de longue durée, en utilisant TCP (RFC 7766), et peut utiliser TLS pour sécuriser cette communication (DoT, « DNS over TLS », RFC 7858) mais les requêtes à l'intérieur de cette connexion n'en profitent pas, elles ne savent pas qu'elles sont liées par le fait qu'elles sont dans la même connexion. D'où ce nouveau système.
Le principe de DSO (DNS Stateful Operations) est de permettre à une requête DNS de créer une session, avec des paramètres communs à toute la session (comme la durée maximale d'inactivité). La session est balisée par des requêtes DNS utilisant l'opcode DSO, de numéro 6 (la création d'un nouvel opcode est très rare). Les paramètres sont encodés en TLV (une nouveauté dans le monde DNS ; les traditionnels Query count et Answer count, avec les sections correspondantes, ne sont pas utilisés). La longueur du message DSO est indiquée par les deux premiers octets du message. Les messages DSO peuvent solliciter une réponse (même si c'est un simple accusé de réception) ou pas. Cette sollicitation est faite par un Message ID différent de zéro. Si, par contre, le Message ID DNS est à zéro, il s'agit d'un message DSO unidirectionnel (retenez ce terme, il va souvent servir dans ce RFC), qui n'attend pas de réponse. (Rappelez-vous que le Message ID sert à faire correspondre requêtes et réponses DNS. Si on n'attend pas de réponse, pas besoin d'un Message ID. Si par contre le message est bidirectionnel, il doit mettre un Message ID non nul.)
DSO (DNS Stateful Operations, sessions - avec état, donc - pour le DNS) ne s'applique qu'avec certains transports sous-jacents (section 4 du RFC). UDP est évidemment exclu, car il faut maintenir l'ordre des messages, et il faut qu'il y ait une connexion à gérer. Cela peut être TCP (RFC 1035, section 4.2.2 et RFC 7766) ou DoT (DNS sur TLS, RFC 7858). DoH (DNS sur HTTPS, RFC 8484) est par contre exclu car HTTP a ses propres mécanimes de gestion de session. (D'autre part, la section 9.2 décrit les conséquences que cela a pour l'anycast.)
Deux importantes utilisations de DSO sont prévues :
La section 5 est le gros du RFC, elle décrit tous les détails du protocole. Pour établir une session DSO, il faut :
Si on sait à l'avance que le correspondant gère DSO, on peut se considérer comme en session dès l'établissement de la connexion. Mais, souvent on ne sait pas ou on n'est pas sûr et il faut donc explicitement ouvrir une session. Cela se fait avec un message DSO (un message où l'opcode DNS vaut 6 ; ces opcodes sont décrits dans le RFC 1035, section 4.1.1). L'acceptation prend la forme d'un message DSO avec un Message ID qui correspond et un code de réponse 0 (rcode = NOERROR). Si le code de réponse est autre chose que NOERROR (par exemple 4, NOTIMP, « type de requête inconnu » ou 5, REFUSED, « je connais peut-être DSO mais je n'ai pas envie d'en faire »), c'est que notre correspondant ne peut pas ou ne veut pas établir une session.
Il n'y a pas de message DSO dédié à l'ouverture de session. On envoie un message DSO de n'importe quel type (par exemple Keepalive). Il peut donc arriver que le copain en face connaisse DSO mais pas ce type particulier. Dans ce cas, il va répondre DSOTYPENI (DSO Type Not Implemented, code 11, une nouveauté dans le registre). La session n'est pas établie et le client doit recommencer avec un autre type (comme Keepalive, qui a l'avantage d'être normalisé depuis le début et d'être obligatoire, donc il marchera partout).
Il y a des cas plus gênants : un serveur qui couperait la connexion de transport sous-jacente, ou bien qui ne répondrait pas aux messages DSO. Ce cas risque de se produire si un boitier intermédiaire bogué est sur le trajet. Il peut être alors nécessaire d'adopter des mesures de contournement comme celles qu'utilisaient les résolveurs DNS avec les serveurs ne gérant pas bien EDNS, mesures de contournement qui ont été abandonnées récemment avec le DNS Flag Day.
Si, par contre, tout se passe bien, la session DSO est établie, et des paramètres comme le délai d'inactivité doivent désormais suivre les règles de DSO et plus celles de normes précédentes comme le RFC 7766 (c'est pour cela que notre RFC met à jour le RFC 7766).
La section 5 détaille également le format des messages DSO. Ce sont des messages DNS ordinaires, commençant par le Message ID sur deux octets, avec l'opcode qui vaut DSO (code numérique 6). Les champs qui indiquent le nombre d'enregistrements dans les différentes sections doivent tous être mis à zéro. Les données DSO sont situées après l'en-tête DNS standard, et sont sous forme de TLV. Le logiciel peut donc analyser ces données même s'il ne connait pas un type DSO spécifique. Dans une requête DSO, il y a toujours au moins un TLV, le « TLV primaire », qui indique le type d'opérations. Les autres éventuels TLV (« TLV additionnels ») sont là pour préciser le message. Rappelons qu'il y a deux sortes de messages DSO, les unidirectionnels et les autres. Les unidirectionnels ont le Message ID à zéro et n'ont jamais de réponse. (Avec le Message ID à zéro, on ne saurait de toute façon pas à quelle demande correspond une réponse.)
Chaque TLV comprend trois champs :
Notez que la définition de chaque type doit préciser s'il est censé être utilisé en TLV primaire ou additionnel. Pour une réponse, il peut n'y avoir aucun TLV présent.
Toutes les sections « normales » d'un message DNS sont vides, y
compris la section additionnelle qu'utilise
EDNS (le champ ARCOUNT
doit être à zéro). Il ne peut donc pas y avoir d'options
EDNS dans un message DSO (pour éviter la confusion qui se
produirait si une option EDNS et un message DSO donnaient des
valeurs différentes au même service). Si on veut le service équivalent à une
option EDNS, il faut créer un nouveau type DSO (section 10.3 du
RFC pour les détails)
et le faire enregistrer.
Combien de temps durent les sessions DSO ? D'un côté, il faut qu'elles soient aussi longues que possible, pour amortir le coût de créer et de maintenir des sessions sur un grand nombre de requêtes, d'un autre, il ne faut pas gaspiller des ressorces à maintenir une session ouverte si elle ne sert plus à rien. La section 6 du RFC discute cette question. DSO a un délai maximal d'inactivité et, quand le délai est dépassé sans activité, le client DSO est censé couper la connexion. (S'il ne le fait pas, le serveur le fera, après un délai plus long.) Le client a évidemment le droit de couper la session avant l'expiration du délai, s'il sait qu'il n'en aura plus besoin.
Le délai maximal d'inactivité est fixé par les messages DSO de type 1. Deux cas spéciaux : zéro indique qu'on doit fermer la connexion immédiatement après la première requête, et 0xFFFFFFFF indique que la session peut être gardée ouverte aussi longtemps qu'on le souhaite.
DSO permet également de spécifier l'intervalle de génération des messages keepalives, messages envoyés périodiquement uniquement pour que les boitiers de traduction d'adresse gardent leur état et ne suppriment pas une correspondance adresse interne <-> adresse externe en pensant qu'elle ne sert plus. Si on sait qu'il n'y a pas de NAT sur le trajet, on peut mettre un intervalle très élevé. Le client peut aussi se dire « j'ai une adresse RFC 1918, le serveur a une adresse IP publique, il y a donc sans doute un machin NAT sur le trajet, je demande des keepalives fréquents ».
Enfin, le client doit être préparé à ce que le serveur ferme la session à sa guise, parce que le serveur estime que le client exagère (il ne ferme pas la session alors que le délai d'inactivité est dépassé, et qu'il n'envoie pas de requêtes), ou bien parce que le serveur va redémarrer. Normalement, c'est le client DSO qui ferme la session mais, dans certains cas, le serveur peut décider de le faire.
La section 7 du RFC décrit les trois TLV de base qui doivent être présents dans toutes les mises en œuvre de DSO : keepalive, délai avant de réessayer, et remplissage. La section 8.2 indique dans quels cas ils peuvent être utilisés par le client ou par le serveur.
Le TLV keepalive contrôle l'envoi de messages servant uniquement à indiquer que la session est toujours ouverte, afin notamment de rassurer les routeurs NAT. Ce même TLV sert également à indiquer le délai d'inactivité maximal. Comme ce type de TLV est obligatoire, c'est un bon candidat pour le message initial d'ouverture de session (il n'y a pas de message particulier pour cette ouverture : on envoie juste un message ordinaire). Il a le type 1 et comprend deux champs de données, le délai maximal d'inactivité, en millisecondes, sur quatre octets, et l'intervalle d'émission des keepalives, également en millisecondes, et sur quatre octets. Il peut être utilisé comme TLV primaire, et il requiert une réponse, le Message ID doit donc être différent de zéro. La valeur du délai maximal d'inactivité émise par le client est un souhait, la valeur à utiliser est celle qui figure dans la réponse du serveur. Si le client ne la respecte pas par la suite, le serveur aura le droit de fermer la session. Notez qu'EDNS avait déjà un mécanisme équivalent, pour définir une durée d'inactivité maximale dans les connexions TCP, normalisé dans le RFC 7828. Mais les limites d'EDNS, comme le fait que les options EDNS ne s'appliquent normalement qu'au message en cours, rendent cette solution peu satisfaisante. Cet ancien mécanisme ne doit donc pas être utilisé avec DSO, qui dispose, d'un autre système, celui utilisant les valeurs spécifiées par un message portant le TLV Keepalive.
Une fois la durée d'émission des keepalives fixée, les messages de keepalive seront des messages unidirectionnels (pas de réponse) et donc envoyés avec un Message ID nul.
Deuxième type de TLV obligatoire, le délai avant de réessayer de se connecter, qui a le code 2. C'est un message unidirectionnel, envoyé par le serveur pour indiquer qu'il va couper et qu'il ne faut pas réessayer avant la durée indiquée en valeur du TLV.
Et enfin, le troisième type (code 3) qui doit être présent dans toute mise en œuvre de DSO est le remplissage. Le but est d'améliorer la protection de la vie privée en insérant des données bidon dans les messages DNS, pour rendre plus difficile l'analyse des données chiffrées. Il n'a évidemment de sens que si la session sous-jacente est chiffrée, par exemple avec le RFC 7858. Pour la longueur du remplissage à choisir, voir le RFC 8467.
Comme toujours sur l'Internet, une grande partie des problèmes opérationnels viendront des middleboxes. Le RFC rappelle à juste titre que la meilleure solution serait de ne pas avoir de middleboxes mais, comme c'est un idéal lointain, en attendant, il faut se pencher sur ce que font ces fichus boitiers intermédiaires, qui se permettent parfois d'intercepter automatiquement le trafic DNS et de le modifier. Si le boitier gère DSO et répond correctement aux spécifications de ce RFC, tout va bien. Si le boitier ne comprend pas DSO et renvoie un NOTIMP ou équivalent, cela empêche d'utiliser DSO mais, au moins, cela ne viole pas la norme : le client réagira comme si le serveur ne connait pas DSO. Si le boitier ne connait pas le DNS, et n'essaie pas de le comprendre, ça devrait marcher si, bien sûr, il établit bien une connexion et une seule pour chaque connexion entrante (c'est ce que fait un routeur NAT qui ne regarde pas les couches supérieures).
Dès que le boitier ne respecte pas ces règles, on peut prévoir des ennuis, et qui seront très difficiles à déboguer. Par exemple si un répartiteur de charge DNS reçoit des connexions TCP, les ouvre, et envoie chaque requête DNS qu'elles contenaient à un serveur différent, le client DSO va certainement souffrir. Il croira avoir une session alors qu'il n'en est rien.
Autre problème pratique qui se posera peut-être : les optimisations de TCP. Deux d'entre elles ont des chances sérieuses de créer des ennuis, l'algorithme de Nagle et les accusés de réception retardés (on attend un peu de voir si un autre segment arrive, pour pouvoir accuser réception des deux avec un seul paquet, RFC 1122, section 4.2.3.2). Pour les messages DSO bidirectionnels, pas de problème. Pour les unidirectionnels, en revanche, le retard de l'accusé de réception pourra atteindre 200 millisecondes, ce qui est énorme dans un centre de données typique, avec des liens qui peuvent débiter plus d'un gibabit par seconde. L'algorithme de Nagle fera qu'on n'enverra pas de données tout de suite, attendant s'il n'y a pas quelque chose à transmettre et, avec l'accusé de réception retardé, la combinaison des deux retardera sérieusement l'envoi.
Débrayer l'algorithme de Nagle, ou bien les accusés de réception retardés, résoudrait le problème mais ferait perdre d'utiles optimisations. En fait, la seule solution propre serait que les API permettent aux applications de dire à TCP « il n'y aura pas de réponse à ce message, envoie l'accusé de réception tout de suite ».
Enfin, un petit mot sur la sécurité pour finir. DSO nécessite des connexions permanentes et, potentiellement, cela peut consommer pas mal de ressources sur le serveur. Pour se protéger, le serveur a donc parfaitement le droit de limiter le nombre de connexions maximal, et de fermer des sessions quand ça lui chante.
Toujours sur la sécurité, DSO permet des établissements de connexion sans aller-retour, avec TCP Fast Open (RFC 7413) et TLS 1.3 (RFC 8446). C'est très rapide, c'est très bien mais les données envoyées avec le premier paquet (early data) ne sont pas forcément bien sécurisées et la définition de chaque type de TLV doit donc indiquer s'il est sûr ou pas de l'utiliser dans le premier paquet.
Il semble qu'à l'heure actuelle, il n'y a pas encore de mise en œuvre de cette technique DSO.
Date de publication du RFC : Février 2020
Auteur(s) du RFC : M. Petit-Huguenin (Impedance Mismatch), G. Salgueiro (Cisco), J. Rosenberg (Five9), D. Wing, R. Mahy, P. Matthews (Nokia)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tram
Première rédaction de cet article le 23 février 2020
Le NAT a toujours été une des plaies de l'Internet, entre autres parce qu'il perturbe les applications qui veulent transmettre une référence à leur adresse IP. STUN, décrit dans ce RFC, est un des protocoles qui permet de limiter les dégâts. (Il s'agit ici d'une mise à jour du RFC 5389.)
Pour plusieurs raisons, dont le manque d'adresses IPv4, de nombreuses machines sont connectées à l'Internet derrière un routeur qui fait du NAT. Leur adresse étant alors privée, elles ne peuvent pas la transmettre à l'extérieur, ce qui est une gêne considérable pour tous les protocoles qui reposent sur la référence à des adresses IP, comme SIP. SIP lui-même (RFC 3261) marche à travers le NAT mais les données audio ou vidéo transmises (typiquement par RTP) le sont à une adresse IP que l'appelant donne à l'appelé et, si cette adresse est privée, le flux multi-média n'arrivera jamais.
La bonne solution serait de déployer IPv6, qui fournit des adresses en quantité illimitée, ou à la rigueur de permettre un meilleur contrôle du routeur NAT avec PCP (RFC 6887) mais, en attendant, STUN est une solution simple et qui marche souvent.
STUN s'inscrit donc dans la catégorie des protocoles de « réparation » du NAT, catégorie décrite dans le RFC 3424.
STUN résout partiellement le problème des NAT de la manière suivante : un serveur STUN doit être installé quelque part sur l'Internet public (il existe des serveurs STUN publics) et reçoit des demandes envoyées en UDP (ou TCP, voir sections 6.2.2 et 6.2.3, ou encore TLS ou DTLS) au port 3478. Le serveur STUN peut alors connaitre l'adresse publique, celle mise par le routeur NAT et, en répondant au client, il pourra informer celui-ci « Voilà à quoi tes paquets ressemblent, vus de l'extérieur ».
Le nom du serveur STUN est typiquement configuré dans le client, puis le serveur recherché dans le DNS via les enregistrements SRV (RFC 2782), comme détaillé dans la section 8. (Le traditionnel client Unix en ligne de commande, montré plus loin, ne connait pas les SRV.)
Le principe est simple, mais le RFC est plus compliqué que cela, notamment en raison des problèmes de sécurité (la section 16 du RFC est très détaillée sur ce point). Par exemple, le client STUN peut avoir besoin de commencer par une connexion TCP pour obtenir un mot de passe temporaire. (D'autres méthodes d'authentification sont possibles.)
Lorsque STUN fonctionne sur UDP, ou bien sur DTLS, le client STUN doit également être préparé à rémettre les paquets qui seraient perdus (section 6.2.1).
Un autre problème est qu'il y a en fait plusieurs sortes de NAT (voir par exemple le RFC 2663). Par exemple, certains routeurs NAT (Symmetric NAT pour le RFC 3489) n'attribuent pas l'adresse externe uniquement en fonction de la source mais aussi en fonction de la destination. Ceux-ci ne fonctionnent pas avec STUN, qui a besoin de NAT cone, c'est-à-dire où les paquets d'une même machine auront toujours la même adresse IP externe.
Voici une démonstration d'un client STUN (assez ancien mais qui marche) qui, en se connectant à un serveur STUN public va apprendre son adresse IP publique, et le port, les deux ensemble formant l'adresse de transport (cf. section 4) :
% ifconfig -a hme0: [...] inet 172.19.1.2 (Adresse privée, probablement NATée...) % stun stun.1und1.de -v STUN client version 0.97 ... MappedAddress = 192.0.2.29:30189 SourceAddress = 212.227.67.33:3478 ChangedAddress = 212.227.67.34:3479 Unknown attribute: 32800 ServerName = Vovida.org 0.96 ... Primary: Independent Mapping, Port Dependent Filter, preserves ports, no hairpin ... (Mon adresse telle que vue de l'extérieur est donc 192.0.2.29, à noter que le terme MappedAddress n'est plus utilisé, le RFC parle de ReflexiveAddress.)
Une fois l'adresse extérieure détectée, tout n'est pas terminé car rien n'indique, par exemple, que les paquets vont effectivement passer. C'est pour cela que la section 13 insiste que STUN n'est qu'un outil, devant s'insérer dans une solution plus globale, comme ICE (RFC 8445). À lui seul, STUN ne permet pas la traversée de tous les NAT. La section 13 décrit en détail ce concept d'utilisation de STUN et les règles que doivent suivre les protocoles qui utilisent STUN.
La section 5 décrit le format des paquets. Un paquet STUN doit
comprendre l'indication d'une méthode, qui
indique le genre de services que désire le client. Notre RFC 8489 ne décrit qu'une seule méthode,
Binding
, qui permet d'obtenir son adresse IP
extérieure mais TURN (RFC 8656), par exemple, en définit d'autres. Après la
méthode et un identificateur de transaction (qui sert au serveur
STUN à séparer ses clients), suivent les
attributs, encodés en TLV (la liste
des attributs figure en section 14). Les numéros des attributs
sont compris entre 0x0000 et 0x7FFF s'ils doivent être reconnus
par le serveur (autrement, la requête est rejetée) et entre 0x8000
et 0xFFFF si leur compréhension est facultative (cette notion
d'attributs obligatoires ou facultatifs facilite les évolutions
ultérieures du protocole). Il y a aussi un
magic cookie, une valeur fixe
(0x2112A442
) qui sert à reconnaitre les
agents STUN conformes (il n'existait pas dans les vieilles
versions de STUN).
La section 19 de notre RFC résume les changements faits depuis le prédécesseur, le RFC 5389. Rien de bien crucial, d'autant plus que la plupart de ces changements sont l'intégration de RFC qui avaient étendu le protocole STUN après la parution du RFC 5389 :
ALTERNATE-DOMAIN
,
pour indiquer que le serveur proposé en alternative peut avoir
un autre nom,Il existe de nombreux logiciels clients et serveurs pour STUN (mais pas forcément déjà conforme aux légers changements de ce RFC). Le client est en général embarqué dans une application qui a besoin de contourner le NAT, par exemple un softphone, ou bien un navigateur Web qui fait du WebRTC. On peut citer :
Et si vous cherchez une liste de serveurs STUN publics, il y a celle-ci.
Date de publication du RFC : Octobre 2018
Auteur(s) du RFC : P. Hoffman (ICANN), P. McManus
(Mozilla)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF doh
Première rédaction de cet article le 22 octobre 2018
Voici un nouveau moyen d'envoyer des requêtes DNS, DoH (DNS over HTTPS). Requêtes et réponses, au lieu de voyager directement sur UDP ou TCP sont encapsulées dans HTTP, plus exactement HTTPS. Le but ? Il s'agit essentiellement de contourner la censure, en fournissant un canal sécurisé avec un serveur supposé digne de confiance. Et le chiffrement sert également à préserver la vie privée du client. Toutes ces fonctions pourraient être assurées en mettant le DNS sur TLS (RFC 7858) mais DoH augmente les chances de succès puisque le trafic HTTPS est rarement bloqué par les pare-feux, alors que le port 853 utilisé par DNS-sur-TLS peut être inaccessible, vu le nombre de violations de la neutralité du réseau. DoH marque donc une nouvelle étape dans la transition vers un Internet « port 443 seulement ».
La section 1 du RFC détaille les buts de DoH. Deux buts principaux sont décrits :
L'annexe A de notre RFC raconte le cahier des charges du protocole DoH de manière plus détaillée :
Passons maintenant à la technique. DoH est un protocole très simple. Au hackathon de l'IETF en mars 2018 à Londres, les sept ou huit personnes travaillant sur DoH avaient très vite réussi à créer clients et serveurs, et à les faire interopérer (même moi, j'y étais arrivé). Vous pouvez lire le compte-rendu du hackathon, et la présentation quelques jours après au groupe de travail DoH.)
DoH peut s'utiliser de plusieurs façons : c'est une technique, pas une politique. Néanmoins, son principal usage sera entre un résolveur simple, situé sur la machine de l'utilisateur ou quelque part dans son réseau local, et un résolveur complet situé plus loin dans le réseau (section 1 du RFC). Ce résolveur simple peut être un démon comme stubby ou systemd, ou bien l'application elle-même (ce qui me semble personnellement une mauvaise idée, car cela empêche de partager configuration et cache). À l'heure actuelle, les serveurs faisant autorité ne parlent pas DoH et il n'est pas prévu qu'ils s'y mettent à brève échéance. Le schéma suivant montre l'utilisation typique de DoH : l'application (le client final) parle à un résolveur simple, en utilisant le protocole DNS (ce qui évite de mettre du DoH dans toutes les applications), le résolveur simple parlera en DoH avec un résolveur de confiance situé quelque part dans l'Internet, et ce résolveur de confiance utilisera le DNS pour parler aux serveurs faisant autorité (soit il sera lui-même un résolveur complet, soit il parlera à un résolveur classique proche de lui, et le serveur DoH sera alors un proxy comme décrit dans le RFC 5625) :
Les requêtes et réponses DNS (RFC 1034 et RFC 1035) ont leur encodage habituel (le DNS est un protocole binaire, donc je ne peux pas faire de copier/coller pour montrer cet encodage), la requête est mise dans le chemin dans l'URL ou dans le corps d'une requête HTTP (RFC 9113), la réponse se trouvera dans le corps de la réponse HTTP. Toute la sécurité (intégrité et confidentialité) est assurée par TLS (RFC 8446), via HTTPS (RFC 2818). Un principe essentiel de DoH est d'utiliser HTTP tel quel, avec ses avantages et ses inconvénients. Cela permet de récupérer des services HTTP comme la négociation de contenu, la mise en cache, l'authentification, les redirections, etc.
La requête HTTP elle-même se fait avec les méthodes GET ou POST
(section 4 du RFC), les deux devant être acceptées (ce qui fut le
sujet d'une assez longue discussion à l'IETF.) Quand la méthode
utilisée est GET, la variable nommée dns
est
le contenu de la requête DNS, suivant l'encodage habituel du DNS,
surencodée en Base64, plus exactement la
variante base64url normalisée dans le RFC 4648. Et, avec GET, le corps de la requête est vide
(RFC 7231, section 4.3.1). Quand on utilise POST, la
requête DNS est dans le corps de la requête HTTP et n'a pas ce
surencodage. Ainsi, la requête avec POST sera sans doute plus
petite, mais par contre GET est certainement plus apprécié par les
caches.
On l'a dit, DoH utilise le HTTP habituel. L'utilisation de HTTP/2, la version 2 de HTTP (celle du RFC 9113) est très recommandée, et clients et serveurs DoH peuvent utiliser la compression et le remplissage que fournit HTTP/2 (le remplissage étant très souhaitable pour la vie privée). HTTP/2 a également l'avantage de multiplexer plusieurs ruisseaux (streams) sur la même connexion HTTP, ce qui évite aux requêtes DoH rapides de devoir attendre le résultat d'une requête lente qui aurait été émise avant. (HTTP 1, lui, impose le respect de l'ordre des requêtes.) HTTP/2 n'est pas formellement imposé, car on ne peut pas forcément être sûr du comportement des bibliothèques utilisées, ni de celui des différents relais sur le trajet.
Requêtes et réponses ont actuellement le type
MIME application/dns-message
,
mais d'autres types pourront apparaitre dans le futur (par exemple
fondés sur JSON et non plus sur l'encodage
binaire du DNS, qui n'est pas amusant à
analyser en
JavaScript). Le client DoH doit donc
inclure un en-tête HTTP Accept:
pour indiquer
quels types MIME il accepte. En utilisant HTTP, DoH bénéfice également de la négociation de
contenu HTTP (RFC 7231, section 3.4).
Petit détail DNS : le champ ID (identification de la requête) doit être mis à zéro. Avec UDP, il sert à faire correspondre une requête et sa réponse mais c'est inutile avec HTTP, et cela risquerait d'empêcher la mise en cache de deux réponses identiques mais avec des ID différents.
En suivant la présentation des requêtes HTTP du RFC 9113 (rappelez-vous que HTTP/2, contrairement à la première version de HTTP, a un encodage binaire), cela donnerait, par exemple :
:method = GET :scheme = https :authority = dns.example.net :path = /?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB accept = application/dns-message
(Si vous vous le demandez,
AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB
est une requête DNS pour l'adresse IPv4 de www.example.com
.)
Et la réponse HTTP ? Aujourd'hui, elle est forcément de type
MIME application/dns-message
, mais d'autres
types pourront apparaitre. En attendant, le corps de la réponse
HTTP est une réponse DNS avec son encodage binaire habituel
normalisé dans le RFC 1035, section 4.1 (tel
qu'utilisé pour UDP ; notez que HTTP
permettant d'indiquer la longueur du message, les deux octets de
longueur utilisés par le DNS au-dessus de
TCP ne sont pas nécessaires et sont donc absents).
Le serveur DoH doit mettre un code de retour HTTP (RFC 7231). 200 signifie que la requête HTTP a bien été traitée. Mais cela ne veut pas dire que la requête DNS, elle, ait connu un succès. Si elle a obtenu une erreur DNS NXDOMAIN (nom non trouvé) ou SERVFAIL (échec de la requête), le code de retour HTTP sera quand même 200, indiquant qu'il y a une réponse DNS, même négative. Le client DoH, en recevant ce 200, devra donc analyser le message DNS et y trouver le code de retour DNS (NOERROR, NXDOMAIN, REFUSED, etc). Le serveur DoH ne mettra un code d'erreur HTTP que s'il n'a pas du tout de réponse DNS à renvoyer. Il mettra 403 s'il refuse de servir ce client DoH, 429 si le client fait trop de requêtes (RFC 6585), 500 si le serveur DoH a une grosse bogue, 415 si le type MIME utilisé n'est pas connu du serveur, et bien sûr 404 si le serveur HTTP ne trouve rien à l'URL indiqué par exemple parce que le service a été retiré. Dans tous ces cas, il n'y a pas de réponse DNS incluse. La sémantique de ces codes de retour, et le comportement attendu du client, suit les règles habituelles de HTTP, sans spécificité DoH. (C'est un point important et général de DoH : c'est du DNS normal sur du HTTP normal). Par exemple, lorsque le code de retour commence par un 4, le client n'est pas censé réessayer la même requête sur le même serveur : elle donnera forcément le même résultat.
Voici un exemple de réponse DoH :
:status = 200 content-type = application/dns-message content-length = 61 cache-control = max-age=3709 [Les 61 octets, ici représentés en hexadécimal pour la lisibilité] 00 00 81 80 00 01 00 01 00 00 00 00 03 77 77 77 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 1c 00 01 c0 0c 00 1c 00 01 00 00 0e 7d 00 10 20 01 0d b8 ab cd 00 12 00 01 00 02 00 03 00 04
La réponse DNS signifie « l'adresse de
www.example.com
est
2001:db8:abcd:12:1:2:3:4
et le
TTL est de 3709 secondes [notez comme il
est repris dans le Cache-control:
HTTP] ».
Comment un client DoH trouve-t-il le serveur ? La section 3 du
RFC répond à cette question. En gros, c'est manuel. DoH ne fournit
pas de mécanisme de sélection automatique. Concevoir un tel
mécanisme et qu'il soit sécurisé est une question non triviale, et
importante : changer le résolveur DNS utilisé par une machine
revient quasiment à pirater complètement cette machine. Cela avait
fait une très longue discussion au sein du groupe de travail DoH à
l'IETF, entre ceux qui pensaient qu'un
mécanisme automatique de découverte du gabarit faciliterait
nettement la vie de l'utilisateur, et ceux qui estimaient qu'un
tel mécanisme serait trop facile à subvertir. Donc, pour
l'instant, le client DoH reçoit manuellement un gabarit d'URI
(RFC 6570) qui indique le serveur DoH à
utiliser. Par
exemple, un client recevra le gabarit
https://dns.example.net/{?dns}
, et il fera
alors des requêtes HTTPS à dns.example.net
,
en passant ?dns=[valeur de la requête]
.
Notez que l'URL dans le gabarit peut comporter un nom de domaine, qui devra lui-même être résolu via le DNS, créant ainsi un amusant problème d'œuf et de poule (cf. section 10 de notre RFC). Une solution possible est de ne mettre que des adresses IP dans l'URL, mais cela peut poser des problèmes pour l'authentification du serveur DoH, toutes les autorités de certification n'acceptant pas de mettre des adresses IP dans le certificat (cf. RFC 6125, section 1.7.2, et annexe B.2).
La section 5 du RFC détaille quelques points liés à
l'intégration avec HTTP. D'abord, les
caches. DNS et HTTP ont chacun son
système. Et entre le client et le serveur aux extrémités, il peut
y avoir plusieurs caches DNS et plusieurs caches HTTP, et ces
derniers ne connaissent pas forcément DoH. Que se passe-t-il si on
réinjecte dans le DNS des données venues d'un cache HTTP ? En général, les réponses aux requêtes POST ne sont pas mises
en cache (elles le peuvent, en théorie) mais les requêtes GET le
sont, et les implémenteurs de DoH doivent donc prêter attention à
ces caches. La méthode recommandée est de mettre une durée de
validité explicite dans la réponse HTTP (comme dans l'exemple plus
haut avec Cache-control:
), en suivant le RFC 9111, notamment sa section 4.2. La durée de
validité doit être inférieure ou égale au plus petit
TTL de la section principale de la réponse
DNS. Par exemple, si un serveur DoH renvoie cette réponse DNS :
ns1.bortzmeyer.org. 27288 IN AAAA 2605:4500:2:245b::42 ns2.bortzmeyer.org. 26752 IN AAAA 2400:8902::f03c:91ff:fe69:60d3 ns4.bortzmeyer.org. 26569 IN AAAA 2001:4b98:dc0:41:216:3eff:fe27:3d3f
alors, la réponse HTTP aura un Cache-Control:
max-age=26569
, le plus petit des TTL.
Si la réponse DNS varie selon le client, le serveur DoH doit en
tenir compte pour construire la réponse HTTP. Le but est d'éviter
que cette réponse adaptée à un client spécifique soit
réutilisée. Cela peut se faire avec Cache-Control:
max-age=0
ou bien avec un en-tête
Vary:
(RFC 7231,
section 7.1.4 et RFC 9111, section 4.1) qui va ajouter une
condition supplémentaire à la réutilisation des données mises en
cache.
S'il y a un en-tête Age:
dans la réponse
HTTP (qui indique depuis combien de temps cette information était
dans un cache Web, RFC 9111, section 5.1), le client DoH
doit en tenir compte pour calculer le vrai TTL. Si le TTL DNS dans
la réponse est de 600 secondes, mais que Age:
indiquait que cette réponse avait séjourné 250 secondes dans le
cache Web, le client DoH doit considérer que cette réponse n'a
plus que 350 secondes de validité. Évidemment, un client qui veut
des données ultra-récentes peut toujours utiliser le
Cache-control: no-cache
dans sa requête HTTP,
forçant un rafraichissement. (Il est à noter que le DNS n'a aucun
mécanisme équivalent, et qu'un serveur DoH ne saura donc pas
toujours rafraichir son cache DNS.)
La définition formelle du type MIME
application/dns-message
figure en section 6
de notre RFC, et ce type est désormais enregistré
à l'IANA.
La section 8 du RFC est consacrée aux questions de vie privée. C'est à la fois un des principaux buts de DoH (empêcher l'écoute par un tiers) et un point qui a fait l'objet de certaines polémiques, puisque DoH peut être utilisé pour envoyer toutes les requêtes à un gros résolveur public auquel on ne fait pas forcément confiance. Le RFC 7626 traite séparément deux problèmes : l'écoute sur le réseau, et l'écoute effectuée par le serveur. Sur le réseau, DoH protège : tout est chiffré, via un protocole bien établi, TLS. Du fait que le serveur est authentifié, l'écoute par un homme du milieu est également empêchée. DNS sur TLS (RFC 7858) a exactement les mêmes propriétés, mais pour principal inconvénient d'utiliser un port dédié, le 853, trop facile à bloquer. Au contraire, le trafic DoH, passant au milieu d'autres échanges HTTP sur le port 443, est bien plus difficile à restreindre.
Mais et sur le serveur de destination ? Une requête DNS normale
contient peu d'informations sur le client (sauf si on utilise la
très dangereuse technique du RFC 7871). Au
contraire, une requête HTTP est bien trop bavarde :
cookies (RFC 6265), en-têtes User-Agent:
et
Accept-Language:
, ordre des en-têtes sont
trop révélateurs de l'identité du client. L'utilisation de HTTP
présente donc des risques pour la vie privée du client, risques
connus depuis longtemps dans le monde HTTP mais qui sont nouveaux
pour le DNS. Il avait été envisagé, pendant la discussion à
l'IETF, de définir un sous-ensemble de HTTP ne présentant pas ces
problèmes, mais cela serait rentré en contradiction avec les buts
de DoH (qui étaient notamment de permettre l'utilisation du code
HTTP existant). Pour l'instant, c'est donc au client DoH de
faire attention. Si la bibliothèque HTTP qu'il utilise le permet,
il doit veiller à ne pas envoyer de cookies, à
envoyer moins d'en-têtes, etc.
Notez que la question de savoir si les requêtes DoH doivent voyager sur la même connexion que le trafic HTTPS normal (ce que permet HTTP/2, avec son multiplexage) reste ouverte. D'un côté, cela peut aider à les dissimuler. De l'autre, les requêtes HTTP typiques contiennent des informations qui peuvent servir à reconnaitre le client, alors qu'une connexion servant uniquement à DoH serait moins reconnaissable, le DNS étant nettement moins sensible au fingerprinting.
Comme TLS ne dissimule pas la taille des messages, et qu'un observateur passif du trafic, et qui peut en plus envoyer des requêtes au serveur DNS, peut en déduire les réponses reçues, le RFC recommande aux clients DoH de remplir les requêtes DNS selon le RFC 7830.
Le choix de Mozilla d'utiliser DoH pour son navigateur Firefox (voir un compte-rendu de la première expérience) et le fait que, dans certaines configurations, le serveur DoH de Cloudflare était systématiquement utilisé a été très discuté (cf. cette discussion sur le forum des développeurs et cet article du Register). Mais cela n'a rien à voir avec DoH : c'est le choix d'utiliser un résolveur public géré par un GAFA qui est un problème, pas la technique utilisée pour accéder à ce résolveur public. DNS-sur-TLS aurait posé exactement le même problème. Si Mozilla a aggravé les choses avec leur discours corporate habituel (« nous avons travaillé très dur pour trouver une entreprise de confiance »), il faut rappeler que le problème de la surveillance et de la manipulation des requête et réponses DNS par les FAI est un problème réel (essayez de demander à votre FAI s'il s'engage à ne jamais le faire). On a vu plus haut que DoH ne prévoit pas de système de découverte du serveur. Il faut donc que cela soit configuré en dur (un travail supplémentaire pour les utilisateurs, s'il n'y a pas de résolveur par défaut). En tout cas, le point important est que DoH (ou DNS-sur-TLS) ne protège la vie privée que si le serveur DoH est honnête. C'est une limitation classique de TLS : « TLS permet de s'assurer qu'on communique bien avec Satan, et qu'un tiers ne peut pas écouter ». Mais DoH n'impose pas d'utiliser un serveur public, et impose encore moins qu'il s'agisse d'un serveur d'un GAFA.
La section 9 de notre RFC traite des autres problèmes de sécurité. D'abord, sur la relation entre DoH et DNSSEC. C'est simple, il n'y en a pas. DNSSEC protège les données, DoH protège le canal (une distinction que les promoteurs de DNSCurve n'ont jamais comprise). DNSSEC protège contre les modifications illégitimes des données, DoH (ou bien DNS-sur-TLS) protège contre l'écoute illégitime. Ils résolvent des problèmes différents, et sont donc tous les deux nécessaires.
Quant à la section 10 du RFC, elle expose diverses considérations pratiques liées à l'utilisation de DoH. Par exemple, si un serveur faisant autorité sert des réponses différentes selon l'adresse IP source du client (RFC 6950, section 4), utiliser un résolveur public, qu'on y accède via DoH ou par tout autre moyen, ne donnera pas le résultat attendu, puisque l'adresse IP vue par le serveur faisant autorité sera celle du résolveur public, probablement très distincte de celle du « vrai » client. Un exemple similaire figure dans le RFC : une technique comme DNS64 (RFC 6147) risque fort de ne pas marcher avec un résolveur DNS extérieur au réseau local.
Quelles sont les mises en œuvre de DoH ? Le protocole est assez récent donc votre système favori n'a pas forcément DoH, ou alors c'est seulement dans les toutes dernières versions. Mais DoH est très simple à mettre en œuvre (c'est juste la combinaison de trois protocoles bien maitrisés, et pour lesquels il existe de nombreuses bibliothèques, DNS, HTTP et TLS) et le déploiement ne devrait donc pas poser de problème.
Voyons maintenant ce qui existe, question logiciels et
serveurs. On a vu que Cloudflare a un serveur public, le fameux
1.1.1.1
étant
accessible en DoH (et également en DNS-sur-TLS). Je ne
parlerai pas ici de la question de la confiance qu'on peut
accorder à ce serveur (je vous laisse lire sa politique
de vie privée et l'évaluer vous-même), qui avait été
contestée lors de la polémique Mozilla citée plus haut. Cloudflare
fournit également une bonne
documentation sur DoH, avec une explication
de l'encodage. Enfin, Cloudflare fournit un résolveur
simple (comme stubby ou systemd cités plus haut) qui est un client
DoH, cloudflared.
Un autre serveur DoH public, cette fois issu du monde du
logiciel libre, est celui de l'équipe
PowerDNS, https://doh.powerdns.org/
(cf. leur
annonce). Il utilise leur logiciel dnsdist.
Vous trouverez une liste de serveurs DoH publics chez DefaultRoutes ou
bien chez
curl ou encore sur
le portail dnsprivacy.org. Testons ces serveurs DoH
publics avec le programme doh-nghttp.c
, qui avait
été écrit au hackathon IETF 101,
on lui donne l'URL du serveur DoH, et le nom à résoudre, et il
cherche l'adresse IPv4 correspondant à ce nom :
% ./doh-nghttp https://doh.powerdns.org/ www.bortzmeyer.org The address is 204.62.14.153 % ./doh-nghttp https://1.1.1.1/dns-query www.bortzmeyer.org The address is 204.62.14.153 % ./doh-nghttp https://doh.defaultroutes.de/dns-query www.bortzmeyer.org The address is 204.62.14.153 % ./doh-nghttp https://mozilla.cloudflare-dns.com/dns-query www.bortzmeyer.org The address is 204.62.14.153
Parfait, tout a bien marché. Un autre serveur DoH a la particularité d'être un résolveur menteur (regardez son nom) :
% ./doh-nghttp https://doh.cleanbrowsing.org/doh/family-filter/ www.bortzmeyer.org The address is 204.62.14.153 % ./doh-nghttp https://doh.cleanbrowsing.org/doh/family-filter/ pornhub.com The search had no results, and a return value of 8. Exiting. % ./doh-nghttp https://doh.powerdns.org/ pornhub.com The address is 216.18.168.16
Bon, et si je veux faire mon propre serveur DoH, on a quelles solutions ? Voyons d'abord le doh-proxy de Facebook. On lui indique le résolveur qu'il va utiliser (il n'est pas inclus dans le code, il a besoin d'un résolveur complet, a priori sur la même machine ou le même réseau local) :
% doh-proxy --port=9443 --upstream-resolver=192.168.2.254 --certfile=server.crt --keyfile=server.key --uri=/ 2018-09-27 10:04:21,997: INFO: Serving on <Server sockets=[<socket.socket fd=6, family=AddressFamily.AF_INET6, type=2049, proto=6, laddr=('::1', 9443, 0, 0)>]>
Et posons-lui des questions avec le même client doh-nghttp
:
% ./doh-nghttp https://ip6-localhost:9443/ www.bortzmeyer.org The address is 204.62.14.153
C'est parfait, il a marché et affiche les visites :
2018-09-27 10:04:24,264: INFO: [HTTPS] ::1 www.bortzmeyer.org. A IN 0 RD 2018-09-27 10:04:24,264: INFO: [DNS] ::1 www.bortzmeyer.org. A IN 56952 RD 2018-09-27 10:04:24,639: INFO: [DNS] ::1 www.bortzmeyer.org. A IN 56952 QR/RD/RA 1/0/0 -1/0/0 NOERROR 374ms 2018-09-27 10:04:24,640: INFO: [HTTPS] ::1 www.bortzmeyer.org. A IN 0 QR/RD/RA 1/0/0 -1/0/0 NOERROR 375ms
Au même endroit, il y a aussi un client DoH :
% doh-client --domain 1.1.1.1 --uri /dns-query --qname www.bortzmeyer.org 2018-09-27 10:14:12,191: DEBUG: Opening connection to 1.1.1.1 2018-09-27 10:14:12,210: DEBUG: Query parameters: {'dns': 'AAABAAABAAAAAAAAA3d3dwpib3J0em1leWVyA29yZwAAHAAB'} 2018-09-27 10:14:12,211: DEBUG: Stream ID: 1 / Total streams: 0 2018-09-27 10:14:12,219: DEBUG: Response headers: [(':status', '200'), ('date', 'Thu, 27 Sep 2018 08:14:12 GMT'), ('content-type', 'application/dns-message'), ('content-length', '103'), ('access-control-allow-origin', '*'), ('cache-control', 'max-age=5125'), ('expect-ct', 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"'), ('server', 'cloudflare-nginx'), ('cf-ray', '460c83ee69f73c53-CDG')] id 0 opcode QUERY rcode NOERROR flags QR RD RA AD edns 0 payload 1452 ;QUESTION www.bortzmeyer.org. IN AAAA ;ANSWER www.bortzmeyer.org. 5125 IN AAAA 2001:4b98:dc0:41:216:3eff:fe27:3d3f www.bortzmeyer.org. 5125 IN AAAA 2605:4500:2:245b::42 ;AUTHORITY ;ADDITIONAL 2018-09-27 10:14:12,224: DEBUG: Response trailers: {}
Ainsi qu'un résolveur simple (serveur DNS et client DoH).
Il ne faut pas confondre ce doh-proxy écrit en Python avec un logiciel du même nom écrit en Rust (je n'ai pas réussi à le compiler, celui-là, des compétences Rust plus avancées que les miennes sont nécessaires).
Et les clients, maintenant ? Commençons par le bien connu curl, qui a DoH est depuis la version 7.62.0 (pas encore publiée à l'heure où j'écris, le développement est documenté ici.) curl (comme Mozilla Firefox) fait la résolution DNS lui-même, ce qui est contestable (il me semble préférable que cette fonction soit dans un logiciel partagé par toutes les applications). Voici un exemple :
% ./src/.libs/curl -v --doh-url https://doh.powerdns.org/ www.bortzmeyer.org ... [Se connecter au serveur DoH] * Connected to doh.powerdns.org (2a01:7c8:d002:1ef:5054:ff:fe40:3703) port 443 (#2) * ALPN, offering h2 ... * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * Server certificate: * subject: CN=doh.powerdns.org ... [Envoyer la requête DoH] * Using Stream ID: 1 (easy handle 0x5631606cbd50) > POST / HTTP/2 > Host: doh.powerdns.org > Accept: */* > Content-Type: application/dns-message > Content-Length: 36 ... < HTTP/2 200 < server: h2o/2.2.5 < date: Thu, 27 Sep 2018 07:39:14 GMT < content-type: application/dns-message < content-length: 92 ... [On a trouvé la réponse DoH] * DOH Host name: www.bortzmeyer.org * TTL: 86392 seconds * DOH A: 204.62.14.153 * DOH AAAA: 2001:4b98:0dc0:0041:0216:3eff:fe27:3d3f * DOH AAAA: 2605:4500:0002:245b:0000:0000:0000:0042 ... [On peut maintenant se connecter au serveur HTTP - le but principal de curl - maintenant qu'on a son adresse IP] * Connected to www.bortzmeyer.org (204.62.14.153) port 80 (#0) > GET / HTTP/1.1 > Host: www.bortzmeyer.org > User-Agent: curl/7.62.0-20180927 > Accept: */* >
Pour les programmeurs Go, l'excellente bibliothèque godns n'a hélas pas DoH (pour des raisons internes). Du code expérimental avait été écrit dans une branche mais a été abandonné. Les amateurs de Go peuvent essayer à la place cette mise en œuvre (on notera que ce client sait parler DoH mais aussi le protocole spécifique et non-standard du résolveur public Google Public DNS).
Pour les programmeurs C, la référence
est la bibliothèque getdns (notez que c'est elle
qui a été utilisée pour le client doh-nghttp
cité plus haut). Le code DoH est, à la parution du RFC, toujours
en cours de développement et pas encore dans un dépôt public. Une
fois que cela sera fait, stubby, qui utilise getdns, pourra parler
DoH.
Voilà, si vous n'êtes pas épuisé·e·s, il y a encore des choses à lire :
Date de publication du RFC : Octobre 2018
Auteur(s) du RFC : L. Song (Beijing Internet
Institute), D. Liu (Beijing Internet
Institute), P. Vixie (TISF), A. Kato
(Keio/WIDE), S. Kerr
Pour information
Première rédaction de cet article le 17 décembre 2018
Ce RFC décrit une expérience, celle qui, de mai 2015 à décembre 2018, a consisté à faire tourner une racine DNS alternative nommée Yeti. Contrairement aux racines alternatives commerciales qui ne sont typiquement que des escroqueries visant à vendre à des gogos des TLD reconnus par personne, Yeti était une expérience technique ; il s'agissait de tester un certain nombre de techniques qu'on ne pouvait pas se permettre de tester sur la « vraie » racine.
Parmi ces techniques, l'utilisation d'un grand nombre de
serveurs
racine (pour casser la légende comme quoi l'actuelle
limite à 13 serveurs aurait une justification technique),
n'utiliser qu'IPv6, jouer avec des
paramètres DNSSEC différents, etc. J'ai
participé à ce projet, à la fois comme gérant de deux des serveurs
racine, et comme utilisateur de la racine Yeti, reconfigurant des
résolveurs DNS pour utiliser Yeti. Les deux serveurs racines
dahu1.yeti.eu.org
et
dahu2.yeti.eu.org
appartenaient au groupe
Dahu, formé par l'AFNIC (cf. cet
article sur le site de l'AFNIC),
Gandi et eu.org. (Le
nom vient d'un animal aussi
mythique que le yéti.)
Outre l'aspect technique, un autre intéret de Yeti était qu'il s'agissait d'un projet international. Réellement international, pas seulement des états-uniens et des européens de divers pays ! Yeti est d'inspiration chinoise, la direction du projet était faite en Chine, aux États-Unis et au Japon, et parmi les équipes les plus impliquées dans le projet, il y avait des Russes, des Indiens, des Français, des Chiliens… Le projet, on l'a dit, était surtout technique (même si certains participants pouvaient avoir des arrière-pensées) et la zone racine servie par Yeti était donc exactement la même que celle de l'IANA, aux noms des serveurs et aux signatures DNSSEC près. Un utilisateur ordinaire de Yeti ne voyait donc aucune différence. Le projet étant de nature expérimentale, les utilisateurs étaient tous des volontaires, conscients des risques possibles (il y a eu deux ou trois cafouillages).
L'annexe E du RFC est consacrée aux controverses sur le principe même du projet Yeti. Le projet a toujours été discuté en public, et présenté à de nombreuses réunions. Mais il y a toujours des râleurs, affirmant par exemple que ce projet était une racine alternative (ce qui n'est pas faux mais attendez, lisez jusqu'au bout) et qu'il violait donc le RFC 2826. Outre que ce RFC 2826 est très contestable, il faut noter qu'il ne s'applique pas à Yeti ; il concerne uniquement les racines alternatives servant un contenu différent de celui de la racine « officielle » alors que Yeti a toujours été prévu et annoncé comme servant exactement la même racine (comme le faisait ORSN). Rien à voir donc avec ces racines alternatives qui vous vendent des TLD bidons, que personne ne pourra utiliser. Comme le disait Paul Vixie, Yeti pratique le Responsible Alternate Rootism. Notez quand même que certains participants à Yeti (notamment en Chine et en Inde) avaient des objectifs qui n'étaient pas purement techniques (s'insérant dans le problème de la gouvernance Internet, et plus spécialement celle de la racine).
La racine du DNS est quelque chose d'absolument critique pour le bon fonctionnement de l'Internet. Quasiment toutes les activités sur l'Internet démarrent par une ou plusieurs requêtes DNS. S'il n'y a plus de résolution DNS, c'est à peu près comme s'il n'y avait plus d'Internet (même si quelques services pair-à-pair, comme Bitcoin, peuvent encore fonctionner). Du fait de la nature arborescente du DNS, si la racine a un problème, le service est sérieusement dégradé (mais pas arrêté, notamment en raison des mémoires - les « caches » - des résolveurs). On ne peut donc pas jouer avec la racine, par exemple en essayant des idées trop nouvelles et peu testées. Cela n'a pas empêché la racine de changer beaucoup : il y a eu par exemple le déploiement massif de l'anycast, qui semblait inimaginable il y a dix-sept ans, le déploiement de DNSSEC (avec le récent changement de clé, qui s'est bien passé), ou celui d'IPv6, plus ancien. Le fonctionnement de la racine était traditionnellement peu ou pas documenté mais il y a quand même eu quelques documents utiles, comme le RFC 7720, la première description de l'anycast, ou les documents du RSSAC, comme RSSAC 001. Celle ou celui qui veut se renseigner sur la racine a donc des choses à lire.
Mais le point important est que la racine est un système en production, avec lequel on ne peut pas expérimenter à loisir. D'où l'idée, portée notamment par BII, mais aussi par TISF et WIDE, d'une racine alternative n'ayant pas les contraintes de la « vraie » racine. Yeti (section 1 du RFC) n'est pas un projet habituel, avec création d'un consortium, longues réunions sur les statuts, et majorité du temps passé en recherches de financement. C'est un projet léger, indépendant d'organismes comme l'ICANN, géré par des volontaires, sans structure formelle et sans budget central, dans la meilleure tradition des grands projets Internet. À son maximum, Yeti a eu 25 serveurs racine, gérés par 16 organisations différentes.
Au passage, puisqu'on parle d'un projet international, il faut noter que ce RFC a été sérieusement ralenti par des problèmes de langue. Eh oui, tout le monde n'est pas anglophone et devoir rédiger un RFC en anglais handicape sérieusement, par exemple, les Chinois.
Parmi les idées testées sur Yeti (section 3 du RFC) :
root-servers.net
),root-servers.net
n'est pas signé),La section 4 du RFC décrit l'infrastructure de Yeti. Elle a
évidemment changé plusieurs fois, ce qui est normal pour un
service voué aux expérimentations. Yeti utilise l'architecture
classique du DNS. Les serveurs racine sont remplacés par ceux de
Yeti, les autres serveurs faisant autorité (ceux de
.fr
ou
.org
, par exemple) ne
sont pas touchés. Les résolveurs doivent évidemment être
reconfigurés pour utiliser Yeti (d'une manière qui est documentée sur le site Web du projet). Au
démarrage, un résolveur ne connait en effet que la liste des noms
et adresses IP des serveurs de la racine. La racine « officielle »
est configurée par défaut et doit ici être remplacée (annexe A du RFC). Il faut
aussi changer la clé de la racine (la root trust
anchor) puisque Yeti signe avec sa propre clé.
Voici la configuration de mon résolveur à la maison, avec Knot sur une Turris Omnia :
config resolver 'common' option keyfile '/etc/kresd/yeti-root.keys' option prefered_resolver 'kresd' config resolver 'kresd' option rundir '/tmp/kresd' option log_stderr '1' option log_stdout '1' option forks '1' option include_config '/etc/kresd/custom.conf'
et custom.conf
contient la liste des serveurs
racine :
hints.root({ ['bii.dns-lab.net.'] = '240c:f:1:22::6', ['yeti-ns.tisf.net .'] = '2001:4f8:3:1006::1:4', ['yeti-ns.wide.ad.jp.'] = '2001:200:1d9::35', ['yeti-ns.as59715.net.'] = '2a02:cdc5:9715:0:185:5:203:53', ['dahu1.yeti.eu.org.'] = '2001:4b98:dc2:45:216:3eff:fe4b:8c5b', ['ns-yeti.bondis.org.'] = '2a02:2810:0:405::250', ['yeti-ns.ix.ru .'] = '2001:6d0:6d06::53', ['yeti.bofh.priv.at.'] = '2a01:4f8:161:6106:1::10', ['yeti.ipv6.ernet.in.'] = '2001:e30:1c1e:1::333', ['yeti-dns01.dnsworkshop.org.'] = '2001:1608:10:167:32e::53', ['yeti-ns.conit.co.'] = '2604:6600:2000:11::4854:a010', ['dahu2.yeti.eu.org.'] = '2001:67c:217c:6::2', ['yeti.aquaray.com.'] = '2a02:ec0:200::1', ['yeti-ns.switch.ch.'] = '2001:620:0:ff::29', ['yeti-ns.lab.nic.cl.'] = '2001:1398:1:21::8001', ['yeti-ns1.dns-lab.net.'] = '2001:da8:a3:a027::6', ['yeti-ns2.dns-lab.net.'] = '2001:da8:268:4200::6', ['yeti-ns3.dns-lab.net.'] = '2400:a980:30ff::6', ['ca978112ca1bbdcafac231b39a23dc.yeti-dns.net.'] = '2c0f:f530::6', ['yeti-ns.datev.net.'] = '2a00:e50:f15c:1000::1:53', ['3f79bb7b435b05321651daefd374cd.yeti-dns.net.'] = '2401:c900:1401:3b:c::6', ['xn--r2bi1c.xn--h2bv6c0a.xn--h2brj9c.'] = '2001:e30:1c1e:10::333', ['yeti1.ipv6.ernet.in.'] = '2001:e30:187d::333', ['yeti-dns02.dnsworkshop.org.'] = '2001:19f0:0:1133::53', ['yeti.mind-dns.nl.'] = '2a02:990:100:b01::53:0' })
Au bureau, avec Unbound, cela donnait :
server: auto-trust-anchor-file: "/var/lib/unbound/yeti.key" root-hints: "yeti-hints"
Et yeti-hints
est disponible en
annexe A du RFC (attention, comme le note la section 7 du RFC, à
utiliser une source fiable, et à le récupérer de manière sécurisée).
Comme Yeti s'est engagé à ne pas modifier le contenu de la zone racine (liste des TLD, et serveurs de noms de ceux-ci), et comme Yeti visait à gérer la racine de manière moins concentrée, avec trois organisations (BII, TISF et WIDE) signant et distribuant la racine, le mécanisme adopté a été :
Voici le SOA Yeti :
% dig SOA . ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45919 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ... ;; ANSWER SECTION: . 86400 IN SOA www.yeti-dns.org. bii.yeti-dns.org. ( 2018121600 ; serial 1800 ; refresh (30 minutes) 900 ; retry (15 minutes) 604800 ; expire (1 week) 86400 ; minimum (1 day) ) . 86400 IN RRSIG SOA 8 0 86400 ( 20181223050259 20181216050259 46038 . BNoxqfGq5+rBEdY4rdp8W6ckNK/GAOtBWQ3P36YFq5N+ ... ;; Query time: 44 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Sun Dec 16 16:01:27 CET 2018 ;; MSG SIZE rcvd: 369
(Notez que l'adresse du responsable de la zone indique le
DM qui a été utilisé par ce résolveur particulier. Un autre
résolveur pourrait montrer un autre SOA, si le DM était différent.)
Comme les serveurs racine « officiels » n'envoient pas de message
NOTIFY
(RFC 1996) aux
serveurs Yeti, la seule solution est d'interroger régulièrement
ces serveurs officiels. (Cela fait que Yeti sera toujours un peu
en retard sur la racine « officielle », cf. section 5.2.2.) Plusieurs de ces serveurs acceptent le
transfert de zone (RFC 5936), par exemple
k.root-servers.net
:
% dig @k.root-servers.net AXFR . > /tmp/root.zone % head -n 25 /tmp/root.zone ; <<>> DiG 9.11.3-1ubuntu1.3-Ubuntu <<>> @k.root-servers.net AXFR . ; (2 servers found) ;; global options: +cmd . 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. ( 2018121600 ; serial 1800 ; refresh (30 minutes) 900 ; retry (15 minutes) 604800 ; expire (1 week) 86400 ; minimum (1 day) ) . 172800 IN DNSKEY 256 3 8 ( AwEAAdp440E6Mz7c+Vl4sPd0lTv2Qnc85dTW64j0RDD7 ...
De son côté, l'ICANN gère deux machines qui
acceptent le transfert de zone,
xfr.cjr.dns.icann.org
et
xfr.lax.dns.icann.org
. On peut enfin
récupérer cette zone par FTP.
Pour l'étape de signature de la zone, Yeti a testé plusieurs façons de répartir le travail entre les trois DM (Distribution Masters) :
Dans les deux cas, Yeti supprime la totalité des signatures de la racine « officielle » avant d'apposer la sienne. Il a été suggéré (mais pas testé) d'essayer d'en conserver une partie, pour faciliter la vérification du fait que Yeti n'avait pas ajouté ou retiré de TLD.
La configuration chez les DM et leur usage de git (les risques de sécurité que cela pose sont discutés en section 7) pour se synchroniser quand c'est nécessaire est documentée ici.
Les serveurs racine de Yeti n'ont plus ensuite qu'à récupérer la zone depuis un des DM ; chaque serveur racine peut utiliser n'importe quel DM et en changer, de façon à éviter de dépendre du bon fonctionnement d'un DM particulier. Voici par exemple la configuration d'un serveur NSD :
server: ip-address: 2001:4b98:dc2:45:216:3eff:fe4b:8c5b nsid: "ascii_dahu1.yeti.eu.org" # RFC 8201 ipv6-edns-size: 1460 zone: name: "." outgoing-interface: 2001:4b98:dc2:45:216:3eff:fe4b:8c5b # We use AXFR (not the default, IXFR) because of http://open.nlnetlabs.nl/pipermail/nsd-users/2016-February/002243.html # BII request-xfr: AXFR 240c:f:1:22::7 NOKEY allow-notify: 240c:f:1:22::7 NOKEY # TISF request-xfr: AXFR 2001:4f8:3:1006::1:5 NOKEY allow-notify: 2001:4f8:3:1006::1:5 NOKEY # WIDE request-xfr: AXFR 2001:200:1d9::53 NOKEY allow-notify: 2001:200:1d9::53 NOKEY
Notez que la configuration réseau était un peu plus complexe, la
machine ayant deux interfaces, une de service, pour les requêtes
DNS, et une d'aministration, pour se connecter via
ssh. Il fallait s'assurer que les messages
DNS partent bien par la bonne interface réseau, donc faire du
routage selon l'adresse IP source. Le fichier de configuration
Linux pour cela est
.
yeti-network-setup.sh
Contrairement aux serveurs de la racine « officielle », qui
sont tous sous le domaine root-servers.net
,
ceux de Yeti, ont des noms variés. Le suffixe identique permet,
grâce à la compression des noms (RFC 1035,
section 4.1.4 et RSSAC
023)
de gagner quelques octets sur la taille des messages DNS. Yeti
cherchant au contraire à tester la faisabilité de messages DNS
plus grands, cette optimisation n'était pas utile.
Une des conséquences est que la réponse initiale à un résolveur (RFC 8109) est assez grande :
% dig @bii.dns-lab.net. NS . ... ;; SERVER: 240c:f:1:22::6#53(240c:f:1:22::6) ;; MSG SIZE rcvd: 1591
On voit qu'elle dépasse la MTU
d'Ethernet. Certains serveurs, pour
réduire la taille de cette réponse, n'indiquent pas la totalité
des adresses IP des serveurs racine (la colle) dans la
réponse. (BIND, avec
minimum-responses: yes
n'envoie même aucune
adresse IP, forçant le résolveur à effectuer des requêtes pour les
adresses IP des serveurs). Cela peut augmenter la latence avant
les premières résolutions réussies, et diminuer la robustesse (si
les serveurs dont l'adresse est envoyée sont justement ceux en
panne). Mais cela n'empêche pas le DNS de fonctionner et Yeti,
après discussion, a décidé de ne pas chercher à uniformiser les
réponses des serveurs racine.
Au moment de la publication du RFC, Yeti avait 25 serveurs racine gérés dans 16 pays différents (section 4.6 du RFC), ici vus par check-soa (rappelez-vous qu'ils n'ont que des adresses IPv6) :
% check-soa -i . 3f79bb7b435b05321651daefd374cd.yeti-dns.net. 2401:c900:1401:3b:c::6: OK: 2018121400 (334 ms) bii.dns-lab.net. 240c:f:1:22::6: OK: 2018121400 (239 ms) ca978112ca1bbdcafac231b39a23dc.yeti-dns.net. 2c0f:f530::6: OK: 2018121400 (170 ms) dahu1.yeti.eu.org. 2001:4b98:dc2:45:216:3eff:fe4b:8c5b: OK: 2018121400 (18 ms) dahu2.yeti.eu.org. 2001:67c:217c:6::2: OK: 2018121400 (3 ms) ns-yeti.bondis.org. 2a02:2810:0:405::250: OK: 2018121400 (24 ms) xn--r2bi1c.xn--h2bv6c0a.xn--h2brj9c. 2001:e30:1c1e:10::333: OK: 2018121400 (188 ms) yeti-ns.as59715.net. 2a02:cdc5:9715:0:185:5:203:53: OK: 2018121400 (43 ms) yeti-ns.datev.net. 2a00:e50:f155:e::1:53: OK: 2018121400 (19 ms) yeti-ns.ix.ru. 2001:6d0:6d06::53: OK: 2018121400 (54 ms) yeti-ns.lab.nic.cl. 2001:1398:1:21::8001: OK: 2018121400 (228 ms) yeti-ns.switch.ch. 2001:620:0:ff::29: OK: 2018121400 (16 ms) yeti-ns.tisf.net. 2001:4f8:3:1006::1:4: OK: 2018121400 (175 ms) yeti-ns.wide.ad.jp. 2001:200:1d9::35: OK: 2018121400 (258 ms) yeti-ns1.dns-lab.net. 2400:a980:60ff:7::2: OK: 2018121400 (258 ms) yeti-ns2.dns-lab.net. 2001:da8:268:4200::6: OK: 2018121400 (261 ms) yeti-ns3.dns-lab.net. 2400:a980:30ff::6: OK: 2018121400 (268 ms) yeti.aquaray.com. 2a02:ec0:200::1: OK: 2018121400 (4 ms) yeti.bofh.priv.at. 2a01:4f8:161:6106:1::10: OK: 2018121400 (31 ms) yeti.ipv6.ernet.in. 2001:e30:1c1e:1::333: OK: 2018121400 (182 ms) yeti.jhcloos.net. 2001:19f0:5401:1c3::53: OK: 2018121400 (108 ms) yeti.mind-dns.nl. 2a02:990:100:b01::53:0: OK: 2018121400 (33 ms)
Notez que l'un d'eux a un nom IDN,
मूल.येती.भारत
(affiché par
check-soa
comme
xn--r2bi1c.xn--h2bv6c0a.xn--h2brj9c
). 18 des
serveurs sont des VPS, le reste étant des
machines physiques. 15 utilisent le noyau
Linux, 4 FreeBSD, 1
NetBSD et 1 (oui, oui) tourne sur
Windows. Question logiciel, 16 utilisent
BIND, 4 NSD, 2
Knot, 1 Bundy
(l'ex-BIND 10), 1 PowerDNS et 1
Microsoft DNS.
Pour tester que la racine Yeti fonctionnait vraiment, il ne suffisait évidemment pas de faire quelques dig, check-soa et tests avec les sondes RIPE Atlas. Il fallait un trafic plus réaliste. Certains résolveurs (dont les miens, à la maison et au bureau) ont été configurés pour utiliser la racine Yeti et fournissaient donc un trafic réel, quoique faible. En raison des caches des résolveurs, le trafic réel ne représentait que quelques dizaines de requêtes par seconde. Il était difficile d'augmenter ce nombre, Yeti étant une racine expérimentale, où des choses risquées étaient tentées, on ne pouvait pas utiliser des résolveurs de production. Il a donc fallu aussi injecter du trafic artificiel.
Tout le trafic atteignant les serveurs racines Yeti était capturé (c'est une autre raison pour laquelle on ne pouvait pas utiliser les résolveurs de production ; Yeti voyait toutes leurs requêtes à la racine) et étudié. Pour la capture, des outils comme dnscap ou pcapdump (avec un petit patch) étaient utilisés pour produire des pcap, ensuite copiés vers BII avec rsync.
La section 5 du RFC décrit les problèmes opérationnels qu'a
connu Yeti. Si vous voulez tous les détails, vous pouvez regarder
les
archives de la liste de diffusion du projet, et le blog du
projet. D'abord, ce qui concerne
IPv6. Comme d'habitude, des ennuis sont
survenus avec la fragmentation. En raison
du nombre de serveurs racine, et de l'absence de schéma de nommage
permettant la compression, les réponses Yeti sont souvent assez
grandes pour devoir être fragmentées (1 754 octets avec toutes les
adresses des serveurs racine, et 1 975 avec le mode « une ZSK par
DM »). Cela ne serait pas un problème (la fragmentation des
datagrammes étant spécifiée dans
IPv4 et IPv6 depuis
le début) si tout le monde configurait son réseau
correctement. Hélas, beaucoup d'incompétents et de maladroits ont
configuré leurs systèmes pour bloquer les fragments IP, ou pour
bloquer les messages ICMP nécessaires à la
découverte de la MTU du chemin (RFC 8201). Ce triste état des choses a été décrit dans le
RFC 7872, dans draft-taylor-v6ops-fragdrop
, et dans
« Dealing
with IPv6 fragmentation in the DNS ». Il a même
été proposé de ne jamais
envoyer de datagrammes de taille supérieure à 1 280
octets.
En pratique, le meilleur contournement de ce problème est de réduire la taille maximale des réponses EDNS. Par exemple, dans NSD :
ipv6-edns-size: 1460
Les réponses resteront à moins de 1 460 octets et ne seront donc en général pas fragmentées.
Les transferts de zone depuis les DM ont levé quelques
problèmes. Les zones sont légèrement différentes d'un DM à l'autre
(SOA et surtout signatures). Les transferts de zone
incrémentaux (IXFR, RFC 1995), ne peuvent
donc pas être utilisés : si un serveur racine interroge un DM,
puis un autre, les résultats seront incompatibles. Ce cas, très
spécifique à Yeti, n'est pas pris en compte par les logiciels. Les serveurs
doivent donc utiliser le transfert complet (AXFR) uniquement (d'où
le AXFR
dans la configuration du serveur
racine NSD vue plus haut). Ce
n'est pas très grave, vu la petite taille de la zone racine.
Lors des essais de remplacement de la KSK (on sait que, depuis
la parution de ce RFC, la KSK
de la racine « officielle » a été successivement
remplacée le 11 octobre 2018) quelques problèmes sont
survenus. Par exemple, la documentation de BIND n'indiquait pas,
lorsque le résolveur utilise l'option
managed-keys
, que celle-ci doit être configurée
dans toutes les vues. (Au passage, j'ai toujours trouvé que les vues sont
un système compliqué et menant à des erreurs déroutantes.)
La capture du trafic DNS avec les serveurs racine Yeti a
entrainé d'autres problèmes (section 5.4 du RFC). Il existe
plusieurs façons d'enregistrer le trafic d'un serveur de noms, de
la plus courante (tcpdump avec l'option
-w
) à la plus précise
(dnstap). dnstap étant encore peu répandu
sur les serveurs de noms, Yeti a utilisé une capture « brute » des
paquets, dans des fichiers pcap qu'il
fallait ensuite analyser. L'un des problèmes avec les fichiers
pcap est qu'une connexion TCP, même d'une seule requête, va se
retrouver sur plusieurs paquets, pas forcément consécutifs. Il
faudra donc réassembler ces connexions TCP, par exemple avec un
outil développé pour Yeti, PcapParser
(décrit plus longuement dans l'annexe D de notre RFC).
Les serveurs racine changent de temps en temps. Dans la racine
« officielle », les changements des noms sont très rares. En
effet, pour des raisons politiques, on ne peut pas modifier la
liste des organisations qui gèrent un serveur racine. Vouloir
ajouter ou retirer une organisation déclencherait une crise du
genre « pourquoi lui ? ». L'ICANN est donc
paralysée sur ce point. Mais les serveurs changent parfois
d'adresse IP. C'est rare, mais ça arrive. Si les résolveurs ne
changent pas leur configuration, ils auront une liste
incorrecte. Un exemple de la lenteur avec laquelle se diffusent
les changements d'adresses IP des serveurs racine est le cas de
j.root-servers.net
qui, treize
ans après son changement d'adresse IP, continue
à recevoir du trafic à l'ancienne adresse. Ceci dit, ce
n'est pas très grave en pratique, car, à l'initialisation du
résolveur (RFC 8109), le résolveur reçoit du
serveur racine consulté une liste à jour. Tant que la liste qui
est dans la configuration du résolveur ne dévie pas trop de la
vraie liste, il n'y a pas de problème, le résolveur finira par
obtenir une liste correcte.
Mais Yeti est différent : les changements sont beaucoup plus
fréquents et, avec eux, le risque que la liste connue par les
résolveurs dévie trop. D'où la création d'un outil spécial, hintUpdate
(personnellement, je ne l'ai jamais utilisé, je modifie la
configuration du résolveur, c'est tout). Un point intéressant
d'hintUpdate est qu'il dépend de DNSSEC
pour vérifier les informations reçues. Cela marche avec Yeti, où
les noms des serveurs racine sont (théoriquement) signés, mais cela ne
marcherait pas avec la racine officielle,
root-servers.net
n'étant pas signé.
Dernier problème, et rigolo, celui-ci, la compression inutile. En utilisant le logiciel Knot pour un serveur racine, nous nous sommes aperçus qu'il comprimait même le nom de la zone racine, faisant passer sa taille de un à deux octets. Une compression négative donc, légale mais inutile. À noter que cela plantait la bibliothèque Go DNS. Depuis, cette bibliothèque a été rendue plus robuste, et Knot a corrigé cette optimisation ratée.
La conclusion du RFC, en section 6, rappelle l'importance de disposer de bancs de test, puisqu'on ne peut pas faire courir de risques à la racine de production. La conclusion s'achève en proposant de chercher des moyens de rendre le DNS moins dépendant de la racine actuelle. Et, vu la mode actuelle, le mot de chaîne de blocs est même prononcé…
Date de publication du RFC : Janvier 2019
Auteur(s) du RFC : J. Abley (Afilias), O. Gudmundsson, M. Majkowski (Cloudflare), E. Hunt (ISC)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 11 janvier 2019
Lorsqu'un client DNS envoie une requête à un serveur DNS, le client indique le type de données souhaité. Contrairement à ce qu'on lit souvent, le DNS ne sert pas à « traduire des noms de domaine en adresses IP ». Le DNS est une base de données généraliste, qui sert pour de nombreux types de données. Outre les types (AAAA pour les adresses IP, SRV pour les noms de serveurs assurant un service donné, SSHFP ou TLSA pour les clés cryptographiques, etc), le DNS permet de demander tous les types connus du serveur pour un nom de domaine donné ; c'est ce qu'on appelle une requête ANY. Pour différentes raisons, l'opérateur du serveur DNS peut ne pas souhaiter répondre à ces requêtes ANY. Ce nouveau RFC spécifie ce qu'il faut faire dans ce cas.
Les requêtes comme ANY, qui n'utilisent pas un type spécifique, sont souvent informellement appelées « méta-requêtes ». Elles sont spécifiées (mais de manière un peu ambigüe) dans le RFC 1035, section 3.2.3. On note que le terme « ANY » n'existait pas à l'époque, il est apparu par la suite.
Pourquoi ces requêtes ANY défrisent-elles certains
opérateurs de serveurs DNS ? La section 2 de notre RFC explique
ce choix. D'abord, quelle était l'idée derrière ces requêtes
ANY ? La principale motivation était de débogage : ANY n'est pas
censé être utilisé dans la cadre du fonctionnement normal du DNS,
mais lorsqu'on veut creuser un problème, vérifier l'état d'un
serveur. Voici un exemple de requête ANY envoyée au serveur
faisant autorité pour le nom de domaine
anna.nic.fr
:
% dig +nodnssec @ns1.nic.fr ANY anna.nic.fr ... ;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 5, ADDITIONAL: 10 ... ;; ANSWER SECTION: anna.nic.fr. 600 IN A 192.134.5.10 anna.nic.fr. 172800 IN TXT "EPP prod" anna.nic.fr. 600 IN AAAA 2001:67c:2218:e::51:41 ... ;; Query time: 2 msec ;; SERVER: 2001:67c:2218:2::4:1#53(2001:67c:2218:2::4:1) ...
Le serveur 2001:67c:2218:2::4:1
a renvoyé
les données pour trois types, A (adresse IPv4), AAAA (adresse
IPv6) et TXT (un commentaire).
Certains programmeurs comprenant mal le DNS ont cru qu'ils pouvaient utiliser les requêtes ANY pour récupérer à coup sûr toutes les données pour un nom (ce fut par exemple une bogue de qmail). Voyons d'abord si ça marche, en essayant le même nom avec un autre serveur DNS :
% dig @::1 ANY anna.nic.fr ... ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ... ;; ANSWER SECTION: anna.nic.fr. 595 IN AAAA 2001:67c:2218:e::51:41 ... ;; Query time: 0 msec ;; SERVER: ::1#53(::1)
On n'a cette fois récupéré que l'adresse IPv6 et pas les trois enregistrements. C'est parce que cette fois, le serveur interrogé était un résolveur, pas un serveur faisant autorité. Il a bien répondu en donnant toutes les informations qu'il avait. Simplement, il ne connaissait pas tous les types possibles. Ce comportement est le comportement normal de ANY ; ANY (n'importe lesquelles) ne veut pas dire ALL (toutes). Il veut dire « donne-moi tout ce que tu connais ». Il n'y a jamais eu de garantie que la réponse à une requête ANY contiendrait toutes les données (c'est la bogue fondamentale de l'usage DNS de qmail). Si la réponse, comme dans l'exemple précédent, n'a pas de données de type A, est-ce que cela veut dire qu'il n'y a pas de telles données, ou simplement que le serveur ne les connaissait pas ? On ne peut pas savoir. Bref, il ne faut pas utiliser les requêtes ANY vers un résolveur ou, plus exactement, il ne faut pas compter sur le fait que cela vous donnera toutes les données pour ce nom. ANY reste utile pour le débogage (savoir ce que le résolveur a dans le cache) mais pas plus.
Et vers un serveur faisant autorité, est-ce que là, au moins, on a une garantie que cela va marcher ? Même pas car certains serveurs faisant autorité ne donnent qu'une partie des données, voire rien du tout, pour les raisons exposées au paragraphe suivant.
En effet, les requêtes ANY ont des inconvénients. D'abord, elles peuvent être utilisées pour des attaques par réflexion, avec amplification. La réponse, si le serveur envoie toutes les données possibles, va être bien plus grosse que la question, assurant une bonne amplification (cf. RFC 5358, et section 8 de notre nouveau RFC). Bien sûr, ANY n'est pas le seul type de requête possible pour ces attaques (DNSKEY ou NS donnent également de « bons » résultats.)
Ensuite, les requêtes ANY peuvent permettre de récupérer facilement toutes les données sur un nom de domaine, ce que certains opérateurs préféreraient éviter. C'est à mon avis l'argument le plus faible, le même effet peut être obtenu avec des requêtes multiples (il y a 65 536 types possibles, mais beaucoup moins en pratique) ou via le passive DNS.
Enfin, avec certaines mises en œuvre des serveurs DNS, récupérer toutes les informations peut être coûteux. C'est par exemple le cas si le dorsal du serveur est un SGBD où les données sont accessibles uniquement via la combinaison {nom, type}.
Bref, il est légitime que le gérant d'un serveur DNS veuille bloquer les requêtes ANY. Mais que doit-il répondre dans ce cas ? Ne pas répondre du tout, comme le font certains pare-feux programmés et configurés avec les pieds n'est pas une solution, le client réémettra, gaspillant des ressources chez tout le monde. Notre RFC suggère un choix de trois méthodes (section 4) :
Toutes ces réponses sont compatibles avec le protocole existant. Le RFC 1035, section 3.2.3, est relativement clair à ce sujet : ANY n'est pas la même chose que ALL (section 7 de notre RFC). Notez que notre nouveau RFC n'impose pas une politique particulière ; ce RFC ne dit pas qu'il faut renvoyer la réponse courte, il dit « si vous le faites, faites-le avec une des trois méthodes indiquées ».
Notez que le comportement du serveur peut dépendre de si la question était posée sur UDP ou sur TCP (section 4.4 du RFC). En effet, avec TCP, le risque d'attaque par réflexion est très faible.
Voici un exemple chez Cloudflare, la société qui a le plus « poussé » pour ce RFC :
% dig +nodnssec @ns5.cloudflare.com. ANY cloudflare.com ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54605 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ... ;; ANSWER SECTION: cloudflare.com. 3789 IN HINFO "ANY obsoleted" "See draft-ietf-dnsop-refuse-any" ;; Query time: 4 msec ;; SERVER: 2400:cb00:2049:1::a29f:209#53(2400:cb00:2049:1::a29f:209) ;; WHEN: Wed Dec 19 16:05:12 CET 2018 ;; MSG SIZE rcvd: 101
Le client recevant ces réponses les mémorise de la manière classique. S'il trouve un HINFO, il peut décider de l'utiliser pour répondre aux requêtes ANY ultérieures. (C'est un changement de la sémantique du type HINFO mais pas grave, ce type étant très peu utilisé.)
Mais pourquoi HINFO, type qui était normalement prévu pour donner des informations sur le modèle d'ordinateur et sur son système d'exploitation (RFC 1035, section 3.3.2) ? La section 6 du RFC traite cette question. Le choix de réutiliser (en changeant sa sémantique) un type existant était dû au fait que beaucoup de boitiers intermédiaires bogués refusent les types DNS qu'ils ne connaissent pas (un nouveau type NOTANY avait été suggéré), rendant difficile le déploiement d'un nouveau type. HINFO est très peu utilisé, donc considéré comme « récupérable ». Ce point a évidemment fait l'objet de chaudes discussions à l'IETF, certains étant choqués par cette réutilisation sauvage d'un type existant. Le type NULL avait été proposé comme alternative mais l'inconvénient est qu'il n'était pas affiché de manière lisible par les clients DNS actuels, contrairement à HINFO, qui permet de faire passer un message, comme dans l'exemple Cloudflare ci-dessus.
Un HINFO réel dans une réponse peut être mémorisé par le résolveur et empêcher certaines requêtes ANY ultérieures. De la même façon, le HINFO synthétique généré en réponse à une requête ANY peut masquer des vrais HINFO. Attention, donc, si vous avez des HINFO réels dans votre zone, à ne pas utiliser ce type dans les réponses aux requêtes ANY.
Mais les HINFO réels sont rares. En janvier 2017, en utilisant
la base DNSDB, je n'avais trouvé que
54 HINFO sur les trois millions de noms de
.fr
, et la plupart
n'étaient plus dans le DNS. Les meilleurs
étaient :
iiel.iie.cnam.fr. IN HINFO "VS3100" "VMS-6.2" wotan.iie.cnam.fr. IN HINFO "AlphaServer-1000" "OSF"
Il y a peu de chance qu'une VaxStation 3100 soit encore en service en 2017 :-) Autres HINFO utilisés de façon créative :
www-cep.cma.fr. IN HINFO "bat. B" "" syndirag.dirag.meteo.fr. IN HINFO "VM Serveur Synergie 1 operationnel" "RHEL 5.4" www.artquid.fr. IN HINFO "Artquid" "ArtQuid, La place de marche du Monde de l'Art (Antiquites, Objets d'art, Art contemporain et Design)"
Le HINFO de syndirag.dirag.meteo.fr
est
toujours en ligne et illustre très bien une raison pour laquelle
les HINFO sont peu utilisés : il est pénible de les maintenir à
jour (la machine n'est probablement plus en
RHEL 5.4).
Notons que d'autres solutions avaient été étudiées à l'IETF pendant la préparation de ce RFC (section 3) :
Le choix a donc été fait de renvoyer quelque chose, afin que le client s'arrête là, et qu'il puisse garder quelque chose dans sa mémoire (cache).
On notera que cela laisse entier le problème du client qui voudrait récupérer, par exemple, adresse IPv4 (A) et IPv6 (AAAA) avec une seule requête. Plusieurs approches ont été proposées à l'IETF mais aucune adoptée.
Les techniques de ce RFC sont-elles disponibles ?
NSD avait depuis sa
version 4.1 une option
refuse-any
, mais pas conforme au RFC (elle répond avec le bit TC indiquant la troncature, ce que le
RFC refuse explicitement). Cela semble s'être amélioré avec la
version 4.1.27 publiée en mars 2019. BIND a depuis la
version 9.11 une option
minimal-any
qui, elle, est conforme. En
mettant minimal-any yes;
dans la
configuration, BIND répondre aux requêtes ANY avec un seul
enregistrement. BIND n'utilise donc pas la solution « HINFO » mais
le RFC permet ce choix.
Date de publication du RFC : Octobre 2018
Auteur(s) du RFC : A. Mayrhofer (nic.at GmbH)
Expérimental
Réalisé dans le cadre du groupe de travail IETF dprive
Première rédaction de cet article le 2 décembre 2018
Chiffrer pour assurer la confidentialité, c'est bien. Pour le DNS, c'est ce que permet le RFC 7858 (DNS sur TLS). Mais un problème de TLS et de pas mal d'autres protocoles cryptographiques est qu'il ne dissimule pas les métadonnées, et notamment la taille des messages échangés sur le réseau. Dans un monde public comme celui du DNS, c'est un problème. En effet, l'attaquant peut facilement mesurer la taille des réponses chiffrées (en envoyant lui-même une requête), voir la taille des réponses, et en déduire les questions qui avaient été posées. La solution classique en cryptographie face à ce risque est le remplissage, normalisé, pour le DNS, dans le RFC 7830. Mais le RFC 7830 ne normalisait que le format, pas le mode d'emploi. Il faut remplir jusqu'à telle taille ? Comment concilier un remplissage efficace pour la confidentialité avec le désir de limiter la consommation de ressources réseaux ? Ce RFC décrit plusieurs stratégies possibles, et recommande un remplissage jusqu'à atteindre une taille qui est le multiple suivant de 468 (octets).
Ce nouveau RFC tente de répondre à ces questions, en exposant les différentes politiques possibles de remplissage, leurs avantages et leurs inconvénients. Le RFC 7830 se limitait à la syntaxe, notre nouveau RFC 8467 étudie la sémantique.
D'abord, avant de regarder les politiques possibles, voyons les choses à garder en tête (section 3 du RFC). D'abord, ne pas oublier de mettre l'option EDNS de remplissage (celle du RFC 7830) en dernier dans la liste des options (car elle a besoin de connaitre la taille du reste du message).
Ensuite, il faut être conscient des compromis à faire. Remplir va améliorer la confidentialité mais va réduire la durée de vie de la batterie des engins portables, va augmenter le débit qu'on injecte dans le réseau, voire augmenter le prix si on paie à l'octet transmis. Lors des discussions à l'IETF, certaines personnes ont d'ailleurs demandé si le gain en confidentialité en valait la peine, vu l'augmentation de taille. En tout cas, on ne remplit les messages DNS que si la communication est chiffrée : cela ne servirait à rien sur une communication en clair.
Enfin, petit truc, mais qui montre l'importance des détails quand on veut dissimuler des informations, le remplissage doit se faire sans tenir compte des deux octets qui, avec certains protocoles de transport du DNS, comme TCP, peut faire fuiter des informations. Avec certaines stratégies de remplissage, les deux octets en question peuvent faire passer de l'autre côté d'un seuil et donc laisser fuiter l'information qu'on était proche du seuil.
Ensuite, après ces préliminaires, passons aux stratégies de remplissage, le cœur de ce RFC (section 4). Commençons par celle qui est la meilleure, et recommandée officiellement par notre RFC : remplissage en blocs de taille fixe. Le client DNS remplit la requête jusqu'à atteindre un multiple de 128 octets. Le serveur DNS, si le client avait mis l'option EDNS de remplissage dans la requête, et si la communication est chiffrée, remplit la réponse de façon à ce qu'elle soit un multiple de 468 octets. Ainsi, requête et réponse ne peuvent plus faire qu'un nombre limité de longueurs, la plupart des messages DNS tenant dans le bloc le plus petit. Voici un exemple vu avec le client DNS dig, d'abord sans remplissage :
% dig +tcp +padding=0 -p 9053 @127.0.0.1 SOA foobar.example ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29832 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ... ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ; COOKIE: dda704b2a06d65b87f0493105c03ca4d2b2c83f2d4e25680 (good) ;; QUESTION SECTION: ;foobar.example. IN SOA ;; ANSWER SECTION: foobar.example. 600 IN SOA ns1.foobar.example. root.foobar.example. ( 2015091000 ; serial 604800 ; refresh (1 week) 86400 ; retry (1 day) 2419200 ; expire (4 weeks) 86400 ; minimum (1 day) ) ... ;; MSG SIZE rcvd: 116
La réponse fait 116 octets. On demande maintenant du remplissage, jusqu'à 468 octets :
% dig +tcp +padding=468 -p 9053 @127.0.0.1 SOA foobar.example ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2117 ;; 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: 4096 ; COOKIE: 854d2f29745a72e5fdd6891d5c03ca4b5d5287daf716e327 (good) ; PAD (348 bytes) ;; QUESTION SECTION: ;foobar.example. IN SOA ;; ANSWER SECTION: foobar.example. 600 IN SOA ns1.foobar.example. root.foobar.example. ( 2015091000 ; serial 604800 ; refresh (1 week) 86400 ; retry (1 day) 2419200 ; expire (4 weeks) 86400 ; minimum (1 day) ) ;; MSG SIZE rcvd: 468
La réponse fait 468 octets, grâce aux 348 octets de remplissage
(notez la ligne PAD (348 bytes)
).
Les avantages de cette méthode est qu'elle est facile à mettre en œuvre, assure une confidentialité plutôt bonne, et ne nécessite pas de générateur de nombres aléatoires. Son principal inconvénient est qu'elle permet de distinguer deux requêtes (ou deux réponses) si elles ont le malheur d'être remplies dans des blocs de taille différente. Mais il ne faut pas chercher une méthode idéale : rappelez-vous qu'il faudra faire des compromis. Cette méthode a un faible coût pour le défenseur, et élève les coûts sensiblement pour l'attaquant, c'est ça qui compte.
Notez que les chiffres 128 et 468 ont été obtenus empiriquement, en examinant du trafic DNS réel. Si DNSSEC continue à se répandre, les tailles des réponses moyennes augmenteront, et il faudra peut-être réviser ces chiffres.
Une autre statégie est celle du remplissage maximal. On met autant d'octets qu'on peut. Si un serveur a une taille maximale de réponse de 4 096 octets (la valeur par défaut la plus courante) et que le client accepte cette taille, on remplit la réponse jusqu'à ce qu'elle fasse 4 096 octets. L'avantage évident de cette méthode est qu'elle fournit la meilleure confidentialité : toutes les réponses ont la même taille. L'inconvénient évident est qu'elle est la méthode la plus consommatrice de ressources. En outre, ces grandes réponses vont souvent excéder la MTU, pouvant entrainer davantage de problèmes liés à la fragmentation.
Autre stratégie envisageable : remplissage aléatoire. On tire au sort le nombre d'octets à ajouter. Cela fournit une bonne distribution des tailles (par exemple, une réponse courte peut désormais être plus grande qu'une réponse longue, ce qui n'arrive jamais avec les deux stratégies précédentes). Inconvénient : comme ça ne change pas la limite de taille inférieure, un attaquant qui voit beaucoup de messages pourrait en déduire des informations. Et cela oblige à avoir un générateur de nombres aléatoires, traditionnellement un problème délicat en cryptographie.
Enfin, une dernière méthode raisonnable est de combiner le remplissage dans des blocs et le tirage au sort : on choisit au hasard une longueur de bloc et on remplit jusqu'à atteindre cette longueur. Contrairement à la précédente, elle n'a pas forcément besoin d'une source aléatoire à forte entropie. Mais c'est sans doute la technique la plus compliquée à mettre en œuvre.
La section 7 du RFC ajoute quelques points supplémentaires qui peuvent mettre en péril la confidentialité des requêtes. Par exemple, si le client DNS remplit correctement sa requête, mais que le serveur ne le fait pas, un attaquant pourra déduire la requête de la réponse (c'est d'autant plus facile, avec le DNS, que la question est répétée dans la réponse). Dans une communication de client à résolveur DNS, il faut bien choisir son résolveur.
Et le remplissage ne brouille qu'une seule des métadonnées. Il y en a d'autres comme l'heure de la question, le temps de réponse ou comme la succession des requêtes/réponses, qui restent accessibles à un éventuel attaquant. La protection contre la fuite d'informations via ces métadonnées nécessiterait d'injecter « gratuitement » du trafic de couverture (qui, lui aussi, éleverait la consommation de ressources réseau).
Et pour terminer le RFC, l'annexe A est consacrée aux mauvaises politiques de remplissage, celles qui non seulement ne sont pas recommandées mais sont activement déconseillées. (Mais on les trouve parfois dans du code réel.) Il y a l'évidente stratégie « pas de remplissage du tout ». Son principal intérêt est qu'elle fournit le point de comparaison pour toutes les autres stratégies. Avantages : triviale à implémenter, il suffit de ne rien faire, et aucune consommation de ressources supplémentaires. Inconvénient : la taille des requêtes et des réponses est exposée, et un observateur malveillant peut en déduire beaucoup de choses.
Une autre méthode inefficace pour défendre la vie privée est celle du remplissage par une longueur fixe. Elle est simple à implémenter mais ne protège rien : une simple soustraction suffit pour retrouver la vraie valeur de la longueur.
Ces différentes stratégies ont été analysées empiriquement (il n'y a pas vraiment de bon cadre pour le faire théoriquement) et le travail est décrit dans l'excellente étude de Daniel Kahn Gillmor (ACLU), « Empirical DNS Padding Policy » présentée à NDSS en 2017. Si vous aimez les chiffres et les données, c'est ce qu'il faut regarder !
Testons un peu les mises en œuvres du remplissage des messages DNS (une liste plus complète figure sur le site du projet).
Essayons avec BIND version 9.13.4. Il
fournit le client de débogage dig et son
option +padding
. Avec un
+padding=256
, le
datagramme va faire 264 octets, incluant
les ports source et
destination d'UDP). Vu par tshark, cela donne :
<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: 213 Option: COOKIE Option Code: COOKIE (10) Option Length: 8 Option Data: 722ffe96cd87b40a Client Cookie: 722ffe96cd87b40a Server Cookie: <MISSING> Option: PADDING Option Code: PADDING (12) Option Length: 197 Option Data: 000000000000000000000000000000000000000000000000... Padding: 000000000000000000000000000000000000000000000000...
Cela, c'était la requête du client. Mais cela ne veut pas dire que le serveur va accepter de répondre avec du remplissage. D'abord, il faut qu'il soit configuré pour cela (BIND 9.13.4 ne le fait pas par défaut). Donc, côté serveur, il faut :
options { ... response-padding {any;} block-size 468; };
(any
est pour accepter le remplissage pour
tous les clients.) Mais cela ne suffit pas, BIND ne répond avec du
remplissage que si l'adresse IP source est raisonnablement sûre
(TCP ou biscuit du RFC 7873, pour éviter les attaques par amplification). C'est pour cela qu'il y a une option
+tcp
dans les appels de dig plus haut. On
verra alors dans le résultat de dig le PAD (392
bytes)
indiquant qu'il y a eu remplissage. La taille
indiquée dans response-padding
est une taille
de bloc : BIND enverra des réponses qui seront un multiple de cette
taille. Par exemple, avec response-padding {any;}
block-size 128;
, une courte réponse est remplie à 128
octets (notez que la taille de bloc n'est pas la même chez le
serveur et chez le client) :
% dig +tcp +padding=468 -p 9053 @127.0.0.1 SOA foobar.example ... ; PAD (8 bytes) ... ;; MSG SIZE rcvd: 128
Alors qu'une réponse plus longue (notez la question
ANY
au lieu de SOA
) va
faire passer dans la taille de bloc au
dessus (128 octets est clairement une taille de bloc trop petite,
une bonne partie des réponses DNS, même sans
DNSSEC, peuvent la dépasser) :
% dig +tcp +padding=468 -p 9053 @127.0.0.1 ANY foobar.example ... ; PAD (76 bytes) ... ;; MSG SIZE rcvd: 256
On voit bien ici l'effet de franchissement du seuil : une taille de bloc plus grande doit être utilisée.
BIND n'est pas forcément que serveur DNS, il peut être client,
quand il est résolveur et parle aux serveurs faisant autorité. La
demande de remplissage dans ce cas se fait dans la configuration
par serveur distant, avec padding
. (Je n'ai
pas testé.)
Le résolveur Knot fait également le
remplissage (option net.tls_padding
) mais, contrairement à BIND, il ne le fait que lorsque
le canal de communication est chiffré (ce qui est logique).
La bibliothèque pour développer des clients DNS getdns a également le remplissage. En revanche, Unbound, dans sa version 1.8.1, ne fait pas encore de remplissage. Et, comme vous avez vu dans l'exemple tshark plus haut, Wireshark sait décoder l'option de remplissage.
Date de publication du RFC : Septembre 2018
Auteur(s) du RFC : D. Margolis, M. Risher (Google), B. Ramakrishnan (Oath), A. Brotman (Comcast), J. Jones (Microsoft)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF uta
Première rédaction de cet article le 9 janvier 2019
La question de la sécurité du courrier électronique va probablement amener à la publication de nombreux autres RFC dans les années qui viennent… Ce nouveau RFC traite du problème de la sécurisation de SMTP avec TLS ; c'est très bien d'avoir SMTP-sur-TLS, comme le font aujourd'hui tous les MTA sérieux. Mais comment le client SMTP qui veut envoyer du courrier va-t-il savoir si le serveur en face gère TLS ? Et si on peut compter dessus, refusant de se connecter si TLS a un problème ? Notre RFC apporte une solution, publier dans le DNS un enregistrement texte qui va indiquer qu'il faut télécharger (en HTTP !) la politique de sécurité TLS du serveur. En la lisant, le client saura à quoi s'attendre. Ne cherchez pas de déploiement de cette technique sur le serveur qui gère mon courrier électronique, je ne l'ai pas fait, pour les raisons expliquées plus loin.
Cette solution se nomme STS, pour Strict Transport Security. La section 1 du RFC explique le modèle de menace auquel répond cette technique. Sans TLS, une session SMTP peut être facilement écoutée par des espions et, pire, interceptée et modifiée. TLS est donc indispensable pour le courrier, comme pour les autres services Internet. À l'origine (RFC 3207), la méthode recommandée pour faire du TLS était dite STARTTLS et consistait, pour le MTA contacté, à annoncer sa capacité TLS puis, pour le MTA qui appelle, à démarrer la négociation TLS s'il le souhaitait, et si le serveur en face avait annoncé qu'il savait faire. Une telle méthode est très vulnérable aux attaques par repli, ici connues sous le nom de SSL striping ; un attaquant actif peut retirer l'indication STARTTLS du message de bienvenue du MTA contacté, faisant croire au MTA appelant que TLS ne sera pas possible.
D'autre part, un très grand nombre de serveurs SMTP ont des certificats expirés, ou auto-signés, qui ne permettent pas d'authentifier sérieusement le MTA qu'on appelle. En cas de détournement BGP ou DNS, on peut se retrouver face à un mauvais serveur et comment le savoir, puisque les serveurs authentiques ont souvent un mauvais certificat. Le MTA qui se connecte à un autre MTA est donc confronté à un choix difficile : si le certificat en face est auto-signé, et donc impossible à évaluer, est-ce parce que l'administrateur de ce MTA est négligent, ou bien parce qu'il y a un détournement ? Pour répondre à cette question, il faut un moyen indépendant de connaitre la politique du MTA distant. C'est ce que fournit notre RFC (c'est aussi ce que fournit DANE, en mieux).
À noter que ce RFC concerne les communications entre MTA. Pour celles entre MUA et MTA, voyez le RFC 8314, qui utilise un mécanisme assez différent, fondé sur le TLS implicite (au contraire du TLS explicite de STARTSSL).
Donc, la technique décrite dans ce RFC, STS, vise à permettre l'expression d'une politique indiquant :
D'abord, découvrir la politique du serveur distant (section 3
du RFC). Cela se fait en demandant un enregistrement
TXT. Si le domaine de courrier est
example.com
, le nom où chercher
l'enregistrement TXT est
_mta-sts.example.com
. Regardons chez
Gmail :
% dig TXT _mta-sts.gmail.com ... ;; ANSWER SECTION: _mta-sts.gmail.com. 300 IN TXT "v=STSv1; id=20171114T070707;"
On peut aussi utiliser le DNS Looking Glass.
Cet enregistrement TXT contient des paires clé/valeur, deux clés sont utilisées :
v
qui indique la version de STS
utilisée, aujourd'hui STSv1
,id
qui indique un numéro de
série (ici 20171114T070707) ; si la politique TLS change, on doit penser à modifier ce
numéro. (Rappelons que la politique est accessible via
HTTP. Dans beaucoup d'organisations, ce
n'est pas la même équipe qui gère le contenu DNS et le contenu
HTTP, et je prévois donc des difficultés opérationnelles.)La présence de cet enregistrement va indiquer qu'une politique TLS pour le MTA est disponible, à récupérer via HTTP.
D'autres clés pourront être créées dans le futur, et mises dans le registre IANA, en suivant la politique « Examen par un expert » du RFC 8126.
Cette politique doit se trouver en (toujours en supposant que
le domaine est example.com
)
https://mta-sts.example.com/.well-known/mta-sts.txt
,
et avoir le type text/plain
. (Le
.well-known
est décrit dans le RFC 8615.) Le nom
mta-sts.txt
a été ajouté dans le
registre IANA. Essayons de récupérer la politique de Gmail,
puisqu'ils ont l'enregistrement TXT :
% curl https://mta-sts.gmail.com/.well-known/mta-sts.txt version: STSv1 mode: testing mx: gmail-smtp-in.l.google.com mx: *.gmail-smtp-in.l.google.com max_age: 86400
On voit que les politiques sont également décrites sous formes de
paires clé/valeur, mais avec le deux-points
au lieu du égal. On voit aussi que
l'utilisation de STS (Strict Transport
Security) nécessite un nom spécial
(mta-sts
, qui n'a pas de
tiret bas car il s'agit d'un nom de machine) ce qui peut entrer en conflit avec
des politiques de nommages locales, et est en général mal
perçu. (Cela a fait râler.) L'utilisation des
enregistrements de service aurait évité
cela mais ils n'ont pas été retenus.
Les clés importantes sont :
version
: aujourd'hui
STSv1
,mode
: indique au MTA émetteur ce
qu'il doit faire si TLS échoue. Il y a le choix entre
none
(continuer comme si de rien n'était),
testing
(signaler les problèmes
- cf. section 6 - mais
continuer, c'est le choix de Gmail dans la politique ci-dessus) et enforce
(refuser d'envoyer le
message en cas de problème, c'est le seul mode qui protège
contre les attaques par repli).max_age
: durée de vie de la
politique en question, on peut la mémoriser pendant ce nombre de
secondes (une journée pour Gmail),mx
: les serveurs de messagerie de ce
domaine.D'autres clés pourront être ajoutées dans le même registre IANA, avec la politique d'examen par un expert (cf. RFC 8126.) Un exemple de politique figure dans l'annexe A du RFC.
(Notez qu'au début du processus qui a mené à ce RFC, la politique était décrite en JSON. Cela a été abandonné au profit d'un simple format « clé: valeur » car JSON n'est pas habituel dans le monde des MTA, et beaucoup de serveurs de messagerie n'ont pas de bibliothèque JSON incluse.)
La politique doit être récupérée en
HTTPS (RFC 2818)
authentifié (RFC 6125), le serveur doit donc
avoir un certificat valide pour
mta-sts.example.com
. L'émetteur doit utiliser
SNI (Server Name Indication, RFC 6066), et parler au moins TLS 1.2. Attention, le client
HTTPS ne doit pas suivre les redirections, et ne doit pas utiliser
les caches Web du RFC 9111.
Notez bien qu'une politique ne s'applique qu'à un domaine, pas
à ses sous-domaines. La politique récupérée en
https://mta-sts.example.com/
ne nous dit rien
sur les serveurs de messagerie du domaine
something.example.com
.
Une fois que l'émetteur d'un courrier connait la politique du récepteur, il va se connecter en SMTP au MTA du récepteur et vérifier (section 4) que :
mx
dans la politique,
L'exigence que le certificat soit signé par une
AC connue de l'émetteur est la raison pour
laquelle je ne déploie pas STS sur mon domaine
bortzmeyer.org
; ses serveurs de messagerie
utilisent des certificats signés par CAcert, et beaucoup de MTA n'ont pas CAcert
dans le magasin qu'ils utilisent. Exiger un certificat signé par
une AC connue de l'émetteur pousse à la concentration vers
quelques grosses AC, puisque c'est la seule façon d'être
raisonnablement sûr qu'elle soit connue partout. Une meilleure
solution que STS, et celle que j'utilise, est d'utiliser
DANE (RFC 7672). Mais STS est issu
de chez Google, qui pousse toujours aux
solutions centralisées, avec un petit nombre d'AC
officielles. (Cf. annonce
initiale où Google dit clairement « nous avons une
solution, nous allons la faire normaliser ».)
Et si les vérifications échouent ? Tout dépend de la valeur
liée à la clé mode
dans la politique. Si elle
vaut enforce
, l'émetteur renonce et le
message n'est pas envoyé, si le mode est
testing
ou none
, on
envoie quand même. Dans le cas testing
,
l'émetteur tentera de signaler au récepteur le problème (section
6), en utilisant le RFC 8460. C'est donc le
récepteur, via la politique qu'il publie, qui décide du sort du
message. L'annexe B du RFC donne, en
pseudo-code, l'algorithme complet.
La section 8 de notre RFC rassemble quelques considérations
pratiques pour celles et ceux qui voudraient déployer cette
technique STS. D'abord, le problème du changement de
politique. Lorsqu'on modifie sa politique, il y a deux endroits à
changer, les données envoyées par le serveur HTTP, et
l'enregistrement TXT dans le DNS (pour modifier la valeur associée
à id
). Deux changements veut dire qu'il y a
du potentiel pour une incohérence, si on modifie un des endroits
mais pas l'autre, d'autant plus que ces deux endroits peuvent être
sous la responsabilité d'équipes différentes. Et l'enregistrement
TXT est soumis au TTL du DNS. Donc,
attention à changer la politique publiée en HTTPS avant de changer
l'enregistrement DNS. Et il faut faire en sorte que les deux
politiques fonctionnent car certains envoyeurs auront l'ancienne.
Un cas courant en matière de courrier électronique est la
délégation de l'envoi du courrier massif à un « routeur » (rien à
voir avec les routeurs
IP). Ainsi, les newsletters, dont le
service marketing croit qu'elles servent à quelque chose, sont
souvent déléguées à un spammeur professionnel, le
« routeur ». Dans ce cas, il faut aussi leur déléguer la
politique, et le _mta-sts
dans le DNS doit
donc être un alias pointant vers un nom du routeur. Idem pour le
nom mta-sts
qui pointe vers le serveur HTTP
où récupérer la politique. Cela doit être un alias DNS, ou bien un
reverse proxy HTTP (pas une redirection HTTP,
prohibée par le RFC).
Notre RFC se termine par la section 10, qui détaille quelques points de sécurité. Comme toute la sécurité de STS dépend du certificat PKIX qui est présenté à l'émetteur du courrier (cf. RFC 6125), la sécurité de l'écosystème X.509 est cruciale. Si une AC délivre un faux certificat (comme c'est arrivé souvent), l'authentification TLS ne protégera plus.
Autre problème potentiel, le DNS. Sans DNSSEC, il est trop facile de tromper un résolveur DNS et, par exemple, de lui dire que l'enregistrement TXT n'existe pas et qu'il n'y a donc pas de politique STS. La bonne solution est évidemment de déployer DNSSEC, mais le RFC donne également quelques conseils pratiques pour ceux et celles qui s'obstinent à ne pas avoir de DNSSEC. (Celles et ceux qui ont DNSSEC ont de toute façon plutôt intérêt à utiliser DANE.) Notamment, il est recommandé que le serveur émetteur mémorise ce que faisait le récepteur et se méfie si la politique disparait (une forme de TOFU).
STS n'est pas la première technique qui vise à atteindre ces
buts. La section 2 de notre RFC en présente d'autres. La
principale est évidemment DANE (RFC 7672), celle que j'ai choisi de déployer pour
le service de courrier de
bortzmeyer.org
. DANE nécessite
DNSSEC, et le RFC présente comme un
avantage que la solution qu'il décrit ne nécessite pas DNSSEC
(c'est une erreur car, sans DNSSEC, la solution de ce RFC marche
mal puisqu'un attaquant pourrait prétendre que l'enregistrement
texte n'existe pas, ou bien lui donner une autre valeur). Par
contre, il est exact que STS permet un mode « test
seulement » que ne permet pas DANE.
Mais ce n'est pas un argument suffisant pour moi.
Qui met en œuvre STS aujourd'hui ? On a vu que Gmail le faisait. Yahoo a également une politique STS :
% dig +short TXT _mta-sts.yahoo.com "v=STSv1; id=20161109010200Z;"
Elle est en testing
, comme celle de Gmail (je
n'ai encore rencontré aucun domaine qui osait utiliser
enforce
). Comme illustration du fait que la
publication de la politique nécessitant deux
endroits (DNS et HTTP) est casse-gueule, regardons chez
Microsoft :
% dig +short TXT _mta-sts.outlook.com "v=STSv1; id=20180321T030303;"
Mais en HTTP, on récupère un 404 (ressource non existante) sur la politique…
ProtonMail n'a rien, pour l'instant STS semble uniquement sur les gros silos centralisés :
% dig TXT _mta-sts.protonmail.com ; <<>> DiG 9.11.3-1ubuntu1.3-Ubuntu <<>> TXT _mta-sts.protonmail.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 12470 ...
Et question logiciels libres de MTA, qu'est-ce qui existe ? Voyons Postfix ; il y a deux cas à distinguer :
Une fois tout cela configuré, il existe un bon
outil de test en
ligne pour voir si votre politique STS est
correcte. Essayez-le avec outlook.com
pour
rire un peu.
Quelques articles sur STS :
Date de publication du RFC : Août 2018
Auteur(s) du RFC : E. Rescorla (RTFM)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 11 août 2018
Après un très long processus, et d'innombrables polémiques, la nouvelle version du protocole de cryptographie TLS, la 1.3, est enfin publiée. Les changements sont nombreux et, à bien des égards, il s'agit d'un nouveau protocole (l'ancien était décrit dans le RFC 5246, que notre nouveau RFC remplace).
Vous pouvez voir l'histoire de ce RFC sur la Datatracker de l'IETF. Le premier brouillon a été publié en avril 2014, plus de trois années avant le RFC. C'est en partie pour des raisons techniques (TLS 1.3 est très différent de ses prédécesseurs) et en partie pour des raisons politiques. C'est que c'est important, la sécurité ! Cinq ans après les révélations de Snowden, on sait désormais que des acteurs puissants et sans scrupules, par exemple les États, espionnent massivement le trafic Internet. Il est donc crucial de protéger ce trafic, entre autres par la cryptographie. Mais dire « cryptographie » ne suffit pas ! Il existe des tas d'attaques contre les protocoles de cryptographie, et beaucoup ont réussi contre les prédécesseurs de TLS 1.3. Il était donc nécessaire de durcir le protocole TLS, pour le rendre moins vulnérable. Et c'est là que les ennuis ont commencé. Car tout le monde ne veut pas de la sécurité. Les États veulent continuer à espionner (le GCHQ britannique s'était clairement opposé à TLS 1.3 sur ce point). Les entreprises veulent espionner leurs employés (et ont pratiqué un lobbying intense contre TLS 1.3). Bref, derrière le désir de « sécurité », partagé par tout le monde, il y avait un désaccord de fond sur la surveillance. À chaque réunion de l'IETF, une proposition d'affaiblir TLS pour faciliter la surveillance apparaissait, à chaque fois, elle était rejetée et, tel le zombie des films d'horreur, elle réapparaissait, sous un nom et une forme différente, à la réunion suivante. Par exemple, à la réunion IETF de Prague en juillet 2017, l'affrontement a été particulièrement vif, alors que le groupe de travail TLS espérait avoir presque fini la version 1.3. Des gens se présentant comme enterprise networks ont critiqué les choix de TLS 1.3, notant qu'il rendait la surveillance plus difficile (c'était un peu le but…) gênant notamment leur débogage. Ils réclamaient un retour aux algorithmes n'ayant pas de sécurité persistante. Le début a suivi le schéma classique à l'IETF : « vous réclamez un affaiblissement de la sécurité » vs. « mais si on ne le fait pas à l'IETF, d'autres le feront en moins bien », mais, au final, l'IETF est restée ferme et n'a pas accepté de compromissions sur la sécurité de TLS. (Un résumé du débat est dans « TLS 1.3 in enterprise networks ».)
Pour comprendre les détails de ces propositions et de ces rejets, il faut regarder un peu en détail le protocole TLS 1.3.
Revenons d'abord sur les fondamentaux : TLS est un mécanisme permettant aux applications client/serveur de communiquer au travers d'un réseau non sûr (par exemple l'Internet) tout en empêchant l'écoute et la modification des messages. TLS suppose un mécanisme sous-jacent pour acheminer les bits dans l'ordre, et sans perte. En général, ce mécanisme est TCP. Avec ce mécanisme de transport, et les techniques cryptographiques mises en œuvre par dessus, TLS garantit :
Ces propriétés sont vraies même si l'attaquant contrôle complètement le réseau entre le client et le serveur (le modèle de menace est détaillé dans la section 3 - surtout la 3.3 - du RFC 3552, et dans l'annexe E de notre RFC).
TLS est un protocole gros et compliqué (ce qui n'est pas forcément optimum pour la sécurité). Le RFC fait 147 pages. Pour dompter cette complexité, TLS est séparé en deux composants :
Pour comprendre le rôle de ces deux protocoles, imaginons un protocole fictif simple, qui n'aurait qu'un seul algorithme de cryptographie symétrique, et qu'une seule clé, connue des deux parties (par exemple dans leur fichier de configuration). Avec un tel protocole, on pourrait se passer du protocole de salutation, et n'avoir qu'un protocole des enregistrements, indiquant comment encoder les données chiffrées. Le client et le serveur pourraient se mettre à communiquer immédiatement, sans salutation, poignée de mains et négociation, réduisant ainsi la latence. Un tel protocole serait très simple, donc sa sécurité serait bien plus facile à analyser, ce qui est une bonne chose. Mais il n'est pas du tout réaliste : changer la clé utilisée serait complexe (il faudrait synchroniser exactement les deux parties), remplacer l'algorithme si la cryptanalyse en venait à bout (comme c'est arrivé à RC4, cf. RFC 7465) créerait un nouveau protocole incompatible avec l'ancien, communiquer avec un serveur qu'on n'a jamais vu serait impossible (puisque on ne partagerait pas de clé commune), etc. D'où la nécessité du protocole de salutation, où les partenaires :
Notez que TLS n'est en général pas utilisé tel quel mais via un protocole de haut niveau, comme HTTPS pour sécuriser HTTP. TLS ne suppose pas un usage particulier : on peut s'en servir pour HTTP, pour SMTP (RFC 7672), pour le DNS (RFC 7858), etc. Cette intégration dans un protocole de plus haut niveau pose parfois elle-même des surprises en matière de sécurité, par exemple si l'application utilisatrice ne fait pas attention à la sécurité (Voir mon exposé à Devoxx, et ses transparents.)
TLS 1.3 est plutôt un nouveau protocole qu'une nouvelle version, et il n'est pas directement compatible avec son prédécesseur, TLS 1.2 (une application qui ne connait que 1.3 ne peut pas parler avec une application qui ne connait que 1.2.) En pratique, les bibliothèques qui mettent en œuvre TLS incluent en général les différentes versions, et un mécanisme de négociation de la version utilisée permet normalement de découvrir la version maximum que les deux parties acceptent (historiquement, plusieurs failles sont venues de ce point, avec des pare-feux stupidement configurés qui interféraient avec la négociation).
La section 1.3 de notre RFC liste les différences importantes entre TLS 1.2 (qui était normalisé dans le RFC 5246) et 1.3 :
https://datatracker.ietf.org/doc/draft-ietf-tls-esni/
et son cahier des charges dans le RFC 8744.)Un bon résumé de ce nouveau protocole est dans l'article de Mark Nottingham.
Ce RFC concerne TLS 1.3 mais il contient aussi quelques changements pour la version 1.2 (section 1.4 du RFC), comme un mécanisme pour limiter les attaques par repli portant sur le numéro de version, et des mécanismes de la 1.3 « portés » vers la 1.2 sous forme d'extensions TLS.
La section 2 du RFC est un survol général de TLS 1.3 (le RFC fait 147 pages, et peu de gens le liront intégralement). Au début d'une session TLS, les deux parties, avec le protocole de salutation, négocient les paramètres (version de TLS, algorithmes cryptographiques) et définissent les clés qui seront utilisées pour le chiffrement de la session. En simplifiant, il y a trois phases dans l'établissement d'une session TLS :
ClientHello
, le serveur répond avec un ServerHello
,CertificateRequest
d'un certificat client), cette partie est
chiffrée, contrairement à la précédente,Certificate
(qui ne contient pas
forcément un certificat, cela peut être une clé brute - RFC 7250 ou une clé d'une session précédente -
RFC 7924).
Un message Finished
termine cette ouverture
de session.
(Si vous êtes fana de futurisme, notez que seule la première étape
pourrait être remplacée par la distribution quantique
de clés, les autres resteraient
indispensables. Contrairement à ce que promettent ses promoteurs,
la QKD ne dispense pas d'utiliser les protocoles existants.)
Comment les deux parties se mettent-elles d'accord sur les clés ? Trois méthodes :
Si vous connaissez la cryptographie, vous savez que les PSK, les clés partagées, sont difficiles à gérer, puisque devant être transmises de manière sûre avant l'établissement de la connexion. Mais, dans TLS, une autre possibilité existe : si une session a été ouverte sans PSK, en n'utilisant que de la cryptographie asymétrique, elle peut être enregistrée, et resservir, afin d'ouvrir les futures discussions plus rapidement. TLS 1.3 utilise le même mécanisme pour des « vraies » PSK, et pour celles issues de cette reprise de sessions précédentes (contrairement aux précédentes versions de TLS, qui utilisaient un mécanisme séparé, celui du RFC 5077, désormais abandonné).
Si on a une PSK (gérée manuellement, ou bien via la reprise de session), on peut même avoir un dialogue TLS dit « 0-RTT ». Le premier paquet du client peut contenir des données, qui seront acceptées et traitées par le serveur. Cela permet une importante diminution de la latence, dont il faut rappeler qu'elle est souvent le facteur limitant des performances. Par contre, comme rien n'est idéal dans cette vallée de larmes, cela se fait au détriment de la sécurité :
La section 8 du RFC et l'annexe E.5 détaillent ces limites, et les mesures qui peuvent être prises.
Le protocole TLS est décrit avec un langage spécifique, décrit de manière relativement informelle dans la section 3 du RFC. Ce langage manipule des types de données classiques :
uint8
, uint16
),Datum[3]
ou
variable, avec indication de la longueur au début -
uint16 longer<0..800>
,enum { red(3), blue(5), white(7) }
Color;
),Par exemple, tirés de la section 4 (l'annexe B fournit la liste complète), voici, dans ce langage, la liste des types de messages pendant les salutations, une énumération :
enum { client_hello(1), server_hello(2), new_session_ticket(4), end_of_early_data(5), encrypted_extensions(8), certificate(11), certificate_request(13), certificate_verify(15), finished(20), key_update(24), message_hash(254), (255) } HandshakeType;
Et le format de base d'un message du protocole de salutation :
struct { HandshakeType msg_type; /* handshake type */ uint24 length; /* bytes in message */ select (Handshake.msg_type) { case client_hello: ClientHello; case server_hello: ServerHello; case end_of_early_data: EndOfEarlyData; case encrypted_extensions: EncryptedExtensions; case certificate_request: CertificateRequest; case certificate: Certificate; case certificate_verify: CertificateVerify; case finished: Finished; case new_session_ticket: NewSessionTicket; case key_update: KeyUpdate; }; } Handshake;
La section 4 fournit tous les détails sur le protocole de
salutation, notamment sur la délicate négociation des paramètres
cryptographiques. Notez que la renégociation en cours de session a
disparu, donc un ClientHello
ne peut
désormais plus être envoyé qu'au début.
Un problème auquel a toujours dû faire face TLS est celui de la
négociation de version, en présence de mises en œuvre boguées, et,
surtout, en présence de boitiers
intermédiaires encore plus bogués
(pare-feux ignorants, par exemple, que des
DSI ignorantes placent un peu partout). Le
modèle original de TLS pour un client était d'annoncer dans le
ClientHello
le plus grand numéro de version
qu'on gère, et de voir dans ServerHello
le
maximum imposé par le serveur. Ainsi, un client TLS 1.2 parlant à
un serveur qui ne gère que 1.1 envoyait
ClientHello(client_version=1.2)
et, en
recevant ServerHello(server_version=1.1)
, se
repliait sur TLS 1.1, la version la plus élevée que les deux
parties gèraient. En pratique, cela ne marche pas aussi bien. On
voyait par exemple des serveurs (ou, plus vraisemblablement, des
pare-feux bogués) qui raccrochaient brutalement en
présence d'un numéro de version plus élevé, au lieu de suggérer un
repli. Le client n'avait alors que le choix de renoncer, ou bien
de se lancer dans une série d'essais/erreurs (qui peut être
longue, si le serveur ou le pare-feu bogué ne répond pas).
TLS 1.3 change donc complètement le mécanisme de
négociation. Le client annonce toujours la version 1.2 (en fait
0x303, pour des raisons historiques), et la vraie version est mise
dans une extension, supported_versions
(section 4.2.1), dont
on espère qu'elle sera ignorée par les serveurs mal
gérés. (L'annexe D du RFC détaille ce problème de la négociation
de version.) Dans la réponse ServerHello
, un
serveur 1.3 doit inclure cette extension, autrement, il faut se
rabattre sur TLS 1.2.
En parlant d'extensions, concept qui avait été introduit
originellement dans le RFC 4366, notre RFC
reprend des extensions déjà normalisées, comme le SNI
(Server Name Indication) du RFC 6066, le battement de cœur du RFC 6520, le remplissage du ClientHello
du RFC 7685,
et en ajoute dix, dont
supported_versions
. Certaines de ces
extensions doivent être présentes dans les messages
Hello
, car la sélection des paramètres
cryptographiques en dépend, d'autres peuvent être uniquement dans
les messages EncryptedExtensions
, une
nouveauté de TLS 1.3, pour les extensions qu'on n'enverra qu'une
fois le chiffrement commencé. Le RFC en profite pour rappeler que
les messages Hello
ne sont pas protégés
cryptographiquement, et peuvent donc être modifiés (le message
Finished
résume les décisions prises et peut
donc protéger contre ce genre d'attaques).
Autrement, parmi les autres nouvelles extensions :
La section 5 décrit le protocole des enregistrements (record protocol). C'est ce sous-protocole qui va prendre un flux d'octets, le découper en enregistrements, les protéger par le chiffrement puis, à l'autre bout, déchiffrer et reconstituer le flux… Notez que « protégé » signifie à la fois confidentialité et intégrité puisque TLS 1.3, contrairement à ses prédécesseurs, impose AEAD (RFC 5116).
Les enregistrements sont typés et marqués handshake (la salutation, vue dans la section précédente), change cipher spec, alert (pour signaler un problème) et application data (les données elle-mêmes) :
enum { invalid(0), change_cipher_spec(20), alert(21), handshake(22), application_data(23), (255) } ContentType;
Le contenu des données est évidemment incompréhensible, en raison du chiffrement (voici un enregistrement de type 23, données, vu par tshark) :
TLSv1.3 Record Layer: Application Data Protocol: http-over-tls Opaque Type: Application Data (23) Version: TLS 1.2 (0x0303) Length: 6316 Encrypted Application Data: eb0e21f124f82eee0b7a37a1d6d866b075d0476e6f00cae7...
Et décrite par la norme dans son langage formel :
struct { ContentType opaque_type = application_data; /* 23 */ ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */ uint16 length; opaque encrypted_record[TLSCiphertext.length]; } TLSCiphertext;
(Oui, le numéro de version reste à TLS 1.2 pour éviter d'énerver les stupides middleboxes.) Notez que des extensions à TLS peuvent introduire d'autres types d'enregistrements.
Une faiblesse classique de TLS est que la taille des données chiffrées n'est pas dissimulée. Si on veut savoir à quelle page d'un site Web un client HTTP a accédé, on peut parfois le déduire de l'observation de cette taille. D'où la possibilité de faire du remplissage pour dissimuler cette taille (section 5.4 du RFC). Notez que le RFC ne suggère pas de politique de remplissage spécifique (ajouter un nombre aléatoire ? Tout remplir jusqu'à la taille maximale ?), c'est un choix compliqué. Il note aussi que certaines applications font leur propre remplissage, et qu'il n'est alors pas nécessaire que TLS le fasse.
La section 6 du RFC est dédiée au cas des alertes. C'est un des types d'enregistrements possibles, et, comme les autres, il est chiffré, et les alertes sont donc confidentielles. Une alerte a un niveau et une description :
struct { AlertLevel level; AlertDescription description; } Alert;
Le niveau indiquait si l'alerte est fatale mais n'est plus utilisé en TLS 1.2, où il faut se fier uniquement à la description, une énumération des problèmes possibles (message de type inconnu, mauvais certificat, enregistrement non décodable - rappelez-vous que TLS 1.3 n'utilise que du chiffrement intègre, problème interne au client ou au serveur, extension non acceptée, etc). La section 6.2 donne une liste des erreurs fatales, qui doivent mener à terminer immédiatement la session TLS.
La section 8 du RFC est entièrement consacrée à une nouveauté délicate, le « 0-RTT ». Ce terme désigne la possibilité d'envoyer des données dès le premier paquet, sans les nombreux échanges de paquets qui sont normalement nécessaires pour établir une session TLS. C'est très bien du point de vue des performances, mais pas forcément du point de vue de la sécurité puisque, sans échanges, on ne peut plus vérifier à qui on parle. Un attaquant peut réaliser une attaque par rejeu en envoyant à nouveau un paquet qu'il a intercepté. Un serveur doit donc se défendre en se souvenant des données déjà envoyées et en ne les acceptant pas deux fois. (Ce qui peut être plus facile à dire qu'à faire ; le RFC contient une bonne discussion très détaillée des techniques possibles, et de leurs limites. Il y en a des subtiles, comme d'utiliser des systèmes de mémorisation ayant des faux positifs, comme les filtres de Bloom, parce qu'ils ne produiraient pas d'erreurs, ils rejetteraient juste certains essais 0-RTT légitimes, cela ne serait donc qu'une légère perte de performance.)
La section 9 de notre RFC se penche sur un problème difficile,
la conformité des mises en œuvres de TLS. D'abord, les algorithmes
obligatoires. Afin de permettre l'interopérabilité,
toute mise en œuvre de TLS doit avoir la
suite de chiffrement TLS_AES_128_GCM_SHA256
(AES en
mode GCM
avec SHA-256). D'autres suites sont
recommandées (cf. annexe B.4). Pour l'authentification,
RSA avec SHA-256 et
ECDSA sont obligatoires. Ainsi, deux
programmes différents sont sûrs de pouvoir trouver des algorithmes
communs. La possibilité
d'authentification par certificats PGP du RFC 6091 a été retirée.
De plus, certaines extensions à TLS sont obligatoires, un pair TLS 1.3 ne peut pas les refuser :
supported_versions
, nécessaire pour
annoncer TLS 1.3,cookie
,signature_algorithms
,
signature_algorithms_cert
,
supported_groups
et
key_share
,server_name
, c'est à dire SNI
(Server Name Indication), souvent nécessaire
pour pouvoir choisir le bon certificat (cf. section 3 du RFC 6066).La section 9 précise aussi le comportement attendu des équipements intermédiaires. Ces dispositifs (pare-feux, par exemple, mais pas uniquement) ont toujours été une plaie pour TLS. Alors que TLS vise à fournir une communication sûre, à l'abri des équipements intermédiaires, ceux-ci passent leur temps à essayer de s'insérer dans la communication, et souvent la cassent. Normalement, TLS 1.3 est conçu pour que ces interférences ne puissent pas mener à un repli (le repli est l'utilisation de paramètres moins sûrs que ce que les deux machines auraient choisi en l'absence d'interférence).
Il y a deux grandes catégories d'intermédiaires, ceux qui tripotent la session TLS sans être le client ou le serveur, et ceux qui terminent la session TLS de leur côté. Attention, dans ce contexte, « terminer » ne veut pas dire « y mettre fin », mais « la sécurité TLS se termine ici, de manière à ce que l'intermédiaire puisse accéder au contenu de la communication ». Typiquement, une middlebox qui « termine » une session TLS va être serveur TLS pour le client et client TLS pour le serveur, s'insérant complètement dans la conversation. Normalement, l'authentification vise à empêcher ce genre de pratiques, et l'intermédiaire ne sera donc accepté que s'il a un certificat valable. C'est pour cela qu'en entreprise, les machines officielles sont souvent installées avec une AC contrôlée par le vendeur du boitier intermédiaire, de manière à permettre l'interception.
Le RFC ne se penche pas sur la légitimité de ces pratiques, uniquement sur leurs caractéristiques techniques. (Les boitiers intermédiaires sont souvent programmés avec les pieds, et ouvrent de nombreuses failles.) Le RFC rappelle notamment que l'intermédiaire qui termine une session doit suivre le RFC à la lettre (ce qui devrait aller sans dire…)
Depuis le RFC 4346, il existe plusieurs registres IANA pour TLS, décrits en section 11, avec leurs nouveautés. En effet, plusieurs choix pour TLS ne sont pas « câblés en dur » dans le RFC mais peuvent évoluer indépendamment. Par exemple, le registre de suites cryptographiques a une politique d'enregistrement « spécification nécessaire » (cf. RFC 8126, sur les politiques d'enregistrement). La cryptographie fait régulièrement des progrès, et il faut donc pouvoir modifier la liste des suites acceptées (par exemple lorsqu'il faudra y ajouter les algorithmes post-quantiques) sans avoir à toucher au RFC (l'annexe B.4 donne la liste actuelle). Le registre des types de contenu, lui, a une politique d'enregistrement bien plus stricte, « action de normalisation ». On crée moins souvent des types que des suites cryptographiques. Même chose pour le registre des alertes ou pour celui des salutations.
L'annexe C du RFC plaira aux programmeurs, elle donne plusieurs conseils pour une mise en œuvre correcte de TLS 1.3 (ce n'est pas tout d'avoir un protocole correct, il faut encore qu'il soit programmé correctement). Pour aider les développeurs à déterminer s'ils ont correctement fait le travail, un futur RFC fournira des vecteurs de test.
Un des conseils les plus importants est évidemment de faire
attention au générateur de nombres
aléatoires, source de tant de failles de sécurité en
cryptographie. TLS utilise des nombres qui doivent être
imprévisibles à un attaquant pour générer des clés de session. Si
ces nombres sont prévisibles, toute la cryptographie s'effondre. Le RFC conseille fortement d'utiliser un générateur
existant (comme /dev/urandom
sur les systèmes
Unix) plutôt que d'écrire le sien, ce qui
est bien plus difficile qu'il ne semble. (Si on tient quand même à
le faire, le RFC 4086 est une lecture
indispensable.)
Le RFC conseille également de vérifier le certificat du partenaire par défaut (quitte à fournir un moyen de débrayer cette vérification). Si ce n'est pas le cas, beaucoup d'utilisateurs du programme ou de la bibliothèque oublieront de le faire. Il suggère aussi de ne pas accepter certains certificats trop faibles (clé RSA de seulement 1 024 bits, par exemple).
Il existe plusieurs moyens avec TLS de ne pas avoir d'authentification du serveur : les clés brutes du RFC 7250 (à la place des certificats), ou bien les certificats auto-signés. Dans ces conditions, une attaque de l'homme du milieu est parfaitement possibe, et il faut donc prendre des précautions supplémentaires (par exemple DANE, normalisé dans le RFC 6698, que le RFC oublie malheureusement de citer).
Autre bon conseil de cryptographie, se méfier des attaques fondées sur la mesure du temps de calcul, et prendre des mesures appropriées (par exemple en vérifiant que le temps de calcul est le même pour des données correctes et incorrectes).
Il n'y a aucune bonne raison d'utiliser certains algorithmes faibles (comme RC4, abandonné depuis le RFC 7465), et le RFC demande que le code pour ces algorithmes ne soit pas présent, afin d'éviter une attaque par repli (annexes C.3 et D.5 du RFC). De la même façon, il demande de ne jamais accepter SSL v3 (RFC 7568).
L'expérience a prouvé que beaucoup de mises en œuvre de TLS ne réagissaient pas correctement à des options inattendues, et le RFC rappelle donc qu'il faut ignorer les suites cryptographiques inconnues (autrement, on ne pourrait jamais introduire une nouvelle suite, puisqu'elle casserait les programmes), et ignorer les extensions inconnues (pour la même raison).
L'annexe D, elle, est consacrée au problème de la communication
avec un vieux partenaire, qui ne connait pas TLS 1.3. Le mécanisme
de négociation de la version du protocole à utiliser a
complètement changé en 1.3. Dans la 1.3, le champ
version
du ClientHello
contient 1.2, la vraie version étant dans l'extension
supported_versions
. Si un client 1.3 parle
avec un serveur <= 1.2, le serveur ne connaitra pas cette
extension et répondra sans l'extension, avertissant ainsi le
client qu'il faudra parler en 1.2 (ou plus vieux). Ça, c'est si le
serveur est correct. S'il ne l'est pas ou, plus vraisemblablement,
s'il est derrière une
middlebox boguée, on
verra des problèmes comme par exemple le refus de répondre aux
clients utilisant des extensions inconnues (ce qui sera le cas
pour supported_versions
), soit en rejettant
ouvertement la demande soit, encore pire, en l'ignorant. Arriver à gérer des
serveurs/middleboxes incorrects est un problème
complexe. Le client peut être tenté de re-essayer avec d'autres
options (par exemple tenter du 1.2, sans l'extension
supported_versions
). Cette méthode n'est pas
conseillée. Non seulement elle peut prendre du temps (attendre
l'expiration du délai de garde, re-essayer…) mais surtout, elle
ouvre la voie à des attaques par repli :
l'attaquant bloque les ClientHello
1.3 et le
client, croyant bien faire, se replie sur une version plus
ancienne et sans doute moins sûre de TLS.
En parlant de compatibilité, le « 0-RTT » n'est évidemment pas
compatible avec les vieilles versions. Le client qui envoie du
« 0-RTT » (des données dans le ClientHello
)
doit donc savoir que, si la réponse est d'un serveur <= 1.2,
la session ne pourra pas être établie, et il faudra donc réessayer
sans 0-RTT.
Naturellement, les plus gros problèmes ne surviennent pas avec
les clients et les serveurs mais avec les
middleboxes. Plusieurs études ont montré leur
caractère néfaste (cf. présentation
à l'IETF 100, mesures
avec Chrome (qui indique également que certains serveurs
TLS sont gravement en tort, comme celui installé dans les
imprimantes Canon), mesures
avec Firefox, et encore
d'autres mesures). Le RFC suggère qu'on limite les risques
en essayant d'imiter le plus possible une salutation de TLS 1.2,
par exemple en envoyant des messages
change_cipher_spec
, qui ne sont plus utilisés
en TLS 1.3, mais qui peuvent rassurer la
middlebox (annexe D.4).
Enfin, le RFC se termine par l'annexe E, qui énumère les propriétés de sécurité de TLS 1.3 : même face à un attaquant actif (RFC 3552), le protocole de salutation de TLS garantit des clés de session communes et secrètes, une authentification du serveur (et du client si on veut), et une sécurité persistante, même en cas de compromission ultérieure des clés (sauf en cas de 0-RTT, un autre des inconvénients sérieux de ce service, avec le risque de rejeu). De nombreuses analyses détaillées de la sécurité de TLS sont listées dans l'annexe E.1.6. À lire si vous voulez travailler ce sujet.
Quant au protocole des enregistrements, celui de TLS 1.3 garantit confidentialité et intégrité (RFC 5116).
TLS 1.3 a fait l'objet de nombreuses analyses de sécurité par des chercheurs, avant même sa normalisation, ce qui est une bonne chose (et qui explique en partie les retards). Notre annexe E pointe également les limites restantes de TLS :
Le 0-RTT introduit un nouveau risque, celui de rejeu. (Et 0-RTT a sérieusement contribué aux délais qu'à connu le projet TLS 1.3, plusieurs participants à l'IETF protestant contre cette introduction risquée.) Si l'application est idempotente, ce n'est pas très grave. Si, par contre, les effets d'une requête précédentes peuvent être rejoués, c'est plus embêtant (imaginez un transfert d'argent répété…) TLS ne promet rien en ce domaine, c'est à chaque serveur de se défendre contre le rejeu (la section 8 donne des idées à ce sujet). Voilà pourquoi le RFC demande que les requêtes 0-RTT ne soient pas activées par défaut, mais uniquement quand l'application au-dessus de TLS le demande. (Cloudflare, par exemple, n'active pas le 0-RTT par défaut.)
Voilà, vous avez maintenant fait un tour complet du RFC, mais vous savez que la cryptographie est une chose difficile, et pas seulement dans les algorithmes cryptographiques (TLS n'en invente aucun, il réutilise des algorithmes existants comme AES ou ECDSA), mais aussi dans les protocoles cryptographiques, un art complexe. N'hésitez donc pas à lire le RFC en détail, et à vous méfier des résumés forcément toujours sommaires, comme cet article.
À part le 0-RTT, le plus gros débat lors de la création de TLS
1.3 avait été autour du concept que ses partisans nomment
« visibilité » et ses adversaires « surveillance ». C'est l'idée
qu'il serait bien pratique si on (on : le patron, la police, le
FAI…) pouvait accéder au contenu des
communications TLS. « Le chiffrement, c'est bien, à condition que
je puisse lire les données quand même » est l'avis des partisans
de la visibilité. Cela avait été proposé dans les
Internet-Drafts draft-green-tls-static-dh-in-tls13
et draft-rhrd-tls-tls13-visibility
. Je
ne vais pas ici pouvoir capturer la totalité
du débat, juste noter quelques points qui sont parfois oubliés
dans la discussion. Côté partisans de la visibilité :
Et du côté des adversaires de la surveillance :
Revenons maintenant aux choses sérieuses, avec les mises en
œuvre de TLS 1.3. Il y en existe au moins une dizaine à l'heure
actuelle mais, en général, pas dans les versions officiellement
publiées des logiciels. Notons quand même que
Firefox 61 sait faire du TLS 1.3. Les autres
mises en œuvre sont prêtes, même si pas forcément publiées. Prenons
l'exemple de la bibliothèque
GnuTLS. Elle dispose de TLS 1.3 depuis la
version 3.6.3. Pour l'instant, il faut compiler cette version avec
l'option ./configure --enable-tls13-support
,
qui n'est pas encore activée par défaut. Un bon
article du mainteneur de GnuTLS explique bien les nouveautés
de TLS 1.3.
Une fois GnuTLS correctement compilé, on peut utiliser le
programme en ligne de commande gnutls-cli
avec
un serveur qui accepte TLS 1.3 :
% gnutls-cli gmail.com ... - Description: (TLS1.3)-(ECDHE-X25519)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM) - Ephemeral EC Diffie-Hellman parameters - Using curve: X25519 - Curve size: 256 bits - Version: TLS1.3 - Key Exchange: ECDHE-RSA - Server Signature: RSA-PSS-RSAE-SHA256 - Cipher: AES-256-GCM - MAC: AEAD ...
Et ça marche, on fait du TLS 1.3. Si vous préférez écrire le programme
vous-même, regardez ce petit
programme. Si
GnuTLS est en /local
, il se compilera avec
cc -I/local/include -Wall -Wextra -o test-tls13 test-tls13.c
-L/local/lib -lgnutls
et s'utilisera avec :
% ./test-tls13 www.ietf.org TLS connection using "TLS1.3 AES-256-GCM" % ./test-tls13 gmail.com TLS connection using "TLS1.3 AES-256-GCM" % ./test-tls13 mastodon.gougere.fr TLS connection using "TLS1.2 AES-256-GCM" % ./test-tls13 www.bortzmeyer.org TLS connection using "TLS1.2 AES-256-GCM" % ./test-tls13 blog.cloudflare.com TLS connection using "TLS1.3 AES-256-GCM"
Cela vous donne une petite idée des serveurs qui acceptent TLS 1.3.
Un pcap d'une session TLS 1.3 est disponible
en tls13.pcap
. Notez que le numéro de version n'est pas encore
celui du RFC (0x304). Ici, 0x7f1c désigne
l'Internet-Draft numéro 28. Voici la session
vue par tshark :
1 0.000000 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 94 36866 → https(443) [SYN] Seq=0 Win=28800 Len=0 MSS=1440 SACK_PERM=1 TSval=3528788861 TSecr=0 WS=128 2 0.003052 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 86 https(443) → 36866 [SYN, ACK] Seq=0 Ack=1 Win=24400 Len=0 MSS=1220 SACK_PERM=1 WS=1024 3 0.003070 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [ACK] Seq=1 Ack=1 Win=28800 Len=0 4 0.003354 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TLSv1 403 Client Hello 5 0.006777 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 74 https(443) → 36866 [ACK] Seq=1 Ack=330 Win=25600 Len=0 6 0.011393 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TLSv1.3 6496 Server Hello, Change Cipher Spec, Application Data 7 0.011413 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [ACK] Seq=330 Ack=6423 Win=41728 Len=0 8 0.011650 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TLSv1.3 80 Change Cipher Spec 9 0.012685 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TLSv1.3 148 Application Data 10 0.015693 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 74 https(443) → 36866 [ACK] Seq=6423 Ack=411 Win=25600 Len=0 11 0.015742 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TLSv1.3 524 Application Data 12 0.015770 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [RST] Seq=411 Win=0 Len=0 13 0.015788 2400:cb00:2048:1::6814:55 → 2001:67c:370:1998:9819:4f92:d0c0:e94d TCP 74 https(443) → 36866 [FIN, ACK] Seq=6873 Ack=411 Win=25600 Len=0 14 0.015793 2001:67c:370:1998:9819:4f92:d0c0:e94d → 2400:cb00:2048:1::6814:55 TCP 74 36866 → https(443) [RST] Seq=411 Win=0 Len=0
Et, complètement décodée par tshark :
Secure Sockets Layer [sic] TLSv1 Record Layer: Handshake Protocol: Client Hello Content Type: Handshake (22) Version: TLS 1.0 (0x0301) Handshake Protocol: Client Hello Handshake Type: Client Hello (1) Version: TLS 1.2 (0x0303) ... Extension: supported_versions (len=9) Type: supported_versions (43) Length: 9 Supported Versions length: 8 Supported Version: Unknown (0x7f1c) Supported Version: TLS 1.2 (0x0303) Supported Version: TLS 1.1 (0x0302) Supported Version: TLS 1.0 (0x0301)
Le texte complet est en tls13.txt
. Notez bien que la négociation est
en clair. D'autres exemples de traces TLS 1.3 figurent dans le RFC 8448.
Quelques autres articles à lire :
Date de publication du RFC : Juillet 2018
Auteur(s) du RFC : A. Keranen, C. Holmberg (Ericsson), J. Rosenberg (jdrosen.net)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ice
Première rédaction de cet article le 5 janvier 2019
Le problème de la traversée des routeurs NAT est traité dans plusieurs RFC, de façon à réparer la grossière erreur qu'avait été le déploiement massif du NAT plutôt que celui de IPv6. ICE est un « méta-protocole », orchestrant plusieurs protocoles comme STUN et TURN pour arriver à découvrir un canal de communication malgré le NAT. ICE avait été normalisé il y a huit ans et le RFC 5245, que notre nouveau RFC remplace. Le protocole ne change guère, mais sa description normalisée est sérieusement modifiée.
L'objectif principal est la session multimédia, transmise entre deux terminaux, en général en UDP. ICE est donc utilisé, par exemple, par les solutions de téléphonie sur IP. Ces solutions ont déjà dû affronter le problème du NAT (RFC 3235) et ont développé un certain nombre de techniques, dont la bonne utilisation est l'objet principal de notre RFC.
En effet, les protocoles de téléphonie sur IP, comme SIP (RFC 3261) ont en commun d'avoir une session de contrôle de la connexion, établie par l'appelant en TCP (et qui passe en général assez bien à travers le NAT, si l'appelé utilise un relais public) et une session de transport des données, en général au-dessus d'UDP. C'est cette session qui est en général perturbée par le NAT. La session de contrôle (signaling session) transmet au partenaire SIP l'adresse IP de son correspondant mais, s'il y a deux domaines d'adressage séparés (par exemple un partenaire sur le domaine public et un autre dans le monde des adresses privées du RFC 1918), cette adresse IP ainsi communiquée ne sert pas à grand'chose.
On voit donc que le non-déploiement d'IPv6, qui aurait permis de se passer du NAT, a coûté très cher en temps de développement. Notre RFC fait 100 pages (et d'autres RFC doivent être lus en prime comme ceux de STUN, TURN, et le futur RFC décrivant l'utilisation de ICE par SIP et SDP), et se traduira par du code réseau très complexe, uniquement pour contourner une mauvaise décision.
Des solutions au NAT, soit standard comme STUN (RFC 8489) soit purement spécifiques à un logiciel fermé comme Skype ont été développées. Mais aucune n'est parfaite, car tous les cas sont spécifiques. Par exemple, si deux machines sont sur le même réseau local, derrière le même routeur NAT, faire appel à STUN est inutile, et peut même échouer (si le routeur NAT ne supporte pas le routage en épingle à cheveux, où un paquet d'origine interne retourne vers le réseau interne). Le principe d'ICE est donc de décrire comment utiliser plusieurs protocoles de traversée de NAT, pour trouver la solution optimale pour envoyer les paquets de la session de données. Et ceci alors que les machines qui communiquent ne savent pas en général si elles sont derrière un NAT, ni, si oui, de quel type de NAT il s'agit.
Le principe d'ICE, exposé dans la section 2 et détaillé dans les sections 5 et suivantes, est donc le suivant : chaque partenaire va déterminer une liste de paires d'adresses de transport candidates (en utilisant leurs adresses IP locales, STUN et TURN), les tester et se mettre d'accord sur la « meilleure paire ». Une fois ces trois étapes passées, on va pouvoir communiquer. Une adresse de transport est un couple (adresse IP, port).
La première étape (section 5) est donc d'établir la liste des paires, et de la trier par ordre de priorité. La section 5.1.2.1 recommande une formule pour calculer la priorité, formule qui fait intervenir une préférence pour le type d'adresses IP (une adresse locale à la machine sera préférée à une adresse publique obtenue par STUN, et celle-ci sera préférée à l'adresse d'un serveur relais TURN, puisque STUN permettra ensuite une communication directe, sans relais, donc plus rapide) et une préférence locale, par exemple pour les adresses IPv6 par rapport à IPv4.
La deuxième étape est de tester les paires (sections 2.2 et 7) pour déterminer celles qui marchent. (Les paires ont d'abord été triées par priorité à l'étape précédente.) ICE poursuit les tests, même en cas de succès, pour voir si un meilleur RTT peut être trouvé (c'est un sérieux changement par rapport au précédent RFC).
Pour ces tests, ICE utilise STUN, mais avec des extensions spécifiques à ICE (sections 7.1 et 16).
La troisième et dernière étape d'ICE (section 8) est de sélectionner la meilleure paire (en terme de priorité) parmi celles qui ont fonctionné. Les couples (adresse IP, port) de celles-ci seront alors utilisées pour la communication.
Des exemples détaillés d'utilisation d'ICE, avec tous les messages échangés, figurent dans la section 15 du RFC.
L'ensemble du processus est relativement compliqué et nécessite de garder un état sur chaque partenaire ICE, alors qu'ICE est inutile pour des machines qui ont une adresse IP publique. La section 2.5 introduit donc la possibilité de mises en œuvre légères (lite implementation) d'ICE, qui peuvent interagir avec les autres machines ICE, sans avoir à gérer tout le protocole (cf. aussi section 8.2).
Tous ces tests prennent évidemment du temps, d'autant plus de temps qu'il y a de paires d'adresse de transport « nominées ». C'est le prix à payer pour la plus grande souplesse d'ICE : il sera toujours plus lent que STUN seul.
Les protocoles de téléphonie sur IP ayant eu leur part de vulnérabilités, la section 19, sur la sécurité, est très détaillée. Par exemple, une attaque classique est d'établir une communication avec un partenaire, puis de lui demander d'envoyer le flux de données vers la victime. C'est une attaque par amplification classique, sauf que l'existence d'une session de données séparée de la session de contrôle fait qu'elle ne nécessite même pas de tricher sur son adresse IP (et les techniques du RFC 2827 sont donc inefficaces). Cette attaque, dite « attaque du marteau vocal », peut être combattue grâce à ICE, puisque le test de connectivité échouera, la victime ne répondant pas (puisqu'elle n'a rien demandé). Si tout le monde utilise ICE, cette attaque peut donc complètement disparaitre.
Notez que le lien direct entre participants est excellent pour les performances, mais est mauvais pour la vie privée (voir la section 19.1) puisqu'il révèle les adresses IP de son correspondant à chaque participant. (WebRTC - RFC 8825 - a le même problème.)
D'innombrables détails compliquent les choses et expliquent en partie la taille de ce RFC. Par exemple, la section 11 décrit l'obligation d'utiliser des keepalives, des paquets inutiles mais qui ont pour seul but de rappeler au routeur NAT que la session sert toujours, afin que les correspondances entre une adresse IP interne et une adresse externe restent ouvertes (le flux de données ne suffit pas forcément, car il peut y avoir des périodes d'inactivité).
Enfin, une intéressante section 17 décrit les problèmes pratiques du déploiement. Par exemple, la planification de la capacité des serveurs est discutée en 17.2.1. Un serveur STUN n'a pas besoin de beaucoup de capacité, mais un serveur TURN, oui, puisqu'il relaie tous les paquets, y compris ceux de la session de données.
La première version d'ICE ne gérait que l'UDP mais, depuis la publication du RFC 6544, TCP est également accepté.
La section 18 discute de la sortie, que le RFC 3424 impose à toutes les propositions concernant le NAT : est-ce que le contournement du NAT pourra être supprimé par la suite, ou bien sera-t-il lui-même un élément d'ossification ? En fait, ICE est utile même dans un Internet sans NAT (par exemple grâce à IPv6), pour tester la connectivité.
La section 21 décrit, mais de façon sommaire, les changements depuis le RFC 5245. Le RFC a été pas mal réorganisé et a un plan différent de celui du RFC 5245. Les principaux changements techniques :
Pour celles et ceux qui veulent creuser vraiment le RFC, et comprendre tous les choix techniques effectués, l'annexe B est là pour cela.
Il existe plusieurs mises en œuvres d'ICE en logiciel libre, comme pjnath, qui fait partie de la bibliothèque PJSIP, ou comme Nice.
Date de publication du RFC : Mai 2018
Auteur(s) du RFC : Y. Nir (Dell EMC), A. Langley (Google)
Pour information
Première rédaction de cet article le 4 janvier 2019
Les algorithmes de cryptographie ChaCha20 et Poly1305 avaient fait l'objet d'une spécification stable, permettant leur référencement pour les protocoles IETF, dans le RFC 7539, que notre RFC remplace, avec très peu de changements. Ce RFC normalise aussi bien leur utilisation isolée (ChaCha20 seul ou Poly1305 seul) que leur combinaison, qui fournit du chiffrement intègre (AEAD).
Pendant longtemps, la référence en matière de chiffrement symétrique était AES, entre autres en raison de ses performances très élevées sur du matériel dédié. AES est quatre à dix fois plus rapide que 3DES, qui était la précédente référence. Il est donc logique que AES soit utilisé partout, et notamment qu'il chiffre sans doute encore la majorité des sessions TLS. Le problème alors est qu'on dépend trop d'AES : si une faille est découverte par la cryptanalyse, on est fichu (AES est par exemple vulnérable dans certains cas). Il serait plus rassurant d'avoir des alternatives sérieuses à AES (n'obligeant pas à revenir à 3DES), pour que d'éventuelles percées en cryptanalyse ne cassent pas toute la crypto d'un coup. D'autant plus qu'AES a un défaut : rapide sur du matériel spécialisé, il l'est moins sur du matériel généraliste.
D'où les algorithmes décrits formellement dans ce RFC. Inventés par Bernstein, ils ont déjà fait l'objet d'un certain nombre d'analyses de sécurité (« New Features of Latin Dances: Analysis of Salsa, ChaCha, and Rumba » et « Latin Dances Revisited: New Analytic Results of Salsa20 and ChaCha »). ChaCha20 est un algorithme de chiffrement symétrique, plus rapide qu'AES sur un matériel générique (mise en œuvre purement en logiciel), Poly1305 est un MAC, et les deux peuvent être combinés pour faire du chiffrement intègre (et cela figure désormais dans le registre sur AEAD).
La section 2 du RFC décrit les algorithmes (je ne la reprends pas ici, la crypto, c'est trop fort pour moi), et ajoute du pseudo-code, des exemples et des vecteurs de test (il y en a d'autres dans l'annexe A). À l'origine, Poly1305 était décrit comme lié à AES, pour obtenir, par chiffrement du numnique, une chaîne de bits unique et secrète. Mais, en fait, n'importe quelle fonction de chiffrement convient, pas uniquement AES.
En cryptographie, ce sont plus souvent les mises en œuvre que les algorithmes qui ont une faille. La section 3 est donc consacrée aux avis aux programmeurs, pour éviter qu'une erreur de leur part n'affaiblisse ces beaux algorithmes. Poly1305 nécessite de travailler avec des grands nombres et le RFC déconseille d'utiliser la plupart des bibliothèques existantes de gestion des grands nombres comme celle d'OpenSSL. Celles-ci sont trop souvent vulnérables à des attaques par mesure du temps écoulé et le RFC conseille d'utiliser uniquement des bibliothèques qui font leur travail en un temps constant, comme NaCl. Un exemple de mise en œuvre de Poly1305 est poly1305-donna.
La section 4, sur la sécurité, détaille les points importants à suivre pour ne pas se faire casser sa jolie crypto. Pour ChaCha20, avant tout, il faut utiliser un numnique (numnique ?) vraiment unique. On peut utiliser un compteur, on peut utiliser un LFSR, mais il doit être unique.
ChaCha20 et Poly1305 n'utilisent que des opérations simples,
normalement faciles à implémenter en un temps constant, qui ne
permettront pas à l'attaquant de déduire quoi que ce soit de la
mesure des temps de réponse. Attention, programmeurs, certaines
fonctions comme la classique
memcmp()
ne s'exécutent pas en un
temps constant, et, si elles sont utilisées, permettent certaines
attaques.
Ces algorithmes sont dans le registre IANA des algorithmes TLS. Notez aussi un bon article d'explication de CloudFlare.
Comme indiqué au début, il n'y a que peu de changements depuis le RFC 7539, à part la correction de plusieurs erreurs techniques.
Date de publication du RFC : Juillet 2018
Auteur(s) du RFC : P. Hoffman (ICANN)
Pour information
Première rédaction de cet article le 11 décembre 2018
Le format des messages DNS circulant sur le réseau est un format binaire, pas forcément évident à analyser. Pour beaucoup d'applications, il serait sans doute préférable d'utiliser un format normalisé et plus agréable, par exemple JSON, dont ce nouveau RFC décrit l'utilisation pour le DNS.
Non seulement le format des messages DNS est du binaire (RFC 1035, section 4) et non pas du texte comme par exemple pour SMTP, XMPP ou HTTP, mais en plus il y a des pièges. Par exemple, la longueur des sections du message est indiquée dans un champ séparé de la section, et peut ne pas correspondre à la vraie longueur. La compression des noms n'arrange rien. Écrire un analyseur de messages DNS est donc difficile.
Il y a un million de formats pour des données structurées mais, aujourd'hui, le format texte le plus populaire pour ces données est certainement JSON, normalisé dans le RFC 8259. L'utilisation de JSON pour représenter les messages DNS (requêtes ou réponses) suit les principes suivants (section 1.1 du RFC) :
QNAME
, le nom de domaine
demandé, soit présent, cf. section 6 du RFC.)La section 2 du RFC donne la liste des membres (au sens JSON de
« champs d'un objet ») d'un objet DNS. Voici un exemple d'un tel
objet, une requête DNS demandant l'adresse IPv4 (code 1, souvent
noté A) d'example.com
:
{ "ID": 19678, "QR": 0, "Opcode": 0, "AA": 0, "TC": 0, "RD": 0, "RA": 0, "AD": 0, "CD": 0, "RCODE": 0, "QDCOUNT": 1, "ANCOUNT": 0, "NSCOUNT": 0, "ARCOUNT": 0, "QNAME": "example.com", "QTYPE": 1, "QCLASS": 1 }
Les noms des membres sont ceux utilisés dans les
RFC DNS, même s'ils
ne sont pas très parlants (RCODE
au lieu
ReturnCode
).
On note que les différents membres qui sont dans le DNS
représentés par des entiers le sont également ici, au lieu
d'utiliser les abréviations courantes. Ainsi,
Opcode
est marqué 0 et pas Q
(query), et QTYPE
(query type) est marqué 1 et pas A (adresse
IPv4). Cela permet de représenter des valeurs inconnues, qui n'ont
pas d'abréviation textuelle, même si ça rend le résultat peu
lisible si on ne connait pas les valeurs des
paramètres DNS par coeur.
Les valeurs d'un seul bit (booléens) sont représentés par 0 ou
1, pas par les false
ou
true
de JSON. (J'avoue ne pas bien comprendre ce choix.)
On note également que les longueurs des sections sont indiquées
explicitement, ici QDCOUNT
(Query
Count, et ne me demandez pas à quoi sert le D après le
Q, le RFC 1035 ne l'explique pas). En JSON,
cela n'est pas obligatoire (la longueur d'un tableau, en JSON,
n'est pas spécifiée explicitement) mais, comme expliqué plus haut,
cela a été décidé pour permettre de représenter des messages DNS
anormaux, par exemple ayant un QDCOUNT
de 0
et une question dans la section Question (cf. section 8 du RFC sur
les conséquences que cela peut avoir pour la sécurité). De tels messages
arrivent assez souvent dans le trafic DNS réel vu par les serveurs
connectés à l'Internet ; attaque délibérée ou bien logiciel écrit
avec les pieds ?
Et voici un exemple de réponse (QR
= 1)
DNS en JSON. La requête a été un succès
(RCODE
= 0) :
{ "ID": 32784, "QR": 1, "AA": 1, "RCODE": 0, "QDCOUNT": 1, "ANCOUNT": 2, "NSCOUNT": 1, ARCOUNT": 0, "answerRRs": [ { "NAME": "example.com.", "TYPE": 1, "CLASS": 1, "TTL": 3600, "RDATAHEX": "C0000201" }, { "NAME": "example.com.", "TYPE": 1, "CLASS": 1, "TTL": 3600, "RDATAHEX": "C000AA01" } ], "authorityRRs": [ { "NAME": "ns.example.com.", "TYPE": 1, "CLASS": 1, "TTL": 28800, "RDATAHEX": "CB007181" } ]
La réponse contient un ensemble d'adresses IP
(TYPE
= 1 identifie une adresse IPv4),
192.0.2.1
et 192.0.170.1
. Leur
valeur est encodée en hexadécimal. C'est
moins joli que si on avait mis l'adresse IP en clair mais c'est
plus général : cela permet d'inclure immédiatement de nouveaux
types de données, au détriment de la lisibilité pour les anciens
types.
Le format de ce RFC permet aussi de décrire l'association entre
une requête et une réponse (section 3 du RFC). On les met dans un
objet JSON ayant un membre queryMessage
et un
responseMessage
.
Si on représente une suite continue de messages DNS, faire un objet JSON avec son accolade ouvrante et la fermante correspondante peut ne pas être pratique. On utilise alors les séquences du RFC 7464, décrites dans la section 4 de notre RFC.
Notre RFC spécifie également (section 7) un type
MIME pour le DNS en JSON, application/dns+json
.
Notez qu'il ne s'agit pas de la première description du DNS en
JSON. Par exemple, j'avais décrit un format pour cela dans le
brouillon draft-bortzmeyer-dns-json
. Ce format
est mis en œuvre dans le DNS Looking
Glass. Mon format était plus joli, car utilisant
toujours des noms plus parlants ("Type": "AAAA"
au lieu du "QTYPE": 28
). Notez toutefois que
le RFC 8427 le permet également ("QTYPEname":
"AAAA"
). Le format plus joli ne peut de toute façon pas
être utilisé systématiquement car il ne permet pas de représenter
les types inconnus. Et mon format ne permet pas non plus de
représenter les messages malformés. (Par exemple, le
ANCOUNT
est toujours implicite.)
Un autre exemple de représentation des données DNS est donné par les sondes RIPE Atlas. Le fichier des résultats d'une mesure est en JSON (ce qui permet le traitement par les outils JSON habituels comme jq). Voici un exemple (si vous voulez un exemple complet, téléchargez par exemple le résultat de la mesure #18061873) :
"resultset": [ { ... "result": { "ANCOUNT": 1, "ARCOUNT": 0, "ID": 38357, "NSCOUNT": 0, "QDCOUNT": 1, "abuf": "ldWBgAABAAEAAAAADmN5YmVyc3RydWN0dXJlAmZyAAAcAAHADAAcAAEAAVGAABAgAUuYDcAAQQIWPv/+Jz0/", "rt": 103.7, "size": 63 }, },
On note que seule une petite partie des
champs de la réponse (ANCOUNT
,
ID
…) est exprimée en JSON, la majorité de la requête
étant dans un membre abuf
qui est la
représentation binaire de la réponse.
Le service de DNS sur HTTPS de Google produit également du JSON (on le passe à jq pour qu'il soit plus joliment affiché) :
% curl -s https://dns.google.com/resolve\?name=laquadrature.net\&type=MX | jq . { "Status": 0, "TC": false, "RD": true, "RA": true, "AD": false, "CD": false, "Question": [ { "name": "laquadrature.net.", "type": 15 } ], "Answer": [ { "name": "laquadrature.net.", "type": 15, "TTL": 475, "data": "5 pi.lqdn.fr." } ] }
en utilisant un format
spécifique à Google.
On notera que le protocole DoH, normalisé dans le RFC 8484, n'utilise pas JSON (et le
service de Google, contrairement à ce qu'on voit parfois écrit,
n'utilise pas DoH) mais le format binaire du DNS, utilisant le
type MIME application/dns-message
. Le RFC 8484 prévoit
toutefois la possibilité de se servir de JSON pour le futur (section
4.2 du RFC 8484).
Date de publication du RFC : Août 2018
Auteur(s) du RFC : Y. Nir (Check Point), S. Josefsson
(SJD AB), M. Pegourie-Gonnard (Independent /
PolarSSL)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 7 août 2018
Ce RFC décrit les algorithmes cryptographiques à base de courbes elliptiques utilisés dans TLS. Il remplace le RFC 4492.
Plus exactement, il normalise les algorithmes utilisés dans les versions de TLS allant jusqu'à 1.2 incluse. L'utilisation des courbes elliptiques par TLS 1.3 est décrite dans le RFC sur TLS 1.3, le RFC 8446. Les deux points importants de ce nouveau RFC sont :
Commençons par l'échange de clés de session (section 2). TLS nécessite que les deux pairs se mettent d'accord sur une clé de chiffrement symétrique qui sera ensuite utilisée pendant toute la session, avec des algorithmes comme AES. Une des façons de synchroniser cette clé de session est qu'un des pairs la génère aléatoirement, puis la chiffre avec la clé publique (chiffrement asymétrique) de son pair avant de lui transmettre (cela marche avec RSA mais je n'ai pas l'impression qu'il y ait un moyen normalisé de faire cela avec les courbes elliptiques). Une autre façon est d'utiliser un échange Diffie-Hellman. Contrairement à l'échange Diffie-Hellman originel, celui présenté dans ce RFC, ECDHE, utilise la cryptographie sur courbes elliptiques. (J'ai simplifié pas mal : par exemple, l'échange ECDHE ne donnera pas directement la clé de session, celle-ci sera en fait dérivée de la clé obtenue en ECDHE.) Le principal avantage de Diffie-Hellman est de fournir de la sécurité même en cas de compromission ultérieure de la clé privée.
Notre RFC présente trois variantes d'ECDHE, selon la manière dont l'échange est authentifié, l'une utilisant ECDSA ou EdDSA, l'autre utilisant le traditionnel RSA, et la troisième n'authentifiant pas du tout, et étant donc vulnérable aux attaques de l'Homme du Milieu, sauf si une authentification a lieu en dehors de TLS. (Attention, dans le cas de ECDHE_RSA, RSA n'est utilisé que pour authentifier l'échange, la génération de la clé se fait bien en utilisant les courbes elliptiques.)
Lorsque l'échange est authentifié (ECDHE_ECDSA
- qui, en dépit de son nom, inclut EdDSA -
ou bien ECDHE_RSA
), les paramètres
ECDH (Diffie-Hellman avec courbes elliptiques)
sont signés par la
clé privée (ECDSA, EdDSA ou RSA). S'il n'est pas authentifié
(ECDH_anon
, mais notez que le nom est trompeur,
bien que le E final - ephemeral - manque, la clé
est éphémère), on n'envoie évidemment pas de certificat, ou de demande
de certificat.
Voilà, avec cette section 2, on a pu générer une clé de session
avec Diffie-Hellman, tout en authentifiant le serveur avec des courbes
elliptiques. Et pour l'authentification du client ? C'est la section 3
de notre RFC. Elle décrit un mécanisme ECDSA_sign
(là encore, en dépit du nom du mécanisme, il fonctionne aussi bien
pour EdDSA), où le client s'authentifie en signant ses messages avec
un algorithme à courbes elliptiques.
Les courbes elliptiques ont quelques particularités qui justifient
deux extensions à TLS que présente la section 4 du RFC. Il y a
Supported Elliptic Curves Extension et Supported Point
Formats Extension, qui permettent de décrire les
caractéristiques de la courbe elliptique utilisée (on verra plus loin
que la deuxième extension ne sert plus guère). Voici, vue par
tshark, l'utilisation de ces extensions dans un
ClientHello
TLS envoyé par OpenSSL :
Extension: elliptic_curves Type: elliptic_curves (0x000a) Length: 28 Elliptic Curves Length: 26 Elliptic curves (13 curves) Elliptic curve: secp256r1 (0x0017) Elliptic curve: secp521r1 (0x0019) Elliptic curve: brainpoolP512r1 (0x001c) Elliptic curve: brainpoolP384r1 (0x001b) Elliptic curve: secp384r1 (0x0018) Elliptic curve: brainpoolP256r1 (0x001a) Elliptic curve: secp256k1 (0x0016) ... Extension: ec_point_formats Type: ec_point_formats (0x000b) Length: 4 EC point formats Length: 3 Elliptic curves point formats (3) EC point format: uncompressed (0) EC point format: ansiX962_compressed_prime (1) EC point format: ansiX962_compressed_char2 (2)
La section 5 du RFC donne les détails concrets. Par exemple, les deux extensions citées plus haut s'écrivent, dans le langage de TLS (cf. section 4 du RFC 5246) :
enum { elliptic_curves(10), ec_point_formats(11) } ExtensionType;
La première extension permet d'indiquer les courbes utilisées. Avec celles du RFC 7748, cela donne, comme possibilités :
enum { deprecated(1..22), secp256r1 (23), secp384r1 (24), secp521r1 (25), x25519(29), x448(30), reserved (0xFE00..0xFEFF), deprecated(0xFF01..0xFF02), (0xFFFF) } NamedCurve;
secp256r1
est la courbe P-256 du
NIST, x25519
est la
Curve-25519 de Bernstein. Notez que beaucoup
des courbes de l'ancien RFC 4492, jamais très utilisées, ont
été abandonnées. (Les courbes se trouvent dans un
registre IANA.)
Normalement, dans TLS, on peut choisir séparément l'algorithme de
signature et celui de condensation (cf. section
7.4.1.4.1 du RFC 5246). Avec certains
algorithmes comme EdDSA dans sa forme « pure », il n'y a pas de
condensation séparée et un « algorithme » bidon,
Intrinsic
(valeur 8) a été créé pour mettre
dans le champ « algorithme de condensation » de l'extension signature_algorithms
.
Voici une négociation TLS complète, vue par curl :
% curl -v https://www.nextinpact.com ... * Connected to www.nextinpact.com (2400:cb00:2048:1::6819:f815) port 443 (#0) ... * Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH ... * SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256 * ALPN, server accepted to use h2 * Server certificate: * subject: C=US; ST=CA; L=San Francisco; O=CloudFlare, Inc.; CN=nextinpact.com ... > GET / HTTP/1.1 > Host: www.nextinpact.com > User-Agent: curl/7.52.1 > Accept: */*
On voit que l'algorithme utilisé par TLS est
ECDHE-ECDSA-AES128-GCM-SHA256
, ce qui indique
ECDHE avec ECDSA. Le certificat du serveur doit donc inclure une clé
« courbe elliptique ». Regardons ledit certificat :
% openssl s_client -connect www.nextinpact.com:443 -showcerts | openssl x509 -text Certificate: ... Signature Algorithm: ecdsa-with-SHA256 Issuer: C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO ECC Domain Validation Secure Server CA 2 ... Subject: OU = Domain Control Validated, OU = PositiveSSL Multi-Domain, CN = ssl378410.cloudflaressl.com Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) ... ASN1 OID: prime256v1 NIST CURVE: P-256 ... X509v3 Subject Alternative Name: DNS:ssl378410.cloudflaressl.com, DNS:*.baseballwarehouse.com, DNS:*.campusgroups.com, DNS:*.cretedoc.gr, DNS:*.groupment.com, DNS:*.icstage.com, DNS:*.ideacouture.com, DNS:*.industrialtour-deloitte.com, DNS:*.jonessnowboards.com, DNS:*.nextinpact.com, DNS:*.pcinpact.com, DNS:*.pinkapple.com, DNS:*.softballrampage.com, DNS:*.undercovercondoms.co.uk, DNS:baseballwarehouse.com, DNS:campusgroups.com, DNS:cretedoc.gr, DNS:groupment.com, DNS:icstage.com, DNS:ideacouture.com, DNS:industrialtour-deloitte.com, DNS:jonessnowboards.com, DNS:nextinpact.com, DNS:pcinpact.com, DNS:pinkapple.com, DNS:softballrampage.com, DNS:undercovercondoms.co.uk Signature Algorithm: ecdsa-with-SHA256
On a bien une clé sur la courbe P-256.
Quel est l'état des mises en œuvre de ces algorithmes dans les bibliothèques TLS existantes ? ECDHE et ECDSA avec les courbes NIST sont très répandus. ECDHE avec la courbe Curve25519 est également dans plusieurs bibliothèques TLS. Par contre, EdDSA, ou ECDHE avec la courbe Curve448, sont certes implémentés mais pas encore largement déployés.
Les changements depuis le RFC 4492 sont résumés dans l'annexe B. Souvent, une norme récente ajoute beaucoup de choses par rapport à l'ancienne mais, ici, pas mal de chose ont au contraire été retirées :
Parmi les ajouts, le plus important est évidemment l'intégration des « courbes Bernstein », Curve25519 et Curve448, introduites par le RFC 7748. Et il y avait également plusieurs erreurs techniques dans le RFC 4492, qui sont corrigées par notre nouveau RFC.
Et, pendant que j'y suis, si vous voulez générer un certificat avec les courbes elliptiques, voici comment faire avec OpenSSL :
% openssl ecparam -out ec_key.pem -name prime256v1 -genkey % openssl req -new -key ec_key.pem -nodes -days 1000 -out cert.csr
J'ai utilisé ici la courbe P-256 (prime256v1
est encore un autre identificateur pour la courbe
NIST P-256, chaque organisme qui normalise dans ce domaine ayant ses
propres identificateurs). Si vous voulez la liste des courbes que
connait OpenSSL :
% openssl ecparam -list_curves
Ce blog est accessible en TLS mais pas avec des courbes elliptiques. En effet, l'AC que j'utilise, CAcert, ne les accepte hélas pas (« The keys you supplied use an unrecognized algorithm. For security reasons these keys can not be signed by CAcert. ») Il y a des raisons pour cela mais c'est quand même déplorable. (Enfin, j'accepte quand même ECDHE.)
Enfin, un échange TLS complet vu par tshark est visible ici.
Merci à Manuel Pégourié-Gonnard pour sa relecture vigilante.
Date de publication du RFC : Novembre 2018
Auteur(s) du RFC : T. Mrugalski, M. Siodelski (ISC), B. Volz, A. Yourtchenko (Cisco), M. Richardson (SSW), S. Jiang (Huawei), T. Lemon (Nibbhaya Consulting), T. Winters (UNH-IOL)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dhc
Première rédaction de cet article le 3 mai 2019
IPv6 dispose de trois mécanismes principaux pour l'allocation d'une adresse IP à une machine. L'allocation statique, « à la main », le système d'« autoconfiguration » SLAAC du RFC 4862 et DHCP. DHCP pour IPv6 avait été normalisé pour la première fois dans le RFC 3315, que notre RFC met à jour. Le protocole n'a guère changé mais le texte du RFC a été sérieusement revu (DHCP est un protocole compliqué).
DHCP permet à une machine (qui n'est pas forcément un ordinateur) d'obtenir une adresse IP (ainsi que plusieurs autres informations de configuration) à partir d'un serveur DHCP du réseau local. C'est donc une configuration « avec état », du moins dans son mode d'utilisation le plus connu. (Notre RFC documente également un mode sans état.) DHCP nécessite un serveur, par opposition à l'autoconfiguration du RFC 4862 qui ne dépend pas d'un serveur (cette autoconfiguration sans état peut être utilisée à la place de, ou bien en plus de DHCP). Deux utilisations typiques de DHCP sont le SoHo où le routeur ADSL est également serveur DHCP pour les trois PC connectés et le réseau local d'entreprise où deux ou trois machines Unix distribuent adresses IP et informations de configuration à des centaines de machines.
Le principe de base de DHCP (IPv4 ou IPv6) est simple : la nouvelle machine annonce à la cantonade qu'elle cherche une adresse IP, le serveur lui répond, l'adresse est allouée pour une certaine durée, le bail, la machine cliente devra renouveler le bail de temps en temps.
L'administrateur d'un réseau IPv6 se pose souvent la question « DHCP ou SLAAC » ? Notez que les deux peuvent coexister, ne serait-ce que parce que certaines possibilités n'existent que pour un seul des deux protocoles. Ainsi, DHCP seul ne peut indiquer l'adresse du routeur par défaut. Pour le reste, c'est une question de goût.
Le DHCP spécifié par notre RFC ne fonctionne que pour IPv6, les RFC 2131 et RFC 2132 traitant d'IPv4. Les deux protocoles restent donc complètement séparés, le RFC 4477 donnant quelques idées sur leur coexistence. Il a parfois été question de produire une description unique de DHCPv4 et DHCPv6, ajoutant ensuite les spécificités de chacun, mais le projet n'a pas eu de suite (section 1.2 de ce RFC), les deux protocoles étant trop différents. De même, l'idée de permettre l'envoi d'informations spécifiques à IPv4 (par exemple l'adresse IPv4, proposée dans le RFC 3315, section 1) au-dessus de DHCPv6 a été abandonnée (mais voyez quand même le RFC 7341).
DHCP fonctionne par diffusion
restreinte. Un client DHCP,
c'est-à-dire une machine qui veut obtenir une adresse, diffuse
(DHCP fonctionne au-dessus d'UDP, RFC 768, le
port source est 546, le port de destination,
où le serveur écoute, est 547) sa demande à l'adresse
multicast locale au lien
ff02::1:2
. Le serveur se reconnait et lui
répond. S'il n'y a pas de réponse, c'est, comme dans le DNS, c'est au client de réémettre
(section 15). L'adresse IP source du client est également une
adresse locale au lien.
(Notez qu'une autre adresse de diffusion restreinte est
réservée, ff05::1:3
; elle inclut également
tous les serveurs DHCP mais, contrairement à la précédente, elle
exclut les relais, qui transmettent les requêtes DHCP d'un réseau
local à un autre.)
Le serveur choisit sur quels critères il alloue les adresses IP. Il peut les distribuer de manière statique (une même machine a toujours la même adresse IP) ou bien les prendre dans un pool d'adresses et chaque client aura donc une adresse « dynamique ». Le fichier de configuration du serveur DHCP ISC ci-dessous montre un mélange des deux approches.
Il faut bien noter (et notre RFC le fait dans sa section 22) que DHCP n'offre aucune sécurité. Comme il est conçu pour servir des machines non configurées, sur lesquelles on ne souhaite pas intervenir, authentifier la communication est difficile. Un serveur DHCP pirate, ou, tout simplement, un serveur DHCP accidentellement activé, peuvent donc être très gênants. Les approches de sécurité suggérées dans le RFC 3315 se sont avérées peu pratiques et plusieurs ont été retirées dans notre nouveau RFC.
Outre l'adresse IP, DHCP peut indiquer des options comme les adresses des serveurs DNS à utiliser (RFC 3646).
Notre version IPv6 de DHCP est assez différente de la version IPv4 (et le RFC est plus de trois fois plus long). Par exemple, l'échange « normal » entre client et serveur prend quatre paquets IP (section 5) et non pas deux. (Il y a aussi un échange simplifié à deux paquets, cf. section 5.1.) L'encodage des messages est très différent, et il y a des différences internes comme l'IA (Identity Association) de la section 12. Il y a aussi des différences visibles à l'utilisateur comme le concept de DUID (DHCP Unique IDentifier), section 11, qui remplace les anciens client identifier et server identifier de DHCP v4. Les différences sont telles que le RFC précise que leur intégration avec DHCP pour IPv4 n'est pas envisagée.
À l'heure actuelle, il existe plusieurs mises en œuvre de DHCPv6, Dibbler (client et serveur, mais qui n'est plus maintenu), celle de l'Institut Leibniz à Dresde (serveur seulement), celles de l'ISC, l'ancien DHCP et le nouveau, nommé Kea (dans les deux cas, serveur seulement) et dhcpcd (client seulement). Pour celles et ceux qui utilisent une Freebox comme serveur DHCP, il semble qu'elle ait DHCPv6 depuis 2018 (je n'ai pas testé). Il parait que la Livebox le fait également. Je n'ai pas non plus essayé pour la Turris Omnia mais cela devrait marcher puisqu'elle utilise le serveur odhcpd, qui sait faire du DHCPv6. Et il y a bien sûr des implémentations non-libres dans des équipements comme les Cisco. Notez que ces mises en œuvre de DHCPv6 n'ont pas forcément déjà intégré les modifications de notre RFC 8415.
Voici un exemple d'utilisation de Dibbler, qui nous affiche les
quatre messages (Solicit - Advertise -
Request - Reply
) :
% sudo dibbler-client run ... 2019.05.02 11:35:15 Client Notice Unable to open DUID file (client-duid), generating new DUID. 2019.05.02 11:35:15 Client Notice DUID creation: Generating 14-bytes long link-local+time (duid-llt) DUID. 2019.05.02 11:35:15 Client Info My DUID is 00:01:00:01:24:5d:76:53:00:1e:8c:76:29:b6. ... 2019.05.02 12:12:38 Client Info Creating SOLICIT message with 1 IA(s), no TA and 0 PD(s) on eth1/2 interface. 2019.05.02 12:12:38 Client Info Received ADVERTISE on eth1/2,trans-id=0x614722, 4 opts: 1 2 3 23 2019.05.02 12:12:39 Client Info Processing msg (SOLICIT,transID=0x614722,opts: 1 3 8 6) 2019.05.02 12:12:39 Client Info Creating REQUEST. Backup server list contains 1 server(s). 2019.05.02 12:12:39 Client Info Received REPLY on eth1/2,trans-id=0x634881, 4 opts: 1 2 3 23 2019.05.02 12:12:39 Client Notice Address fde8:9fa9:1aba:0:fafa::2/128 added to eth1/2 interface. 2019.05.02 12:12:39 Client Notice Setting up DNS server 2001:db8:2::dead:beef on interface eth1/2.
Le serveur en face était un Kea ainsi configuré :
"subnet6": [ { "interface": "eth0", "subnet": "fde8:9fa9:1aba:0::/64", "pools": [ { "pool": "fde8:9fa9:1aba:0:fafa::/80" } ], ...
Pour que Kea puisse écouter sur le port DHCP, il a aussi fallu :
% sudo setcap 'cap_net_bind_service=+ep' /usr/sbin/kea-dhcp6
(Sinon, c'est le Permission denied.) Si vous
voulez, le pcap de l'échange est disponible (capture faite avec tcpdump -w /tmp/dhcpv6.pcap udp and port 546 or port 547
).
tcpdump voit le trafic ainsi :
12:17:44.531859 IP6 fe80::21e:8cff:fe76:29b6.546 > ff02::1:2.547: dhcp6 solicit 12:17:44.555202 IP6 fe80::d40b:5ff:fee8:a36b.547 > fe80::21e:8cff:fe76:29b6.546: dhcp6 advertise 12:17:45.559247 IP6 fe80::21e:8cff:fe76:29b6.546 > ff02::1:2.547: dhcp6 request 12:17:45.567875 IP6 fe80::d40b:5ff:fee8:a36b.547 > fe80::21e:8cff:fe76:29b6.546: dhcp6 reply
On voit bien les quatre messages (Solicit - Advertise -
Request - Reply
). Avec l'option
-vvv
, tcpdump est plus bavard et montre qu'il
analyse bien DHCPv6 :
12:17:44.531859 IP6 (flowlabel 0x90d05, hlim 1, next-header UDP (17) payload length: 58) fe80::21e:8cff:fe76:29b6.546 > ff02::1:2.547: [udp sum ok] dhcp6 solicit (xid=aecc66 (client-ID hwaddr/time type 1 time 610104915 001e8c7629b6) (IA_NA IAID:1 T1:4294967295 T2:4294967295) (elapsed-time 0) (option-request DNS-server)) 12:17:44.555202 IP6 (flowlabel 0xa6d2a, hlim 64, next-header UDP (17) payload length: 128) fe80::d40b:5ff:fee8:a36b.547 > fe80::21e:8cff:fe76:29b6.546: [udp sum ok] dhcp6 advertise (xid=aecc66 (client-ID hwaddr/time type 1 time 610104915 001e8c7629b6) (server-ID hwaddr/time type 1 time 610105065 d60b05e8a36b) (IA_NA IAID:1 T1:1000 T2:2000 (IA_ADDR fde8:9fa9:1aba:0:fafa::4 pltime:3000 vltime:4000)) (DNS-server 2001:db8:2::dead:beef 2001:db8:2::cafe:babe)) 12:17:45.559247 IP6 (flowlabel 0x90d05, hlim 1, next-header UDP (17) payload length: 104) fe80::21e:8cff:fe76:29b6.546 > ff02::1:2.547: [udp sum ok] dhcp6 request (xid=dc7ba (client-ID hwaddr/time type 1 time 610104915 001e8c7629b6) (IA_NA IAID:1 T1:4294967295 T2:4294967295 (IA_ADDR fde8:9fa9:1aba:0:fafa::4 pltime:3000 vltime:4000)) (option-request DNS-server) (server-ID hwaddr/time type 1 time 610105065 d60b05e8a36b) (elapsed-time 0)) 12:17:45.567875 IP6 (flowlabel 0xa6d2a, hlim 64, next-header UDP (17) payload length: 128) fe80::d40b:5ff:fee8:a36b.547 > fe80::21e:8cff:fe76:29b6.546: [udp sum ok] dhcp6 reply (xid=dc7ba (client-ID hwaddr/time type 1 time 610104915 001e8c7629b6) (server-ID hwaddr/time type 1 time 610105065 d60b05e8a36b) (IA_NA IAID:1 T1:1000 T2:2000 (IA_ADDR fde8:9fa9:1aba:0:fafa::4 pltime:3000 vltime:4000)) (DNS-server 2001:db8:2::dead:beef 2001:db8:2::cafe:babe))
Mais si vous préférez tshark, l'analyse de cet échange est également disponible.
Quant au fichier de configuration du traditionnel serveur ISC (celui avant Kea), il ressemble beaucoup à ce qu'il est en v4 :
subnet6 2001:db8:dead:babe::/64 { range6 2001:db8:dead:babe::100 2001:db8:dead:babe::FFF; # On peut aussi utiliser préfixe/longueur au lieu d'indiquer les # adresses de début et de fin de la plage }
On doit lancer le serveur avec l'option -6 (le même démon ne peut pas servir le v4 et le v6 en même temps, les deux protocoles étant trop différents) :
# dhcpd -6 -d -f Internet Systems Consortium DHCP Server 4.0.0 ... Listening on Socket/eth0/2001:db8:dead:babe::/64 Sending on Socket/eth0/2001:db8:dead:babe::/64 [Puis arrive une requête] Solicit message from fe80::219:b9ff:fee4:25f9 port 546, transaction ID 0x4BB14F00 Picking pool address 2001:db8:dead:babe::fbb Sending Advertise to fe80::219:b9ff:fee4:25f9 port 546 Request message from fe80::219:b9ff:fee4:25f9 port 546, transaction ID 0x46B10900 Sending Reply to fe80::219:b9ff:fee4:25f9 port 546
(Le concept de transaction ID est décrit
sections 8 et 16.1.) La requête est émise depuis une adresse
lien-local (ici
fe80::219:b9ff:fee4:25f9
) pas depuis une
adresse « tout zéro » comme en IPv4 (section 17 du RFC). Vu avec
tcpdump, la requête est :
15:07:43.455918 IP6 fe80::219:b9ff:fee4:25f9.546 > ff02::1:2.547: dhcp6 solicit 15:07:43.456098 IP6 fe80::219:b9ff:fee4:2987.547 > fe80::219:b9ff:fee4:25f9.546: dhcp6 advertise 15:07:44.512946 IP6 fe80::219:b9ff:fee4:25f9.546 > ff02::1:2.547: dhcp6 request 15:07:44.513233 IP6 fe80::219:b9ff:fee4:2987.547 > fe80::219:b9ff:fee4:25f9.546: dhcp6 reply
On note que l'échange a été celui à quatre messages
(Solicit - Advertise - Request - Reply
),
décrit section 5.2 (et la liste des types possibles est en section 7.3). Le serveur n'a pas répondu directement avec un
Reply
, parce que le client n'a pas inclus
l'option Rapid Commit
(section 21.14). Cette
option n'est pas actuellement gérée par le client DHCP utilisé
(l'option dhcp6.rapid-commit
existe mais la
documentation précise qu'elle est ignorée). Dans l'échange à quatre
message,s, le client demande à tous (Solicit
),
un(s) serveur(s) DHCP répond(ent) (Advertise
), le client
envoie alors sa requête au serveur choisi
(Request
), le serveur donne (ou pas) son
accord (Reply
).
L'échange à deux messages (Solicit- Reply
)
est, lui, spécifié dans la section 5.1. Il s'utilise si le client
n'a pas besoin d'une adresse IP, juste d'autres informations de
configuration comme l'adresse du serveur
NTP, comme décrit dans le RFC 4075. Même si le client demande une adresse IP, il est
possible d'utiliser l'échange à deux messages, via la procédure
rapide avec l'option Rapid Commit
.
Actuellement, l'attribution d'adresses statiques à une machine,
en la reconnaissant, par exemple, à son adresse
MAC est plus délicate avec le serveur de l'ISC (merci à
Shane Kerr pour son aide). Il faut trouver le client
identifier (section 21.2 du RFC, deux méthodes possibles
pour le trouver sont expliquées plus loin) et le mettre dans
dhcpd.conf
:
host lilith { host-identifier option dhcp6.client-id 0:1:0:1:47:96:21:f7:0:19:b9:e4:25:f9; fixed-address6 2001:db8:dead:babe::2; }
et cette adresse IP fixe est donnée au client.
Pour trouver le client identifier, une
méthode spécifique au client DHCP de l'ISC est de regarder dans le
fichier des baux du client (typiquement
/var/db/dhclient6.leases
) :
... option dhcp6.client-id 0:1:0:1:47:96:21:f7:0:19:b9:e4:25:f9;
Il suffit alors de le copier-coller.
Une autre méthode, plus complexe, mais qui marche avec tous les clients DHCP est de lancer tcpdump en mode bavard :
# tcpdump -n -vvv ip6 and udp and port 547 12:24:15.084006 IP6 (hlim 64, next-header UDP (17) payload length: 60) fe80::219:b9ff:fee4:25f9.546 > ff02::1:2.547: dhcp6 solicit (xid=4323ac (client ID hwaddr/time type 1 time 1201021431 0019b9e425f9) (option request DNS DNS name)[|dhcp6ext])
Tout client ou serveur DHCP v6 a un DUID (DHCP Unique
Identifier, décrit en section 11). Le DUID est opaque et
ne devrait pas être analysé par la machine qui le reçoit. La seule
opération admise est de tester si deux DUID sont égaux (indiquant
qu'en face, c'est la même machine). Il existe plusieurs façons de
générer un DUID (dans l'exemple plus haut, Dibbler avait choisi la
méthode duid-llt
, adresse locale et heure) et de nouvelles pourront apparaitre dans le
futur. Par exempe, un DUID peut être fabriqué à partir d'un
UUID (RFC 6355).
Le client identifier de l'exemple avec le serveur de l'ISC ci-dessus, le DUID, a été fabriqué, comme pour Dibbler, en concaténant le type de DUID (ici, 1, Link-layer Address Plus Time, section 11.2 du RFC), le type de matériel (1 pour Ethernet), le temps (ici 1201021431, notons que ce client DHCP violait le RFC en comptant les secondes à partir de 1970 et pas de 2000) et l'adresse MAC, ce qui redonne le même résultat au prix de quelques calculs avec bc.
Mais l'utilisation exclusive du DUID, au détriment de l'adresse MAC, n'est pas une obligation du RFC (le RFC, section 11, dit juste « DHCP servers use DUIDs to identify clients for the selection of configuration parameters », ce qui n'interdit pas d'autres méthodes), juste un choix des développeurs de l'ISC. Le serveur de Dresde, dhcpy6d, permet d'utiliser les adresses MAC, comme on le fait traditionnellement en IPv4. En combinaison avec des commutateurs qui filtrent sur l'adresse MAC, cela peut améliorer la sécurité.
La section 6 de notre RFC décrit les différentes façons d'utiliser DHCPv6. On peut se servir de DHCPv6 en mode sans état (section 6.1), lorsqu'on veut juste des informations de configuration, ou avec état (section 6.2, qui décrit la façon historique d'utiliser DHCP), lorsqu'on veut réserver une ressource (typiquement l'adresse IP) et qu'il faut alors que le serveur enregistre (et pas juste dans sa mémoire, car il peut redémarrer) ce qui a été réservé. On peut aussi faire quelque chose qui n'a pas d'équivalent en IPv4, se faire déléguer un préfixe d'adresses IP entier (section 6.3). Un client DHCP qui reçoit un préfixe, mettons, /60, peut ensuite redéléguer des bouts, par exemple ici des /64. (Le RFC 7084 est une utile lecture au sujet des routeurs installés chez M. Toutlemonde.)
Le format détaillé des messages est dans la section 8. Le début des messages est toujours le même, un type d'un octet (la liste des types est en section 7.3) suivi d'un identificateur de transaction de trois octets. Le reste est variable, dépendant du type de message.
On a déjà parlé du concept de DUID plus haut, donc sautons la section 11 du RFC, qui parle du DUID, et allons directement à la section 12, qui parle d'IA (Identity Association). Une IA est composée d'un identifiant numérique, l'IAID (IA IDentifier) et d'un ensemble d'adresses et de préfixes. Le but du concept d'IA est de permettre de gérer collectivement un groupe de ressources (adresses et préfixes). Pour beaucoup de clients, le concept n'est pas nécessaire, on n'a qu'une IA, d'identificateur égal à zéro. Pour les clients plus compliqués, on a plusieurs IA, et les messages DHCP (par exemple d'abandon d'un bail) indiquent l'IA concernée.
Comme pour DHCPv4, une bonne partie des informations est transportée dans des options, décrites dans la section 21. Certaines options sont dans ce RFC, d'autres pourront apparaitre dans des RFC ultérieurs. Toutes les options commencent par deux champs communs, le code identifiant l'option (deux octets), et la longueur de l'option. Ces champs sont suivis par des données, spécifiques à l'option. Ainsi, l'option Client Identifier a le code 1, et les données sont un DUID (cf. section 11). Autre exemple, l'option Vendor Class (code 16) permet d'indiquer le fournisseur du logiciel client (notez qu'elle pose des problèmes de sécurité, cf. RFC 7824, et section 23 de notre RFC). Notez qu'il peut y avoir des options dans les options, ainsi, l'adresse IP (code 5) est toujours dans les données d'une option IA (les IA sont décrites en section 12).
Puisqu'on a parlé de sécurité, la section 22 du RFC détaille les questions de sécurité liées à DHCP. Le fond du problème est qu'il y a une profonde incompatibilité entre le désir d'une autoconfiguration simple des clients (le but principal de DHCP) et la sécurité. DHCP n'a pas de chiffrement et tout le monde peut donc écouter les échanges de messages, voire les modifier. Et, de toute façon, le serveur n'est pas authentifié, donc le client ne sait jamais s'il parle au serveur légitime. Il est trivial pour un méchant de configurer un serveur DHCP « pirate » et de répondre à la place du vrai, indiquant par exemple un serveur DNS que le pirate contrôle. Les RFC 7610 et RFC 7513 décrivent des solutions possibles à ce problème.
Des attaques par déni de service sont également possibles, par exemple via un client méchant qui demande des ressources (comme des adresses IP) en quantité. Un serveur prudent peut donc limiter la quantité de ressources accessible à un client.
Nouveauté de ce RFC (elle était quasiment absente du RFC 3315), les questions de vie privée. La section 23 rappelle que DHCP est très indiscret. Le RFC 7824 décrit les risques que DHCP fait courir à la vie privée du client (et le RFC 7844 des solutions possibles).
Les registres IANA ne changent pas par rapport à l'ancien RFC. Les différents paramètres sont en ligne.
L'annexe A de notre RFC décrit les changements depuis le vieil RFC 3315. Ils sont nombreux mais en général pas cruciaux. On notera :
Date de publication du RFC : Août 2018
Auteur(s) du RFC : S. Josefsson (SJD AB), J. Schaad (August Cellars)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 2 février 2019
Ce RFC spécifie l'utilisation des
courbes elliptiques
Curve25519 et
Curve448 dans PKIX,
c'est-à-dire dans les certificats utilisés notamment pour
TLS. Il réserve des identifiants pour les
algorithmes, comme Ed25519
.
Les courbes elliptiques Curve25519 et Curve448 sont normalisées dans le RFC 7748. Elles sont utilisées pour diverses opérations cryptographiques comme la signature. L'algorithme EdDSA, utilisant ces courbes, est, lui, dans le RFC 8032. Quand on écrit « Ed25519 », cela veut dire « EdDSA avec la courbe Curve25519 ». D'autre part, quand on écrit « X25519 », cela signifie « échange de clés Diffie-Hellman avec la courbe Curve25519 ».
Un certificat suivant le profil
PKIX du RFC 5280
contient une clé publique, pour un algorithme cryptographique
donné. Il faut donc un identificateur formel pour cet
algorithme. Sa syntaxe est celle d'un OID,
par exemple 1.2.840.113549.1.1.1
(cf. RFC 8017) pour
RSA. Notre
RFC définit
(section 3) quatre nouveaux identificateurs :
id-X25519
:
1.3.101.110
(ou, en détaillé,
{iso(1) identified-organization(3) thawte(101)
id-X25519(110)}
) ; notez qu'il avait été enregistré
par Thawte, depuis racheté
par Symantec, ce qui explique pourquoi le
RFC remercie Symantec,id-X448
: 1.3.101.111
,id-Ed25519
: 1.3.101.112
,id-Ed448
: 1.3.101.113
.Le RFC recommande aussi des noms plus sympathiques que les OID, OID qu'on ne peut vraiment pas montrer aux utilisateurs. Ces noms sont ceux décrits plus haut, comme Ed25519 pour EdDSA avec Curve25519. Si on ne sait pas quelle courbe est utilisée, on dit simplement « EdDSA » pour la signature, et « ECDH » pour l'échange de clés Diffie-Hellman.
Les nouveaux identificateurs et OID sont désormais dans le le registre IANA.
Le RFC fournit aussi des structures ASN.1, avec des exemples en section 10.
Notez qu'OpenSSL, avec openssl
x509 -text
, ou GnuTLS, avec
certtool --certificate-info
, n'affichent pas
les OID, seulement les identificateurs texte.
Date de publication du RFC : Juillet 2018
Auteur(s) du RFC : K. Moriarty (Dell EMC), A. Morton
(AT&T Labs)
Pour information
Première rédaction de cet article le 23 novembre 2018
La vie privée sur l'Internet fait aujourd'hui l'objet d'innombrables attaques et l'une des techniques de défense les plus efficaces contre ces attaques est le chiffrement des données. Il protège également contre une autre menace, la modification des données en transit, comme le font certaines FAI (par exemple Orange Tunisie). Il y a de nombreuses campagnes de sensibilisation pour promouvoir le chiffrement (voir par exemple le RFC 7258), avec des bons résultats. Évidemment, ce chiffrement gène ceux qui voudraient espionner et modifier le trafic, et il fallait donc s'attendre à voir une réaction. Ce RFC est l'expression de cette réaction, du côté de certains opérateurs réseau, qui regrettent que le chiffrement empêche leurs mauvaises pratiques.
Dès le début, ce RFC était basé sur une hypocrisie : prétendre être purement descriptif (une liste de pratiques que certains opérateurs réseau utilisent, et qui sont impactées par le chiffrement), sans forcément en conclure que le chiffrement était mauvais. Mais, en réalité, le RFC porte un message souvent répété : le chiffrement gène, et il ne faudrait pas en abuser. On note par exemple que le RFC ne s'indigne pas de certaines des pratiques citées, alors que beaucoup sont scandaleuses. Le discours est « il faut trouver un équilibre entre la protection de la vie privée et la capacité des opérateurs à gérer leurs réseaux ». Une telle référence à l'équilibre m'a toujours énervé : non, il ne faut pas d'équilibre entre le bien et le mal, il faut faire ce qu'on peut pour gêner la surveillance. Certaines des pratiques citées dans le RFC sont mauvaises et tant mieux si le chiffrement les rend difficiles. Certaines autres sont plus neutres, mais devront quand même s'adapter, les révélations de Snowden ont largement montré que la surveillance de masse est un fait, et qu'elle justifie des mesures radicales de protection. (Notez que les premières versions de ce RFC étaient bien pires, et qu'il a été affadi par les discussions successives, et suite à l'opposition rencontrée.)
Hypocritement, les textes sacrés comme le RFC 1958, RFC 1984, RFC 2804, et bien sûr les RFC 7258 et RFC 7624 sont cités, hommage du vice à la vertu. Mais tout en le faisant, ce nouveau RFC 8404 déforme ces textes. Quand le RFC 7258 dit qu'évidemment, il faut que les opérateurs puissent continuer à gérer leurs réseaux, ce RFC 8404 lui fait dire qu'il faut donc limiter le chiffrement, comme si « gérer un réseau » voulait forcément dire « accéder aux communications ». De même, on trouve une référence au principe de bout en bout (RFC 2775, RFC 3724, RFC 7754), alors même que le reste de ce RFC ne fait que citer des pratiques violant ce principe.
Ce RFC 8404 se veut, on l'a dit, une description de pratiques actuelles, et prétend hypocritement « ne pas forcément soutenir toutes les pratiques présentées ici ». En effet, tous les exemples cités par la suite sont réellement utilisées mais pas forcément par tous les opérateurs. C'est une des choses les plus désagréables de ce RFC que de laisser entendre que tous les opérateurs seraient unanimes dans leur désir de lire les communications de leurs clients, voire de les modifier. (Par exemple en utilisant le terme tribal de community pour parler des opérateurs réseau, comme s'ils étaient un clan unique, d'accord sur l'essentiel.)
Il est exact qu'au début de l'Internet, rien ou presque n'était chiffré. Tout circulait en clair, en partie parce que la perte de performances semblait excessive, en partie parce que les États faisaient tout pour décourager le chiffrement et limiter son usage, en partie parce que la menace semblait lointaine (même les plus paranoïaques des experts en sécurité ne se doutaient pas que la surveillance atteignait les proportions révélées par Snowden.) Certains opérateurs ont alors pris de mauvaises habitudes, en examinant en détail les communications, à des fins plus ou moins innocentes. Les choses changent aujourd'hui, avec un déploiement plus important du chiffrement, et ces mauvaises pratiques doivent donc disparaitre, ce que les auteurs du RFC ont du mal à avaler.
Des premiers exemples de ces mauvaises pratiques sont données
en section 1.2. Par exemple, le RFC cite un rapport
de l'EFF montrant que certains opérateurs inséraient des
données dans les flux SMTP, notamment à des
fins de suivi des utilisateurs, et, pour empêcher ces flux d'être
protégés par TLS, retiraient la commande
STARTTLS
de la négociation (ce qu'on nomme le
SSL stripping, cf. RFC 7525, section 3.2). Le RFC ose même dire que pour
certains, c'était « considéré comme une attaque » comme si cela
n'était pas évident que c'est une attaque ! Notant que le
chiffrement systématique empêcherait cette attaque, le RFC demande
qu'on fournisse à ces opérateurs réseau malhonnêtes une
alternative à leurs pratiques de piratage des sessions !
Un autre exemple donné est celui des réseaux d'entreprise où il y a, dit le RFC, un « accord » pour que le contenu du trafic des employés soit surveillé par le patron (dans la réalité, il n'y a pas d'accord, cette surveillance est une condition de l'embauche). Ce serait donc un cas radicalement différent de celui des réseaux publics, via le FAI, où il n'y a pas de tel « accord » et, pourtant, déplore le RFC, le chiffrement rend cette surveillance plus difficile. (Ce point particulier a fait l'objet de nombreux débats lors de la mise au point de la version 1.3 du protocole TLS, qui visait en effet à rendre la surveillance plus difficile. Cf. RFC 8446.)
À partir de la section 2, commence le gros du RFC, les études de cas. Le RFC est très détaillé et je ne vais pas tout mentionner ici. Notons que la section 2 commence par du chantage : si on ne donne pas accès aux opérateurs au contenu des communications, ils « vont déployer des méthodes regrettables du point de vue de la sécurité », et il faut donc leur donner les données, pour éviter qu'ils n'utilisent des moyens encore pires.
D'abord, ce sont les chercheurs qui effectuent des mesures sur l'Internet (par exemple CAIDA) qui sont mentionnés pour expliquer que le chiffrement va rendre ces études bien plus difficiles. (Au passage, puisque le RFC ne le dit pas, signalons que la science ne justifie pas tout et que ces études doivent se conformer à l'éthique, comme tout le monde.) Le RFC oublie de dire que beaucoup de ces études n'utilisent que l'en-tête IP, et parfois les en-têtes TCP et UDP, qui ne sont pas affectés par le chiffrement. Comme cette réthorique est souvent présente dans ce RFC, cela vaut la peine de préciser : les techniques de chiffrement les plus déployées aujourd'hui (TLS et, loin derrière, SSH) laissent intacts les en-têtes IP et TCP, que les chercheurs peuvent donc regarder comme avant. IPsec, lui, masque l'en-tête TCP (et, dans certains cas, une partie de l'information venue de l'en-tête IP), mais il est très peu utilisé dans l'Internet public (à part une partie de l'accès au VPN de l'entreprise). QUIC, s'il sera un jour déployé massivement, dissimulera également une partie de l'information de couche 4. Bref, aujourd'hui, la partie en clair du trafic donne déjà plein d'information.
Après les chercheurs, le RFC cite les opérateurs qui regardent en détail un flux réseau pour déboguer des problèmes applicatifs. Là encore, l'exemple est très malhonnête :
Le RFC ne mentionne pas assez que le développement du chiffrement est en bonne partie le résultat des pratiques déplorables de certains opérateurs. Si on fait tout passer sur le port 443 (celui de HTTPS), et en chiffré, c'est précisément parce que des opérateurs se permettaient, en violation du principe de neutralité, de traiter différemment les applications, se fiant au port TCP utilisé pour reconnaitre les différentes applications. Refusant de comprendre ce problème, le RFC déplore au contraire que « tout le monde étant sur le port 443, on ne peut plus détecter certains applications afin de les prioriser », et affirme que cela justifie le DPI. Pas un seul questionnement sur la légitimité de ce traitement différencié. (Notez également un truc réthorique très malhonnête : parler de prioriser certaines applications. Comme la capacité du réseau est finie, si on en priorise certaines applications, on en ralentit forcément d'autres. Mais c'est plus vendeur de dire qu'on priorise que d'admettre qu'on discrimine.) Pour éviter de dire ouvertement qu'on viole la neutralité du réseau, le RFC la redéfinit en disant qu'il n'y a pas violation de la neutralité si on différencie entre applications, seulement si on différencie entre utilisateurs.
Il n'y a évidemment pas que les opérateurs réseau qui sont responsables de la surveillance et de l'interférence avec les communications. La section 2.4 du RFC rappelle à juste titre que les opérateurs travaillent dans un certain cadre légal et que l'État les oblige souvent à espionner (ce qui s'appelle en novlangue « interception légale ») et à censurer. Par exemple, en France, les opérateurs sont censés empêcher l'accès aux sites Web présents sur la liste noire (secrète) du ministère de l'intérieur. Le chiffrement rend évidemment plus difficile ces activités (c'est bien son but !) Le RFC note par exemple que le filtrage par DNS menteur, la technique de censure la plus commune en Europe, est plus difficile si on utilise le chiffrement du RFC 7858 pour parler à un résolveur DNS externe, et traite cela comme si c'était un problème, alors que c'est au contraire le résultat attendu (s'il n'y avait pas de surveillance, et pas d'interférence avec le trafic réseau, le chiffrement serait inutile).
Mais le RFC mélange tout par la suite, en citant comme exemple de filtrage légitime le contrôle parental. Il n'y a nul besoin qu'il soit fait par le FAI, puisqu'il est souhaité par le client, il peut être fait sur les machines terminales, ou dans le routeur de la maison. C'est par exemple ce qui est fait par les bloqueurs de publicité, qui ne sont pas une violation de la neutralité du réseau puisqu'ils ne sont pas dans le réseau, mais sur la machine de l'utilisateur, contrôlée par elle ou lui.
Un autre exemple où le RFC déplore que le chiffrement empêche une pratique considérée par l'opérateur comme utile est celui du zero rating, cette pratique où l'accès à certains services fait l'objet d'une tarification réduite ou nulle. Le chiffrement, note le RFC, peut servir à dissimuler les services auxquels on accède. Là encore, on peut se demander si c'est vraiment un problème : le zero rating est également une violation de la neutralité du réseau, et ce n'est pas forcément mauvais qu'il devienne plus difficile à déployer.
Mais l'exemple suivant est bien pire, et beaucoup plus net. La section 2.3.4 du RFC décrit la pratique de modification des flux HTTP, notamment l'insertion d'en-têtes. Plusieurs vendeurs de matériel fournissent des équipements permettant d'« enrichir » les en-têtes, en ajoutant des informations personnelles, comme le numéro de téléphone, permettant un meilleur ciblage par les publicitaires. Et ils n'en ont même pas honte, annonçant cyniquement cette capacité dans leurs brochures. Et le RFC décrit cette pratique en termes neutres, sans préciser qu'elle est mauvaise, devrait être interdite, et que le chiffrement permet heureusement de la rendre plus difficile ! La soi-disant neutralité de la description mène donc à décrire des pratiques scandaleuses au même niveau que l'activité normale d'un opérateur réseaux. Le RFC cite bien le RFC 8165, qui condamne cette pratique, mais n'en tire pas les conséquences, demandant, là encore, qu'on fournisse aux opérateurs malhonnêtes un moyen de continuer ces opérations (par exemple par une insertion des données personnelles directement par le logiciel sur la machine du client, ce qui est possible dans le monde du mobile, beaucoup plus fermé et contrôlé), et ce malgré le chiffrement.
La section 3.1.2 du RFC revient sur les buts de la surveillance, lorsqu'on gère un réseau. Elle mentionne plusieurs de ces buts, par exemple :
La section 3.2 du RFC mentionne d'autres exemples d'usages actuels que le chiffrement massif peut gêner. Par exemple, elle cite la lutte contre le spam en notant que le chiffrement du courrier (par exemple avec PGP ou DarkMail) empêche un intermémdiaire (comme le serveur de messagerie) de lutter contre le spam. (La bonne solution est évidemment de faire l'analyse du spam sur la machine finale, par exemple avec bogofilter.)
Les révélations de Snowden ont montré que l'espionnage des liaisons internes aux organisations était une pratique courante de la NSA. Pendant longtemps, beaucoup d'administrateurs réseau ont considéré que « pas besoin d'utiliser HTTPS, c'est un site Web purement interne ». C'était une grosse erreur technique (l'attaquant est souvent interne, et les attaques contre le DNS ou le routage peuvent donner accès aux communications censées être internes) et elle a donc logiquement été exploitée. Aujourd'hui, les organisations sérieuses chiffrent systématiquement, même en interne.
Mais en même temps, beaucoup d'entreprises ont en interne des règles qui autorisent la surveillance de toutes les communications des employés. (En France, il existe des limites à cette surveillance, mais pas aux États-Unis. Le RFC rappelle juste que certaines politiques définies par le patron autorisent des accès protégés contre la surveillance, par exemple pour se connecter à sa banque. Écrit avec un point de vue très états-unien, le RFC ne mentionne que les politiques de la direction, pas les limites légales.) Techniquement, cela passe, par exemple, par des relais qui terminent la session TLS, examinent le contenu de la communication, puis démarrent une nouvelle session avec le serveur visé. Cela permet par exemple de détecter le logiciel malveillant que la machine Windows télécharge via une page Web piégée. Ou bien une attaque XSS que l'utilisateur n'a pas vue. (Notez que, par une incohérence fréquente en entreprise, la sécurité est présentée comme absolument essentielle, mais les postes de travail tournent sur Windows, certainement la cible la plus fréquente de ces logiciels malveillants.) Mais cela permet aussi de surveiller les communications des employés.
Si le but est de se protéger contre le logiciel malveillant, une solution simple est de faire l'examen des messages suspects sur la machine terminale. Cela peut être plus compliqué à déployer mais, normalement, c'est faisable à la fois juridiquement puisque cette machine appartient à l'entreprise, et techniquement puisqu'elle est a priori gérée à distance.
La section 5 du RFC détaille certaines techniques de surveillance pour des systèmes particuliers. Par exemple, les fournisseurs de messagerie examinent les messages, notamment pour déterminer s'il s'agit de spam. SMTP sur TLS ne gêne pas cette technique puisqu'il est de serveur à serveur, pas de bout en bout. Même PGP ou S/MIME n'empêchent pas l'examen des métadonnées, qui peuvent être suffisante pour détecter le spam.
De même (section 6 du RFC), TLS ne masque pas tout et permet quand même certaines activités de surveillance. Ainsi, le nom du serveur contacté, indiqué dans l'extension TLS SNI (Server Name Indication, section 3 du RFC 6066) n'est pas chiffré et peut donc être vu. (Des efforts sont en cours actuellement à l'IETF pour boucher cette faille dans la protection de la vie privée, et semblent bien avancer.)
En résumé, ce RFC, qui a commencé comme très « anti-chiffrement » a été un peu modifié lors des très vives discussions qu'il a généré à l'IETF, et essaie désormais de ménager la chèvre et le chou. La tonalité demeure quand même hostile au chiffrement sérieux, mais on y trouve suffisamment d'éléments techniques précis pour que tout le monde puisse y trouver une lecture intéressante.
Date de publication du RFC : Mai 2018
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 30 juillet 2018
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 a par la suite été remplacé par le RFC 9549.
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 8399 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
). 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.
Nouveauté plus importante de notre RFC 8399, 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 vont accepter
Unicode. J'ai testé avec Let's
encrypt (avec le client Dehydrated, en mettant le
Punycode dans domains.txt
) et ça marche, 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=Let's Encrypt Authority X3,O=Let's Encrypt,C=US', serial 0x03ed9617bb88bab3ad5b236675d1dd6e5d27, ...
D'autres AC acceptent ces noms en Unicode :
Gandi le fait aussi, regardez le certificat
de
. On
notera que le célèbre service de test de la qualité des
configurations TLS, SSLlabs, gère bien les IDN :
https://réussir-en.fr
Enfin, le registre du
.ru
a participé au
développement
de logiciels pour traiter l'Unicode dans les certificats.
Date de publication du RFC : Mai 2018
Auteur(s) du RFC : M. Sethi, J. Arkko, A. Keranen (Ericsson), H. Back (Nokia)
Pour information
Réalisé dans le cadre du groupe de travail IETF lwig
Première rédaction de cet article le 2 janvier 2019
On le sait, la sécurité de l'Internet des Objets est absolument catastrophique. Les objets vendus au grand public, par exemple, non seulement envoient toutes vos données personnelles quelque part chez le vendeur de l'objet, mais en outre permettent en général facilement à des tiers de copier ces données. Ce RFC décrit les défis que pose la sécurisation de réseaux d'objets connectés, spécialement pour ceux qui disposent de ressources (comme l'électricité) limitées.
Le problème n'est pas purement technique : si les innombrables objets connectés que vous avez reçus comme cadeau de Noël cette année ont une sécurité abyssalement basse, c'est parce que les vendeurs sont totalement incompétents, et qu'ils ne font aucun effort pour apprendre. Cela n'a la plupart du temps rien à voir avec le manque de ressources (processeur, électricité) de l'objet. Les télévisions connectées ou voitures connectées n'ont pas une meilleure sécurité, alors qu'elles disposent de bien plus de ressources qu'un Raspberry Pi 1, qui peut pourtant faire du SSH ou TLS sans problème. Mais les vendeurs agissent dans une impunité totale, et ne sont donc pas motivés pour améliorer les choses. Aucun vendeur d'objets connectés n'a jamais eu à subir les conséquences (légales ou commerciales) d'une faille de sécurité. Les objets à la sécurité pourrie se vendent aussi bien que les autres. Et la loi ne protège pas le client. Les mêmes qui s'indignent contre la vente de bitcoins à des particuliers sans expérience financière ne protestent jamais contre la vente d'objets espions facilement piratables.
Mais comme la plupart des RFC, ce document se focalise sur l'aspect technique du problème, et suggère des solutions techniques. Je suis pessimiste quant aux chances de déploiement de ces solutions, en raison de l'absence de motivations (cf. ci-dessus). Mais il est vrai que ce RFC vise plutôt les objets utilisés en milieu industriel que ceux destinés au grand public.
D'abord, les défis auxquels font face les objets connectés (section 3 du RFC). D'abord, il y a le fait que, dans certains objets, les ressources sont limitées : le processeur est très lent, la mémoire est réduite, et la batterie n'a pas des réserves infinies. On a vu que ces ressources limitées sont parfois utilisées comme excuses pour ne pas mettre en œuvre certaines techniques de sécurité (une télévision connectée qui utilise HTTP et pas HTTPS avec l'argument que la cryptographie est trop coûteuse !) Mais, pour certaines catégories d'objets, l'argument est réel : un capteur industriel peut effectivement ne pas avoir autant de ressources que l'expert en sécurité le souhaiterait. Certains objets sont vendus à des prix individuels très bas et doivent donc être fabriqués à faible coût. Conséquences : il faut utiliser CoAP et non pas HTTP, les clés cryptographiques ne peuvent pas être trop longues, l'engin ne peut pas être allumé en permanence et n'est donc pas forcément joignable, son interface utilisateur très réduite ne permet pas de définir un mot de passe, etc.
Outre le fait que ces ressouces limitées sont souvent un faux prétexte pour justifier la paresse des vendeurs, le RFC note que le développement de ces objets se fait souvent dans le mauvais ordre : on conçoit le matériel puis, bien après, on cherche les solutions de sécurité qui arriveraient à tourner sur ce matériel. Pour sortir de l'état catastrophique de la sécurité des objets connectés, il faudrait procéder en sens inverse, et intégrer la sécurité dès le début, concevant le matériel en fonction des exigences de sécurité.
Deuxième défi pour le concepteur d'un objet connecté, l'avitaillement (provisioning). Comment mettre dans l'objet connectés les informations nécessaires, mots de passe, clés et autres données ? Par exemple, si une technique de sécurité utilise un mot de passe, mettre le même dans chaque objet lors de sa fabrication est facile, mais évidemment très mauvais pour la sécurité. Mais demander à M. Michu, qui a acheté un gadget connecté, de rentrer un mot de passe avec une interface à deux boutons et un minuscule écran n'est pas évident. Et si une entreprise a acheté une centaine de capteurs, faut-il une intervention manuelle sur chacun, pour mettre le mot de passe ? Le RFC considère que c'est sans doute le principal défi pour la sécurité des objets connectés. Le problème est difficile à résoudre car :
Résultat, les objets connectés sont souvent vendus avec des informations statiques, et une identité stable, qui facilite certes des aspects de l'administration du réseau, comme le suivi d'un objet donné, mais met sérieusement en danger la sécurité.
En outre, troisième défi, chaque objet ayant souvent des capacités limitées, la communication est fréquemment relayée par des passerelles. Même lorsque l'objet a une visibilité directe, il faut souvent passer par une passerelle car l'objet peut passer la majorité de son temps endormi, donc injoignable, afin d'économiser l'électricité. Les modèles traditionnels de sécurité, fondés sur le principe de bout en bout, ne fonctionnent pas bien dans ce contexte.
Pour affronter ces défis, notamment celui de l'avitaillement, la section 4 de notre RFC décrit un modèle de déploiement, où les identités des objets sont dérivées de clés cryptographiques, auto-générées sur chaque objet, comme les CGA du RFC 3972 ou les HIT du RFC 7401. Dans ce modèle, chaque objet génère une paire de clés. L'opération de génération de l'identité consiste typiquement à appliquer une fonction de condensation cryptographique à la concaténation d'une clé publique de l'engin et d'une information locale. Une fois qu'on connait l'identité d'une machine, on peut communiquer avec elle de manière sûre, puisqu'elle peut signer ses messages avec sa clé privée, qui n'est jamais communiquée.
Avec ce modèle, il n'y a plus de mots de passe définis en usine et identiques sur toutes les machines. Mais cela impose, au moment du déploiement, de récolter ces identités (par exemple via un code QR affiché par l'objet, ou bien via NFC) pour les enregistrer dans la base de données qui sera utilisée par le, la ou les administrateurs du réseau. Ce mécanisme permettra d'authentifier les objets, mais pas aux objets d'authentifier un éventuel partenaire, par exemple celui qui leur envoie des ordres. Pour cela, il faudra en autre indiquer la clé publique de ce partenaire au moment de l'installation, ce qui nécessitera un mécanisme de communication, par exemple via le port USB. On ne pourra pas sortir les objets du carton et aller les poser sur le terrain sans autre formalité. Mais la sécurité est à ce prix.
Ces identités stables posent potentiellement un problème de vie privée. Il faut donc prévoir leur renouvellement, soit périodiquement, soit lorsque l'objet change de propriétaire. Un bouton ou autre mécanisme « oublie tout et crée une nouvelle identité » est donc nécessaire.
Une fois qu'on a ces identités, on peut les utiliser de plusieurs façons, de bout en bout ou bien via des intermédiaires. Plusieurs architectures et protocoles sont possibles. Ce serait par exemple le cas de HIP. Mais les auteurs du RFC privilégient une solution qui se situerait au niveau des applications, bâtie sur CoAP (RFC 7252) car cela permettrait :
S'agit-il d'une idée en l'air, d'un vague projet ? Non, les auteurs du RFC ont déjà identifié plusieurs bibliothèques logicielles qui peuvent permettre de mettre en œuvre ces idées en fournissant des opérations cryptographiques nécessaires à cette architecture, même à des machines peu gonflées :
Certains systèmes d'exploitation sont spécialement conçus pour des objets contraints, et disposent des bibliothèques nécessaires. C'est le cas par exemple de mbed, qui tourne sur plusieurs membres de la famille ARM Cortex.
Ce n'est pas tout d'avoir du code, encore faut-il qu'il tourne de manière efficace. La section 6 de notre RFC est dédiée aux tests de performance, notamment lors des opérations de signature. C'est ainsi que RSA avec une clé de 2 048 bits et le code d'AvrCryptolib prend vraiment trop de temps (sur une machine apparemment non spécifiée) pour être utilisable.
ECDSA avec TinyECC sur un Arduino Uno tourne par contre en un temps supportable. Sans utiliser le code en langage d'assemblage qui est disponible dans cette bibliothèque, la consommation de RAM reste la même mais le temps d'exécution augmente de 50 à 80 %. D'autres mesures figurent dans cette section, avec diverses bibliothèques, et divers algorithmes. La conclusion est la même : la cryptographie asymétrique, sur laquelle repose l'architecture de sécurité proposée dans notre RFC est réaliste sur des objets très contraints mais probablement uniquement avec les courbes elliptiques. Les courbes recommandées sont celles du RFC 7748, bien plus rapides que celles du NIST (tests avec la bibliothèque NaCl).
Les problèmes concrets ne s'arrêtent pas là. Il faut aussi voir que certains objets contraints, comme l'Arduino Uno, n'ont pas de générateur aléatoire matériel. (Contrairement à, par exemple, le Nordic nRF52832.) Ces engins n'ayant pas non plus d'autres sources d'entropie, générer des nombres aléatoires de qualité (RFC 4086), préliminaire indispensable à la création des clés, est un défi.
La section 7 du RFC décrit une application développée pour illustrer les principes de ce RFC. Il s'agit de piloter des capteurs qui sont éteints pendant l'essentiel du temps, afin d'économiser leur batterie, et qui se réveillent parfois pour effectuer une mesure et en transmettre le résultat. Des machines sont affectées à la gestion de la communication avec les capteurs et peuvent, par exemple, conserver un message lorsque le capteur est endormi, le distribuant au capteur lorsqu'il se réveille. Plus précisément, il y avait quatre types d'entités :
Le modèle de sécurité est TOFU ; au premier démarrage, les capteurs génèrent les clés, et les enregistrent dans l'annuaire, suivant le format du RFC 6690. (L'adresse IP de l'annuaire est codée en dur dans les capteurs, évitant le recours au DNS.) Le premier enregistrement est supposé être le bon. Ensuite, chaque fois qu'un capteur fait une mesure, il l'envoie au courtier en JSON signé en ECDSA avec JOSE (RFC 7515). Il peut aussi utiliser CBOR avec COSE (RFC 8152). La signature peut alors être vérifiée. On voit qu'il n'y a pas besoin que la machine de vérification (typiquement celle qui porte l'application) soit allumée en même temps que le capteur. Ce prototype a bien marché. Cela montre comment on peut faire de la sécurité de bout en bout bien qu'il y ait au moins un intermédiaire (le courtier).
La question de la faisabilité de l'architecture décrite dans ce RFC est discutée plus en détail dans la section 8.1. On entend souvent que les objets connectés n'ont pas de vraie sécurité car « la cryptographie, et surtout la cryptographie asymétrique, sont bien trop lentes sur ces objets contraints ». Les expériences faites ne donnent pas forcément un résultat évident. La cryptographie asymétrique est possible, mais n'est clairement pas gratuite. RSA avec des clés de tailles raisonnables pourrait mettre plusieurs minutes à signer, ce qui n'est pas tolérable. Heureusement, ce n'est pas le seul algorithme. (Rappelons que cela ne s'applique qu'aux objets contraints. Il n'y a aucune bonne raison de ne pas utiliser la cryptographie pour des objets comme une télévision ou une caméra de surveillance.) La loi de Moore joue ici en notre faveur : les microcontrôleurs de 32 bits deviennent aussi abordables que ceux de 8 bits.
Quant aux exigences d'énergie électrique, il faut noter que le plus gourmand, et de loin, est la radio (cf. Margi, C., Oliveira, B., Sousa, G., Simplicio, M., Paulo, S., Carvalho, T., Naslund, M., et R. Gold, « Impact of Operating Systems on Wireless Sensor Networks (Security) Applications and Testbeds », à WiMAN en 2010). Signer et chiffrer, ce n'est rien, par rapport à transmettre.
L'ingénierie, c'est toujours faire des compromis, particulièrement quand on travaille avec des systèmes aussi contraints en ressources. La section 8 du RFC détaille certains de ces compromis, expliquant les choix à faire. Ainsi, la question de la fraîcheur des informations est délicate. Quand on lit le résultat d'une mesure sur le courtier, est-on bien informé sur l'ancienneté de cette mesure ? Le capteur n'a en général pas d'horloge digne de ce nom et ne peut pas se permettre d'utiliser NTP. On peut donc être amené à sacrifier la résolution de la mesure du temps aux contraintes pratiques.
Une question aussi ancienne que le modèle en couches est celle de la couche où placer la sécurité. Idéalement, elle devrait être dans une seule couche, pour limiter le code et le temps d'exécution sur les objets contraints. En pratique, elle va sans doute se retrouver sur plusieurs couches :
Enfin, dernière étude sur les compromis à faire, le choix entre la cryptographie symétrique et la cryptographie asymétrique, bien plus gérable en matière de distribution des clés, mais plus consommatrice de ressources. On peut faire de la cryptographie symétrique à grande échelle mais, pour la cryptographie asymétrique, il n'y a pas encore de déploiements sur, disons, des centaines de millions d'objets. On a vu que dans certains cas, la cryptographie asymétrique coûte cher. Néanmoins, les processeurs progressent et, question consommation énergétique, la cryptographie reste loin derrière la radio. Enfin, les schémas de cryptographie des objets connectés n'utiliseront probablement la cryptographie asymétrique que pour s'authentifier au début, utilisant ensuite la cryptographie symétrique pour l'essentiel de la communication.
Enfin, la section 9 de notre RFC résume les points importants :
La section 2 de notre RFC décrit les autres travaux menés en matière de sécurité, en dehors de ce RFC. Ainsi, la spécification du protocole CoAP, le « HTTP du pauvre objet » (RFC 7252) décrit comment utiliser CoAP avec DTLS ou IPsec. Cette question de la sécurité des objets connectés a fait l'objet d'un atelier de l'IAB en 2011, atelier dont le compte-rendu a été publié dans le RFC 6574.
Date de publication du RFC : Mai 2018
Auteur(s) du RFC : R. Winter (University of Applied Sciences
Augsburg), M. Faath (Conntac
GmbH), F. Weisshaar (University of Applied Sciences
Augsburg)
Pour information
Réalisé dans le cadre du groupe de travail IETF intarea
Première rédaction de cet article le 29 novembre 2018
Plusieurs protocoles applicatifs utilisent la diffusion, par exemple pour la découverte d'un service, et envoient donc des messages qui vont toucher toutes les machines du réseau local. Cela a des conséquences pour la vie privée : un observateur, même purement passif, peut apprendre plein de choses en écoutant. Il est donc important lorsqu'on conçoit des protocoles applicatifs de veiller à ne pas être trop bavard.
La diffusion, c'est envoyer à tout le monde. Comme il n'existe pas (heureusement !) de mécanisme fiable pour envoyer à tout l'Internet, en pratique, la diffusion se limite au réseau local. Mais c'est déjà beaucoup ! Connecté dans un café ou dans un autre endroit à WiFi, les messages diffusés arrivent à un groupe inconnu : un nombre potentiellement grand de machines. (L'utilisation d'un commutateur ne protège pas, si c'est de la diffusion.) La diffusion est très importante pour certaines fonctions (auto-configuration lorsqu'on ne connait pas sa propre adresse IP, ou bien résolution locale de noms ou d'adresses). La diffusion est tellement pratique (cf. RFC 919 et RFC 3819) qu'elle est utilisée par beaucoup d'applications.
Mais la diffusion est dangereuse ; à la conférence TRAC 2016, les auteurs du RFC avaient, dans un excellent exposé, publié un premier résultat de leurs travaux sur la question (Faath, M., Weisshaar, F., et R. Winter, How Broadcast Data Reveals Your Identity and Social Graph, 7th International Workshop on TRaffic Analysis and Characterization IEEE TRAC 2016, September 2016). En une journée à écouter le trafic diffusé sur leur université, ils avaient récolté 215 Mo de données. Les protocoles les plus bavards : 1) mDNS 2) SSDP 3) LLMNR 4) NetBIOS 5) Dropbox. Le seul client Dropbox diffuse à la cantonade l'ID du client, et celui des shares où il se connecte. Il est facile de faire un graphe des utilisateurs en mettant ensemble ceux qui se connectent au même share. Les mêmes auteurs avaient mené une expérience à grande échelle en écoutant le trafic diffusé lors de la réunion IETF 93 à Prague, et cela avait suscité bien des débats, notamment juridico-légaux (a-t-on le droit d'écouter du trafic qui est diffusé à tous ?) Comme en médecine, la science ne justifie pas tout et il est nécessaire de se pencher sur les conséquences de ses expériences.
Bien sûr, du moment qu'on envoie des données sur un réseau, elles peuvent être écoutées par des indiscrets. Mais la diffusion aggrave le problème de deux façons :
Il est donc justifié de se préoccuper de près des conséquences de la diffusion sur la confidentialité (RFC 6973).
Pour certains protocoles conçus à l'IETF, il y a déjà eu des réflexions sur les problèmes de vie privée liés à leur usage de la diffusion. C'est évidemment le cas pour DHCP, dont les RFC 7819 et RFC 7824 ont pointé la grande indiscrétion. C'est aussi le cas des mécanismes de génération des adresses IPv6, expliqué dans le RFC 7721. Mais il y a également beaucoup de protocoles non-IETF qui utilisent imprudemment la diffusion, comme celui de Dropbox, présenté à la conférence TRAC. Ces protocoles privés sont en général peu étudiés, et la préservation de la vie privée est située très bas sur l'échelle des préoccupations de leurs auteurs. Et ils sont souvent non documentés, ce qui rend difficile toute analyse.
La section 1.1 de notre RFC résume les différents types de
diffusion qui existent dans le monde
IP. IPv4 a la
diffusion générale (on écrit à
255.255.255.255
, cf. section 5.3.5.1 du RFC 1812) et la diffusion dirigée (on écrit à une
adresse qui est celle du préfixe du réseau local, avec tous les
bits « machine » à 1, cf. section 5.3.5.2 du même
RFC). IPv6, officiellement, n'a pas de
diffusion mais uniquement du
multicast mais c'est
jouer avec les mots : il a les mêmes possibilités qu'IPv4 et les
mêmes problèmes de confidentialité. Si une machine IPv6 écrit à
ff02::1
, cela donnera le même résultat que si
une machine IPv4 écrit à
255.255.255.255
. Parmi les protocoles
IETF qui utilisent ces adresses de
diffusion, on trouve mDNS (RFC 6762), LLMNR (RFC 4795), DHCP pour IPv4 (RFC 2131),
DHCP pour IPv6 (RFC 8415), etc.
La section 2 détaille les problèmes de vie privée que l'envoi de messages en diffusion peut entrainer. D'abord, le seul envoi des messages, même sans analyser ceux-ci, permet de surveiller les activités d'un utilisateur : quand est-ce qu'il est éveillé, par exemple. Plus les messages sont fréquents, meilleure sera la résolution temporelle de la surveillance. Notre RFC conseille donc de ne pas envoyer trop souvent des messages périodiques.
Mais un problème bien plus sérieux est celui des identificateurs stables. Bien des protocoles incluent un tel identificateur dans leurs messages, par exemple un UUID. Même si la machine change de temps en temps d'adresse IP et d'adresse MAC (par exemple avec macchanger), ces identificateurs stables permettront de la suivre à la trace. Et si l'identificateur stable est lié à la machine et pas à une de ses interfaces réseau, même un changement de WiFi à Ethernet ne suffira pas à échapper à la surveillance. C'était le cas par exemple du protocole de Dropbox qui incluait dans les messages diffusés un identificateur unique, choisi à l'installation et jamais changé ensuite. D'une manière générale, les identificateurs stables sont mauvais pour la vie privée, et devraient être utilisés avec prudence, surtout quand ils sont diffusés.
Ces identificateurs stables ne sont pas forcément reliés à
l'identité étatique de la personne. Si on ne connait pas la
sécurité, et qu'on ne sait pas la différence entre
anonymat et
pseudonymat, on peut penser que diffuser
partout qu'on est
88cb0252-3c97-4bb6-9f74-c4c570809432
n'est
pas très révélateur. Mais outre que d'avoir un lien entre
différentes activités est déjà un danger, certains protocoles font
qu'en plus ce pseudonyme peut être corrélé avec des informations
du monde extérieur. Par exemple, les iPhone
diffusent fièrement à tout le réseau local « je suis l'iPhone de
Jean-Louis » (cf. RFC 8117). Beaucoup d'utilisateurs donnent à leur machine leur
nom officiel, ou leur prénom, ou une autre caractéristique
personnelle. (C'est parfois fait automatiquement à l'installation,
où un programme demande « comment vous appelez-vous ? » et nomme
ensuite la machine avec ce nom. L'utilisateur n'est alors pas
conscient d'avoir baptisé sa machine.) Et des protocoles diffusent cette information.
En outre, cette information est parfois accompagnés de détails sur le type de la machine, le système d'exploitation utilisé. Ces informations peuvent permettre de monter des attaques ciblées, par exemple si on connait une vulnérabilité visant tel système d'exploitation, on peut sélectionner facilement toutes les machines du réseau local ayant ce système. Bref, le RFC conseille de ne pas diffuser aveuglément des données souvent personnelles.
Comme souvent, il faut aussi se méfier de la corrélation. Si
une machine diffuse des messages avec un identificateur stable
mais non parlant (qui peut donc être ce
700a2a3e-4cda-46df-ad6e-2f062840d1e3
?), un
seul message donnant une autre information (par exemple nom et
prénom) est suffisant pour faire la corrélation et savoir
désormais à qui se réfère cet identificateur stable
(700a2a3e-4cda-46df-ad6e-2f062840d1e3
, c'est
Jean-Louis). Lors de l'expérience à Prague citée plus haut, il
avait été ainsi possible aux chercheurs de récolter beaucoup
d'informations personnelles, et même d'en déduire une partie du
graphe social (la machine de Jean-Louis demande souvent en
mDNS celle de Marie-Laure, il doit y avoir
un lien entre eux).
La plupart des systèmes d'exploitation n'offrent pas la possibilité de faire la différence entre un réseau supposé sûr, où les machines peuvent diffuser sans crainte car diverses mesures de sécurité font que tout le monde n'a pas accès à ce réseau, et un réseau public complètement ouvert, genre le WiFi du McDo, où tout est possible. Il serait intéressant, affirme le RFC, de généraliser ce genre de service et d'être moins bavard sur les réseaux qui n'ont pas été marqués comme sûrs.
La section 3 du RFC note que certains points d'accès WiFi permettent de ne pas passer systématiquement la diffusion d'une machine à l'autre, et de ne le faire que pour des protocoles connus et supposés indispensables. Ainsi, les requêtes DHCP, terriblement indiscrètes, pourraient ne pas être transmises à tous, puisque seul le point d'accès en a besoin. Évidemment, cela ne marche que pour des protocoles connus du point d'accès, et cela pourrait donc casser des protocoles nouveaux (si on bloque par défaut) ou laisser l'utilisateur vulnérable (si, par défaut, on laisse passer).
En résumé (section 4 du RFC), les conseils suivants sont donnés aux concepteurs de protocoles et d'applications :
Date de publication du RFC : Mai 2018
Auteur(s) du RFC : P. Pfister (Cisco Systems), T. Lemon
(Nominum)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF homenet
Première rédaction de cet article le 18 mai 2018
Ce nouveau RFC a l'air compliqué comme
cela, mais en fait il ne fait qu'une chose : remplacer, dans le
protocole Homenet/HNCP (Home Networking Control Protocol), le
nom de domaine .home
par home.arpa
.
home.arpa
est désormais enregistré
dans la liste officielle des noms de domaine
spéciaux, ceux qui ne passent pas par les mécanismes
habituels d'enregistrement de noms de domaine, et/ou les mécanismes habituels
de résolution DNS. (Cette liste a été créée
par le RFC 6761, et critiquée par le RFC 8244. home.arpa
n'étant
pas un TLD, il pose moins de problèmes politiciens.)
Quelle est l'utilité de ce nom home.arpa
?
La série de protocoles Homenet
(l'architecture de Homenet est décrite dans le RFC 7368)
vise à doter la maison de l'utilisateur normal (pas participant à
l'IETF) d'un ensemble de réseaux
IPv6 qui marchent automatiquement, sans
intervention humaine. Parmi les protocoles Homenet,
HNCP, normalisé dans le protocole RFC 7788 est le protocole de configuration.
Il utilise un suffixe pour les noms de domaines
comme nas.SUFFIXE
ou
printer.SUFFIX
. C'est ce
home.arpa
qui va désormais servir de
suffixe.
Mais quel était le problème avec le suffixe
.home
du RFC 7788 ?
D'abord, le RFC 7788 avait commis une grosse
erreur, enregistrée sous le numéro
4677 : il ne tenait pas compte des règles du RFC 6761, et réservait ce TLD
.home
sans suivre les procédures du RFC 6761. Notamment, il ne listait pas les
particularités qui font que ce domaine est spécial (pour
home.arpa
, notre nouveau RFC 8375 le fait dans sa section 5), et il ne demandait pas à
l'IANA de le mettre dans le registre
des noms de domaine spéciaux. Cela avait des conséquences
pratiques comme le fait que ce .home
ne
pouvait pas marcher à travers un résolveur DNS validant (puisque ce nom n'existait pas
du tout dans la racine). Un bon article sur ce choix et sur les
problèmes qu'il posait était « Homenet, and the hunt for a name ».
On peut aussi ajouter que le risque de « collision » entre deux
noms de domaine était élevé puisque pas mal de réseaux locaux sont
nommés sous .home
et que ce nom est un de
ceux qui « fuitent » souvent vers les serveurs
racines (voir par exemple les statistiques
du serveur racine L.). On peut consulter à ce sujet les
documents de l'ICANN « New
gTLD Collision Risk Mitigation » et
« New
gTLD Collision Occurence Management ». Notons
qu'il y avait eu plusieurs
candidatures
(finalement rejetées en
février 2018) pour un .home
en cours auprès de l'ICANN. Exit, donc,
.home
, plus convivial mais trop
convoité. Demander à l'ICANN de déléguer un
.home
pour l'IETF (ce qui aurait été
nécessaire pour faire une délégation DNSSEC
non signée, cf. RFC 4035, section 4.3)
aurait pris dix ou quinze ans.
À la place, voici home.arpa
, qui profite
du RFC 3172, et du caractère décentralisé
du DNS, qui permet de déléguer des
noms sous .arpa
.
L'utilisation de home.arpa
n'est pas
limitée à HNCP, tous les protocoles visant le même genre d'usage
domestique peuvent s'en servir. Il n'a évidemment qu'une
signification locale.
La section 3 décrit le comportement général attendu avec
home.arpa
. Ce n'est pas un nom de domaine comme les
autres. Sa signification est purement
locale. printer.home.arpa
désignera une
machine à un endroit et une autre machine dans une autre
maison. Les serveurs DNS globaux ne peuvent
pas être utilisés pour résoudre les noms sous home.arpa
. Tous les
noms se terminant par ce suffixe doivent être traités uniquement
par les résolveurs locaux, et jamais transmis à l'extérieur.
Notez que, la plupart du temps, les utilisateurs ne verront pas
le suffixe home.arpa
, l'interface des
applications « Homenet » leur masquera cette
partie du nom. Néanmoins, dans certains cas, le nom sera sans
doute visible, et il déroutera sans doute certains utilisateurs,
soit à cause du suffixe arpa
qui n'a pas de
signification pour eux, soit parce qu'ils ne sont pas anglophones et qu'ils ne comprennent
pas le home
. Il n'y a pas de solution miracle
à ce problème.
La section 4 est le formulaire d'enregistrement dans le registre des noms spéciaux, suivant les formalités du RFC 6761, section 5. (Ce sont ces formalités qui manquaient au RFC 7788 et qui expliquent l'errata.) Prenons-les dans l'ordre (relisez bien la section 5 du RFC 6761) :
home.arpa
. Elles devraient passer
par le mécanisme de résolution normal. Une exception : si la
machine a été configurée pour utiliser un autre résolveur DNS
que celui de la maison (un résolveur public, par
exemple, qui ne connaîtra pas votre home.arpa
), il peut être nécessaire de mettre une règle
particulière pour faire résoudre ces noms par un résolveur local..arpa
retournent une délégation normale.Voici la délégation :
% dig @a.root-servers.net ANY home.arpa ; <<>> DiG 9.10.3-P4-Debian <<>> @a.root-servers.net ANY home.arpa ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48503 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;home.arpa. IN ANY ;; AUTHORITY SECTION: home.arpa. 172800 IN NS blackhole-1.iana.org. home.arpa. 172800 IN NS blackhole-2.iana.org. home.arpa. 86400 IN NSEC in-addr.arpa. NS RRSIG NSEC home.arpa. 86400 IN RRSIG NSEC 8 2 86400 ( 20180429000000 20180415230000 56191 arpa. K4+fNoY6SXQ+VtHsO5/F0oYrRjZdNSG0MSMaeDSQ78aC NHko4uqNAzoQzoM8a2joFeP4wOL6kVQ72UJ5zqd/iZJD 0ZSh/57lCUVxjYK8sL0dWy/3xr7kbaqi58tNVTLkp8GD TfyQf5pW1rtRB/1pGzbmTZkK1jXw4ThG3e9kLHk= ) ;; Query time: 24 msec ;; SERVER: 2001:503:ba3e::2:30#53(2001:503:ba3e::2:30) ;; WHEN: Mon Apr 16 09:35:35 CEST 2018 ;; MSG SIZE rcvd: 296
La section 5 rassemble les changements dans la norme HNCP (RFC 7788. C'est juste un remplacement de
.home
par home.arpa
.
Quelques petits trucs de sécurité (section 6). D'abord, il ne
faut pas s'imaginer que ces noms locaux en
home.arpa
sont plus sûrs que n'importe quel
autre nom. Ce n'est pas parce qu'il y a home
dedans qu'on peut leur faire confiance. D'autant plus qu'il y a,
par construction, plusieurs home.arpa
, et
aucun moyen, lorsqu'on se déplace de l'un à l'autre, de les
différencier. (Des travaux ont lieu pour concevoir un mécanisme
qui pourrait permettre d'avertir l'utilisateur « ce n'est pas le
home.arpa
que vous pensez » mais ils n'ont
pas encore abouti.)
home.arpa
n'est pas sécurisé par
DNSSEC. Il ne serait pas possible de mettre
un enregistrement DS dans
.arpa
puisqu'un tel
enregistrement est un condensat de la clé
publique de la zone et que chaque home.arpa
qui serait signé aurait sa propre clé. Une solution possible aurait été
de ne pas déléguer
home.arpa
. .arpa
étant
signé, une telle non-délégation aurait pu être validée par DNSSEC
(« denial of existence »). La réponse DNS
aurait été (commande tapée avant la délégation de home.arpa
) :
% dig A printer.home.arpa ... ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 37887 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1 ... ;; AUTHORITY SECTION: arpa. 10800 IN SOA a.root-servers.net. nstld.verisign-grs.com. ( 2017112001 ; serial 1800 ; refresh (30 minutes) 900 ; retry (15 minutes) 604800 ; expire (1 week) 86400 ; minimum (1 day) ) arpa. 10800 IN RRSIG SOA 8 1 86400 ( 20171203120000 20171120110000 36264 arpa. QqiRv85fb6YO/79ZdtQ8Ke5FmZHF2asjLrNejjcivAAo... arpa. 10800 IN RRSIG NSEC 8 1 86400 ( 20171203120000 20171120110000 36264 arpa. dci8Yr95yQtL9nEBFL3dpdMVTK3Z2cOq+xCujeLsUm+W... arpa. 10800 IN NSEC as112.arpa. NS SOA RRSIG NSEC DNSKEY e164.arpa. 10800 IN RRSIG NSEC 8 2 86400 ( 20171203120000 20171120110000 36264 arpa. jfJS6QuBEFHWgc4hhtvdfR0Q7FCCgvGNIoc6169lsxz7... e164.arpa. 10800 IN NSEC in-addr.arpa. NS DS RRSIG NSEC ;; Query time: 187 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Mon Nov 20 20:28:27 CET 2017 ;; MSG SIZE rcvd: 686
Ici, on reçoit un NXDOMAIN (ce domaine n'existe pas), et les
enregistrements NSEC qui prouvent que
home.arpa
n'existe pas non plus (rien entre
e164.arpa
et in-addr.arpa
).
Mais cela
aurait nécessité un traitement spécial de
home.arpa
par le résolveur validant (par
exemple, sur Unbound,
domain-insecure: "home.arpa"
). Finalement, le
choix fait a été celui d'une délégation non sécurisée (section 7
du RFC), vers les
serveurs blackhole-1.iana.org
et blackhole-2.iana.org
:
% dig NS home.arpa ; <<>> DiG 9.10.3-P4-Debian <<>> NS home.arpa ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64059 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;home.arpa. IN NS ;; ANSWER SECTION: home.arpa. 190 IN NS blackhole-1.iana.org. home.arpa. 190 IN NS blackhole-2.iana.org. ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Mon Apr 16 09:36:25 CEST 2018 ;; MSG SIZE rcvd: 98
Cette délégation a été faite le 15 mars 2018.
Le domaine home.arpa
a été ajouté dans
le registre des noms de domaine spéciaux
ainsi que dans celui
des noms servis localement.
En testant avec les
sondes RIPE Atlas, on voit que tous les résolveurs ne
voient pas la même chose, ce qui est normal, chaque maison pouvant
avoir son home.arpa
local :
% blaeu-resolve -r 1000 -q SOA home.arpa [prisoner.iana.org. hostmaster.root-servers.org. 1 604800 60 604800 604800] : 548 occurrences [prisoner.iana.org. hostmaster.root-servers.org. 1 1800 900 604800 604800] : 11 occurrences [prisoner.iana.org. hostmaster.root-servers.org. 1 1800 900 604800 15] : 33 occurrences [prisoner.iana.org. hostmaster.root-servers.org. 2002040800 1800 900 604800 60480] : 229 occurrences [ERROR: FORMERR] : 1 occurrences [ERROR: SERVFAIL] : 132 occurrences [] : 4 occurrences [prisoner.iana.org. hostmaster.root-servers.org. 1 604800 60 604800 3600] : 11 occurrences [prisoner.iana.org. hostmaster.trex.fi. 1 604800 86400 2419200 86400] : 4 occurrences [prisoner.iana.org. ops.inx.net.za. 1513082668 10800 3600 604800 3600] : 2 occurrences [TIMEOUT(S)] : 19 occurrences Test #12177308 done at 2018-04-16T07:38:32Z
On voit sur ce premier test que la grande majorité des sondes
voient le vrai SOA (numéro de série 1 ou 2002040800 ; curieusement,
les serveurs faisant autorité envoient des numéros
différents). Certaines voient un tout autre SOA (par exemple celle
où l'adresse du responsable est en Afrique du
Sud ou bien en Finlande), et le
numéro de série très différent. Ce n'est pas un problème ou un
piratage : le principe de home.arpa
est que
chacun peut avoir le sien.
Pour une autre description de ce home.arpa
,
voyez l'article de
John Shaft (où il utilise Unbound) et
(en anglais), regardez celui
de Daniel Aleksandersen.
À l'heure actuelle, toutes les mises en œuvre en logiciel libre
que j'ai regardées utilisent encore .home
, mais
elles semblent souvent non maintenues.
Date de publication du RFC : Avril 2018
Auteur(s) du RFC : K. Sriram (USA NIST)
Pour information
Première rédaction de cet article le 1 mai 2018
Ce RFC est un peu spécial : il ne normalise pas un protocole, ni des procédures internes à l'IETF, et il n'est pas non plus la description d'un problème à résoudre, ou un cahier des charges d'une solution à développer. Non, ce RFC est la documentation a posteriori des choix qui ont été effectués lors du développement de BGPsec, une solution de sécurisation du routage Internet. C'est donc un document utile si vous lisez les RFC sur BGPsec, comme le RFC 8205 et que vous vous demandez « mais pourquoi diable ont-ils choisi cette approche et pas cette autre, qui me semble bien meilleure ? »
Un petit rappel du contexte : le protocole de
routage BGP
fonctionne en échangeant des informations entre pairs, sur les
routes que chaque pair sait joindre. Par défaut, un pair peut
raconter n'importe quoi, dire qu'il a une route vers
2001:db8::/32
alors qu'il n'est pas le
titulaire de ce préfixe et n'a pas non plus appris cette route
d'un de ses pairs. Cela rend donc le routage Internet assez
vulnérable. Pour le sécuriser, il existe plusieurs mécanismes qui
font que, en pratique, ça ne marche pas trop
mal. L'IETF a développé une solution
technique, qui a deux couches : une infrastructure à clés publiques, la
RPKI, normalisée dans les RFC 6480 et RFC 6481, et une seconde
couche, les services qui utilisent la RPKI pour authentifier tel
ou tel aspect du routage. Deux de ces services sont normalisés,
les ROA (Route Origin Authorization) des RFC 6482
et RFC 6811, qui servent à authentifier
l'AS d'origine d'un préfixe, et
BGPsec (RFC 8205),
qui sert à authentifier le chemin d'AS, la
liste des AS empruntés par une annonce de
route (cf. section 2.1.1 de notre RFC). Sans BGPsec, les ROA,
utilisés seuls, ne peuvent pas arrêter certaines attaques
(cf. RFC 7132, qui explique quelles menaces
traite BGPsec, et RFC 7353, cahier des
charges de BGPsec). Par exemple, si l'AS 64641, malhonnête, veut
tromper son pair, l'AS 64642, à propos du préfixe
2001:db8::/32
, et que ce préfixe a un ROA
n'autorisant que 64643 à être à l'origine, le malhonnête peut
fabriquer une annonce avec le chemin d'AS 64641
[éventuellement d'autres AS] 64643
(rappelez-vous que
les chemins d'AS se lisent de droite à gauche) et l'envoyer à
64641. Si celui-ci vérifie le ROA, il aura l'impression que tout
est normal, l'AS 64643 étant bien indiqué comme l'origine. Et cela
marchera même si les annonces de l'AS 64643 ne sont jamais passées
par ce chemin ! BGPsec répare ce problème en transportant un
chemin d'AS signé,
authentifiant toutes les étapes.
Lors du développement de BGPsec, un document avait été rédigé pour documenter tous les choix effectués, mais n'avait pas été publié. C'est désormais chose faite avec ce RFC, qui documente les choix, les justifie, et explique les différences entre ces choix initiaux et le protocole final, modifié après un long développement. Parmi les points importants du cahier des charges (RFC 7353) :
Finie, l'introduction, passons tout de suite à certains des choix qui ont été effectués. (Il est évidemment recommandé de lire le RFC 8205 avant, puis de lire notre RFC 8374 après, si on veut tous les choix documentés.) D'abord, à quoi ressemblent les signatures, et les nouveaux messages BGP (section 2 de notre RFC). Les signatures BGPsec signent le préfixe IP, l'AS signataire, et l'AS suivant à qui on va transmettre l'annonce. Le premier point important est qu'on signe l'AS suivant, de manière à créer une chaîne de signatures vérifiables. Autrement, un attaquant pourrait fabriquer un faux chemin à partir de signatures correctes.
Si on utilise le prepending d'AS, la
première version de BGPsec prévoyait une signature à chaque fois,
mais on peut finalement mettre une seule signature pour la
répétition d'AS, avec une variable pCount
qui indique leur nombre (RFC 8205, section 3.1), afin
de diminuer le nombre de signatures (les signatures peuvent
représenter la grande majorité des octets d'une annonce BGP).
Le second point important est que certains attributs ne sont
pas pas signés, comme par exemple la
préférence locale (RFC 4271, section 5.1.5)
ou les communautés (cf. RFC 1997). Ces
attributs pourront donc être modifiés par un attaquant à sa
guise. Ce n'est pas si grave que ça en a l'air car la plupart de
ces attributs n'ont de sens qu'entre deux AS (c'est le cas de la
communauté NO_EXPORT
) ou sont internes à un
AS (la
préférence locale). Sur ces courtes distances, on espère de toute
façon que la session BGP sera protégée, par exemple par AO (RFC 5925).
La signature est transportée dans un attribut BGP optionnel et non-transitif (qui n'est pas transmis tel quel aux routeurs suivants). Optionnel car, sinon, il ne serait pas possible de déployer BGPsec progressivement. Et non-transitif car un routeur BGPsec n'envoie les signatures que si le routeur suivant lui a dit qu'il savait gérer BGPsec.
Dans le message BGP signé, le routeur qui signe est identifié par sa clé publique, pas par le certificat entier. Cela veut dire qu'on ne peut valider les signatures que si on a accès à une dépôt de la RPKI, avec les certificats.
La section 3 de notre RFC traite le cas des retraits de route : contrairement aux annonces, ils ne sont pas signés. Un AS est toujours libre de retirer une route qu'il a annoncée (BGP n'accepte pas un retrait venant d'un autre pair). Si on a accepté l'annonce, il est logique d'accepter le retrait (en supposant évidemment que la session entre les deux routeurs soit raisonnablement sécurisée).
La section 4, elle, parle des algorithmes de signature. Le RFC 8208 impose ECDSA avec la courbe P-256 (cf. RFC 6090). RSA avait été envisagé mais ECDSA avait l'avantage de signatures bien plus petites (cf. l'étude du NIST sur les conséquences de ce choix).
Une autre décision importante dans cette section est la possibilité d'avoir une clé par routeur et pas par AS (cf. RFC 8207). Cela évite de révoquer un certificat global à l'AS si un seul routeur a été piraté. (Par contre, il me semble que c'est indiscret, permettant de savoir quel routeur de l'AS a relayé l'annonce, une information qu'on n'a pas forcément actuellement.)
Décision plus anecdotique, en revanche, celle comme quoi le nom
dans le certificat (Subject) sera la chaîne
router
suivie du numéro d'AS (cf. RFC 5396) puis de
l'identité BGP du routeur. Les détails figurent dans le RFC 8209.
Voyons maintenant les problèmes de performance (section 5). Par exemple, BGPsec ne permet pas de regrouper plusieurs préfixes dans une annonce, comme on peut le faire traditionnellement avec BGP. C'est pour simplifier le protocole, dans des cas où un routeur recevrait une annonce avec plusieurs préfixes et n'en transmettrait que certains. Actuellement, il y a en moyenne quatre préfixes par annonce (cf. l'étude faite par l'auteur du RFC). Si tout le monde adoptait BGPsec, on aurait donc quatre fois plus d'annonces, et il faudra peut-être dans le futur optimiser ce cas.
On l'a vu plus haut, il n'est pas envisageable de déployer BGPsec partout du jour au lendemain. Il faut donc envisager les problèmes de coexistence entre BGPsec et BGP pas sécurisé (section 6 de notre RFC). Dès que deux versions d'un protocole, une sécurisée et une qui ne l'est pas, coexistent, il y a le potentiel d'une attaque par repli, où l'attaquant va essayer de convaindre une des parties de choisir la solution la moins sécurisée. Actuellement, BGPsec ne dispose pas d'une protection contre cette attaque. Ainsi, il n'y a pas de moyen de savoir si un routeur qui envoie des annonces non-signées le fait parce qu'il ne connait pas BGPsec, ou simplement parce qu'un attaquant a modifié le trafic.
La possibilité de faire du BGPsec est négociée à l'établissement de la session BGP. Notez qu'elle est asymétrique. Par exemple, il est raisonnable qu'un routeur de bordure signe ses propres annonces mais accepte tout de la part de ses transitaires, et ne lui demande donc pas d'envoyer les signatures. Pendant un certain temps (probablement plusieurs années), nous aurons donc des ilots BGPsec au milieu d'un océan de routeurs qui font du BGP traditionnel. On peut espérer qu'au fur et à mesure du déploiement, ces ilots se rejoindront et formeront des iles, puis des continents.
La question de permettre des chemins d'AS partiellement signés avait été discutée mais cela avait été rejeté : il faut signer tout le chemin, ou pas du tout. Des signatures partielles auraient aidé au déploiement progressif mais auraient été dangereuses : elle aurait permis aux attaquants de fabriquer des chemins valides en collant des bouts de chemins signés - et donc authentiques - avec des bouts de chemins non-signés et mensongers.
La section 7 de notre RFC est consacrée aux interactions entre BGPsec et les fonctions habituelles de BGP, qui peuvent ne pas bien s'entendre avec la nouvelle sécurité. Par exemple, les très utiles communautés BGP (RFC 1997 et RFC 8092). On a vu plus haut qu'elles n'étaient pas signées du tout et donc pas protégées. La raison est que les auteurs de BGPsec considèrent les communautés comme mal fichues, question sécurité. Certaines sont utilisées pour des décisions effectives par les routeurs, d'autres sont juste pour le débogage, d'autres encore purement pour information. Certaines sont transitives, d'autres utilisées seulement entre pairs se parlant directement. Et elles sont routinement modifiées en route. Le RFC conseille, pour celles qui ne sont utilisées qu'entre pairs directs, de plutôt sécuriser la session BGP.
Pour les communautés qu'on veut voir transmises transitivement, il avait été envisagé d'utiliser un bit libre pour indiquer que la communauté était transitive et donc devait être incluse dans la signature. Mais la solution n'a pas été retenue. Conseil pratique, dans la situation actuelle : attention à ne pas utiliser des communautés transmises transitivement pour des décisions de routage.
Autre cas pratique d'interaction avec un service très utile, les serveurs de route. Un point d'échange peut fonctionner de trois façons :
NEXT_HOP
, c'est un serveur de routes, pas
un routeur). Cette méthode est de loin la plus rare des
trois. Là aussi, BGPsec n'a rien de particulier à faire.pCount
à 0 (sa valeur normale est 1, ou
davantage si on utilise le prepending) pour
indiquer qu'il ne faut pas en tenir compte pour les décisions de
routage (fondées sur la longueur du chemin), seulement pour la validaton.Un point de transition intéressant est celui des numéros d'AS de quatre octets, dans le RFC 4893. La technique pour que les AS ayant un tel numéro puisse communiquer avec les vieux routeurs qui ne comprennent pas ces AS est un bricolage utilisant un AS spécial (23456), bricolage incompatible avec BGPsec, qui, d'ailleurs, exige que les AS de quatre octets soient acceptés. En pratique, on peut espérer que les derniers routeurs ne gérant pas les AS de quatre octets auront disparu bien avant que BGPsec soit massivement déployé.
La section 8 du RFC discute de la validation des chemins d'AS signés. Par exemple, le RFC 8205 demande qu'un routeur transmette les annonces ayant des signatures invalides. Pourquoi ? Parce que la RPKI n'est que modérement synchrone : il est parfaitement possible qu'un nouveau certificat ne soit arrivé que sur certains routeurs et que, donc, certains acceptent la signature et d'autres pas. Il ne faut donc pas préjuger de ce que pourront valider les copains.
Une question qui revient souvent avec les techniques de sécurité (pas seulement BGPsec mais aussi des choses comme DNSSEC) est « et si la validation échoue, que doit-on faire des données invalides ? » Vous ne trouverez pas de réponse dans le RFC : c'est une décision locale. Pour BGPsec, chaque routeur, ou plus exactement son administrateur, va décider de ce qu'il faut faire avec les annonces dont le chemin d'AS signé pose un problème. Contrairement à DNSSEC, où la validation peut donner trois résultats (oui, en fait, quatre, mais je simplifie, cf. RFC 4035), « sûr », « non sûr », et « invalide », BGPsec n'a que deux résultats possibles, « valide » et « invalide ». L'état « invalide » regroupe aussi bien les cas où le chemin d'AS n'est pas signé (par exemple parce qu'un routeur non-BGPsec se trouvait sur le trajet) que le cas où une signature ne correspond pas aux données (les deux états « non sûr » et « invalide » de DNSSEC se réduisent donc à un seul ici). Il avait été discuté de faire une division plus fine entre les différents cas d'invalidité mais il avait semblé trop complexe de rendre en compte tous les cas possibles. Notez que « invalide » couvre même le cas où un ROA valide l'origine (un cas particulier des chemins partiellement signés, déjà traités).
Donc, si une annonce est invalide, que doit faire le routeur ? D'abord, la décision d'accepter ou pas une route dépend de plusieurs facteurs, la validation BGPsec n'en étant qu'un seul. Ensuite, il n'est pas évident de traiter tous les cas. D'où la décision de laisser le problème à l'administrateur réseaux.
Ah, et si vous utilisez iBGP, notez que la validation BGPsec ne se fait qu'en bord d'AS. Vous pouvez transporter l'information comme quoi une annonce était valide ou invalide comme vous voulez (une communauté à vous ?), il n'existe pas de mécanisme standard dans iBGP pour cela.
Enfin, la section 9 de notre RFC traite de quelques problèmes d'ordre opérationnel. Mais pensez à lire le RFC 8207 avant. Par exemple, elle estime que BGPsec, contrairement à la validation avec les ROA seuls, nécessitera sans doute du nouveau matériel dans les routeurs, comme un coprocesseur cryptographique, et davantage de RAM. C'est une des raisons pour lesquelles on ne verra certainement pas de déploiement significatif de BGPsec avant des années. Ceci dit, au début, les routeurs BGPsec auront peu de travail supplémentaire, précisément puisqu'il y aura peu d'annonces signées, donc pourront retarder les mises à jour matérielles. D'ici que BGPsec devienne banal, des optimisations comme celles décrites dans cet exposé ou celui-ci, ou encore dans l'article « Design and analysis of optimization algorithms to minimize cryptographic processing in BGP security protocols » aideront peut-être.
Date de publication du RFC : Mai 2018
Auteur(s) du RFC : R. Gellens (Core Technology Consulting)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF slim
Première rédaction de cet article le 26 mai 2018
Le groupe de travail SLIM de l'IETF s'occupe de définir des mécanismes pour le choix d'une langue lors de la communication. Son premier RFC, le RFC 8255, concernait le courrier électronique. Ce nouveau RFC concerne, lui, les communications « en temps réel », comme la téléphonie sur IP.
Un scénario d'usage typique est celui d'un client qui appelle le support d'une société internationale. Mettons que le client a pour langue maternelle l'ourdou mais peut se débrouiller en anglais. On veut qu'il puisse préciser cet ordre de préférences, et, idéalement, que le logiciel utilisé dans le call center le route automatiquement vers un employé qui parle ourdou ou, si aucun n'est disponible, vers un employé qui parle anglais. Plus vital, le scénario d'un appel d'urgence où un touriste danois en vacances en Italie appelle le 112 et où il faut trouver très vite quelqu'un qui peut parler une langue qu'il comprend (sachant qu'en situation d'urgence, on est moins à l'aise avec les langues étrangères). Comme le dit avec euphémisme le RFC « avoir une langue en commun est utile pour la communication ». Pour gérer tous ces scénarios, notre RFC va utiliser les attributs de SDP (RFC 4566, SDP est un format - pas un protocole, en dépit de son nom - déjà très utilisé dans les protocoles de communication instantanée pour transmettre des métadonnées au sujet d'une communication).
Parfois, on a déjà l'information disponible (si on appelle une personne qu'on connait et qui nous connait), et déjà choisi une langue (par exemple une audioconférence dans une entreprise où la règle est que tout se fasse en anglais). Notre RFC traite le cas où on n'a pas cette information, et il faut donc une négociation au début de la communication. Cela implique que le logiciel des deux côtés ait été configuré avec les préférences et capacités linguistiques des deux parties (une question d'interface utilisateur, non spécifiée par ce RFC).
Notez qu'il peut y avoir plusieurs langues différentes utilisées, une pour chaque flux de données. Par exemple, l'appelant peut parler dans une langue que son interlocuteur comprend, mais qu'il a du mal à parler, et il utilisera donc une autre langue pour la réponse. Notez aussi que la communication n'est pas uniquement orale, elle peut être écrite, par exemple pour les malentendants. Le RFC rappelle à juste titre qu'un sourd n'est pas forcément muet et qu'il ou elle peut donc choisir l'oral dans une direction et le texte dans une autre. (Au passage, la synchronisation des lèvres, pour la lecture sur les lèvres, est traitée dans le RFC 5888.)
La solution choisie est décrite en détail dans la section 5 de
notre RFC. Elle consiste en deux attributs SDP,
hlang-send
et
hlang-recv
(hlang
=
human language). Leur valeur est évidemment une
étiquette de langue, telles qu'elles sont
normalisées dans le RFC 5646. Dans une offre
SDP, hlang-send
est une liste (pas une langue
unique) de langues que l'offreur sait parler, séparées par des
espaces, donnée dans l'ordre de
préférence décroissante, et
hlang-recv
une liste de langues qu'elle ou
lui comprend. Notez qu'il est de la responsabilité de l'offreur
(typiquement celui ou celle qui appelle) de proposer des choix
réalistes (le RFC donne le contre-exemple d'un offreur qui demanderait à
parler en hongrois et à avoir la réponse en
portugais…) D'autre part, notre RFC
recommande de bien lire la section 4.1 du RFC 5646, qui indique d'étiqueter intelligement, et
notamment de ne pas être trop spécifique : si on est australien et qu'on comprend bien
l'anglais, indiquer comme langue en
est
suffisant, préciser (ce qui serait une étiquette légale)
en-AU
est inutile et même dangereux si le
répondant se dit « je ne sais pas parler avec l'accent australien,
tant pis, je raccroche ».
La langue choisie par le répondant est indiquée dans la
réponse. hlang-send
et
hlang-recv
sont cette fois des langues
uniques. Attention, ce qui est envoi pour l'une des parties est
réception pour l'autre : hlang-send
dans la
réponse est donc un choix parmi les
hlang-recv
de l'offre. L'offreur (l'appelant) est ainsi prévenu du choix qui a
été effectué et peut se préparer à parler la langue indiquée par
le hlang-recv
du répondant, et à comprendre
celle indiquée par le hlang-send
.
Voici un exemple simple d'un bloc SDP (on n'en montre qu'une partie), où seul l'anglais est proposé ou accepté (cet exemple peut être une requête ou une réponse) :
m=audio 49170 RTP/AVP 0 a=hlang-send:en a=hlang-recv:en
Le cas où hlang-send
et
hlang-recv
ont la même valeur sera sans doute
fréquent. Il avait même été envisagé de permettre un seul
attribut (par exemple hlang
) dans ce cas
courant mais cela avait été écarté, au profit de la solution
actuelle, plus générale.
Un exemple un peu plus compliqué où la demande propose trois langues (espagnol, basque et anglais dans l'ordre des préférences décroissantes) :
m=audio 49250 RTP/AVP 20 a=hlang-send:es eu en a=hlang-recv:es eu en
Avec une réponse où l'espagnol est utilisé :
m=audio 49250 RTP/AVP 20 a=hlang-send:es a=hlang-recv:es
Et si ça rate ? S'il n'y a aucune langue en commun ? Deux choix sont possibles, se résigner à utiliser une langue qu'on n'avait pas choisi, ou bien raccrocher. Le RFC laisse aux deux parties la liberté du choix. En cas de raccrochage, le code d'erreur SIP à utiliser est 488 (Not acceptable here) ou bien 606 (Not acceptable), accompagné d'un en-tête d'avertissement avec le code 308, par exemple :
Warning: 308 code proxy.example.com "Incompatible language specification: Requested languages "fr zh" not supported. Supported languages are: "es en".
Si la langue indiquée est une langue des signes, elle peut être utilisée dans un canal vidéo, mais évidemment pas dans un canal audio. (Le cas d'un canal texte est laissé à l'imagination des lecteurs. Le cas des sous-titres, ou autres textes affichés dans une vidéo, n'est pas traité par notre RFC.)
Voici un exemple bien plus riche, avec plusieurs médias. La vidéo en langue des signes argentine, le texte en espagnol (ou à la rigueur en portugais), et un canal audio, mêmes préférences que le texte :
m=video 51372 RTP/AVP 31 32 a=hlang-send:aed m=text 45020 RTP/AVP 103 104 a=hlang-send:es pt m=audio 49250 RTP/AVP 20 a=hlang-recv:es pt
Voici une réponse possible à cette requête, avec de l'espagnol pour le canal texte et pour la voix. Aucune vidéo n'est proposée, sans doute car aucune n'était disponible dans la langue demandée :
m=video 0 RTP/AVP 31 32 m=text 45020 RTP/AVP 103 104 a=hlang-recv:es m=audio 49250 RTP/AVP 20 a=hlang-send:es
Notez que ce RFC ne fournit pas de mécanisme pour exprimer des préférences entre les différents canaux (texte et audio, par exempe), uniquement entre langues pour un même canal.
Les deux attributs hlang-recv
et
hlang-send
ont été ajoutés au registre
IANA des attributs SDP.
Notons que la section 8 du RFC, sur la protection de la vie privée, rappelle qu'indiquer les préférences linguistiques peut permettre d'apprendre des choses sur l'utilisateur, par exemple sa nationalité. Une section Privacy considerations, quoique non obligatoire, est de plus en plus fréquente dans les RFC.
Enfin, question alternatives, le RFC note aussi (section 4) qu'on aurait pu utiliser
l'attribut existant lang
, qui existe déjà
dans SDP (RFC 4566, section 6). Mais il n'est pas
mentionné dans le RFC 3264, ne semble pas
utilisé à l'heure actuelle, et ne permet pas de spécifier une
langue différente par direction de communication.
À ma connaissance, il n'y a pas encore de mise en œuvre de ce RFC mais comme il est cité dans des documents normatifs, par exemple dans le NENA 08-01 de la North American Emergency Number Association, il est possible qu'elles apparaissent bientôt.
Date de publication du RFC : Avril 2018
Auteur(s) du RFC : G. Huston (APNIC), G. Michaelson
(APNIC), C. Martinez
(LACNIC), T. Bruijnzeels (RIPE
NCC), A. Newton (ARIN), D. Shaw
(AFRINIC)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 6 avril 2018
La RPKI est un ensemble de formats et de règles pour certifier qui est le titulaire d'une ressource Internet, une ressource étant un préfixe d'adresses IP, un numéro de système autonome, etc. La RPKI est utilisée dans les mécanismes de sécurisation de BGP, par exemple pour vérifier qu'un AS est bien autorisé à être à l'origine d'un préfixe donné. Les règles initiales de la RPKI pour valider un certificat était trop strictes et ce RFC les assouplit légèrement (normalement, en gardant le même niveau de sécurité).
La RPKI est normalisée dans le RFC 6480. Elle prévoit notamment un système de certificats par lequel une autorité affirme qu'une autorité de niveau inférieur a bien reçu une délégation pour un groupe de ressources Internet. (On appelle ces ressources les INR, pour Internet Number Resource.) La validation des certificats est décrite dans le RFC 6487. L'ancienne règle était que le certificat de l'autorité inférieure ne devait lister que des ressources qui étaient dans le certificat de l'autorité qui signait (RFC 6487, section 7.2, notamment la condition 6 de la seconde énumération). En pratique, cette règle s'est avérée trop rigide, et la nouvelle, décrite dans notre RFC 8360 est que le certificat de l'autorité inférieure n'est accepté que pour des ressources qui étaient dans le certificat de l'autorité qui signait. S'il y a d'autres ressources, le certificat n'est plus invalide, simplement, ces ressources sont ignorées. Dit autrement, l'ensemble des ressources dont la « possession » est certifiée, est l'intersection des ressources du certificat de l'autorité parente et de celui de l'autorité fille. Cette intersection se nomme VRS, pour Verified Resource Set.
Voici d'abord une chaîne de certificats qui était valide avec l'ancienne règle, et qui l'est toujours :
Certificate 1 (trust anchor): Issuer TA, Subject TA, Resources 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32, AS64496-AS64500 Certificate 2: Issuer TA, Subject CA1, Resources 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32 Certificate 3: Issuer CA1, Subject CA2, Resources 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32 ROA 1: Embedded Certificate 4 (EE certificate): Issuer CA2, Subject R1, Resources 192.0.2.0/24
La chaîne part d'un certificat (TA, pour Trust Anchor, le point de départ de la validation). Elle aboutit à un ROA (Route Origin Authorization, cf. RFC 6482). Chaque certificat est valide puisque chacun certifie un jeu de ressources qui est un sous-ensemble du jeu de ressources de l'autorité du dessus. Idem pour le ROA.
Et voici par contre une chaîne de certificats qui était invalide selon les anciennes règles, et est désormais valide avec les règles de notre RFC 8360 :
Certificate 1 (trust anchor): Issuer TA, Subject TA, Resources 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32, AS64496-AS64500 Certificate 2: Issuer TA, Subject CA1, Resources 192.0.2.0/24, 2001:db8::/32 Certificate 3 (invalid before, now valid): Issuer CA1, Subject CA2, Resources 192.0.2.0/24, 198.51.100.0/24, 2001:db8::/32 ROA 1 (invalid before, now valid): Embedded Certificate 4 (EE certificate, invalid before, now valid): Issuer CA2, Subject R1, Resources 192.0.2.0/24
La chaîne était invalide car le troisième certificat ajoutait
198.51.100.0/24
, qui n'était pas couvert par
le certificat supérieur. Désormais, elle est valide mais vous
noterez que seuls les préfixes 192.0.2.0/24
et
2001:db8::/32
sont
couverts, comme précédemment (198.51.100.0/24
est ignoré). Avec les règles de notre nouveau RFC, le ROA final
est désormais valide (il n'utilise pas
198.51.100.0/24
). Notez que les nouvelles
règles, comme les anciennes, n'autoriseront jamais l'acceptation
d'un ROA pour des ressources dont l'émetteur du certificat n'est
pas titulaire (c'est bien le but de la RPKI). Ce point est
important car l'idée derrière ce nouveau RFC de rendre plus
tolérante la validation n'est pas passée toute seule à l'IETF.
Un cas comme celui des deux chaînes ci-dessus est probablement rare. Mais il pourrait avoir des conséquences sérieuses si une ressource, par exemple un préfixe IP, était supprimée d'un préfixe situé très haut dans la hiérarchie : plein de certificats seraient alors invalidés avec les règles d'avant.
La section 4 de notre RFC détaille la procédure de validation. Les anciens certificats sont toujours validés avec
l'ancienne politique, celle du RFC 6487. Pour avoir la nouvelle politique, il faut de
nouveaux certificats, identifiés par un OID
différent. La section 1.2 du RFC 6484
définissait id-cp-ipAddr-asNumber / 1.3.6.1.5.5.7.14.2
comme OID pour
l'ancienne politique. La nouvelle politique est
id-cp-ipAddr-asNumber-v2 / 1.3.6.1.5.5.7.14.3
et c'est elle qui doit
être indiquée dans un certificat pour que lui soient appliquées
les nouvelles règles. (Ces OID sont dans un
registre IANA.) Comme les logiciels existants rejetteront
les certificats avec ces nouveaux OID, il faut adapter les
logiciels avant que les AC ne se mettent à
utiliser les nouveaux OID (cf. section 6 du RFC). Les changements
du RFC n'étant pas dans le protocole mais uniquement dans les
règles de validation, le déploiement devrait être relativement facile.
Le cœur des nouvelles règles est dans la
section 4.2.4.4 de notre RFC, qui remplace la section 7.2 du RFC 6487. Un nouveau concept est introduit, le
VRS (Verified Resource Set). C'est
l'intersection des ressources d'un certificat
et de son certificat supérieur. Dans le deuxième exemple plus
haut, le VRS du troisième certificat est
{192.0.2.0/24
,
2001:db8::/32
}, intersection de
{192.0.2.0/24
,
2001:db8::/32
} et
{192.0.2.0/24
,
198.51.100.0/24
,
2001:db8::/32
}. Si le VRS est vide, le
certificat est invalide (mais on ne le rejette pas, cela peut être
un cas temporaire puisque la RPKI n'est pas cohérente en permanence).
Un ROA est valide si les ressources qu'il contient sont un sous-ensemble du VRS du certificat qui l'a signé (si, rappelons-le, ce certificat déclare la nouvelle politique). La règle exacte est un peu plus compliquée, je vous laisse lire le RFC pour les détails. Notez qu'il y a également des règles pour les AS, pas seulement pour les préfixes IP.
La section 5 du RFC contient davantage d'exemples, illustrant les nouvelles règles.
Date de publication du RFC : Mars 2018
Auteur(s) du RFC : R. Housley (Vigil Security)
Pour information
Première rédaction de cet article le 13 mars 2018
Dernière mise à jour le 5 avril 2018
Les Internet-Drafts sont signés suivant les règles du RFC 5485, afin qu'une lectrice ou un lecteur puissent vérifier qu'un Internet-Draft n'a pas été modifié en cours de route. Ce nouveau RFC modifie légèrement le RFC 5485 sur un seul point : la signature d'Internet-Drafts qui sont écrits en Unicode.
En effet, depuis le RFC 7997, les RFC ne sont plus forcément en ASCII, ils peuvent intégrer des caractères Unicode. Le premier RFC publié avec ces caractères a été le RFC 8187, en septembre 2017. Bientôt, cela sera également possible pour les Internet-Drafts. Cela affecte forcément les règles de signature, d'où cette légère mise à jour.
Le RFC 5485 normalisait l'utilisation de CMS (RFC 5652) pour le format des signatures. Vous pouvez télécharger ces signatures sur n'importe lequel des sites miroirs. CMS utilise ASN.1, avec l'obligation d'utiliser l'encodage DER, le seul encodage d'ASN.1 qui ne soit pas ambigu (une seule façon de représenter un texte).
Les Internet-Drafts sont actuellement tous en texte
brut, limité à ASCII. Mais cela
ne va pas durer (RFC 7990). Les signatures des
Internet-Drafts sont détachées de l'Internet-Draft (section 2 de notre RFC), dans un fichier portant le même
nom auquel on ajoute l'extension .p7s
(RFC 5751). Par exemple avec wget, pour
récupérer un Internet-Draft et sa signature :
% wget https://www.ietf.org/id/draft-bortzmeyer-dname-root-05.txt % wget https://www.ietf.org/id/draft-bortzmeyer-dname-root-05.txt.p7s
(Ne le faites pas avec un Internet-Draft trop récent, les signatures n'apparaissent qu'au bout de quelques jours, la clé privée n'est pas en ligne.)
La signature est au format CMS (RFC 5652). Son adaptation aux RFC et Internet-Drafts est
normalisée dans le RFC 5485. Le champ
SignedData.SignerInfo.EncapsulatedContentInfo.eContentType
du CMS identifie le type d'Internet-Draft signé. Les valeurs
possibles figurent dans un registre
IANA. Il y avait déjà des valeurs comme
id-ct-asciiTextWithCRLF
qui identifiait
l'Internet-Draft classique en texte brut en ASCII, notre RFC ajoute (section
5) id-ct-utf8TextWithCRLF
(texte brut en
UTF-8),
id-ct-htmlWithCRLF
(HTML) et id-ct-epub
(EPUB). Chacun de ces types a un
OID, par exemple le texte brut en UTF-8
sera 1.2.840.113549.1.9.16.1.37
.
Maintenant, passons à un morceau délicat, la canonicalisation des Internet-Drafts. Signer nécessite de canonicaliser, autrement, deux textes identiques aux yeux de la lectrice pourraient avoir des signatures différentes. Pour le texte brut en ASCII, le principal problème est celui des fins de ligne, qui peuvent être représentées différemment selon le système d'exploitation. Nous utilisons donc la canonicalisation traditionnelle des fichiers texte sur l'Internet, celle de FTP : le saut de ligne est représenté par deux caractères, CR et LF. Cette forme est souvent connue sous le nom de NVT (Network Virtual Terminal) mais, bien que très ancienne, n'avait été formellement décrite qu'en 2008, dans l'annexe B du RFC 5198, qui traitait pourtant un autre sujet.
Pour les Internet-Drafts au format XML, notre RFC
renvoie simplement au W3C et à sa norme
XML, section 2.11 de la cinquième
édition, qui dit qu'il faut translating both the
two-character sequence #xD #xA and any #xD that is not followed by
#xA to a single #xA character. La canonicalisation
XML (telle que faite par xmllint
--c14n
) n'est pas prévue.
Les autres formats ne subissent aucune opération particulière de canonicalisation. Un fichier EPUB, par exemple, est considéré comme une simple suite d'octets. On notera que le texte brut en Unicode ne subit pas de normalisation Unicode. C'est sans doute à cause du fait que le RFC 7997, dans sa section 4, considère que c'est hors-sujet. (Ce qui m'a toujours semblé une drôle d'idée, d'autant plus qu'il existe une norme Internet sur la canonicalisation du texte brut en Unicode, RFC 5198, qui impose la normalisation NFC.)
À l'heure actuelle, les Internet-Drafts sont signés, les outils doivent encore être adaptés aux nouvelles règles de ce RFC, mais elles sont simples et ça ne devrait pas être trop dur. Pour vérifier les signatures, la procédure (qui est documentée) consiste d'abord à installer le logiciel de canonicalisation :
% wget https://www.ietf.org/id-info/canon.c % make canon
Puis à télécharger les certificats racine :
% wget https://www.ietf.org/id-info/verifybundle.pem
Vous pouvez examiner ce groupe de certificats avec :
% openssl crl2pkcs7 -nocrl -certfile verifybundle.pem | openssl pkcs7 -print_certs -text
Téléchargez ensuite des Internet-Drafts par exemple en
https://www.ietf.org/id/
:
% wget https://www.ietf.org/id/draft-ietf-isis-sr-yang-03.txt % wget https://www.ietf.org/id/draft-ietf-isis-sr-yang-03.txt.p7s
On doit ensuite canonicaliser l'Internet-Draft :
% ./canon draft-ietf-isis-sr-yang-03.txt draft-ietf-isis-sr-yang-03.txt.canon
On peut alors vérifier les signatures :
% openssl cms -binary -verify -CAfile verifybundle.pem -content draft-ietf-isis-sr-yang-03.txt.canon -inform DER -in draft-ietf-isis-sr-yang-03.txt.p7s -out /dev/null Verification successful
Si vous avez à la place :
Verification failure 139818719196416:error:2E09A09E:CMS routines:CMS_SignerInfo_verify_content:verification failure:../crypto/cms/cms_sd.c:819: 139818719196416:error:2E09D06D:CMS routines:CMS_verify:content verify error:../crypto/cms/cms_smime.c:393:
c'est sans doute que vous avez oublié l'option -binary
.
Si vous trouvez la procédure compliquée, il y a un script qui
automatise tout ça idsigcheck
:
% ./idsigcheck --setup % ./idsigcheck draft-ietf-isis-sr-yang-03.txt
Si ça vous fait bad interpreter: /bin/bash^M
,
il faut recoder les sauts de ligne :
% dos2unix idsigcheck dos2unix: converting file idsigcheck to Unix format...
Ce script appelle OpenSSL mais pas avec les bonnes options à l'heure actuelle, vous risquez donc d'avoir la même erreur que ci-dessus.
Date de publication du RFC : Mars 2018
Auteur(s) du RFC : J. Brzozowski, J. Leddy
(Comcast), C. Filsfils, R. Maglione, M. Townsley
(Cisco)
Pour information
Réalisé dans le cadre du groupe de travail IETF spring
Première rédaction de cet article le 29 mars 2018
Le sigle SPRING signifie « Source Packet Routing In NetworkinG ». C'est quoi, le routage par la source (source routing) ? Normalement, la transmission de paquets IP se fait uniquement en fonction de l'adresse de destination, chaque routeur sur le trajet prenant sa décision indépendemment des autres, et sans que l'émetteur original du paquet n'ait son mot à dire. L'idée du routage par la source est de permettre à cet émetteur d'indiquer par où il souhaite que son paquet passe. L'idée est ancienne, et resurgit de temps en temps sur l'Internet. Ce nouveau RFC décrit les cas où une solution de routage par la source serait utile.
L'idée date des débuts de l'Internet. Par exemple, la norme IPv4, le RFC 791, spécifie, dans sa section 3.1, deux mécanismes de routage par la source, « Loose Source Routing » et « Strict Source Routing ». Mais ces mécanismes sont peu déployés et vous n'avez guère de chance, si vous mettez ces options dans un paquet IP, de voir un effet. En effet, le routage par la source est dangereux, il permet des attaques variées, et il complique beaucoup le travail des routeurs. Le but du projet SPRING, dont c'est le deuxième RFC, est de faire mieux. Le cahier des charges du projet est dans le RFC 7855.
L'architecture technique de SPRING est dans le RFC 8402. Ce segment routing est déjà mis en œuvre dans le noyau Linux depuis la version 4.14 (cf. le site du projet). Notre nouveau RFC 8354 ne contient, lui, que les scénarios d'usage. (Certains étaient déjà dans la section 3 du RFC 7855.) Seul IPv6 est pris en compte.
D'abord, le cas du SOHO connecté à
plusieurs fournisseurs
d'accès. Comme chacun de ces fournisseurs n'acceptera
que des paquets dont l'adresse IP source est dans un préfixe qu'il
a alloué au client, il est essentiel de pouvoir router en fonction
de la source, afin d'envoyer les paquets ayant une adresse source
du FAI A vers le FAI A et seulement celui-ci. Voici par exemple
comment faire sur Linux, quand on veut
envoyer les paquets ayant l'adresse IP source
2001:db8:dc2:45:216:3eff:fe4b:8c5b
vers un
routeur différent de l'habituel (le RFC 3178
peut être une bonne lecture, quoique daté, notamment sa section 5) :
#!/bin/sh DEVICE=eth1 SERVICE=2001:db8:dc2:45:216:3eff:fe4b:8c5b TABLE=CustomTable ROUTER=2001:db8:dc2:45:1 echo 200 ${TABLE} >> /etc/iproute2/rt_tables ip -6 rule add from ${SERVICE} table ${TABLE} ip -6 route add default via ${ROUTER} dev ${DEVICE} table ${TABLE} ip -6 route flush cache
Notez que la décision correcte peut être prise par la machine terminale, comme dans l'exemple ci-dessus, ou bien par un routeur situé plus loin sur le trajet (dans le projet SPRING, la source n'est pas forcément la machine terminale initiale).
Outre le fait que le FAI B rejetterait probablement les paquets ayant une adresse source qui n'est pas à lui (RFC 3704), il peut y avoir d'autres raisons pour envoyer les paquets sur une interface de sortie particulière :
Autre cas où un routage par la source peut être utile, le FAI peut s'en servir pour servir certains utilisateurs ou certains usages dans des conditions différentes, par exemple avec des prix à la tête du client. Cela viole sans doute la neutralité du réseau mais c'est peut-être un scénario qui en tentera certains (sections 2.2 et 2.5 du RFC). Cela concerne le réseau d'accès (de M. Michu au FAI) et aussi le cœur de réseau ; « l'opérateur peut vouloir configurer un chemin spécial pour les applications sensibles à la latence ».
De plus haute technologie est le scénario présenté dans la section 2.3. Ici, il s'agit d'un centre de données entièrement IPv6. Cela simplifie considérablement la gestion du réseau, et cela permet d'avoir autant d'adresses qu'on veut, sans se soucier de la pénurie d'adresses IPv4. Certains opérateurs travaillent déjà à de telles configurations. Dans ce cas, le routage par la source serait un outil puissant pour, par exemple, isoler différents types de trafic et les acheminer sur des chemins spécifiques.
Si le routage par la source, une très vieille idée, n'a jamais vraiment pris, c'est en grande partie pour des raisons de sécurité. La section 4 rappelle les risques associés, qui avaient mené à l'abandon de la solution « Type 0 Routing Header » (cf. RFC 5095).
Date de publication du RFC : Avril 2018
Auteur(s) du RFC : C. Gomez (UPC), M. Kovatsch (ETH Zurich), H. Tian (China Academy of Telecommunication Research), Z. Cao (Huawei Technologies)
Pour information
Réalisé dans le cadre du groupe de travail IETF lwig
Première rédaction de cet article le 7 avril 2018
Les objets contraints, engins connectés ayant peu de capacités (un capteur de température dans la nature, par exemple), ont en général une réserve d'énergie électrique très limitée, fournie par des piles ou batteries à la capacité réduite. L'usage des protocoles de communication traditionnels, souvent très bavards, épuiserait rapidement ces réserves. Ce RFC étudie les mécanismes utilisables pour limiter la consommation électrique, et faire ainsi durer ces objets plus longtemps.
La plupart des protocoles de couche 2 utilisés pour ces « objets contraints » disposent de fonctions pour diminuer la consommation électrique. Notre RFC décrit ces fonctions, et explique comment les protocoles de couches supérieures, comme IP peuvent en tirer profit. Si vous ne connaissez pas le monde des objets contraints, et les problèmes posés par leur connexion à l'Internet, je recommande fortement la lecture des RFC 6574, RFC 7228, et RFC 7102. Le but des techniques présentées dans ce RFC est bien de faire durer plus longtemps la batterie, ce n'est pas un but écologique. Si l'objet est alimenté en permanence (une télévision connectée, ou bien un grille-pain connecté), ce n'est pas considéré comme problème.
Des tas de travaux ont déjà eu lieu sur ce problème de la diminution de la consommation électrique de ces objets. Il y a eu moins d'efforts sur celle des protocoles réseau, et ce sont ces efforts que résume ce RFC. Les protocoles traditionnels étaient conçus pour des machines alimentées en permanence et pour qui la consommation électrique n'était pas un problème. Diffuser très fréquemment une information, même inutile, n'était pas perçu comme du gaspillage, contrairement à ce qui se passe avec l'Internet des Objets. (Voir le RFC 7772 pour une solution à un tel problème.)
L'IETF a déjà développé un certain nombre de protocoles qui sont spécifiques à cet « Internet des Objets ». Ce sont par exemple 6LoWPAN (RFC 6282, RFC 6775 et RFC 4944), ou bien le protocole de routage RPL (RFC 6550) ou encore l'alternative à HTTP CoAP (RFC 7252). En gros, l'application fait tourner du CoAP sur IPv6 qui est lui-même au-dessus de 6LoWPAN, couche d'adaptation d'IP à des protocoles comme IEEE 802.15.4. Mais l'angle « économiser l'énergie » n'était pas toujours clairement mis en avant, ce que corrige notre nouveau RFC.
Qu'est ce qui coûte cher à un engin connecté (cher en terme d'énergie, la ressource rare) ? En général, faire travailler le CPU est bien moins coûteux que de faire travailler la radio, ce qui justifie une compression énergique. Et, question réseau, la réception n'est pas forcément moins coûteuse que la transmission. L'étude Powertrace est une bonne source de mesures dans ce domaine mais on peut également lire « Measuring Power Consumption of CC2530 With Z-Stack ». Le RFC contient aussi (section 2) des chiffres typiques, mesurés sur Contiki et publiés dans « The ContikiMAC Radio Duty Cycling Protocol » :
Une technique courante quand on veut réduire la consommation électrique lors de la transmission est de réduire la puissance d'émission. Mais cela réduit la portée, donc peut nécessiter d'introduire du routage, ce qui augmenterait la consommation.
On a vu que la simple écoute pouvait consommer beaucoup d'énergie. Il faut donc trouver des techniques pour qu'on puisse couper l'écoute (éteindre complètement la radio), tout en gardant la possibilité de parler à l'objet si nécessaire. Sans ce duty-cycling (couper la partie radio de l'objet, cf. RFC 7228, section 4.3), la batterie ne durerait que quelques jours, voire quelques heures, alors que certains objets doivent pouvoir fonctionner sans entretien pendant des années. Comment couper la réception tout en étant capable de recevoir des communications ? Il existe trois grandes techniques de RDC (Radio Duty-Cycling, section 3 de notre RFC) :
Dans les trois cas, la latence va évidemment en souffrir. On doit faire un compromis entre réduire la consommation électrique et augmenter la latence. On va également diminuer la capacité du canal puisqu'il ne pourra pas être utilisé en permanence. Ce n'est pas forcément trop grave, un capteur a peu à transmettre, en général.
Il existe plein de méthodes pour gagner quelques μJ, comme le regroupement de plusieurs paquets en un seul (il y a un coût fixe par paquet, indépendamment de leur taille). Normalement, le RDC est quelque part dans la couche 2 et les protocoles IETF, situés plus haut, ne devraient pas avoir à s'en soucier. Mais une réduction sérieuse de la consommation électrique nécessite que tout le monde participe, et que les couches hautes fassent un effort, et connaissent ce que font les couches basses, pour s'adapter.
La section 3.6 de notre RFC détaille quelques services que fournissent les protocoles de couche 2 de la famille IEEE 802.11. Une station (un objet ou un ordinateur) peut indiquer au point d'accès (AP, pour access point) qu'il va s'endormir. L'AP peut alors mémoriser les trames qui lui étaient destinées, pour les envoyer plus tard. IEEE 802.11v va plus loin en incluant des mécanismes comme le proxy ARP (répondre aux requêtes ARP à la place de l'objet endormi).
Bluetooth a un ensemble de services pour la faible consommation, nommés Bluetooth LE (pour Low Energy). 6LoWPAN peut d'ailleurs en tirer profit (cf. RFC 7668).
IEEE 802.15.4 a aussi des solutions. Il permet par exemple d'avoir deux types de réseaux, avec ou sans annonces périodiques (beacons). Dans un réseau avec annonces, des machines nommés les coordinateurs envoient régulièrement des annonces qui indiquent quand on peut émettre et quand on ne peut pas, ces dernières périodes étant le bon moment pour s'endormir afin de diminuer la consommation : on est sûr de ne rien rater. Les durées de ces périodes sont configurables par l'administrateur réseaux. Dans les réseaux sans annonces, les différentes machines connectées n'ont pas d'informations et transmettent quand elles veulent, en suivant CSMA/CA.
Enfin, DECT a aussi un mode à basse consommation, DECT ULE. Elle est également utilisable avec 6LoWPAN (RFC 8105). DECT est très asymétrique, il y a le FP (Fixed Part, la base), et le PP (Portable Part, l'objet). DECT ULE permet au FP de prévenir quand il parlera (et le PP peut dormir jusque là). À noter que si la « sieste » est trop longue (plus de dix secondes), le PP devra refaire une séquence de synchronisation avec le FP, qui peut être coûteuse en énergie. Il faut donc bien calculer son coup : si on s'endort souvent pour des périodes d'un peu plus de dix secondes, le bilan global sera sans doute négatif.
La section 4 de notre RFC couvre justement ce que devrait faire IP, en supposant qu'on utilisera 6LoWPAN. Il y a trois services importants de 6LoWPAN pour la consommation électrique :
Il y a aussi des optimisations qui ne changent pas le protocole. Par exemple, Contiki ne suit pas, dans sa mise en œuvre, une stricte séparation des couches, afin de pouvoir optimiser l'activité réseau.
De même, les protocoles de routage doivent tenir compte des contraintes de consommation électrique (section 5 du RFC). Le protocole « officiel » de l'IETF pour le routage dans les réseaux contraints est RPL (RFC 6550). L'étude Powertrace déjà citée étudie également la consommation de RPL et montre qu'il est en effet assez efficace, si le réseau est stable (s'il ne l'est pas, on consommera évidemment du courant à envoyer et recevoir les mises à jours des routes). On peut adapter les paramètres de RPL, via l'algorithme Trickle (RFC 6206, rien à voir avec le protocole de transfert de fichiers de BitNet), pour le rendre encore plus économe, mais au prix d'une convergence plus lente lorsque le réseau change.
À noter que le RFC ne parle que de RPL, et pas des autres protocoles de routage utilisables par les objets, comme Babel (RFC 8966).
Et les applications ? La section 6 du RFC leur donne du boulot, à elles aussi. Bien sûr, il est recommandé d'utiliser CoAP (RFC 7252) plutôt que HTTP, entre autre en raison de son en-tête plus court, et de taille fixe. D'autre part, CoAP a un mode de pure observation, où le client indique son intérêt pour certaines ressources, que le serveur lui enverra automatiquement par la suite, lorsqu'elles changeront, économisant ainsi une requête. Comme HTTP, CoAP peut utiliser des relais qui mémorisent la ressource demandée, ce qui permet de récupérer des ressources sans réveiller le serveur, si l'information était dans le cache. D'autres protocoles étaient à l'étude, reprenant les principes de CoAP, pour mieux gérer les serveurs endormis. (Mais la plupart de ces projets semblent… endormis à l'heure actuelle.)
Enfin, la section 7 de notre RFC résume les points importants :
Date de publication du RFC : Mars 2018
Auteur(s) du RFC : M. Nottingham, E. Nygren
(Akamai)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 22 mars 2018
Le concept d'origine est crucial pour la
sécurité de HTTP. L'idée est d'empêcher du
contenu actif (code JavaScript, par
exemple) d'interagir avec des serveurs autres que ceux de
l'origine, de l'endroit où on a chargé ce contenu actif. Dans la
version 1 de HTTP, cela ne posait pas (trop) de problèmes. Mais la
version 2 de HTTP permet d'avoir, dans une même connexion HTTP
vers un serveur donné, accès à des ressources d'origines
différentes (par exemple parce qu'hébergées sur des
Virtual Hosts
différents). Ce nouveau RFC ajoute donc au protocole HTTP/2 un
nouveau type de trame, ORIGIN
, qui permet de
spécifier les origines utilisées dans une connexion.
L'origine est un concept ancien, mais sa description formelle n'est venue au'avec le RFC 6454, dont la lecture est fortement recommandée, avant de lire ce nouveau RFC 8336. Son application à HTTP/2, normalisé dans le RFC 7540, a posé quelques problèmes (sections 9.1.1 et 9.1.2 du RFC 7540). En effet, avec HTTP/2, des origines différentes peuvent coexister sur la même connexion HTTP. Si le serveur ne peut pas produire une réponse, par exemple parce qu'il sépare le traitement des requêtes entre des machines différentes, il va envoyer un code de retour 421, indiquant à un client HTTP de re-tenter, avec une connexion différente. Pour lui faire gagner du temps, notre nouveau RFC 8336 va indiquer préalablement les origines acceptables sur cette connexion. Le client n'aura donc pas à essayer, il saura d'avance si ça marchera ou pas. Cette méthode évite également au client HTTP de se faire des nœuds au cerveau pour déterminer si une requête pour une origine différente a des chances de réussir ou pas, processus compliqué, et qui ne marche pas toujours.
Ce n'est pas clair ? Voici un exemple concret. Le client, un
navigateur Web,
vient de démarrer et on lui demande de se connecter à
https://www.toto.example/
. Il établit une
connexion TCP, puis lance
TLS, et enfin fait du HTTP/2. Dans la phase
d'établissement de la connexion TLS, il a récupéré un
certificat qui liste des noms possibles
(subjectAltName
),
www.toto.example
mais aussi
foobar.example
. Et, justement, quelques
secondes plus tard, l'utilisateur demande à visiter
https://foobar.example/ToU/TLDR/
. Un point
central de HTTP/2 est la réutilisation des connexions, pour
diminuer la latence, due entre autres
à l'établissement de connexion, qui peut être
long avec TCP et, surtout TLS. Notre navigateur va donc se dire
« chic, je garde la même connexion puisque c'est la même
adresse IP et que ce serveur m'a dit
qu'il gérait aussi foobar.example
, c'était
dans son certificat » (et la section 9.1.1 du RFC 7540 le lui permet explicitement). Mais patatras,
l'adresse IP est en fait celle d'un répartiteur de
charge qui envoie les requêtes pour
www.toto.example
et
foobar.example
à des machines différentes. La
machine qui gère foobar.example
va alors
renvoyer 421 Misdirected Request
au navigateur
qui sera fort marri, et aura perdu du temps pour rien. Alors
qu'avec la trame ORIGIN
de notre RFC 8336, le serveur de
www.toto.example
aurait dès le début envoyé
une trame ORIGIN
disant « sur cette
connexion, c'est www.toto.example
et rien
d'autre ». Le navigateur aurait alors été prévenu.
La section 2 du RFC décrit en détail ce nouveau type de trame
(RFC 7540, section 4, pour le concept de
trame). Le type de la trame est 12 (cf. le registre des types), et elle contient une liste
d'origines, chacune sous forme d'un doublet longueur-valeur. Une
origine est identifiée par un nom de
domaine (RFC 6454, sections 3 et
8). Il n'y a pas de limite de taille à la liste, programmeurs,
faites attention aux débordements de
tableau. Un nom de la liste ne peut pas inclure de jokers (donc, pas d'origine
*.example.com
, donc attention si vous avez
des certificats utilisant des jokers). Ce type de trames doit être envoyée sur le ruisseau HTTP/2 de
numéro 0 (celui
de contrôle).
Comme toutes les trames d'un type inconnu du récepteur, elles
sont ignorées par le destinataire. Donc, en pratique, le serveur
peut envoyer ces trames sans inquiétude, le client HTTP trop
vieux pour les connaitre les ignorera. Ces trames
ORIGIN
n'ont de sens qu'en cas de liaison
directe, les relais
doivent les ignorer, et ne pas les transmettre.
Au démarrage, le client HTTP/2 a un jeu d'origines qui est
déterminé par les anciennes règles (section 9.1.1 du RFC 7540). S'il reçoit une trame
ORIGIN
, celle-ci remplace complètement ce
jeu, sauf pour la première origine vue (le serveur auquel on s'est
connecté, identifié par son adresse IP et, si on utilise
HTTPS, par le nom indiqué dans l'extension
TLS SNI, cf. RFC 6066) qui, elle, reste
toujours en place. Ensuite, les
éventuelles réponses 421 (Misdirected request)
supprimeront des entrées du jeu d'origines.
Notez bien que la trame ORIGIN
ne fait
qu'indiquer qu'on peut utiliser cette connexion HTTP/2 pour cette
origine. Elle n'authentifie pas le serveur. Pour cela, il faut
toujours compter sur le certificat
(cf. section 4 du RFC).
En parlant de sécurité, notez que le RFC 7540, section
9.1.1 obligeait le client HTTP/2 à vérifier le
DNS et le nom dans le
certificat, avant d'ajouter une origine. Notre nouveau RFC est
plus laxiste, on ne vérifie que le certificat quand on reçoit une
nouvelle origine dans une trame ORIGIN
envoyée sur HTTPS (cela avait suscité des réactions diverses lors
de la discussion à l'IETF). Cela veut dire qu'un méchant qui a pu avoir un
certificat valable pour un nom, via une des nombreuses
AC du magasin, n'a plus besoin de faire une
attaque de l'Homme du Milieu (avec, par
exemple, un détournement DNS). Il lui
suffit, lorsqu'un autre nom qu'il contrôle est visité, d'envoyer
une trame ORIGIN
et de présenter le
certificat. Pour éviter cela, le RFC conseille au client de
vérifier le certificat plus soigneusement, par exemple avec les
journaux publics du RFC 6962, ou bien avec
une réponse OCSP (RFC 6960 montrant que le certificat n'a pas été révoqué, en
espérant qu'un certificat « pirate » sera détecté et révoqué…)
Les développeurs regarderont avec intérêt l'annexe B, qui donne
des conseils pratiques. Par exemple, si un serveur a une très
longue liste d'origines possibles, il n'est pas forcément bon de
l'envoyer dès le début de la connexion, au moment où la latence
est critique. Il vaut mieux envoyer une liste réduite, et attendre
un moment où la connexion est tranquille pour envoyer la liste
complète. (La liste des origines, dans une trame
ORIGIN
, ne s'ajoute pas aux origines
précédentes, elle les remplace. Pour retirer une origine, on
envoie une nouvelle liste, sans cette origine, ou bien on compte
sur les 421. Ce point avait
suscité beaucoup de discussions au sein du groupe de travail.)
Pour l'instant, la gestion de ce nouveau type de trames ne semble se trouver que dans Firefox, et n'est dans aucun serveur, mais des programmeurs ont annoncé qu'ils allaient s'y mettre.
Date de publication du RFC : Février 2018
Auteur(s) du RFC : R. Bonica (Juniper), R. Thomas (Juniper), J. Linkova (Google), C. Lenart (Verizon), M. Boucadair (Orange)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF intarea
Première rédaction de cet article le 23 février 2018
Pour tester qu'une machine est bien joignable, vous utilisez
ping ou, plus rigoureusement, vous envoyez
un message ICMP de type
echo
, auquel la machine visée va répondre
avec un message ICMP echo reply
. Ce test
convient souvent mais il a plusieurs limites. L'une des
limites de ce test est qu'il ne teste qu'une seule interface
réseau de la machine, celle par laquelle vous lui parlez (deux
interfaces, dans certains cas de routage asymétrique). Si la
machine visée est un gros routeur avec
plein d'interfaces réseau, le test ne vous dira pas si toutes
fonctionnent. D'où cette extension aux messages ICMP permettant de
spécifier l'interface qu'on veut vérifier.
A priori, ce RFC ne s'intéresse qu'aux
routeurs, les serveurs
n'ayant souvent qu'une seule interface réseau. La nouvelle
technique, nommée PROBE
, n'a pas de vocation
générale, contrairement à ping, et
concernera surtout les administrateurs réseau. D'autant plus que,
comme elle est assez indiscrète, elle ne sera a priori pas ouverte
au public. Notez qu'elle permet non seulement de tester une autre
interface du routeur, mais également une interface d'une machine
directement connectée au routeur. Les scénarios d'usage proposés
sont exposés dans la section 5, une liste non limitative de cas où
ping ne suffit pas :
En théorie, SNMP pourrait servir au moins partiellement à ces tests mais, en pratique, c'est compliqué.
ping, la technique classique, est
très sommairement décrit dans le RFC 2151,
section 3.2, mais sans indiquer comment il fonctionne. La
méthodologie est simple : la machine de test envoie un message
ICMP Echo
(type 8
en IPv4 et 128 en
IPv6) à la machine visée (l'amer). L'amer répond avec un Echo
Reply
(type 0 en IPv4 et 129 en IPv6). La réception
de cet Echo Reply
indique que la liaison
marche bien dans les deux sens. La non-réception indique d'un
problème s'est produit, mais on n'en sait pas plus (notamment, on
ne sait pas si le problème était à l'aller ou bien au
retour). Ici, on voit le test effectué par une sonde Atlas sur l'amer
2605:4500:2:245b::42
(l'un des serveurs hébergeant ce blog), vu par tshark :
13.013422 2a02:1811:c13:1902:1ad6:c7ff:fe2a:6ac → 2605:4500:2:245b::42 ICMPv6 126 Echo (ping) request id=0x0545, seq=1, hop limit=56 13.013500 2605:4500:2:245b::42 → 2a02:1811:c13:1902:1ad6:c7ff:fe2a:6ac ICMPv6 126 Echo (ping) reply id=0x0545, seq=1, hop limit=64 (request in 11)
ICMP est normalisé dans les RFC 792 pour IPv4 et RFC 4443 pour IPv6. L'exemple ci-dessus montre un test classique, avec une requête et une réponse.
Notre RFC parle d'« interface testée » (probed interface) et d'« interface testante » (probing interface). Dans l'exemple ci-dessus, l'interface Ethernet de l'Atlas était la testante et celle du serveur était la testée. Le succès du test montre que les deux interfaces sont actives et peuvent se parler.
Au contraire de ping, PROBE
va envoyer le
message, non pas à l'interface testée mais à une interface
« relais » (proxy). Celle-ci répondra si
l'interface testée fonctionne bien (état
oper-status
, cf. RFC 7223). Si l'interface testée n'est pas sur le nœud qui
sert de relais, ce dernier détermine l'état de cette interface en
regardant la table ARP (RFC 826) ou NDP (RFC 4861). Aucun test actif n'est effectué, l'interface est
considérée comme active si on lui a parlé récemment (et donc si
l'adresse IP est dans un cache). PROBE
utilise, comme ping,
ICMP. Il se sert des messages ICMP structurés du RFC 4884. Une des parties du message structuré sert à
identifier l'interface testée.
L'extension à ICMP Extended Echo est décrite en section 2 du RFC. Le type de la requête est 42 pour IPv4 et 160 pour IPv6 (enregistré à l'IANA, pour IPv4 et IPv6). Parmi les champs qu'elle comprend (les deux premiers existent aussi pour l'ICMP Echo traditionnel) :
Cette structure suit la forme décrite dans la section 7 du RFC 4884. Elle contient un objet d'identification de l'interface. L'interface qu'on teste peut être désignée par son adresse IP (si elle n'est pas locale - bit L à zéro, c'est la seule méthode acceptée), son nom ou son index. Notez que l'adresse IP identifiant l'adresse testée n'est pas forcément de la même famille que celle du message ICMP. On peut envoyer en IPv4 un message ICMP demandant à la machine distante de tester une interface IPv6.
Plus précisément, l'objet d'identification de l'interface est composé, comme tous les objets du RFC 4884, d'un en-tête et d'une charge utile. L'en-tête contient les champs :
if-index
, et enfin 3 = par adresse),L'adresse est représentée elle-même par une structure à trois champs, la famille (4 pour IPv4 et 6 pour IPv6), la longueur et la valeur de l'adresse. Notez que le RFC 5837 a un mécanisme de description de l'interface, portant le numéro de classe 2, et utilisé dans un contexte proche.
La réponse à ces requêtes a le type 43 en IPv4 et 161 en IPv6 (section 3 du RFC). Elle comprend :
La section 4 du RFC détaille le traitement que doit faire la machine qui reçoit l'ICMP Extended Echo. D'abord, elle doit jeter le paquet (sans répondre) si ICMP Extended Echo n'est pas explicitement activé (rappelez-vous que ce service est assez indiscret, cf. section 8 du RFC) ou bien si l'adresse IP de la machine testante n'est pas autorisée (même remarque). Si les tests sont passés et que la requête est acceptée, la machine réceptrice fabrique une réponse : le code est mis à 1 si la requête est anormale (pas de partie structurée par exemple), 2 si l'interface testée n'existe pas, 3 si elle n'est pas locale et n'apparait pas dans les tables (caches) ARP ou NDP. Si on trouve l'interface, on la teste et on remplit les bits A, 4, 6 et l'état, en fonction de ce qu'on trouve sur l'interface testée.
Reste la question de la sécurité (section 8 du RFC). Comme
beaucoup de mécanismes, PROBE
peut être utilisé pour le bien
(l'administrateur réseaux qui détermine
l'état d'une interface d'un routeur dont il s'occupe), mais aussi
pour le mal (chercher à récolter des informations sur un réseau
avant une attaque, par exemple, d'autant plus que les noms
d'interfaces dans les routeurs peuvent être assez parlants,
révélant le type de réseau, le modèle de routeur…) Le RFC exige
donc que le mécanisme ICMP Extended Echo ne
soit pas activé par défaut, et soit configurable (liste blanche
d'adresses IP autorisées, permission - ou non - de tester des
interfaces non locales, protection des différents réseaux les uns
contre les autres, si on y accueille des clients différents…) Et, bien sûr, il faut pouvoir
limiter le nombre de messages.
Ne comptez pas utilise PROBE
tout de suite. Il n'existe
apparemment pas de mise en œuvre de ce mécanisme
publiée. Juniper en a réalisé une mais elle
n'apparait encore dans aucune version de JunOS.
Date de publication du RFC : Mars 2018
Auteur(s) du RFC : J. Gould (VeriSign), W. Tan (Cloud Registry), G. Brown (CentralNic)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 5 mars 2018
Les registres de noms de domaine ont parfois des périodes d'enregistrement spéciales, par exemple lors de la phase de lancement d'un nouveau domaine d'enregistrement, ou bien lorsque les règles d'enregistrement changent. Pendant ces périodes, les conditions d'enregistrement ne sont pas les mêmes que pendant les périodes « standards ». Les registres qui utilisent le protocole EPP pour l'enregistrement peuvent alors utiliser les extensions EPP de ce nouveau RFC pour gérer ces périodes spéciales.
Un exemple de période spéciale est l'ouverture d'un tout nouveau TLD à l'enregistrement. Un autre exemple est une libéralisation de l'enregistrement, passant par exemple de vérifications a priori strictes à un modèle plus ouvert. Dans les deux cas, on peut voir des conflits se faire jour, par exemple entre le titulaire le plus rapide à enregistrer un nom, et un détenteur de propriété intellectuelle qui voudrait reprendre le nom. Les périodes spéciales sont donc définies par des privilèges particuliers pour certains utilisateurs, permettant par exemple aux titulaires d'une marque déposée d'avoir un avantage pour le nom de domaine correspondant à cette marque. La période spéciale est qualifiée de « phase de lancement » (launch phase). Les extensions à EPP décrites dans ce nouveau RFC permettent de mettre en œuvre ces privilèges.
La classe (mapping) décrivant les domaines en EPP figure dans le RFC 5731. Elle est prévue pour le fonctionnement standard du registre, sans intégrer les périodes spéciales. Par exemple, en fonctionnement standard, une fois que quelqu'un a enregistré un nom, c'est fini, personne d'autre ne peut le faire. Mais dans les phases de lancement, il arrive qu'on accepte plusieurs candidatures pour un même nom, qui sera ensuite attribué en fonction de divers critères (y compris parfois une mise aux enchères). Ou bien il peut y avoir des vérifications supplémentaires pendant une phase de lancement. Par exemple, certaines phases peuvent être réservées aux titulaires de propriété intellectuelle, et cela est vérifié via un organisme de validation, comme la TMCH (RFC 7848).
D'où ce RFC qui étend la classe domain
du RFC 5731. La section 2 du RFC décrit les
nouveaux attributs et éléments des domaines, la section 3 la façon de les
utiliser dans les commandes EPP et la
section 4 donne le schéma XML. Voyons
d'abord les nouveaux éléments et attributs.
D'abord, comme il peut y avoir plusieurs candidatures pour un
même nom, il faut un moyen de les distinguer. C'est le but de
l'identificateur de candidature (application
identifier). Lorsque le serveur EPP reçoit une commande
<domain:create>
pour un nom, il attribue un
identificateur de candidature, qu'il renvoie au client, dans un
élément <launch:applicationID>
, tout en
indiquant que le domaine est en état
pendingCreate
(RFC 5731, section 2.3) puisque le domaine n'a pas encore été
créé.
Au passage, launch
dans
<launch:applicationID>
est une
abréviation pour l'espace de noms XML
urn:ietf:params:xml:ns:launch-1.0
. Un
processeur XML correct ne doit évidemment pas tenir compte de
l'abréviation (qui peut être ce qu'on veut) mais uniquement de
l'espace de noms associé. Cet espace est désormais enregistré
à l'IANA (cf. RFC 3688).
Autre nouveauté, comme un serveur peut utiliser plusieurs
organismes de validation d'une marque
déposée, il existe désormais un attribut
validatorID
qui indique l'organisme. Par
défaut, c'est la TMCH (identificateur
tmch
). On pourra utiliser cet attribut
lorsqu'on indiquera un identificateur de marque, par exemple
lorsqu'on se sert de l'élément
<mark:mark>
du RFC 7848.
Les périodes spéciales ont souvent plusieurs phases, et notre
RFC en définit plusieurs (dans une ouverture réelle, toutes ne
sont pas forcément utilisées), qui seront utilisées dans l'élément
<launch:phase>
:
draft-ietf-regext-tmch-func-spec
),
et on peut alors renoncer ou continuer (si on est d'humeur à
affronter les avocats de la propriété intellectuelle), en
annonçant, si on continue « oui, j'ai vu, j'y vais quand même »,La section 2 définit aussi les états d'une candidature. Notamment :
pendingValidation
(validation en
attente),validated
(c'est bon, mais voyez plus
loin),invalid
(raté, vous n'avez pas de
droits sur ce nom),pendingAllocation
(une fois qu'on est
validé, tout n'est pas fini, il peut y avoir plusieurs
candidatures, avec un mécanisme de sélection, par exemple fondé
sur une enchère),allocated
(c'est vraiment
bon),rejected
(c'est fichu…)Les changements d'état ne sont pas forcément synchrones. Parfois, il faut attendre une validation manuelle, par exemple. Dans cas, il faut notifier le client EPP, ce qui se fait avec le mécanisme des messages asynchrones (poll message) du RFC 5730, section 2.9.2.3.
Comme toutes les extensions EPP, elle n'est utilisée par le client que si le serveur l'indique à l'ouverture de la session, en listant les espaces de noms XML des extensions qu'il accepte, par exemple :
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> <greeting><svID>EPP beautiful server for .example</svID> <svDate>2018-02-20T15:37:20.0Z</svDate> <svcMenu><version>1.0</version><lang>en</lang> <objURI>urn:ietf:params:xml:ns:domain-1.0</objURI> <objURI>urn:ietf:params:xml:ns:contact-1.0</objURI> <svcExtension> <extURI>urn:ietf:params:xml:ns:rgp-1.0</extURI> <extURI>urn:ietf:params:xml:ns:secDNS-1.1</extURI> <extURI>urn:ietf:params:xml:ns:launch-1.0</extURI> </svcExtension> </svcMenu> </greeting> </epp>
Maintenant qu'on a défini les données, la section 3 du RFC
explique comment les utiliser. (Dans tous les exemples ci-dessous,
C:
identifie ce qui est envoyé par le client
EPP et S:
ce que le serveur répond.) Par exemple, la commande EPP
<check>
(RFC 5730,
section 2.9.2.1) sert à vérifier si on peut enregistrer un objet
(ici, un nom de domaine). Elle prend ici des éléments
supplémentaires, par exemple pour tester si un nom correspond à
une marque. Ici, on demande si une marque existe (notez
l'extension <launch:check>
) :
C:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> C: <command> C: <check> C: <domain:check C: xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> C: <domain:name>domain1.example</domain:name> C: </domain:check> C: </check> C: <extension> C: <launch:check C: xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" C: type="trademark"/> C: </extension> C: </command> C:</epp>
Et on a la réponse (oui, la marque existe dans la TMCH) :
S:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> S: <response> S: <result code="1000"> S: <msg>Command completed successfully</msg> S: </result> S: <extension> S: <launch:chkData S: xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"> S: <launch:cd> S: <launch:name exists="1">domain1.example</launch:name> S: <launch:claimKey validatorID="tmch"> S: 2013041500/2/6/9/rJ1NrDO92vDsAzf7EQzgjX4R0000000001 S: </launch:claimKey> S: </launch:cd> S: </launch:chkData> S: </extension> S: </response> S:</epp>
Avec la commande EPP <info>
, qui
sert à récupérer des informations sur un nom, on voit ici qu'un
nom est en attente (pendingCreate
), et on a
l'affichage de la phase actuelle du lancement, dans l'élément
<launch:phase>
:
C:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> C: <command> C: <info> C: <domain:info C: xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> C: <domain:name>domain.example</domain:name> C: </domain:info> C: </info> C: <extension> C: <launch:info C: xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" C: includeMark="true"> C: <launch:phase>sunrise</launch:phase> C: </launch:info> C: </extension> C: </command> C:</epp>
Et le résultat, avec entre autre l'identificateur de candidature :
S:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> S: <response> S: <result code="1000"> S: <msg>Command completed successfully</msg> S: </result> S: <resData> S: <domain:infData S: xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> S: <domain:name>domain.example</domain:name> S: <domain:status s="pendingCreate"/> S: <domain:registrant>jd1234</domain:registrant> S: <domain:contact type="admin">sh8013</domain:contact> S: <domain:crDate>2012-04-03T22:00:00.0Z</domain:crDate> ... S: </domain:infData> S: </resData> S: <extension> S: <launch:infData S: xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"> S: <launch:phase>sunrise</launch:phase> S: <launch:applicationID>abc123</launch:applicationID> S: <launch:status s="pendingValidation"/> S: <mark:mark S: xmlns:mark="urn:ietf:params:xml:ns:mark-1.0"> S: ... S: </mark:mark> S: </launch:infData> S: </extension> S: </response> S:</epp>
C'est bien joli d'avoir des informations mais, maintenant, on
voudrait créer des noms de domaine. La commande EPP
<create>
(RFC 5730, section 2.9.3.1) sert à cela. Selon la phase de
lancement, il faut lui passer des extensions différentes. Pendant
le lever de soleil (sunrise), il faut indiquer
la marque déposée sur laquelle on s'appuie, dans
<launch:codeMark>
(il y a d'autres
moyens de l'indiquer, cf. section 2.6) :
C:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> C: <command> C: <create> C: <domain:create C: xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> C: <domain:name>domain.example</domain:name> C: <domain:registrant>jd1234</domain:registrant> ... C: </domain:create> C: </create> C: <extension> C: <launch:create C: xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"> C: <launch:phase>sunrise</launch:phase> C: <launch:codeMark> C: <launch:code validatorID="sample1"> C: 49FD46E6C4B45C55D4AC</launch:code> C: </launch:codeMark> C: </launch:create> C: </extension> C: </command> C:</epp>
On reçoit une réponse qui dit que le domaine n'est pas encore
créé, mais on a un identificateur de candidature (un numéro de
ticket, quoi) en <launch:applicationID>
. Notez le code de retour 1001 (j'ai
compris mais je ne vais pas le faire tout de suite) et non pas
1000, comme ce serait le cas en régime de croisière :
S:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> S: <response> S: <result code="1001"> S: <msg>Command completed successfully; action pending</msg> S: </result> S: <resData> S: <domain:creData S: xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> S: <domain:name>domain.example</domain:name> S: <domain:crDate>2010-08-10T15:38:26.623854Z</domain:crDate> S: </domain:creData> S: </resData> S: <extension> S: <launch:creData S: xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"> S: <launch:phase>sunrise</launch:phase> S: <launch:applicationID>2393-9323-E08C-03B1 S: </launch:applicationID> S: </launch:creData> S: </extension> S: </response> S:</epp>
De même, des extensions permettent de créer un domaine pendant la phase où il faut indiquer qu'on a vu les prétentions qu'avait un titulaire de marque sur ce nom. Le RFC décrit aussi l'extension à utiliser dans la phase de ruée (landrush), mais j'avoue n'avoir pas compris son usage (puisque, pendant la ruée, les règles habituelles s'appliquent).
On peut également retirer une candidature, avec la commande EPP
<delete>
qui, en mode standard, sert à
supprimer un domaine. Il faut alors indiquer l'identifiant de la
candidature qu'on retire :
C:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> C: <command> C: <delete> C: <domain:delete C: xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> C: <domain:name>domain.example</domain:name> C: </domain:delete> C: </delete> C: <extension> C: <launch:delete C: xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"> C: <launch:phase>sunrise</launch:phase> C: <launch:applicationID>abc123</launch:applicationID> C: </launch:delete> C: </extension> C: </command> C:</epp>
Et les messages non sollicités (poll), envoyés de manière asynchrone par le serveur ? Voici un exemple, où le serveur indique que la candidature a été jugée valide (le mécanisme par lequel on passe d'un état à un autre dépend de la politique du serveur) :
S:<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"> S: <response> S: <result code="1301"> S: <msg>Command completed successfully; ack to dequeue</msg> S: </result> S: <msgQ count="5" id="12345"> S: <qDate>2013-04-04T22:01:00.0Z</qDate> S: <msg>Application pendingAllocation.</msg> S: </msgQ> S: <resData> S: <domain:infData S: xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"> S: <domain:name>domain.example</domain:name> S: ... S: </domain:infData> S: </resData> S: <extension> S: <launch:infData S: xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"> S: <launch:phase>sunrise</launch:phase> S: <launch:applicationID>abc123</launch:applicationID> S: <launch:status s="pendingAllocation"/> S: </launch:infData> S: </extension> S: </response> S:</epp>
Voilà, vous savez l'essentiel, si vous voulez tous les détails, il faudra lire la section 3 complète, ainsi que la section 4, qui contient le schéma XML des extensions pour les phases de lancement. Comme toutes les extensions à EPP, celle de ce RFC est désormais dans le registre des extensions EPP, décrit dans le RFC 7451.
Notez que ce RFC ne fournit pas de moyen pour indiquer au client EPP quelle est la politique d'enregistrement pendant la période spéciale. Cela doit être fait par un mécanisme externe (page Web du registre, par exemple).
Quelles sont les mises en œuvre de ce RFC ? L'extension pour les phases de lancement est ancienne (première description en 2011) et de nombreux registres offrent désormais cette possibilité. C'est d'autant plus vrai que l'ICANN impose aux registres de ses nouveaux TLD de gérer les phases de lancement avec cette extension. Ainsi :
.com
et
.net
(mais également
pour bien d'autres TLD) gère cette
extension (code non libre et non public, cette fois)..za
a
aussi cette extension..nl
) fait partie de
ceux qui ont mis en œuvre ce RFC.tdw
, pas dans le logiciel originel de
Patrick Mevzek),
cf. LaunchPhase.pm
.Date de publication du RFC : Mars 2018
Auteur(s) du RFC : D. Bider (Bitvise)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 21 mars 2018
Les RFC normalisant le protocole SSH mentionnaient la possibilité pour le serveur d'avoir une clé publique RSA, mais uniquement avec l'algorithme de condensation SHA-1. Celui-ci a subi plusieurs attaques et n'est plus du tout conseillé aujourd'hui. Ce nouveau RFC est la description officielle de l'utilisation de RSA avec SHA-2 dans SSH.
Le RFC officiel du protocole
SSH, le RFC 4253,
définit l'algorithme ssh-rsa
comme utilisant
the SHA-1 hash pour la signature et la vérification. Ce n'est plus cohérent avec ce
qu'on sait aujourd'hui des faiblesses de
SHA-1. Par exemple, le NIST
(dont les règles s'imposent aux organismes gouvernementaux
états-uniens), interdit
SHA-1 (voir aussi la section 5.1 du RFC).
(Petit point au passage, dans SSH, le format de la clé ne
désigne que la clé elle-même, alors que l'algorithme désigne un
format de clé et des procédures de signature et de vérification,
qui impliquent un algorithme de
condensation. Ainsi, ssh-keygen
-t rsa
va générer une clé RSA, indépendamment de
l'algorithme qui sera utilisé lors d'une session SSH. Le registre IANA indique désormais séparement le
format et l'algorithme.)
Notre nouveau RFC définit donc deux nouveaux algorithmes :
rsa-sha2-256
, qui utilise
SHA-256 pour la condensation, et dont la
mise en œuvre est recommandée pour tout programme ayant
SSH,rsa-sha2-512
, qui utilise
SHA-512 et dont la mise en œuvre est facultative.
Les deux algorithmes sont maintenant dans le
registre IANA. Le format, lui, ne change pas, et est toujours qualifié de
ssh-rsa
(RFC 4253,
section 3.) Il n'est donc pas nécessaire de
changer ses clés RSA. (Si vous utilisez RSA : SSH permet d'autres
algorithmes de cryptographie
asymétrique, voir également la section 5.1 du RFC.)
Les deux « nouveaux » (ils sont déjà présents dans plusieurs
programmes SSH) algorithmes peuvent être utilisés aussi bien pour
l'authentification du client que pour celle du serveur. Ici, par
exemple, un serveur OpenSSH version 7.6p1
annonce les algorithme qu'il connait (section 3.1 de notre RFC), affichés par un client
OpenSSH utilisant l'option -v
:
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,rsa-sha2-256,rsa-sha2-512,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>
On y voit la présence des deux nouveaux
algorithmes. ssh-rsa
est celui du RFC 4253, avec SHA-1 (non explicitement indiqué).
En parlant d'OpenSSH, la version 7.2
avait une bogue (corrigée en 7.2p2), qui faisait que la signature
était étiquetée ssh-rsa
quand elle aurait dû
l'être avec rsa-sha2-256
ou
rsa-sha2-512
. Pour compenser cette bogue, le
RFC autorise les programmes à accepter ces signatures quand même.
Autre petit piège pratique (et qui a suscité les discussions
les plus vives dans le groupe de travail), certains serveurs SSH « punissent »
les clients qui essaient de s'authentifier avec des algorithmes que le serveur ne sait
pas gèrer, pour diminuer l'efficacité d'éventuelles
attaques par repli. Pour éviter d'être
pénalisé (serveur qui raccroche brutalement, voire qui vous met en
liste noire), le RFC recommande que les serveurs déploient
le protocole qui permet de négocier des extensions,
protocole normalisé dans le RFC 8308. (L'extension intéressante est server-sig-algs
.) Le
client prudent peut éviter d'essayer les nouveaux
algorithmes utilisant SHA-2, si le serveur n'annonce pas cette
extension. L'ancien algorithme, utilisant SHA-1, devrait
normalement être abandonné au fur et à mesure que tout le monde migre.
Les nouveaux algorithmes de ce RFC sont présents dans :
Vous pouvez aussi regarder le
tableau de comparaison des versions de SSH. Voici encore un
ssh -v
vers un serveur dont la clé de machine
est RSA et qui utilise l'algorithme RSA-avec-SHA512 :
... debug1: kex: host key algorithm: rsa-sha2-512 ... debug1: Server host key: ssh-rsa SHA256:yiol3GPr1dgVo/SNXPvtFqftLw4UF+nL+ECa1yXAtG0 ...
Par comparaison avec les SSH récents, voici un ssh -v
OpenSSH vers un serveur un peu vieux :
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>
On voit que les nouveaux algorithmes manquent (pour RSA ; SHA-2 est utilisable pour ECDSA). Et pour sa clé de machine :
... debug1: kex: host key algorithm: ssh-rsa ... debug1: Server host key: ssh-rsa SHA256:a6cLkwFRGuEorbmN0oRjvKrXELhIVXdgHRCcbQOM2w8
Le serveur utilise le vieil algorithme,
ssh-rsa
, ce qui veut dire SHA-1 (le
SHA256
qui apparait a été généré par le client).
Date de publication du RFC : Mars 2018
Auteur(s) du RFC : P. Francois, B. Decraene (Orange), C. Pelsser (Strasbourg University), K. Patel (Arrcus), C. Filsfils (Cisco Systems)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF grow
Première rédaction de cet article le 7 mars 2018
Voici une nouvelle communauté BGP,
GRACEFUL_SHUTDOWN
, qui va permettre
d'annoncer une route à son pair BGP, tout en l'avertissant que le
lien par lequel elle passe va être bientôt coupé pour
une maintenance prévue. Le pair pourra alors automatiquement limiter l'usage de cette route
et chercher tout de suite des alternatives. Cela évitera les
pertes de paquets qui se produisent quand
on arrête un lien ou un
routeur.
Le protocole BGP (RFC 4271) qui assure le routage entre
les AS qui composent
l'Internet permet aux routeurs d'échanger des informations
entre eux « pour aller vers
2001:db8:bc9::/48
, passe donc par moi ». Avec
ces informations, chaque routeur calcule les routes à suivre pour
chaque destination. Une fois que c'est fait, tout le monde se
repose ? Non, parce qu'il y a tout le temps des
changements. Certains peuvent être imprévus et accidentels (la
fameuse pelleteuse), d'autres sont planifiés à
l'avance : ce sont les opérations de maintenance « le 7 février à
2300 UTC, nous allons remplacer une
line card du routeur, coupant
toutes les sessions BGP de cette carte ». Voici par exemple un
message reçu sur la liste de diffusion des
opérateurs connectés au France-IX :
Date: Wed, 31 Jan 2018 15:57:34 +0000 From: Quelqu'un <quelquun@opérateur> To: "paris@members.franceix.net" <paris@members.franceix.net> Subject: [FranceIX members] [Paris] [OPÉRATEUR/ASXXXXX] - France-IX port maintenance Dear peers, Tomorrow morning CEST, we will be conducting a maintenance that will impact one of our connection to France-IX (IPs: x.y.z.t/2001:x:y:z::t). All sessions will be shut down before and brought back up once the maintenance will be over. Please note that our MAC address will change as the link will be migrated to a new router. Cheers
Lorsqu'une telle opération est effectuée, le résultat est le
même que pour une coupure imprévue : les sessions BGP sont
coupées, les routeurs retirent les routes apprises via ces
sessions, et vont chercher d'autres routes dans les annonces
qu'ils ont reçues. Ils propagent ensuite ces changements à leurs
voisins, jusqu'à ce que tout l'Internet soit au courant. Le
problème est que cela prend du temps (quelques secondes au moins,
des dizaines de secondes, parfois, à moins que les routeurs
n'utilisent le RFC 7911 mais ce n'est pas
toujours le cas). Et pendant ce temps, les paquets continuent à
arriver à des routeurs qui ne savent plus les traiter (section 3
du RFC). Ces paquets seront jetés, et devront être réémis (pour le
cas de TCP). Ce n'est pas
satisfaisant. Bien sûr, quand la coupure est imprévue, il n'y a
pas le choix. Mais quand elle est planifiée, on devrait pouvoir
faire mieux, avertir les routeurs qu'ils devraient cesser
d'utiliser cette route. C'est justement ce que permet la nouvelle
communauté GRACEFUL_SHUTDOWN
. (Les
communautés BGP sont décrites dans le RFC 1997.) Elle s'utilise avant la
coupure, indiquant aux pairs qu'ils devraient commencer le
recalcul des routes, mais qu'ils peuvent continuer à utiliser les
anciennes routes pendant ce temps. (Notez qu'un cahier des charges
avait été établi pour ce problème, le RFC 6198. Et que ce projet d'une communauté pour les arrêts
planifiés est ancien, au moins dix ans.)
Ce RFC décrit donc deux choses, la
nouvelle communauté normalisée,
GRACEFUL_SHUTDOWN
(section 5), et la procédure à utiliser
pour s'en servir proprement (section 4). L'idée est que les routes
qui vont bientôt être coupées pour maintenance restent utilisées,
mais avec une préférence locale très faible (la valeur 0 est
recommandée, la plus petite valeur possible). La notion de
préférence locale est décrite dans le RFC 4271, section 5.1.5. Comme son nom l'indique, elle est
locale à un AS, et représente sa préférence
(décidée unilatéralement) pour une route plutôt que pour une
autre. Lors du choix d'une route par BGP, c'est le premier critère
consulté.
Pour mettre en œuvre cette idée, chaque routeur au bord des
AS (ASBR, pour Autonomous System
Border Router) doit avoir une règle qui, lorsqu'une
annonce de route arrive avec la communauté
GRACEFUL_SHUTDOWN
, applique une préférence
locale de 0. Notez que cela peut se faire avec les routeurs
actuels, aucun code nouveau n'est nécessaire, ce RFC ne décrit
qu'une procédure. Une fois que cette règle est en place, tout le
reste sera automatique, chez les pairs de l'AS qui coupe un lien
ou un routeur.
Et l'AS qui procéde à une opération de maintenance, que doit-il faire ? Dans l'ordre :
GRACEFUL_SHUTDOWN
aux routes qu'on annonce
(outbound policy),GRACEFUL_SHUTDOWN
aux routes qu'on reçoit
(inbound policy), et
mettre leur préférence locale à zéro,J'ai dit plus haut qu'il n'était pas nécessaire de modifier le
logiciel des routeurs BGP mais évidemment tout est plus simple
s'ils connaissent la communauté
GRACEFUL_SHUTDOWN
et simplifient ainsi la
tâche de l'administrateur réseaux. Cette communauté est « bien
connue » (elle n'est pas spécifique à un
AS), décrite
dans la section 5 du RFC, enregistrée à
l'IANA et sa valeur est 0xFFFF0000 (qui peut aussi s'écrire
65535:0, dans la notation habituelle des communautés).
La section 6 du RFC fait le tour de la sécurité de ce
système. Comme il permet d'influencer le routage chez les pairs
(on annonce une route avec la communauté
GRACEFUL_SHUTDOWN
et paf, le pair met une
très faible préférence à ces routes), il ouvre la porte à de
l'ingénierie du trafic pas toujours
bienveillante. Il est donc prudent de regarder ce qu'annoncent ses
pairs, et d'engueuler ou de dépairer ceux et celles qui abusent de
ce mécanisme.
Pour les amateurs de solutions alternatives, l'annexe A
explique les autres techniques qui auraient pu être utilisées lors
de la réception des routes marquées avec GRACEFUL_SHUTDOWN
. Au
lieu d'influencer la préférence locale, on aurait par exemple pu utiliser le
MED (multi-exit discriminator, RFC 4271, section 5.1.4) mais il n'est considéré par les
pairs qu'après d'autres critères, et il ne garantit donc pas que
le lien bientôt coupé ne sera plus utilisé.
L'annexe B donne des exemples de configuration pour différents types de routeurs. (Configurations pour l'AS qui reçoit la notification d'un arrêt proche, pas pour ceux qui émettent.) Ainsi, pour IOS XR :
! 65535:0 = 0xFFFF0000 community-set comm-graceful-shutdown 65535:0 end-set route-policy AS64497-ebgp-inbound ! Règles appliquées aux annonces reçues du pair, l'AS 64497. Bien ! sûr, en vrai, il y aurait plein d'autres règles, par exemple de filtrage. if community matches-any comm-graceful-shutdown then set local-preference 0 endif ! On a appliqué la règle du RFC : mettre la plus faible ! préférence locale possible end-policy ! La configuration de la session BGP avec le pair router bgp 64496 neighbor 2001:db8:1:2::1 remote-as 64497 address-family ipv6 unicast send-community-ebgp route-policy AS64497-ebgp-inbound in
Pour BIRD, cela sera :
# (65535, 0) = 0xFFFF0000 function honor_graceful_shutdown() { if (65535, 0) ~ bgp_community then { bgp_local_pref = 0; } } filter AS64497_ebgp_inbound { # Règles appliquées aux annonces reçues du pair, l'AS 64497. Bien # sûr, en vrai, il y aurait plein d'autres règles, par # exemple de filtrage. honor_graceful_shutdown(); } protocol bgp peer_64497_1 { neighbor 2001:db8:1:2::1 as 64497; local as 64496; import keep filtered; import filter AS64497_ebgp_inbound; }
Et sur OpenBGPD (on voit qu'il connait
GRACEFUL_SHUTDOWN
, il n'y a pas besoin de
donner sa valeur) :
AS 64496 router-id 192.0.2.1 neighbor 2001:db8:1:2::1 { remote-as 64497 } # Règles appliquées aux annonces reçues du pair, l'AS 64497. Bien # sûr, en vrai, il y aurait plein d'autres règles, par exemple de filtrage. match from any community GRACEFUL_SHUTDOWN set { localpref 0 }
Enfin, l'annexe C du RFC décrit quelques détails supplémentaires, par exemple pour IBGP (BGP interne à un AS).
Notez que ce nouveau RFC est prévu pour le cas où la transmission des paquets (forwarding plane) est affectée. Si c'est uniquement la session BGP (control plane) qui est touchée, la solution du RFC 4724, Graceful Restart, est plus appropriée.
Date de publication du RFC : Février 2018
Auteur(s) du RFC : J. Klensin
Pour information
Première rédaction de cet article le 28 février 2018
Le DNS est une infrastructure essentielle
de l'Internet. S'il est en panne, rien ne marche (sauf si vous
faites partie de la minorité qui fait uniquement des ping
-n
et des traceroute -n
). S'il est
lent, tout rame. Comme le DNS, heureusement, marche très bien, et
s'est montré efficace, fiable et rapide, il souffre aujourd'hui de
la malédiction des techniques à succès : on essaie de charger la
barque, de lui faire faire plein de choses pour lesquelles il
n'était pas prévu. D'un côté, c'est un signe de succès. De l'autre,
c'est parfois fragilisant. Dans ce RFC
individuel (qui exprime juste le point de vue d'un individu, et
n'est pas du tout une norme
IETF), John Klensin, qui ne participe plus
activement au développement du DNS depuis des années, revient sur
certaines de ces choses qu'on essaie de faire faire au DNS et se
demande si ce n'est pas trop, et à partir de quel point il faudrait
arrêter de « perfectionner » le DNS et plutôt passer à « autre
chose » (« quand le seul outil qu'on a est un marteau, tous les
problèmes ressemblent à des clous… »). Une bonne lecture pour
celleszetceux qui ne veulent pas seulement faire marcher le DNS mais
aussi se demander « pourquoi c'est comme ça ? » et « est-ce que ça
pourrait être différent ? »
Améliorer le système petit à petit ou bien le remplacer complètement ? C'est une question que se posent régulièrement les ingénieurs, à propos d'un logiciel, d'un langage de programmation, d'un protocole réseau. À l'extrême, il y a l'ultra-conservateur qui ne voit que des inconvénients aux solutions radicales, à l'autre il y a l'ultra-optimiste qui en a marre des rustines et qui voudrait jeter le vieux système, pour le remplacer par un système forcément meilleur, car plus récent. Entre les deux, beaucoup d'informaticiens hésitent. L'ultra-conservateur oublie que les rustines successives ont transformé l'ancien système en un monstre ingérable et difficile à maintenir, l'ultra-optimiste croit naïvement qu'un système nouveau, rationnellement conçu (par lui…) sera à coup sûr plus efficace et moins bogué. Mais les deux camps, et tout celleszetceux qui sont entre les deux peuvent tirer profit de ce RFC, pour approfondir leur réflexion.
Klensin note d'abord que le DNS est vieux. La première réflexion à ce sujet était le RFC 799 en 1981, et le premier RFC décrivant le DNS est le RFC 882, en novembre 1983. (Paul Mockapetris a raconté le développement du DNS dans « Development of the Domain Name System », j'ai fait un résumé des articles d'histoire du DNS.) Le DNS remplaçait l'ancien système fondé sur un fichier centralisé de noms de machines (RFC 810, RFC 952, et peut-être aussi RFC 953). Tout n'est pas écrit : le DNS n'a pas aujourd'hui une spécification unique et à jour, aux RFC de base (RFC 1034 et RFC 1035, il faut ajouter des dizaines de RFC qui complètent ou modifient ces deux-ci, ainsi que pas mal de culture orale. (Un exemple de cette difficulté était que, pendant le développement du RFC 7816, son auteur s'est aperçu que personne ne se souvenait pourquoi les résolveurs envoyaient le FQDN complet dans les requêtes.) Plusieurs techniques ont même été supprimées comme les requêtes inverses (RFC 3425) ou comme les types d'enregistrement WKS, MD, MF et MG.
D'autres auraient dû être supprimées, car inutilisables en pratique, comme les classes (que prétendait utiliser le projet Net4D), et qui ont fait l'objet de l'Internet-Draft « The DNS Is Not Classy: DNS Classes Considered Useless », malheureusement jamais adopté dans une IETF parfois paralysée par la règle du consensus.
D'autres évolutions ont eu lieu : le DNS original ne proposait aucun mécanisme d'options, tous les clients et tous les serveurs avaient exactement les mêmes capacités. Cela a changé avec l'introduction d'EDNS, dans le RFC 2671 en 1999.
Beaucoup d'articles ont été écrits sur les systèmes de nommage. (Le RFC recommande l'article de V. Cerf, « Desirable Properties of Internet Identifiers », ou bien le livre « Signposts in Cyberspace: The Domain Name System and Internet Navigation ». Je me permets de rajouter mes articles, « Inventer un meilleur système de nommage : pas si facile », « Un DNS en pair-à-pair ? » et « Mon premier nom Namecoin enregistré ».)
Pourquoi est-ce que les gens ne sont pas contents du DNS actuel et veulent le changer (section 4 du RFC, la plus longue du RFC) ? Il y a des tas de raisons. Certaines, dit l'auteur, peuvent mener à des évolutions raisonnables du DNS actuel. Certaines nécessiteraient un protocole complètement nouveau, incompatible avec le DNS. D'autres enfin seraient irréalistes, quel que soit le système utilisé. La section 4 les passe en revue (rappelez-vous que ce RFC est une initiative individuelle, pas une opinion consensuelle à l'IETF).
Premier problème, les requêtes « multi-types ». À l'heure actuelle, une requête DNS est essentiellement composée d'un nom (QNAME, Query Name) et d'un type (QTYPE, Query Type, par exemple AAAA pour une adresse IP, TLSA pour une clé publique, etc). Or, on aurait parfois besoin de plusieurs types. L'exemple classique est celui d'une machine double-pile (IPv6 et le vieil IPv4), qui ne sait pas quelle version d'IP est acceptée en face et qui demande donc l'adresse IPv6 et l'adresse IPv4 du pair. Il n'y a actuellement pas de solution pour ce problème, il faut faire deux requêtes DNS. (Et, non, ANY ne résout pas ce problème, notamment en raison de l'interaction avec les caches : que doit faire un cache qui ne connait qu'une seule des deux adresses ?)
Deuxième problème, la sensibilité à la casse. La norme originale prévoyait des requêtes insensibles à la casse (RFC 1034, section 3.1), ce qui semble logique aux utilisateurs de l'alphabet latin. Mais c'est plutôt une source d'ennuis pour les autres écritures (et c'est une des raisons pour lesquelles accepter l'Unicode dans les noms de domaine nécessite des méthodes particulières). Avec ASCII, l'insensibilité à la casse est facile (juste un bit à changer pour passer de majuscule en minuscule et réciproquement) mais ce n'est pas le cas pour le reste d'Unicode. En outre, il n'est pas toujours évident de connaitre la correspondance majuscule-minuscule (cf. les débats entre germanophones sur la majuscule de ß). Actuellement, les noms de domaine en ASCII sont insensibles à la casse et ceux dans le reste du jeu de caractères Unicode sont forcément en minuscules (cf. RFC 5890), libre à l'application de mettre ses propres règles d'insensibilité à la casse si elle veut, lorsque l'utilisateur utilise un nom en majuscules comme RÉUSSIR-EN.FR. On referait le DNS en partant de zéro, peut-être adopterait-on UTF-8 comme encodage obligatoire, avec normalisation NFC dans les serveurs de noms, mais c'est trop tard pour le faire.
En parlant d'IDN, d'ailleurs, ce sujet a été à l'origine de nombreuses discussions, incluant pas mal de malentendus (pour lesquels, à mon humble avis, l'auteur de RFC a une sérieuse responsabilité). Unicode a une particularité que n'a pas ASCII : le même caractère peut être représenté de plusieurs façons. L'exemple classique est le É qui peut être représenté par un point de code, U+00C9 (LATIN CAPITAL LETTER E WITH ACUTE), ou par deux, U+0045 (LATIN CAPITAL LETTER E) et U+0301 (COMBINING ACUTE ACCENT). Je parle bien de la représentation en points de code, pas de celle en bits sur le réseau, qui est une autre affaire ; notez que la plupart des gens qui s'expriment à propos d'Unicode sur les forums ne connaissent pas Unicode. La normalisation Unicode vise justement à n'avoir qu'une forme (celle à un point de code si on utilise NFC) mais elle ne traite pas tous les cas gênants. Par exemple, dans certains cas, la fonction de changement de casse dépend de la langue (que le DNS ne connait évidemment pas). Le cas le plus célèbre est celui du i sans point U+0131, qui a une règle spécifique en turc. Il ne sert à rien de râler contre les langues humaines (elles sont comme ça, point), ou contre Unicode (dont la complexité ne fait que refléter celles des langues humaines et de leurs écritures). Le point important est qu'on n'arrivera pas à faire en sorte que le DNS se comporte comme M. Toutlemonde s'y attend, sauf si on se limite à un M. Toutlemonde étatsunien (et encore).
Les IDN ont souvent été accusés, y compris dans ce RFC, de
permettre, ou en tout cas de faciliter, le
hameçonnage par la confusion possible entre
deux caractères visuellement proches. En fait, le problème n'est pas
spécifique aux IDN (regardez google.com
et
goog1e.com
) et les études montrent que les
utilisateurs ne vérifient pas les noms, de toute façon. Bref,
il s'agit de simple propagande de la part de ceux qui n'ont jamais
vraiment accepté Unicode.
Les IDN nous amènent à un problème proche,
celui des synonymes. Les noms de domaine
color.example
et
colour.example
sont différents alors que, pour
tout anglophone, color
et colour
sont « équivalents ». J'ai mis le mot « équivalent » entre
guillemets car sa définition même est floue. Est-ce que
« Saint-Martin » est équivalent à « St-Martin » ? Et est-ce que
« Dupont » est équivalent à « Dupond » ? Sans même aller chercher des
exemples comme l'équivalence entre sinogrammes simplifiés et sinogrammes
traditionnels, on voit que l'équivalence est un concept
difficile à cerner. Souvent, M. Michu s'agace « je tape
st-quentin.fr
, pourquoi est-ce que ça n'est pas
la même chose que
saint-quentin-en-yvelines.fr
? »
Fondamentalement, la réponse est que le DNS ne gère pas les requêtes
approximatives, et qu'il n'est pas évident que tout le monde soit
d'accord sur l'équivalence de deux noms. Les humains se débrouillent
avec des requêtes floues car ils ont un
contexte. Si on est dans les
Yvelines, je sais que « St-Quentin » est
celui-ci
alors que, si on est dans l'Aisne, mon interlocuteur parle
probablement de celui-là. Mais le DNS n'a pas ce
contexte.
Plusieurs RFC ont été écrit à ce sujet, RFC 3743, RFC 4290, RFC 6927 ou RFC 7940, sans résultats convaincants. Le DNS a bien sûr des mécanismes permettant de dire que deux noms sont équivalents, comme les alias (enregistrements CNAME) ou comme les DNAME du RFC 6672. Mais :
Même écrire un cahier des charges des « variantes » n'a jamais été possible. (C'est également un sujet sur lequel j'avais écrit un article.)
Passons maintenant aux questions de protection de la vie privée. L'auteur du RFC note que la question suscite davantage de préoccupations aujourd'hui mais ne rappelle pas que ces préoccupations ne sont pas irrationnnelles, elles viennent en grande partie de la révélation de programmes de surveillance massive comme MoreCowBell. Et il « oublie » d'ailleurs de citer le RFC 7626, qui décrit en détail le problème de la vie privée lors de l'utilisation du DNS.
J'ai parlé plus haut du problème des classes dans le DNS, ce
paramètre supplémentaire des enregistrements DNS (un enregistrement
est identifié par trois choses, le nom, la classe et le
type). L'idée au début (RFC 1034, section 3.6)
était de gérer depuis le DNS plusieurs protocoles très différents
(IP, bien sûr, mais aussi
CHAOS et d'autres futurs), à l'époque où le
débat faisait rage entre partisans d'un réseau à protocole unique
(le futur Internet) et ceux et celles qui
préféraient un catenet, fondé sur
l'interconnexion de réseaux techniquement différents. Mais,
aujourd'hui, la seule classe qui sert réellement est IN (Internet)
et, en pratique, il y a peu de chances que les autres soient jamais
utilisées. Il a parfois été suggéré d'utiliser les classes pour
partitionner l'espace de noms (une classe IN pour
l'ICANN et créer une classe UN afin de la donner à
l'UIT pour qu'elle puisse jouer à la gouvernance ?) mais le fait que les
classes aient été
très mal normalisées laisse peu d'espoir. (Est-ce que IN
example
, CH example
et UN example
sont la même
zone ? Ont-ils les mêmes serveurs de noms ? Cela n'a jamais été
précisé.)
Une particularité du DNS qui déroute souvent les nouveaux administrateurs système est le fait que les données ne soient que faiblement synchronisées : à un moment donné, il est parfaitement normal que plusieurs valeurs coexistent dans l'Internet. Cela est dû à plusieurs choix, notamment :
Cela a donné naissance à la légende de la propagation du DNS et aux chiffres fantaisistes qui accompagnent cette légende comme « il faut 24 h pour que le DNS se propage ».
Ces choix ont assuré le succès du DNS, en lui permettant de passer à l'échelle, vers un Internet bien plus grand que prévu à l'origine. Un modèle à synchronisation forte aurait été plus compliqué, plus fragile et moins performant.
Mais tout choix en ingéniérie a des bonnes conséquences et des mauvaises : la synchronisation faible empêche d'utiliser le DNS pour des données changeant souvent. Des perfectionnements ont eu lieu (comme la notification non sollicitée du RFC 1996, qui permet aux serveurs secondaires d'être au courant rapidement d'un changement, mais qui ne marche que dans le cas où on connait tous les secondaires) mais n'ont pas fondamentalement changé le tableau. Bien sûr, les serveurs faisant autorité qui désireraient une réjuvénation plus rapide peuvent toujours abaisser le TTL mais, en dessous d'une certaine valeur (typiquement 30 à 60 minutes), les TTL trop bas sont parfois ignorés.
Un autre point où les demandes de beaucoup d'utilisateurs
rentrent en friction avec les concepts du DNS est celui des noms
privés, des noms qui n'existeraient qu'à l'intérieur d'une
organisation particulière, et qui ne nécessiteraient pas
d'enregistrement auprès d'un tiers. La bonne méthode pour avoir des
noms privés est d'utiliser un sous-domaine d'un domaine qu'on a
enregistré (aujourd'hui, tout le monde peut avoir son domaine assez
facilement, voir gratuitement), et de le déléguer à
des serveurs de noms qui ne sont accessibles qu'en interne. Si on
est l'association Example et qu'on est titulaire de
example.org
, on crée
priv.example.org
et on y met ensuite les noms
« privés » (je mets privé entre guillemets car, en pratique, comme
le montrent les statistiques des serveurs de noms publics, de tels
noms fuitent souvent à l'extérieur, par exemple quand un ordinateur
portable passe du réseau interne à celui d'un
FAI public).
Il faut noter que beaucoup d'organisations, au lieu d'utiliser la
bonne méthode citée ci-dessus, repèrent un
TLD actuellement inutilisé
(.home
, .lan
,
.private
…) et s'en servent. C'est une très
mauvaise idée, car, un jour, ces TLD seront peut-être délégués, avec
les risques de confusion que cela entrainera (cf. le
cas de .box et celui
de .dev).
Les administrateurs système demandent souvent « mais quel est le
TLD réservé pour les usages internes » et sont surpris d'apprendre
qu'il n'en existe pas. C'est en partie pour de bonnes raisons
(imaginez deux entreprises utilisant ce TLD et fusionnant… Ou
simplement s'interconnectant via un VPN… Un
problème qu'on voit souvent avec le RFC 1918.)
Mais c'est aussi en partie parce que les tentatives d'en créer un se
sont toujours enlisées dans les sables de la bureaucratie (personne
n'a envie de passer dix ans de sa vie professionnelle à faire du
lobbying auprès de l'ICANN pour réserver un
tel TLD). La dernière tentative était celle de .internal
mais elle n'a pas marché.
Il y a bien un registre des noms de domaines (pas uniquement des
TLD) « à usage spécial », créé par le RFC 6761. Il a malheureusement été gelé par
l'IESG et fait l'objet de contestations (RFC 8244). Aucun des noms qu'il contient ne convient vraiment
au besoin de ceux qui voudraient des noms de domaine internes (à part
.test
qui devrait logiquement être utilisé pour
les bancs de test, de développement, etc). Le RFC note qu'un des
principaux problèmes d'un tel registre est qu'il est impossible de
garder à jour tous les résolveurs de la planète quand ce registre
est modifié. On ne peut donc pas garantir qu'un nouveau TLD réservé
sera bien traité de manière spéciale par tous les résolveurs.
Une caractéristique du DNS qui a suscité beaucoup de débats, pas toujours bien informés et pas toujours honnêtes, est l'existence de la racine du DNS, et des serveurs qui la servent. Lors de la mise au point du DNS, la question s'était déjà posée, certains faisant remarquer que cette racine allait focaliser les problèmes, aussi bien techniques que politiques. L'expérience a montré qu'en fait la racine marchait bien, mais cela n'a pas évité les polémiques. Le RFC note que le sujet est très chaud : qui doit gérer un serveur racine ? Où faut-il les placer physiquement ? Si l'anycast a largement résolu la seconde question (RFC 7094), la première reste ouverte. Le RFC n'en parle pas mais, si la liste des onze (ou douze, ça dépend comment on compte) organisations qui gèrent un serveur racine n'a pas évolué depuis vingt ans, ce n'est pas pour des raisons techniques, ni parce qu'aucune organisation n'est capable de faire mieux que les gérants actuels, mais tout simplement parce qu'il n'existe aucun processus pour supprimer ou ajouter un serveur racine. Comme pour les membres permanents du Conseil de Sécurité de l'ONU, on en reste au statu quo, aussi inacceptable soit-il, simplement parce qu'on ne sait pas faire autrement.
Le problème de la gestion de la racine n'est pas uniquement celui
de la gestion des serveurs racine. Le contenu de la zone racine est
tout aussi discuté. Si les serveurs racine sont les imprimeurs du
DNS, le gérant de la zone racine en est l'éditeur. Par exemple,
combien faut-il de TLD ? Si quelqu'un veut
créer .pizza
, faut-il le permettre ? Et
.xxx
? Et .vin
, que le
gouvernement français avait vigoureusement
combattu ? Ou encore .home
, déjà
largement utilisé informellement dans beaucoup de réseaux locaux,
mais pour lequel il y avait trois candidatures à l'ICANN (rejetées peu de
temps avant la publication du RFC). Ces
questions, qui se prêtent bien aux jeux politiciens, occupent
actuellement un bon bout des réunions
ICANN.
La base technique à ces discussions est qu'il n'y a qu'une seule racine (RFC 2826). Son contrôle va donc forcément susciter des conflits. Un autre système de nommage que le DNS, si on le concevait de nos jours, pourrait éviter le problème en évitant ces points de contrôle. Les techniques à base de chaînes de blocs comme Namecoin sont évidemment des candidates possibles. Outre les problèmes pratiques (avec Namecoin, quand on perd sa clé privée, on perd son domaine), la question de fond est « quelle gouvernance souhaite-t-on ? »
La question de la sémantique dans les noms de domaines est
également délicate. L'auteur affirme que les noms de domaines sont
(ou en tout cas devraient être) de purs identificateurs techniques,
sans sémantique. Cela permet de justifier les limites des noms (RFC 1034, section 3.5) : s'ils sont de purs
identificateurs techniques, il n'est pas nécessaire de permettre les
IDN, par exemple. On peut se contenter des
lettres ASCII, des chiffres et du
tiret, la règle dite LDH, qui vient du RFC 952. Cette règle
« Letters-Digits-Hyphen » a été une première fois
remise en cause vers 1986
lorsque 3Com a voulu son nom de domaine
3com.com
(à l'époque, un nom devait commencer
par une lettre, ce qui a été changé par la norme actuelle, RFC 1123). Mais cela laisse d'autres marques sans
nom de domaine adapté, par exemple C&A
ne peut pas avoir c&a.fr
. Sans parler des
cas de ceux et celles qui n'utilisent pas l'alphabet latin.
L'argument de Klensin est que ce n'est pas grave : on demande juste aux noms de domaine d'être des identificateurs uniques et non ambigus. Qu'ils ne soient pas très « conviviaux » n'est pas un problème. Inutile de dire que ce point de vue personnel ne fait pas l'unanimité.
Bien sûr, il y a aussi un aspect technique. Si on voulait, dit l'auteur ironiquement, permettre l'utilisation de la langue naturelle dans les noms de domaine, il faudrait aussi supprimer la limite de 63 caractères par composant (255 caractères pour le nom complet). Il est certain qu'il est difficile d'avoir des identificateurs qui soient à la fois utiles pour les programmes (simples, non ambigus) et pour les humains.
Le DNS n'est pas figé, et a évolué depuis ses débuts. Notamment, beaucoup de nouveaux types (RRTYPE, pour Resource Record Type) ont été créés avec le temps (cf. RFC 6895). Ce sont, par exemple :
dig
+short URI 78100.cp.bortzmeyer.fr
va vous donner un URI
OpenStreetMap correspondant au
code postal 78100),Une observation à partir de l'étude du déploiement de tous les nouveaux types d'enregistrement est que ça se passe mal : pare-feux débiles qui bloquent les types qu'ils ne connaissent pas, interfaces de gestion du contenu des zones qui ne sont jamais mises à jour (bien des hébergeurs DNS ne permettent pas d'éditer URI ou TLSA, voir simplement SRV), bibliothèques qui ne permettent pas de manipuler ces types… Cela a entrainé bien des concepteurs de protocole à utiliser le type « fourre-tout » TXT. Le RFC 5507 explique ses avantages et (nombreux) inconvénients. (Le RFC 6686 raconte comment le type générique TXT a vaincu le type spécifique SPF.)
Aujourd'hui, tout le monde et son chien a un nom de domaine. Des
noms se créent en quantité industrielle, ce qui est facilité par
l'automatisation des procédures, et le choix de certains registres
de faire des promotions commerciales. Il n'est pas exagéré de dire
que, surtout dans les nouveaux TLD ICANN, la
majorité des noms sont créés à des fins malveillantes. Il est donc
important de pouvoir évaluer la réputation d'un nom : si
mail.enlargeyourzob.xyz
veut m'envoyer du
courrier, puis-je utiliser la réputation de ce domaine (ce qu'il a
fait précédemment) pour décider de rejeter le message ou pas ? Et si
un utilisateur clique sur
http://www.bitcoinspaschers.town/
, le
navigateur Web doit-il l'avertir que ce domaine a mauvaise
réputation ? Le RFC, souvent nostalgique, rappelle que le modèle original du DNS,
formalisé dans le RFC 1591, était que chaque
administrateur de zone était compétent, responsable et
honnête. Aujourd'hui, chacune de ces qualités est rare et leur
combinaison est encore plus rare. L'auteur du RFC regrette que les
registres ne soient pas davantage comptables du contenu des zones
qu'ils gèrent, ce qui est un point de vue personnel et très
contestable : pour un TLD qui est un service public, ce serait une
violation du principe de neutralité.
Bref, en pratique, il est clair aujourd'hui qu'on
trouve de tout dans le DNS. Il serait donc souhaitable qu'on puisse
trier le bon grain de l'ivraie mais cela présuppose qu'on connaisse
les frontières administratives. Elles ne coïncident pas forcément
avec les frontières techniques
(.fr
et
gouv.fr
sont actuellement dans la même zone
alors que le premier est sous la responsabilité de
l'AFNIC et le second sous celle du
gouvernement français). Rien dans le DNS ne les indique (le
point dans un nom de domaine indique une
frontière de domaine, pas forcément une frontière de zone, encore
moins une frontière de responsabilité). Beaucoup de légendes
circulent à ce sujet, par exemple beaucoup de gens croient à tort
que tout ce qui se trouve avant les deux derniers composants d'un
nom est sous la même autorité que le nom de deuxième niveau
(cf. mon article sur l'analyse
d'un nom). Il n'y a pas actuellement de mécanisme standard et
sérieux pour déterminer les frontières de responsabilité dans un nom
de domaine. Plusieurs efforts avaient été tentés à l'IETF mais ont
toujours échoué. La moins mauvaise
solution, aujourd'hui, est la Public Suffix
List.
Beaucoup plus technique, parmi les problèmes du DNS, est celui de la taille des paquets. Car la taille compte. Il y a très très longtemps, la taille d'une réponse DNS était limitée à 512 octets. Cette limite a été supprimée en 1999 avec le RFC 2671 (c'est d'ailleurs une excellente question pour un entretien d'embauche lorsque le candidat a mis « DNS » dans la liste de ses compétences : « quelle est la taille maximale d'une réponse DNS ? »). En théorie, les réponses peuvent désormais être plus grandes (la plupart des serveurs sont configurés pour 4 096 octets) mais on se heurte à une autre limite : la MTU de 1 500 octets tend à devenir une valeur sacrée, et les réponses plus grandes que cette taille ont du mal à passer, par exemple parce qu'un pare-feu idiot bloque les fragments IP, ou parce qu'un pare-feu tout aussi crétin bloque l'ICMP, empêchant les messages Packet Too Big de passer (cf. RFC 7872).
Bref, on ne peut plus trop compter sur la fragmentation, et les serveurs limitent parfois leur réponse en UDP (TCP n'a pas de problème) à moins de 1 500 octets pour cela.
Une section entière du RFC, la 5, est consacrée au problème de la
requête inverse, c'est-à-dire comment trouver un nom de domaine en
connaissant le contenu d'un enregistrement lié à ce nom. La norme
historique prévoyait une requête spécifique pour cela, IQUERY (RFC 1035, sections 4.1.1 et 6.4). Mais elle n'a
jamais réellement marché (essentiellement parce qu'elle suppose
qu'on connaisse déjà le serveur faisant autorité… pour un nom de
domaine qu'on ne connait pas encore) et a été retirée dans le RFC 3425. Pour permettre quand même des « requêtes
inverses » pour le cas le plus demandé, la résolution d'une adresse
IP en un nom de domaine, un truc spécifique a été développé,
l'« arbre inverse »
in-addr.arpa
(puis plus
tard ip6.arpa
pour
IPv6). Ce truc n'utilise pas les IQUERY mais
des requêtes normales, avec le type
PTR. Ainsi, l'option -x
de
dig permet à la fois de fabriquer le nom en
in-addr.arpa
ou ip6.arpa
et de faire une requête de type PTR pour lui :
% dig -x 2001:678:c::1 ... ;; ANSWER SECTION: 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.c.0.0.0.8.7.6.0.1.0.0.2.ip6.arpa. 172796 IN PTR d.nic.fr.
Cette technique pose des problèmes dans le cas de préfixes
IPv4 ne tenant pas sur une frontière d'octets
(cas traité par le RFC 2317). Globalement,
c'est toujours resté un truc, parfois utile, souvent surestimé, mais
ne fournissant jamais un service général. Une tentative avait été
faite à l'IETF pour décrire ce truc et son
utilisation, mais elle n'a jamais abouti (draft-ietf-dnsop-reverse-mapping-considerations
),
la question étant trop sensible (même écrire « des gens trouvent que
les enregistrements PTR sont utiles, d'autres personnes ne sont pas
d'accord », même une phrase comme cela ne rencontrait pas de
consensus).
Enfin, la section 7 du RFC est consacrée à un problème
difficile : le DNS ne permet pas de recherche floue. Il faut
connaitre le nom exact pour avoir les données. Beaucoup
d'utilisateurs voudraient quelque chose qui ressemble davantage à un
moteur de recherche. Ils n'ont pas d'idée
très précise sur comment ça devrait fonctionner mais ils voudraient
pouvoir taper « st quentin » et que ça arrive sur
www.saint-quentin-en-yvelines.fr
. Le fait qu'il
existe plusieurs villes nommées Saint-Quentin
ne les arrête pas ; ils voudraient que ça marche « tout seul ». Le
DNS a un objectif très différent : fournir une réponse exacte à une
question non ambigüe.
Peut-être aurait-on pu développer un service de recherche floue au dessus du DNS. Il y a eu quelques réflexions à ce sujet (comme le projet IRNSS ou comme le RFC 2345) mais ce n'est jamais allé très loin. En pratique, les moteurs de recherche jouent ce rôle, sans que l'utilisateur comprenne la différence. Peu d'entre eux savent qu'un nom de domaine, ce n'est pas comme un terme tapé dans un moteur de recherche. On voit aussi bien des gens taper un nom de domaine dans la boîte de saisie du terme de recherche, que des gens utiliser le moteur de recherche comme fournisseurs d'identificateurs (« pour voir notre site Web, tapez "trucmachin" dans Google »). Le dernier clou dans le cercueil de la compréhension de la différence entre identificateur et moteur de recherche a été planté quand les navigateurs ont commencé à fusionner barre d'adresses et boite de saisie de la recherche.
Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : S. Dawkins (Wonder Hamster)
Première rédaction de cet article le 1 février 2018
Un petit RFC bureaucratique pour traiter un manque du RFC 7437, qui ne disait pas que l'IAOC devait envoyer quelqu'un au NomCom, le comité de nomination. (Mais de toute façon, l'IAOC ayant été supprimée depuis, ce RFC n'est plus pertinent, voir le RFC 8713.)
En effet, le NomCom, le comité chargé d'étudier les personnes qui pourront occuper des postes dans divers organismes de l'IETF (cf. RFC 7437), ce NomCom s'occupe entre autres des postes à l'IAOC (RFC 4071), mais ne disait pas que l'IAOC pouvait envoyer un membre au NomCom, comme le faisaient les autres organes. A priori, ce n'était pas grave puisque le NomCom pouvait toujours ajouter qui il voulait comme observateur mais, en 2017, le NomCom a travaillé sur un poste à l'IAOC sans avoir de représentant de l'IAOC à ses réunions.
Ce n'est pas la fin du monde mais cela a justifié ce RFC, qui ajoute juste aux règles existantes que le NomCom devrait veiller à avoir quelqu'un de l'IAOC, ou qui connait l'IAOC.
Les discussions qui ont eu lieu autour de ce minuscule changement sont résumées dans l'annexe A. Par exemple, après un long débat, ce « représentant » de l'IAOC sera qualifié d'« advisor » et pas de « liaison » (en gros, un « liaison » a un boulot plus formel et plus sérieux.)
Date de publication du RFC : Février 2018
Auteur(s) du RFC : M. Bäuerle (STZ Elektronik)
Chemin des normes
Première rédaction de cet article le 14 février 2018
Cela peut sembler étonnant, mais le service des News fonctionne toujours. Et il est régulièrement perfectionné. Ce nouveau RFC normalise une extension au format des articles qui permettra de sécuriser un petit peu l'opération d'annulation d'articles.
Une fois qu'un article est lancé sur un réseau social décentralisé,
comme Usenet (RFC 5537), que faire si on le regrette ? Dans un système
centralisé comme Twitter, c'est simple, on
s'authentifie, on le supprime et plus personne ne le voit. Mais
dans un réseau décentralisé, il faut encore propager la demande de
suppression (d'annulation, sur les News). Et cela pose évidemment
des questions de sécurité : il ne faut pas permettre aux méchants
de fabriquer trop facilement une demande d'annulation. Notre RFC
propose donc une mesure de sécurité, l'en-tête
Cancel-Lock:
.
Cette mesure de sécurité est simple, et ne fournit certainement
pas une sécurité de niveau militaire. Pour la comprendre, il faut
revenir au mécanisme d'annulation d'un article
d'Usenet. Un article de contrôle est un
article comme les autres, envoyé sur
Usenet, mais il ne vise pas à être lu par les
humains, mais à être interprété par le logiciel. Un exemple
d'article de contrôle est l'article de contrôle
d'annulation, défini dans le RFC 5337, section 5.3. Comme son nom l'indique, il demande
la suppression d'un article, identifié par son Message
ID. Au début d'Usenet, ces messages de contrôle
n'avaient aucune forme d'authentification. On a donc vu apparaitre
des faux messages de contrôle, par exemple à des fins de censure
(supprimer un article qu'on n'aimait pas). Notre nouveau RFC
propose donc qu'un logiciel proche de la source du message mette
un en-tête Cancel-Lock:
qui indique la clé
qui aura le droit d'annuler le message.
Évidemment, ce Cancel-Lock:
n'apporte pas beaucoup de sécurité, entre autre parce qu'un
serveur peut toujours le retirer et mettre le sien, avant de
redistribuer (c'est évidemment explicitement interdit par le RFC
mais il y a des méchants). Mais cela ne change de toute façon pas grand'chose à la situation
actuelle, un serveur peut toujours jeter un article, de toute
façon. Si on veut quand même une solution de sécurité
« sérieuse », il faut utiliser PGP, comme
mentionné en passant par le RFC 5537 (mais
jamais normalisé dans un RFC).
La section 2 du RFC décrit en détail le mécanisme de
sécurité. La valeur de l'en-tête Cancel-Lock:
est l'encodage en base64 d'une
condensation d'une clé secrète (en fait, on
devrait plutôt l'appeler mot de passe). Pour authentifier une
annulation, le message de contrôle comportera un autre en-tête,
Cancel-Key:
, qui révélera la clé (qui ne
devra donc être utilisée qu'une fois).
Voici un exemple. On indique explicitement l'algorithme de condensation (ici, SHA-256, la liste est dans un registre IANA). D'abord, le message original aura :
Cancel-Lock: sha256:s/pmK/3grrz++29ce2/mQydzJuc7iqHn1nqcJiQTPMc=
Et voici le message de contrôle, authentifié :
Cancel-Key: sha256:qv1VXHYiCGjkX/N1nhfYKcAeUn8bCVhrWhoKuBSnpMA=
La section 3 du RFC détaille comment on utilise ces
en-têtes. Le Cancel-Lock:
peut être mis par
l'auteur originel de l'article, ou bien par un intermédiaire (par
exemple le modérateur qui a approuvé
l'article). Plusieurs Cancel-Lock:
peuvent
donc être présents. Notez qu'il n'y a aucun moyen de savoir si le
Cancel-Lock:
est « authentique ». Ce
mécanisme est une solution de sécurité faible.
Pour annuler un message, on envoie un message de contrôle avec
un Cancel-Key:
correspondant à un des
Cancel-Lock:
. Les serveurs recevant ce
message de contrôle condenseront la clé (le
mot de passe) et vérifieront s'ils retombent bien sur le condensat
contenu dans un des Cancel-Lock:
.
La section 4 donne les détails sur le choix de la clé (du mot
de passe). Évidemment, elle doit être difficile à deviner, donc
plutôt choisie par un algorithme pseudo-aléatoire (et pas "azerty123"). Et elle doit être à
usage unique puisque, une fois révélée par un
Cancel-Key:
, elle n'est plus
secrète. L'algorithme recommandé par le RFC est d'utiliser un
HMAC (RFC 2104) d'un
secret et de la concaténation du Message ID du message
avec le nom de l'utilisateur. Comme cela, générer un
Cancel-Key:
pour un message donné peut se
faire avec juste le message, sans avoir besoin de mémoriser les
clés. Voici un exemple, tiré de la section 5, et utilisant OpenSSL. Le secret est
frobnicateZ32
. Le message est le
<12345@example.net>
et l'utilisateur est
stephane
:
% printf "%s" "<12345@example.net>stephane" \ | openssl dgst -sha256 -hmac "frobnicateZ32" -binary \ | openssl enc -base64 f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=
Voilà, nous avons notre clé,
f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=
. Pour
le condensat, nous nous servirons de
SHA-256 :
% printf "%s" "f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=" \ | openssl dgst -sha256 -binary \ | openssl enc -base64 RBJ8ZsgqBnW/tYT/qu1JcXK8SA2O9g+qJLDzRY5h1cg=
Nous pouvons alors former le Cancel-Lock:
:
Cancel-Lock: sha256:RBJ8ZsgqBnW/tYT/qu1JcXK8SA2O9g+qJLDzRY5h1cg=
Et, si nous voulons annuler ce message, le
Cancel-Key:
dans le message de contrôle
d'annulation aura l'air :
Control: cancel <12345@example.net> Cancel-Key: sha256:f0rHwfZXp5iKFjTbX/I5bQXh9Dta33nWBzLi8f9oaoM=
Pour vérifier ce message de contrôle, le logiciel calculera le
condensat de la clé et vérifiera s'il retombe bien sur RBJ8ZsgqBnW/tYT/qu1JcXK8SA2O9g+qJLDzRY5h1cg=
.
Enfin, la section 7 du RFC détaille la sécurité de ce mécanisme. Cette sécurité est plutôt faible :
Control-Lock:
. Si cela vous défrise, vous
devez utiliser PGP.Les deux nouveaux en-têtes ont été ajoutés au registre des en-têtes.
À noter que, comme il n'y a pas de groupe de travail IETF sur les
News, ce RFC a été surtout discuté… sur les
News, en l'occurrence les groupes
news.software.nntp
et
de.comm.software.newsserver
. Comme ce RFC
décrit une technique ancienne, il y a déjà de nombreuses mises en
œuvre comme celle de
gegeweb, la bibliothèque
canlock (paquetage Debian
libcanlock2), dans le
serveur INN, ou les clients News
Gnus (regardez cet
article sur son usage), flnews,
slrn ou tin. Vous pouvez aussi lire l'article
de Brad Templeton comparant
Cancel-Lock:
aux signatures.
Merci à Julien Élie pour sa relecture.
Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : K. Moore (Windrock), C. Newman (Oracle)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF uta
Première rédaction de cet article le 31 janvier 2018
Ce RFC s'inscrit dans le cadre des efforts de sécurisation de l'Internet contre la surveillance massive, efforts notablement accélérés depuis les révélations Snowden. Désormais, l'usage de texte en clair (non chiffré) pour le courrier électronique est officiellement abandonné : POP, IMAP et SMTP doivent être chiffrés.
Plus précisément, ce nouveau RFC du groupe de travail UTA vise l'accès au courrier (POP - RFC 1939 et IMAP - RFC 9051), ainsi que la soumission de messages par SMTP - RFC 6409 (entre le MUA et le premier MTA). Les messages de ces protocoles doivent désormais être systématiquement chiffrés avec TLS - RFC 5246. Le but est d'assurer la confidentialité des échanges. Le cas de la transmission de messages entre MTA est couvert par des RFC précédents, RFC 3207 et RFC 7672 (voir aussi le RFC 8461).
Pour résumer les recommandations concrètes de ce RFC :
Ce RFC ne traite pas le cas du chiffrement de bout en bout, par
exemple avec PGP (RFC 9580 et RFC 3156). Ce chiffrement
de bout en bout est certainement la meilleure solution mais il est
insuffisamment déployé aujourd'hui. En attendant qu'il se
généralise, il faut bien faire ce qu'on peut pour protéger les
communications. En outre, PGP ne protège pas certaines métadonnées
comme les en-têtes (From:
,
Subject:
, etc), alors qu'un chiffrement TLS
du transport le fait. Bref, on a besoin des deux.
La section 3 du RFC rappelle ce qu'est le « TLS implicite », qui est désormais recommandé. Le client TLS se connecte à un serveur TLS sur un port dédié, où tout se fait en TLS, et il démarre la négociation TLS immédiatement. Le TLS implicite s'oppose au « TLS explicite » qui était l'approche initiale pour le courrier. Avec le TLS explicite (RFC 2595 et RFC 3207), le serveur devait annoncer sa capacité à faire du TLS :
% telnet smtpagt1.ext.cnamts.fr. smtp Trying 93.174.145.55... Connected to smtpagt1.ext.cnamts.fr. Escape character is '^]'. 220 smtpagt1.ext.cnamts.fr ESMTP CNAMTS (ain1) EHLO mail.example.com 250-smtpagt1.ext.cnamts.fr Hello mail.example.com [192.0.2.187], pleased to meet you 250-ENHANCEDSTATUSCODES 250-PIPELINING 250-8BITMIME 250 STARTTLS
Et le client devait dire qu'il allait démarrer la session TLS avec la commande STARTTLS. L'inconvénient principal de STARTTLS est qu'il est vulnérable à l'attaque « SSL stripping » où un attaquant actif modifie la communication avant que TLS ne démarre, pour faire croire que le partenaire ne sait pas faire de TLS. (Et il y a aussi la vulnérabilité CERT #555316.) Bien sûr, les serveurs peuvent se protéger en refusant les connexions sans STARTTLS mais peu le font. L'approche STARTTLS était conçue pour gérer un monde où certains logiciels savaient faire du TLS et d'autres pas, mais, à l'heure où la recommandation est de faire du TLS systématiquement, elle n'a plus guère d'utilité. (La question est discutée plus en détail dans l'annexe A. Notez qu'un des auteurs de notre nouveau RFC, Chris Newman, était l'un des auteurs du RFC 2595, qui introduisait l'idée de STARTTLS.)
Avec le TLS implicite, cela donne :
% openssl s_client -connect mail.example.com:465 ... subject=/CN=mail.example.com issuer=/O=CAcert Inc./OU=http://www.CAcert.org/CN=CAcert Class 3 Root ... Peer signing digest: SHA512 Server Temp Key: ECDH, P-256, 256 bits ... New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384 Server public key is 2048 bit ... 220 mail.example.com ESMTP Postfix EHLO toto 250-mail.example.com 250-AUTH DIGEST-MD5 NTLM CRAM-MD5 250-ENHANCEDSTATUSCODES 250-8BITMIME 250-DSN 250 SMTPUTF8
Donc, concrètement, pour POP, cela veut dire établir la connexion sur le port du TLS implicite, le 995 (et non pas sur le port 110, prévu pour le texte en clair), lancer TLS et authentifier avec le RFC 7817. Puis on fait du POP classique. Pour IMAP, c'est le port 993. Dans les deux cas, cette recommandation de notre RFC ne sera pas trop dure à suivre, le TLS implicite est déjà courant pour ces deux protocoles.
Pour la soumission SMTP (RFC 6409), c'est le port 465 (utilisé auparavant pour du SMTP classique, non-soumission, cf. le registre IANA et les section 7.3 et annexe A du RFC qui justifient cette réaffectation). Le mécanisme avec STARTTLS sur le port 587 (TLS explicite) est très répandu, contrairement à ce qui se passe pour POP et IMAP. La transition sera donc plus longue, et il faudra donc maintenir les deux services, TLS implicite et explicite, pendant un certain temps.
Voici les serveurs IMAP (pour récupérer le courrier) et SMTP (pour soumettre du courrier) conformes aux recommandations de ce RFC (TLS implicite), tels qu'affichés par Thunderbird :
Et voici la configuration du MTA Postfix pour accepter du TLS implicite sur le port 465 :
submissions inet n - - - - smtpd -o syslog_name=postfix/submissions -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o smtpd_etrn_restrictions=reject -o smtpd_sasl_authenticated_header=yes
(À mettre dans le master.cf
.) Cette
configuration active TLS et exige une authentification du client
(ce qui est normal pour soumettre du courrier.) Pensez aussi à
vérifier que le port est bien défini dans
/etc/services
(smtps 465/tcp ssmtp submissions
).
La section 4 du RFC fournit des détails sur l'utilisation de TLS, du côté des fournisseurs de service de courrier. Le point essentiel est « chiffrement partout, tout le temps » (section 4.1). L'objectif ne pourra pas forcément être atteint immédiatement par tout le monde mais il faut commencer. Par exemple, on peut interdire tout accès en texte clair à certains utilisateurs, puis généraliser à tous. Et, dans ce cas, le message envoyé doit être indépendant de si le mot de passe était valide ou pas (pour ne pas donner d'indication à un éventuel écoutant). Le RFC conseille aussi de traiter les accès avec les vieilles versions de TLS (ou, pire, avec SSL) de la même façon que les accès en clair. Une fois l'accès en clair coupé, il est recommandé de forcer un changement de mot de passe, si l'ancien avait été utilisé en clair.
Et les autres points ? Les fournisseurs de services de
courrier électronique doivent annoncer les
services POP, IMAP et soumission SMTP en utilisant les
enregistrements SRV du RFC 6186, afin de faciliter la tâche des
MUA. Un nouvel enregistrement SRV arrive
avec ce RFC, d'ailleurs, le _submissions._tcp
,
pour la soumission SMTP sur TLS. Le RFC demande que ces enregistrements
SRV (ainsi que les
enregistrements MX utilisés pour le
courrier entrant, donc a priori pas le sujet de ce RFC) soient
signés avec DNSSEC. Et, à propos du
DNS, le RFC demande également la
publication d'enregistrements TLSA (RFC 6698).
La cryptographie, c'est bien, mais il est également souhaitable
de signaler qu'elle a été utilisée, et dans quelles conditions,
pour informer les utilisateurs de la sécurité dont ils ont pu
bénéficier. Par exemple, le RFC demande que les algorithmes de
cryptographie utilisées soient mis dans l'en-tête
Received:
du courrier (cf. aussi le RFC 3848), via une clause
tls
(dans registre
IANA). Notez que beaucoup de serveurs SMTP le font déjà,
avec une syntaxe différente :
Received: from mail4.protonmail.ch (mail4.protonmail.ch [185.70.40.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.bortzmeyer.org (Postfix) with ESMTPS id 38DAF31D16 for <stephane@bortzmeyer.org>; Sat, 20 Jan 2018 16:56:59 +0100 (CET)
La section 5 décrit les exigences pour l'autre côté, le client, cette fois, et non plus le serveur de la section 4. D'abord, comme les MUA sont en contact direct avec l'utilisateur humain, ils doivent lui indiquer clairement le niveau de confidentialité qui a été obtenu (par exemple par une jolie icône, différente si on a utilisé TLS ou pas). Notez que cette indication de la confidentialité est un des points du projet Caliopen. Gmail, par exemple, peut afficher ces informations et cela donne :
Comment est-ce qu'un MUA doit trouver s·on·es serveur·s, au
fait ? La méthode recommandée est d'utiliser les
enregistements SRV du RFC 6186. Aux _imap._tcp
et
_pop3._tcp
du RFC précédent s'ajoute
_submissions._tcp
pour le SMTP de soumission
d'un message, avec TLS implicite. Attention, le but étant la
confidentialité, le MUA ne devrait pas utiliser les serveurs
annoncés via les SRV s'ils ne satisfont pas à des exigences
minimales de sécurité TLS. Le MUA raisonnable devrait vérifier que
le SRV est signé avec DNSSEC, ou, sinon,
que le serveur indiqué est dans le même domaine que le domaine de
l'adresse de courrier. La section 5.2 donne d'autres idées de
choses à vérifier (validation du certificat
du serveur suivant le RFC 7817, de la version de TLS…) D'une
manière générale, le MUA ne doit pas envoyer d'informations
sensibles comme un mot de passe si la session n'est pas sûre. La
tentation pourrait être d'afficher à l'utilisateur un dialogue du
genre « Nous n'avons pas réussi à établir une connexion TLS
satisfaisante car la version de TLS n'est que la 1.1 et le groupe Diffie-Hellman ne fait que 512
bits, voulez-vous continuer quand même ? » Mais le RFC s'oppose à
cette approche, faisant remarquer qu'il est alors trop facile pour
l'utilisateur de cliquer OK et de prendre ainsi des risques qu'il
ne maitrise pas.
Autre question de sécurité délicate, l'épinglage du certificat. Il s'agit de garder un certificat qui a marché autrefois, même s'il ne marche plus, par exemple parce qu'expiré. (Ce n'est donc pas le même épinglage que celui qui est proposé pour HTTP par le RFC 7469.) Le RFC autorise cette pratique, ce qui est du bon sens : un certificat expiré, ce n'est pas la même chose qu'un certificat faux. Et ces certificats expirés sont fréquents car, hélas, bien des administrateurs système ne supervisent pas l'expiration des certificats. Voici la configuration d'Icinga pour superviser un service de soumission via SMTP :
apply Service "submissions" { import "generic-service" check_command = "ssmtp" vars.ssmtp_port = 465 assign where (host.address || host.address6) && host.vars.submissions vars.ssmtp_certificate_age = "7,3" }
Et, une fois ce service défini, on peut ajouter à la
définition d'un serveur vars.submissions =
true
et il sera alors supervisé :
Notre RFC recommande également aux auteurs de MUA de faire en sorte que les utilisateurs soient informés du niveau de sécurité de la communication entre le client et le serveur. Tâche délicate, comme souvent quand on veut communiquer avec les humains. Il ne faut pas faire de fausses promesses (« votre connection est cryptée avec des techniques military-grade, vous êtes en parfaite sécurité ») tout en donnant quand même des informations, en insistant non pas sur la technique (« votre connexion utilise ECDHE-RSA-AES256-GCM-SHA384, je vous mets un A+ ») mais sur les conséquences (« Ce serveur de courrier ne propose pas de chiffrement de la communication, des dizaines d'employés de la NSA, de votre FAI, et de la Fsociety sont en train de lire le message où vous parlez de ce que vous avez fait hier soir. »).
Voilà, vous avez l'essentiel de ce RFC. Quelques détails, maintenant. D'abord, l'interaction de ces règles avec les antivirus et antispam. Il y a plusieurs façons de connecter un serveur de messagerie à un logiciel antivirus et·ou antispam (par exemple l'interface Milter, très répandue). Parfois, c'est via SMTP, avec l'antivirus et·ou antispam qui se place sur le trajet, intercepte les messages et les analyse avant de les retransmettre. C'est en général une mauvaise idée (RFC 2979). Dès qu'il y a du TLS dans la communication, c'est encore pire. Puisque le but de TLS est de garantir l'authenticité et l'intégrité de la communication, tout « intercepteur » va forcément être très sale. (Et les logiciels qui font cela sont d'abominables daubes.)
Ah, et un avertissement lié à la vie privée (section 8 du RFC) : si on présente un certificat client, on révèle son identité à tout écoutant. Le futur TLS 1.3 aidera peut-être à limiter ce risque mais, pour l'instant, attention à l'authentification par certificat client.
Si vous aimez connaitre les raisons d'un choix technique, l'annexe A couvre en détail les avantages et inconvénients respectifs du TLS implicite sur un port séparé et du TLS explicite sur le port habituel. La section 7 du RFC 2595 donnait des arguments en faveur du TLS explicite. Certaines des critiques que ce vieux RFC exprimait contre le TLS implicite n'ont plus de sens aujourd'hui (le RFC 6186 permet que le choix soit désormais fait par le logiciel et plus par l'utilisateur, et les algorithmes export sont normalement abandonnés). D'autres critiques ont toujours été mauvaises (par exemple, celle qui dit que le choix entre TLS et pas de TLS est binaire : un MUA peut essayer les deux.) Outre la sécurité, un avantage du port séparé pour TLS est qu'il permet de déployer des frontaux - répartiteurs de charge, par exemple - TLS génériques.
Et pour terminer, d'autres exemples de configuration. D'abord avec mutt, par Keltounet :
smtp_url=smtps://USER@SERVER:465
Et le courrier est bien envoyé en TLS au port de soumission à TLS implicite.
Avec Dovecot, pour indiquer les
protocoles utilisés dans la session TLS, Shaft suggère, dans conf.d/10-logging.conf
:
login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k
(Notez le %k
à la fin, qui n'est pas dans le
fichier de configuration d'exemple.)
Et pour OpenSMTPd ? n05f3ra1u propose :
pki smtp.monserveur.example key "/etc/letsencrypt/live/monserveur.example/privkey.pem" pki smtp.monserveur.example certificate "/etc/letsencrypt/live/monserveur.example/cert.pem" ... listen on eno1 port 465 hostname smtp.monserveur.example smtps pki smtp.monserveur.example auth mask-source
Date de publication du RFC : Février 2018
Auteur(s) du RFC : I. Rhee (NCSU), L. Xu (UNL), S. Ha (Colorado), A. Zimmermann, L. Eggert (NetApp), R. Scheffenegger
Pour information
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 8 février 2018
Longtemps après sa mise au point et son déploiement massif sur Linux, voici la description officielle de l'algorithme CUBIC, un algorithme de contrôle de la congestion dans TCP. (Cette description a depuis été remplacée par celle du RFC 9438.)
CUBIC doit son nom au fait que la fonction de calcul de la fenêtre d'envoi des données est une fonction cubique (elle a un terme du troisième degré) et non pas linéaire. CUBIC est l'algorithme utilisé par défaut dans Linux depuis pas mal d'années :
% sudo sysctl net.ipv4.tcp_congestion_control net.ipv4.tcp_congestion_control = cubic
CUBIC est plus énergique lorsqu'il s'agit d'agrandir la fenêtre d'envoi de données, lorsque le réseau a une grande capacité mais un RTT important. Dans le cas de ces réseaux « éléphants » (terme issu de la prononciation en anglais de LFN, Long and Fat Network, voir RFC 7323, section 1.1), le RTT élevé fait que l'émetteur TCP met du temps à recevoir les accusés de réception, et donc à comprendre que tout va bien et qu'il peut envoyer davantage de données, pour remplir le long tuyau. CUBIC permet d'accélérer cette phase.
Notez que CUBIC ne contrôle que l'émetteur, le récepteur est inchangé. Cela facilite le déploiement : un émetteur CUBIC peut parfaitement communiquer avec un récepteur traditionnel.
Avant de lire la suite du RFC, il est recommandé de (re)lire le RFC 5681, la bible sur le contrôle de congestion TCP, et notamment sur cette notion de fenêtre d'envoi (ou fenêtre de congestion).
TCP (RFC 793) a évidemment une mission difficile. L'intérêt de l'émetteur est d'envoyer le plus de données le plus vite possible. Mais à condition qu'elles arrivent sinon, s'il y a de la congestion, les données seront perdues et il faudra recommencer (ré-émettre). Et on n'est pas tout seul sur le réseau : il faut aussi tenir compte des autres, et chercher à partager équitablement l'Internet. L'algorithme doit donc être énergique (chercher à utiliser les ressources au maximum) mais pas bourrin (il ne faut pas dépasser le maximum), tout en étant juste (on n'est pas dans la startup nation, il ne faut pas écraser les autres, mais partager avec eux).
Le problème des éléphants, des réseaux à fort BDP, est connu depuis longtemps (article de T. Kelly, « Scalable TCP: Improving Performance in HighSpeed Wide Area Networks », et RFC 3649.) Dans ces réseaux, TCP tend à être trop prudent, à ouvrir sa fenêtre (les données qu'on peut envoyer tout de suite) trop lentement. Cette prudence l'honore, mais peut mener à des réseaux qui ne sont pas utilisés à fond. L'article de Ha, S., Kim, Y., Le, L., Rhee, I., et L. Xu, « A Step toward Realistic Performance Evaluation of High-Speed TCP Variants » expose ce problème. Il touche toutes les variantes de TCP depuis le TCP Reno décrit dans le RFC 5681 : le New Reno des RFC 6582 et RFC 6675, et même des protocoles non-TCP mais ayant le même algorithme, comme UDP (TFRC, RFC 5348) ou SCTP (RFC 9260).
CUBIC a été originellement documenté dans l'article de S. Ha, Injong Rhee, et Lisong Xu, « CUBIC: A New TCP-Friendly High-Speed TCP Variant », en 2008. Sur Linux, il a remplacé BIC pour les réseaux à haut BDP.
La section 3 du RFC rappelle les principes de conception de CUBIC, notamment :
La section 4 du RFC spécifie précisement l'algorithme, après beaucoup de discussion avec les développeurs du noyau Linux (puisque le code a été écrit avant le RFC). Cette section est à lire si vous voulez comprendre tous les détails. Notez l'importance du point d'inflexion entre la partie concave et la partie convexe de la courbe qui décrit la fonction de changement de taille de la fenêtre. Ce point d'inflexion est mis à la valeur de la fenêtre d'envoi juste avant la dernière fois où TCP avait détecté de la congestion.
Notez que Linux met en outre en œuvre l'algorithme HyStart, décrit dans « Taming the Elephants: New TCP Slow Start ». Hystart mesure le RTT entre émetteur et récepteur pour détecter (par l'augmentation du RTT) un début de congestion avant que des pertes de paquets se produisent. (Merci à Lucas Nussbaum pour l'information.)
La section 5 analyse le comportement CUBIC selon les critères de comparaison des algorithmes de contrôle de la congestion décrits dans le RFC 5033.
Pour finir, voici une intéressante comparaison des algorithmes de contrôle de congestion.
Date de publication du RFC : Mars 2018
Auteur(s) du RFC : S. Dickinson (Sinodun), D. Gillmor (ACLU), T. Reddy (McAfee)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dprive
Première rédaction de cet article le 22 mars 2018
Afin de mieux protéger la vie privée des utilisateurs du DNS, le RFC 7858 normalise comment faire du DNS sur TLS, le chiffrement empêchant la lecture des requêtes et réponses DNS. Mais le chiffrement sans authentification n'a qu'un intérêt limité. Notamment, il ne protège pas contre un attaquant actif, qui joue les hommes du milieu. Le RFC 7858 ne proposait qu'un seul mécanisme d'authentification, peu pratique, les clés publiques configurées statiquement dans le client DNS. Ce nouveau RFC décrit d'autres mécanismes d'authentification, classés selon deux profils, un Strict (sécurité maximum) et un Opportuniste (disponibilité maximum).
Le problème de la protection de la vie privée quand on utilise le DNS est décrit dans le RFC 7626. Les solutions, comme toujours quand il s'agit de vie privée, se répartissent en deux catégories, la minimisation des données (RFC 9156) et le chiffrement (RFC 7858 et RFC 8094). Le chiffrement protège bien contre un attaquant purement passif. Mais si celui qui veut écouter les échanges DNS est capable de lancer des attaques actives (ARP spoofing, par exemple), le chiffrement ne suffit pas, il faut le doubler d'une authentification du serveur. Par exemple, en mars 2014, en Turquie, l'attaquant (le gouvernement) a pu détourner le trafic avec Google Public DNS. Même si Google Public DNS avait permis le chiffrement, il n'aurait pas servi, lors de cette attaque active. Sans l'authentification, on risque de parler en chiffré… à l'attaquant.
Le problème de l'authentification, c'est que si elle échoue, que faut-il faire ? Renoncer à envoyer des requêtes DNS ? Cela revient à se couper d'Internet. Notre nouveau RFC, considérant qu'il n'y a pas une solution qui conviendra à tous les cas, propose deux profils :
Notez bien qu'un profil spécifie des propriétés, une fin et pas un moyen. Un profil est défini par ces propriétés, qu'on implémente ensuite avec des mécanismes, décrits en section 6. Le RFC 7858 spécifiait déjà, dans sa section 4.2, un mécanisme d'authentification, fondé sur la connaissance préalable, par le client DNS, de la clé publique du serveur (SPKI, pour Subject Public Key Info, une solution analogue à celle du RFC 7469 pour HTTP). Mais beaucoup de détails manquaient. Ce nouveau RFC 8310 :
À propos de ces « informations nécessaires à
l'authentification », il est temps d'introduire un acronyme
important (section 2 du RFC), ADN (Authentication
Domain Name), le nom du serveur qu'on veut
authentifier. Par exemple, pour Quad9,
ce sera dns.quad9.net
. Autres termes
importants :
Note importante, la section 3 du RFC pointe le fait que les
mécanismes d'authentification présentés ici ne traitent que
l'authentification d'un résolveur DNS par son
client. L'éventuelle future authentification d'un serveur faisant
autorité par le résolveur est hors-sujet, tout comme
l'authentification du client DNS par le serveur. Sont également
exclus les idées d'authentifier le nom de l'organisation qui gère
le serveur ou son pays : on authentifie uniquement l'ADN, « cette
machine est bien dns-resolver.yeti.eu.org
»,
c'est tout.
La section 5 présente les deux profils normalisés. Un profil est une politique choisie par le client DNS, exprimant ses exigences, et les compromis qu'il est éventuellement prêt à accepter si nécessaire. Un profil n'est pas un mécanisme d'authentification, ceux-ci sont dans la section suivante, la section 6. La section 5 présente deux profils :
Les discussions à l'IETF (par exemple pendant la réunion de Séoul en novembre 2016) avaient été vives, notamment sur l'utilité d'avoir un profil strict, qui peut mener à ne plus avoir de résolution DNS du tout, ce qui n'encouragerait pas son usage.
Une petite nuance s'impose ici : pour les deux profils, il faudra, dans certains cas, effectuer une requête DNS au tout début pour trouver certaines informations nécessaires pour se connecter au serveur DNS (par exemple son adresse IP si on n'a que son nom). Cette « méta-requête » peut se faire en clair, et non protégée contre l'écoute, même dans le cas du profil strict. Autrement, le déploiement de ce profil serait très difficile (il faudrait avoir toutes les informations stockées en dur dans le client).
Le profil strict peut être complètement inutilisable si les requêtes DNS sont interceptées et redirigées, par exemple dans le cas d'un portail captif. Dans ce cas, la seule solution est d'avoir un mode « connexion » pendant lequel on n'essaie pas de protéger la confidentialité des requêtes DNS, le temps de passer le portail captif, avant de passer en mode « accès Internet ». C'est ce que fait DNSSEC-trigger (cf. section 6.6).
Le tableau 1 du RFC résume les possibilités de ces deux profils, face à un attaquant actif et à un passif. Dans tous les cas, le profil strict donnera une connexion DNS chiffrée et authentifiée, ou bien pas de connexion du tout. Dans le meilleur cas, le profil opportuniste donnera la même chose que le strict (connexion chiffrée et authentifiée). Si le serveur est mal configuré ou si le client n'a pas les informations nécessaires pour authentifier, ou encore si un Homme du Milieu intervient, la session ne sera que chiffrée, et, dans le pire des cas, le profil opportuniste donnera une connexion en clair. On aurait pu envisager d'autres profils (par exemple un qui impose le chiffrement, mais pas l'authentification) mais le groupe de travail à l'IETF a estimé qu'ils n'auraient pas eu un grand intérêt, pour la complexité qu'ils auraient apporté.
Un mot sur la détection des problèmes. Le profil opportuniste peut permettre la détection d'un problème, même si le client continue ensuite malgré les risques. Par exemple, si un résolveur DNS acceptait DNS-sur-TLS avant, que le client avait enregistré cette information, mais que, ce matin, les connexions vers le port 853 sont refusées, avec le profil opportuniste, le client va quand même continuer sur le port 53 (en clair, donc) mais peut noter qu'il y a un problème. Il peut même (mais ce n'est pas une obligation) prévenir l'utilisateur. Cette possibilité de détection est le « D » dans le tableau 1, et est détaillée dans la section 6.5. La détection permet d'éventuellement prévenir l'utilisateur d'une attaque potentielle, mais elle est aussi utile pour le débogage.
Évidemment, seul le profil strict protège réellement l'utilisateur contre l'écoute et toute mise en œuvre de DNS-sur-TLS devrait donc permettre au moins ce profil. Le profil opportuniste est là par réalisme : parfois, il vaut mieux une connexion DNS écoutée que pas de DNS du tout.
Les deux profils vont nécessiter un peu de configuration (le nom ou l'adresse du résolveur) mais le profil strict en nécessite davantage (par exemple la clé du résolveur).
Maintenant qu'on a bien décrit les profils, quels sont les mécanismes d'authentification disponibles ? La section 6 les décrit rapidement, et le tableau 2 les résume, il y en a six en tout, caractérisés par l'information de configuration nécessaire côté client (ils seront détaillés en section 8) :
subjectAltName
._853._tcp.ADN
. Cela nécessite
un client capable de faire de la validation
DNSSEC (à l'heure actuelle, le
résolveur sur la machine cliente est en général un logiciel
minimal, incapable de valider). Et les requêtes DANE (demande
de l'enregistrement TLSA) peuvent passer en clair.Cela fait beaucoup de mécanismes d'authentification ! Comment se combinent-ils ? Lesquels essayer et que faire s'ils donnent des résultats différents ? La méthode recommandée, si on a un ADN et une clé, est de tester les deux et d'exiger que les deux fonctionnent.
On a parlé à plusieurs reprises de l'ADN
(Authentication Domain Name). Mais comment
on obtient son ADN ? La section 7 du RFC détaille les sources
d'ADN. La première est évidemment le cas où l'ADN est
configuré manuellement. On pourrait imaginer, sur
Unix, un
/etc/resolv.conf
avec une nouvelle
syntaxe :
nameserver 2001:db8:53::1 adn resolver.example.net
Ici, on a configuré manuellement l'adresse IP et le nom
(l'ADN) du résolveur. Cela convient au cas de résolveurs
publics comme Quad9. Mais on pourrait imaginer des cas où seul
l'ADN serait configuré quelque part, le résolveur dans
/etc/resolv.conf
étant rempli par
DHCP, et n'étant utilisé que pour
les méta-requêtes. Par exemple un (mythique, pour l'instant) /etc/tls-resolver.conf
:
adn resolver.example.net # IP address will be found via the "DHCP" DNS resolver, and checked # with DNSSEC and/or TLS authentication
Troisième possibilité, l'ADN et l'adresse IP pourraient être découverts dynamiquement. Il n'existe à l'heure actuelle aucune méthode normalisée pour cela. Si on veut utiliser le profil strict, cette future méthode normalisée devra être raisonnablement sécurisée, ce qui n'est typiquement pas le cas de DHCP. On peut toujours normaliser une nouvelle option DHCP pour indiquer l'ADN mais elle ne serait, dans l'état actuel des choses, utilisable qu'avec le profil opportuniste. Bon, si vous voulez vous lancer dans ce travail, lisez bien la section 8 du RFC 7227 et la section 22 du RFC 8415 avant.
La section 11 du RFC décrit les mesures à mettre en œuvre contre deux attaques qui pourraient affaiblir la confidentialité, même si on chiffre. La première est l'analyse des tailles des requêtes et des réponses. L'accès au DNS étant public, un espion peut facilement récolter l'information sur la taille des réponses et, puisque TLS ne fait rien pour dissimuler cette taille, déduire les questions à partir des tailles. La solution recommandée contre l'attaque est le remplissage, décrit dans le RFC 7830.
Seconde attaque possible, un résolveur peut inclure l'adresse IP de son client dans ses requêtes au serveur faisant autorité (RFC 7871). Cela ne révèle pas le contenu des requêtes et des réponses, mais c'est quand même dommage pour la vie privée. Le client DNS-sur-TLS doit donc penser à mettre l'option indiquant qu'il ne veut pas qu'on fasse cela (RFC 7871, section 7.1.2).
Enfin, l'annexe A de notre RFC rappelle les dures réalités de l'Internet d'aujourd'hui : même si votre résolveur favori permet DNS-sur-TLS (c'est le cas par exemple de Quad9), le port 853 peut être bloqué par un pare-feu fasciste. Le client DNS-sur-TLS a donc intérêt à mémoriser quels résolveurs permettent DNS-sur-TLS, et depuis quels réseaux.
Pour l'instant, les nouveaux mécanismes d'authentification, et la possibilité de configurer le profil souhaité, ne semblent pas encore présents dans les logiciels, il va falloir patienter (ou programmer soi-même).
Merci à Willem Toorop pour son aide.
Date de publication du RFC : Mars 2018
Auteur(s) du RFC : D. Bider (Bitvise Limited)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 21 mars 2018
Dernière mise à jour le 28 mars 2018
Le protocole SSH n'avait pas de mécanisme propre pour négocier des extensions au protocole de base (comme celle du RFC 4335). En outre, une partie de la négociation, par exemple des algorithmes cryptographiques, se déroulait avant le début du chiffrement et n'était donc pas protégée. Ce nouveau RFC ajoute à SSH un mécanisme pour négocier les extensions après l'échange de clés et le début de la communication sécurisée, une fois que tout est confidentiel.
Un mécanisme de négociation pour un protocole cryptographique est toujours délicat (comme TLS s'en est aperçu à plusieurs reprises). Si on n'en a pas, le client et le serveur perdent beaucoup de temps à faire des essais/erreurs « tiens, il vient de déconnecter brusquement, c'est peut-être qu'il n'aime pas SHA-512, réessayons avec SHA-1 ». Et ces deux mécanismes (négociation explicite et essai/erreur) ouvrent une nouvelle voie d'attaque, celle des attaques par repli, où l'Homme du Milieu va essayer de forcer les deux parties à utiliser des algorithmes de qualité inférieure. (La seule protection serait de ne pas discuter, de choisir des algorithmes forts et de refuser tout repli. En sécurité, ça peut aider d'être obtus.)
Notez aussi que la méthode essais/erreurs a un danger spécifique, car bien des machines SSH mettent en œuvre des mécanismes de limitation du trafic, voire de mise en liste noire, si la machine en face fait trop d'essais, ceci afin d'empêcher des attaques par force brute. D'où l'intérêt d'un vrai mécanisme de négociation (qui peut en outre permettre de détecter certaines manipulations par l'Homme du Milieu).
Vue par tshark, voici
le début d'une négociation SSH, le message
SSH_MSG_KEXINIT
, normalisé dans le RFC 4253, section 7.1 :
SSH Protocol SSH Version 2 Packet Length: 1332 Padding Length: 5 Key Exchange Message Code: Key Exchange Init (20) Algorithms Cookie: 139228cb5cfbf6c97d6b74f6ae99453d kex_algorithms length: 196 kex_algorithms string: curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,ext-info-c server_host_key_algorithms length: 290 server_host_key_algorithms string [truncated]: ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-s encryption_algorithms_client_to_server length: 150 encryption_algorithms_client_to_server string: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc encryption_algorithms_server_to_client length: 150 encryption_algorithms_server_to_client string: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc mac_algorithms_client_to_server length: 213 mac_algorithms_client_to_server string [truncated]: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-2 mac_algorithms_server_to_client length: 213 mac_algorithms_server_to_client string [truncated]: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-2 compression_algorithms_client_to_server length: 26 compression_algorithms_client_to_server string: none,zlib@openssh.com,zlib compression_algorithms_server_to_client length: 26 compression_algorithms_server_to_client string: none,zlib@openssh.com,zlib languages_client_to_server length: 0 languages_client_to_server string: [Empty] languages_server_to_client length: 0 languages_server_to_client string: [Empty] KEX First Packet Follows: 0 Reserved: 00000000 Padding String: 0000000000
Le message était en clair (c'est pour cela que tshark a pu le décoder). L'autre machine envoie un message équivalent. Suite à cet échange, les deux machines sauront quels algorithmes sont disponibles.
Notre RFC 8308 (section 2.1) ajoute à ce message le nouveau
mécanisme d'extension. Pour préserver la compatibilité avec les
anciennes mises en œuvre de SSH, et comme il n'y a pas de place
« propre » disponible dans le message, le nouveau mécanisme se
place dans la liste des algorithmes cryptographiques (champ
kex_algorithms
, celui qui commence par
curve25519-sha256@libssh.org,ecdh-sha2-nistp256…
). On
ajoute à cette liste ext-info-c
si on est
client et ext-info-s
si on est serveur. (Le
nom est différent dans chaque direction, pour éviter que les deux
parties ne se disent « cool, cet algorithme est commun,
utilisons-le ».) Vous
pouvez donc savoir si votre SSH gère le nouveau mécanisme en
capturant ce premier paquet SSH et en examinant la liste des
algorithmes d'échange de clé. (C'était le cas ici, avec
OpenSSH 7.2. Vous l'aviez repéré ?)
Une fois qu'on a proposé à son pair d'utiliser le nouveau
mécanisme de ce RFC 8308, on peut s'attendre, si le pair
est d'accord, à recevoir un message SSH d'un nouveau type,
SSH_MSG_EXT_INFO
, qui sera, lui, chiffré. Il contient la liste des
extensions gérées avec ce mécanisme. Notez qu'il peut être envoyé
plusieurs fois, par exemple avant et après l'authentification du
client, pour le cas d'un serveur timide qui ne voulait pas révéler
sa liste d'extensions avant l'authentification. Ce nouveau type de
message figure désormais dans le
registre des types de message, avec le numéro 7.
La section 3 définit les quatre extensions actuelles (un registre accueillera les extensions futures) :
server-sig-algs
donne la
liste des algorithmes de signature acceptés
par le serveur.delay-compression
indique les
algorithmes de compression acceptés. Il y
en a deux, un du client vers le serveur et un en sens
inverse. Ils étaient déjà indiqués dans le message
SSH_MSG_KEXINIT
, dans les champs
compression_algorithms_client_to_server
et
compression_algorithms_server_to_client
mais, cette fois, ils sont transmis dans un canal sécurisé
(confidentiel et authentifié).no-flow-control
, dont le nom indique
bien la fonction.elevation
sert aux
systèmes d'exploitation qui ont un
mécanisme d'élévation des privilèges (c'est le cas de
Windows, comme documenté dans le
blog de Microsoft).Les futures extensions, après ces quatre-là, nécessiteront un examen par l'IETF, via un RFC IETF (cf. RFC 8126, politique « IETF review »). Elle seront placées dans le registre des extensions.
Quelques petits problèmes de déploiement et d'incompatibilité
ont été notés avec ce nouveau
mécanisme d'extensions. Par exemple OpenSSH 7.3 et 7.4 gérait
l'extension server-sig-algs
mais n'envoyait
pas la liste complète des algorithmes acceptés. Un client qui
considérait cette liste comme ferme et définitive pouvait donc
renoncer à utiliser certains algorithmes qui auraient pourtant
marché. Voir ce
message pour une explication détaillée.
Autre gag, les valeurs des extensions peuvent contenir des octets nuls et un logiciel maladroit qui les lirait comme si c'était des chaînes de caractères en C aurait des problèmes. C'est justement ce qui est arrivé à OpenSSH, jusqu'à la version 7.5 incluse, ce qui cassait brutalement la connexion. Le RFC conseille de tester la version du pair, et de ne pas utiliser les extensions en cause si on parle à un OpenSSH ≤ 7.5.
Aujourd'hui, ce mécanisme d'extension est mis en œuvre dans OpenSSH, Bitvise SSH, AsyncSSH et SmartFTP. (Cf. ce tableau de comparaison, mais qui n'est pas forcément à jour.)
Merci à Manuel Pégourié-Gonnard pour avoir détecté une erreur dans la première version.
Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : C. Bormann (Universitaet Bremen
TZI)
Chemin des normes
Première rédaction de cet article le 3 janvier 2018
Il existe une norme pour un préfixe de chemin dans un
URI, préfixe nommée
.well-known
, et après lequel plusieurs noms
sont normalisés, pour des ressources « bien connues »,
c'est-à-dire auxquelles on peut accéder sans lien qui y mène. Le
RFC 8615 normalise ce
.well-known
. Son prédécesseur n'était prévu à l'origine que
pour les plans http:
et
https:
. Ce très court
RFC l'a étendu aux plans
ws:
et wss:
, ceux des
Web sockets du RFC 6455. Depuis, notre RFC a été intégré dans la nouvelle
norme de .well-known
, le RFC 8615.
Les gens de CoAP avaient déjà étendu
l'usage de .well-known
en permettant (RFC 7252) qu'il soit utilisé pour les plans
coap:
et coaps:
.
Il existe un registre IANA des
suffixes (les termes après .well-known
). Ce
registre est le même quel que soit le plan d'URI utilisé. Il ne
change donc pas suite à la publication de ce RFC.
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : D. Schinazi, T. Pauly (Apple)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF v6ops
Première rédaction de cet article le 21 décembre 2017
Une machine connectée à l'Internet et répondant aux requêtes venues du réseau a souvent plusieurs adresses IP pour son nom. C'est parfois une adresse IPv4 et une IPv6 mais cela peut aussi être plusieurs adresses IPv6, ou bien un mélange en proportions quelconques. Les développeurs d'application et les administrateurs système qui déploieront ces applications ensuite, ont un choix difficile si certaines de ces adresses marchent et d'autres pas (ou mal). Si les différentes adresses IP de cette machine passent par des chemins différents, certains marchant et d'autres pas, l'application arrivera-t-elle à se rabattre sur une autre adresse très vite ou bien imposera-t-elle à l'utilisateur un long délai avant de détecter enfin le problème ? Cette question est connue comme « le bonheur des globes oculaires » (les dits globes étant les yeux de l'utilisateur qui attend avec impatience la page d'accueil de PornHub) et ce RFC spécifie les exigences pour l'algorithme de connexion du client. En les suivant, les globes oculaires seront heureux. Il s'agit de la version 2 de l'algorithme, bien plus élaborée que la version 1 qui figurait dans le RFC 6555.
La section 1 rappelle les données du problème : on veut évidemment que cela marche aussi bien en IPv6 (RFC 8200) qu'en IPv4 (pas question d'accepter des performances inférieures) or, dans l'état actuel du déploiement d'IPv6, bien des sites ont une connexion IPv6 totalement ou partiellement cassée. Si un serveur a IPv4 et IPv6 et que son client n'a qu'IPv4, pas de problème. Mais si le client a IPv6, tente de l'utiliser, mais que sa connexion est plus ou moins en panne, ou simplement sous-optimale, ses globes oculaires vont souffrir d'impatience. On peut aussi noter que le problème n'est pas spécifique à IPv6 : du moment que la machine visée a plusieurs adresses, qu'elles soient IPv4 ou IPv6, le risque que certaines des adresses ne marchent pas (ou moins bien) existe, et l'algorithme des globes oculaires heureux doit être utilisé. (C'est un des gros changements avec le précédent RFC, le RFC 6555, qui n'envisageait que le cas IPv6.)
La bonne solution est donc que l'application elle-même gère le problème (ou, sinon l'application elle-même, la bibliothèque logicielle qu'elle utilise et où se trouve la fonction de connexion). Il existe plusieurs algorithmes pour cela, déjà largement déployés depuis des années. On peut donc se baser sur l'expérience pour spécifier ces algorithmes. Ce RFC normalise les caractéristiques que doivent avoir ces algorithmes. Si on suit ce RFC, le trafic (IP et DNS) va légèrement augmenter (surtout si la connectivité IPv6 marche mal ou pas du tout) mais la qualité du vécu de l'utilisateur va être maintenue, même en présence de problèmes, ce qui compense largement. Autrement, il existerait un risque élevé que certains utilisateurs coupent complètement IPv6, plutôt que de supporter ces problèmes de délai de connexion.
La cible principale de notre RFC est composée des protocoles de transport avec connexion (TCP, SCTP), les protocoles sans connexion comme UDP soulevant d'autres questions (s'ils ont une sémantique requête/réponse, comme dans ICE, les algorithmes de ce RFC peuvent être utilisés).
Donc, on a un nom de machine qu'on veut contacter, mettons
www.example.com
, avec plusieurs adresses
associées, peut-être de familles (v4 et v6) différentes. Prenons
une machine ayant une seule adresse IPv4 et une seule adresse
IPv6, avec une connexion IPv6 qui marche mal. Avec l'algorithme
naïf qu'utilisent encore certains logiciels voici la séquence
d'évenements traditionnelle :
192.0.2.1
et
2001:db8::1
./etc/gai.conf
). L'initiateur envoie un paquet
TCP SYN
à
2001:db8::1
.SYN
à 192.0.2.1
.SYN+ACK
en échange,
l'initiateur réplique par un ACK
et la connexion
TCP est établie.Le problème de cet algorithme naïf est donc la longue attente lors des essais IPv6. On veut au contraire un algorithme qui bascule rapidement en IPv4 lorsqu'IPv6 ne marche pas, sans pour autant gaspiller les ressources réseau en essayant par exemple toutes les adresses en même temps.
L'algorithme recommandé (sections 3 à 5, cœur de ce RFC) aura donc l'allure suivante :
192.0.2.1
et
2001:db8::1
. Il sait donc qu'il a plusieurs
adresses, de famille différente.SYN
à
2001:db8::1
, avec un très court délai de garde.SYN
à 192.0.2.1
.SYN+ACK
en échange,
l'initiateur réplique par un ACK
et la connexion
TCP est établie.Si le répondeur réagit à une vitesse normale en IPv6, la connexion sera établie en IPv6. Sinon, on passera vite en IPv4, et l'utilisateur humain ne s'apercevra de rien. Naturellement, si le DNS n'avait rapporté qu'une seule adresse (v4 ou v6), on reste à l'algorithme traditionnel (« essayer, patienter, ré-essayer »).
Maintenant, les détails. D'abord, le DNS (section 3 de notre RFC). Pour récupérer les adresses appartenant aux deux familles (IPv4 et IPv6), il faut envoyer deux requêtes, de type A et AAAA. Pas de délai entre les deux, et le AAAA en premier, recommande le RFC. Notez qu'il n'existe pas de type de requête DNS pour avoir les deux enregistrements d'un coup, il faut donc deux requêtes.
Il ne faut pas attendre d'avoir la réponse aux deux avant de commencer à tenter d'établir une connexion. En effet, certains pare-feux configurés avec les pieds bloquent les requêtes AAAA, qui vont finir par timeouter. Du point de vue du programmeur, cela signifie qu'il faut faire les deux requêtes DNS dans des fils différents (ou des goroutines différentes en Go), ou bien, utiliser une API asynchrone, comme getdns. Ensuite, si on reçoit la réponse AAAA mais pas encore de A, on essaye tout de suite de se connecter, si on a la réponse A, on attend quelques millisecondes la réponse AAAA puis, si elle ne vient pas, tant pis, on essaie en IPv4. (La durée exacte de cette attente est un des paramètres réglables de l'algorithme. Il se nomme Resolution Delay et sa valeur par défaut recommandée est de 50 ms.)
À propos de DNS, notez que le RFC recommande également de privilégier IPv6 pour le transport des requêtes DNS vers les résolveurs (on parle bien du transport des paquets DNS, pas du type des données demandées). Ceci dit, ce n'est pas forcément sous le contrôle de l'application.
Une fois récupérées les adresses, on va devoir les trier selon l'ordre de préférence. La section 4 décrit comment cela se passe. Rappelons qu'il peut y avoir plusieurs adresses de chaque famille, pas uniquement une v4 et une v6, et qu'il est donc important de gérer une liste de toutes les adresses reçues (imaginons qu'on ne récupère que deux adresses v4 et aucune v6 : l'algorithme des globes oculaires heureux est quand même crucial car il est parfaitement possible qu'une des adresses v4 ne marche pas).
Pour trier, le RFC recommande de suivre les règles du RFC 6724, section 6. Si le client a un état (une mémoire des connexions précédentes, ce qui est souvent le cas chez les clients qui restent longtemps à tourner, un navigateur Web, par exemple), il peut ajouter dans les critères de tri le souvenir des succès (ou échecs) précédents, ainsi que celui des RTT passés. Bien sûr, un changement de connectivité (détecté par le DNA des RFC 4436 ou RFC 6059) doit entraîner un vidage complet de l'état (on doit oublier ce qu'on a appris, qui n'est plus pertinent).
Dernier détail sur le tri : il faut mêler les adresses des deux familles. Imaginons un client qui récupère trois adresses v6 et trois v4, client qui donne la priorité à IPv4, mais dont la connexion IPv4 est défaillante. Si sa liste d'adresses à tester comprend les trois adresses v4 en premier, il devra attendre trois essais avant que cela ne marche. Il faut donc plutôt créer une liste {une adressse v4, une adresse v6, une adresse v4…}. Le nombre d'adresses d'une famille à inclure avant de commencer l'autre famille est le paramètre First Address Family Count, et il vaut un par défaut.
Enfin, on essaie de se connecter en envoyant des paquets
TCP SYN (section 5). Il est important de ne pas tester IPv4 tout de suite. Les
premiers algorithmes « bonheur des globes oculaires » envoyaient
les deux paquets SYN
en même temps, gaspillant des ressources réseau et
serveur. Ce double essai faisait que les équipements IPv4 du réseau
avaient autant de travail qu'avant, alors qu'on aurait souhaité les
retirer du service petit à petit. En outre, ce test simultané fait
que, dans la moitié des cas, la connexion sera établie en IPv4,
empêchant de tirer profit des avantages d'IPv6 (cf. RFC 6269). Donc, on doit tester en IPv6
d'abord, sauf si on se souvient des tentatives précédentes (voir
plus loin la variante « avec état ») ou bien si l'administrateur
système a délibérement configuré la machine pour préférer IPv4.
Après chaque essai, on attend pendant une durée paramétrable, Connection Attempt Delay, 250 ms par défaut (bornée par les paramètres Minimum Connection Attempt Delay, 100 ms par défaut, qu'on ne devrait jamais descendre en dessous de 10 ms, et Maximum Connection Attempt Delay, 2 s par défaut).
L'avantage de cet algorithme « IPv6 d'abord puis rapidement basculer en IPv4 » est qu'il est sans état : l'initiateur n'a pas à garder en mémoire les caractéristiques de tous ses correspondants. Mais son inconvénient est qu'on recommence le test à chaque connexion. Il existe donc un algorithme avec état (cf. plus haut), où l'initiateur peut garder en mémoire le fait qu'une machine (ou bien un préfixe entier) a une adresse IPv6 mais ne répond pas aux demandes de connexion de cette famille. Le RFC recommande toutefois de re-essayer IPv6 au moins toutes les dix minutes, pour voir si la situation a changé.
Une conséquence de l'algorithme recommandé est que, dans
certains cas, les deux connexions TCP (v4 et
v6) seront établies (si le SYN
IPv6 voyage
lentement et que la réponse arrive après que l'initiateur de la
connexion se soit impatienté et soit passé à IPv4). Cela peut être
intéressant dans certains cas rares, mais le RFC recommande plutôt
d'abandonner la connexion perdante (la deuxième). Autrement, cela
pourrait entraîner des problèmes avec, par exemple, les sites Web
qui lient un cookie à l'adresse IP du
client, et seraient surpris de voir deux connexions avec des
adresses différentes.
La section 9 du RFC rassemble quelques derniers problèmes
pratiques. Par exemple, notre algorithme des globes oculaires
heureux ne prend en compte que l'établissement de la connexion. Si
une adresse ne marche pas du tout, il choisira rapidement la
bonne. Mais si une adresse a des problèmes de
MTU et pas l'autre, l'établissement de la
connexion, qui ne fait appel qu'aux petits paquets TCP
SYN
, se passera bien alors que le reste de
l'échange sera bloqué. Une solution possible est d'utiliser
l'algorithme du RFC 4821.
D'autre part, l'algorithme ne tient compte que de la possibilité d'établir une connexion TCP, ce qui se fait typiquement uniquement dans le noyau du système d'exploitation du serveur. L'algorithme ne garantit pas qu'une application écoute, et fonctionne.
Parmi les problèmes résiduels, notez que l'algorithme des globes oculaires heureux est astucieux, mais tend à masquer les problèmes (section 9.3). Si un site Web publie les deux adresses mais que sa connectivité IPv6 est défaillante, aucun utilisateur ne lui signalera puisque, pour eux, tout va bien. Il est donc recommandé que l'opérateur fasse des tests de son côté pour repérer les problèmes (le RFC 6555 recommandait que le logiciel permette de débrayer cet algorithme, afin de tester la connectivité avec seulement v4 ou seulement v6, ou bien que le logiciel indique quelque part ce qu'il a choisi, pour mieux identifier d'éventuels problèmes v6.)
Pour le délai entre le premier SYN
IPv6 et le
premier SYN
IPv4, la section 5 donne des
idées quantitatives en suggérant 250 ms entre deux essais. C'est
conçu pour être quasiment imperceptible à un
utilisateur humain devant son navigateur Web, tout en évitant de
surcharger le réseau inutilement. Les algorithmes avec état ont le
droit d'être plus impatients, puisqu'ils peuvent se souvenir des
durées d'établissement de connexion précédents.
Notez que les différents paramètres réglables indiqués ont des valeurs par défaut, décrites en section 8, et qui ont été déterminées empiriquement.
Si vous voulez une meilleure explication de la version 2 des globes oculaires heureux, il y a cet exposé au RIPE.
Enfin, les implémentations. Notez que les vieilles mises en œuvre du RFC 6555 (et présentées à la fin de mon précédent article) sont toujours conformes à ce nouvel algorithme, elles n'en utilisent simplement pas les raffinements. Les versions récentes de macOS (Sierra) et iOS (10) mettent en œuvre notre RFC, ce qui est logique, puisqu'il a été écrit par des gens d'Apple (l'annonce est ici, portant même sur des versions antérieures). Apple en a d'ailleurs profité pour breveter cette technologie. À l'inverse, un exemple récent de logiciel incapable de gérer proprement le cas d'un pair ayant plusieurs adresses IP est Mastodon (cf. bogue #3762.)
Dans l'annexe A, vous trouverez la liste complète des importants changements depuis le RFC 6555. Le précédent RFC n'envisageait qu'un seul cas, deux adresses IP, une en v4, l'autre en v6. Notre nouveau RFC 8305 est plus riche, augmente le parallélisme, et ajoute :
Date de publication du RFC : Février 2018
Auteur(s) du RFC : G. Fairhurst, T. Jones (University of Aberdeen)
Pour information
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 8 février 2018
Le RFC 8303, du groupe de travail TAPS, décrit les fonctions et services rendus par les protocoles de transport aux applications. Ce RFC 8304 se focalise sur deux protocoles sans connexion, UDP et UDP-Lite. Quels services fournissent-ils ?
UDP est normalisé dans le RFC 768 et UDP-Lite dans le RFC 3828. Tous les deux permettent l'envoi et la réception de datagrammes, sans connexion préalable, sans garantie d'être prévenu en cas de perte du datagramme, sans garantie qu'ils soient distribués dans l'ordre. Une application qui utilise UDP va donc devoir connaitre les services exacts que ce protocole fournit. Aujourd'hui, de nombreuses applications utilisent UDP, l'une des plus connues étant sans doute le DNS, sans compter celles qui utilisent un autre protocole de transport mais qui l'encapsulent dans UDP pour passer les boitiers intermédiaires (cf. par exemple RFC 8261). UDP-Lite est très proche d'UDP et ajoute juste la possibilité de distribuer à l'application des paquets potentiellement endommagés. Pour utiliser UDP intelligement, les programmeurs ont tout intérêt à consulter le RFC 8085.
L'API la plus courante est
l'API dite « socket », normalisée par
POSIX, et dont la meilleure documentation
est évidemment le livre de
Stevens. Les applications peuvent envoyer des données avec
send()
,
sendto()
et
sendmsg()
, en recevoir avec
recvfrom()
et
recvmsg()
. Elles peuvent être
dans l'obligation de configurer certaines informations de bas
niveau comme la permission de fragmenter ou
pas, alors que TCP gère cela tout seul.
Des options existent dans l'API socket pour
définir ces options.
Notre RFC 8304 suit la démarche du RFC 8303 pour la description d'un protocole de transport. Plus précisement, il suite la première passe, celle de description de ce que sait faire le protocole.
Donc, pour résumer (section 3 du RFC), UDP est décrit dans le RFC 768, et UDP-Lite dans le RFC 3828. Tous les deux fournissent un service de datagramme, non connecté, et qui préserve les frontières de message (contrairement à TCP, qui envoie un flot d'octets). Le RFC 768 donne quelques idées sur l'API d'UDP, il faut notamment que l'application puisse écouter sur un port donné, et puisse choisir le port source d'envoi.
UDP marche évidemment également sur IPv6, et une extension à l'API socket pour IPv6 est décrite dans le RFC 3493.
UDP est très basique : aucun contrôle de congestion (le RFC 8085 détaille ce point), pas de retransmission des paquets perdus, pas de notification des pertes, etc. Tout doit être fait par l'application. On voit que, dans la grande majorité des cas, il vaut mieux utiliser TCP. Certains développeurs utilisent UDP parce qu'ils ont lu quelque part que c'était « plus rapide » mais c'est presque toujours une mauvaise idée.
Notre RFC décrit ensuite les primitives UDP utilisables par les applications :
CONNECT
est très différent de celui
de TCP. C'est une opération purement locale (aucun paquet n'est
envoyé sur le réseau), qui associe des ports source et
destination à une prise (socket), c'est
tout. Une fois cette opération faite, on pourra envoyer ou
recevoir des paquets. Comme CONNECT
,
CLOSE
n'a d'effet que local (pas de
connexion, donc pas de fermeture de connexion).SEND
et RECEIVE
,
dont le nom indique bien ce qu'elles font.SET_IP_OPTIONS
qui permet d'indiquer
des options IP, chose qui n'est en général pas nécessaire en TCP
mais peut être utile en UDP.SET_DF
est important car il permet de
contrôler la fragmentation des
paquets. TCP n'en a pas besoin car la MSS
est négociée au début de la connexion. Avec UDP, c'est le
programmeur de l'application qui doit choisir s'il laissera le
système d'exploitation et les routeurs (en
IPv4 seulement, pour les routeurs)
fragmenter, ce qui est risqué, car pas mal de
pare-feux mal configurés bloquent les
fragments, ou bien s'il refusera la fragmentation (DF =
Don't Fragment) et devra donc faire attention
à ne pas envoyer des paquets trop gros. (Voir aussi les RFC 1191 et RFC 8201 pour
une troisième possibilité.)SET_TTL
(IPv4) et
SET_IPV6_UNICAST_HOPS
permettent de définir
le TTL des paquets IP envoyés. (Le terme
de TTL est objectivement incorrect, et est devenu
hops en IPv6, mais c'est le plus utilisé.)SET_DSCP
permet de définir la qualité
de service souhaitée (RFC 2474).SET_ECN
permet de dire qu'on va
utiliser ECN (RFC 3168). Comme les précédentes, elle n'a pas de sens en
TCP, où le protocole de transport gère cela seul.Certaines primitives ont été exclues de la liste car n'étant plus d'actualité (cf. le RFC 6633).
Avec ça, on n'a parlé que d'UDP et pas d'UDP-Lite. Rappelons
qu'UDP-Lite (RFC 3828) est à 95 % de l'UDP
avec un seul changement, la possibilité que la somme de
contrôle ne couvre qu'une partie du paquet. Il faut
donc une primtive SET_CHECKSUM_COVERAGE
qui
indique jusqu'à quel octet le paquet est couvert par la somme de
contrôle.
Notez enfin que le RFC se concentre sur l'unicast mais, si cela vous intéresse, l'annexe A décrit le cas du multicast.
Date de publication du RFC : Février 2018
Auteur(s) du RFC : M. Welzl (University of Oslo), M. Tuexen (Muenster Univ. of Appl. Sciences), N. Khademi (University of Oslo)
Pour information
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 8 février 2018
La famille de protocoles TCP/IP dispose d'un grand nombre de protocoles de transport. On connait bien sûr TCP mais il y en a d'autres comme UDP et SCTP et d'autres encore moins connus. Cette diversité peut compliquer la tâche des développeurs d'application, qui ne savent pas toujours bien lequel choisir. Et elle pose également problème pour les développements futurs à l'IETF, par exemple la réflexion en cours sur les API. On a besoin d'une description claire des services que chaque protocole fournit effectivement. C'est le but du groupe de travail TAPS dont voici le deuxième RFC : une description de haut niveau des services que fournit la couche transport. À faire lire à tous les développeurs d'applications réseau, à tous les participants à la normalisation, et aux étudiants en réseaux informatiques.
Ce RFC parle de TCP, MPTCP, SCTP, UDP et UDP-Lite, sous l'angle « quels services rendent-ils aux applications ? » (QUIC, pas encore normalisé à l'époque, n'y figure pas. LEDBAT, qui n'est pas vraiment un protocole de transport, est par contre présent.) L'idée est de lister un ensemble d'opérations abstraites, qui pourront ensuite être exportées dans une API. J'en profite pour recommander la lecture du RFC 8095, premier RFC du groupe TAPS.
La section 1 de notre RFC décrit la terminologie employée. Il faut notamment distinguer trois termes :
Un exemple de programme qui permet d'essayer différents types
de prises (sockets) est socket-types.c
. Vous pouvez le compiler et le lancer en
indiquant le type de service souhaité. STREAM
va donner du TCP, SEQPACKET
du SCTP,
DATAGRAM
de l'UDP :
% make socket-types cc -Wall -Wextra socket-types.c -o socket-types % ./socket-types -v -p 853 9.9.9.9 STREAM 9.9.9.9 is an IP address "Connection" STREAM successful to 9.9.9.9 % ./socket-types -v -p 443 -h dns.bortzmeyer.org STREAM Address(es) of dns.bortzmeyer.org is(are): 2605:4500:2:245b::42 204.62.14.153 "Connection" STREAM successful to 2605:4500:2:245b::42 256 response bytes read
À première vue, l'API sockets nous permet
de sélectionner le service, indépendamment du protocole qui le
fournira. Mais ce n'est pas vraiment le cas. Si on veut juste un
flot de données, cela pourrait être fait avec TCP ou SCTP, mais
seul TCP sera sélectionné si on demande
STREAM
. En fait, les noms de services dns
l'appel à socket()
sont toujours
en correspondance univoque avec les protocoles de transport.
Autres termes importants :
La section 2 décrit le problème général. Actuellement, les applications sont liées à un protocole, même quand elles ne le souhaitent pas. Si une application veut que ses octets soient distribués dans l'ordre, et sans pertes, plusieurs protocoles peuvent convenir, et il n'est pas forcément utile de se limiter à seulement TCP, par exemple. Pour avancer vers un moindre couplage entre application et protocole, ce RFC décrit les fonctions et les services, un futur RFC définira des services minimaux, un autre peut-être une API.
Le RFC essaie de s'en tenir au minimum, des options utiles mais pas indispensables (comme le fait pour l'application de demander à être prévenue de certains changements réseau, cf. RFC 6458) ne sont pas étudiées.
Le RFC procède en trois passes : la première (section 3 du RFC)
analyse et explique ce que chaque protocole permet, la seconde (section 4) liste les
primitives et événements possibles, la troisième (section 5) donne
pour chaque fonction les protocoles qui la mettent en œuvre. Par
exemple, la passe 1 note que TCP fournit à l'application un moyen
de fermer une connexion en cours, la passe 2 liste une primitive
CLOSE.TCP
, la passe 3 note que TCP et SCTP
(mais pas UDP) fournissent cette opération de fermeture (qui fait
partie des fonctions de base d'un protocole orienté connexion,
comme TCP). Bon, d'accord, cette méthode est un peu longue et elle rend le RFC pas
amusant à lire mais cela permet de s'assurer que rien n'a été
oublié.
Donc, section 3 du RFC, la passe 1. Si vous connaissez bien tous ces
protocoles, vous pouvez sauter cette section. Commençons par le
protocole le plus connu, TCP (RFC 793). Tout le monde sait que c'est un
protocole à connexion, fournissant un transport fiable et ordonné
des données. Notez que la section 3.8 de la norme TCP décrit,
sinon une API, du moins l'interface (les primitives) offertes aux
applications. Vous verrez que les noms ne correspondent pas du
tout à celle de l'API sockets
(OPEN
au lieu de
connect/listen
, SEND
au lieu de
write
, etc).
TCP permet donc à l'application d'ouvrir une connexion (activement - TCP se connecte à un TCP distant - ou passivement - TCP attend les connexions), d'envoyer des données, d'en recevoir, de fermer la connexion (gentiment - TCP attend encore les données de l'autre - ou méchamment - on part en claquant la porte). Il y a aussi des événements, par exemple la fermeture d'une connexion déclenchée par le pair, ou bien une expiration d'un délai de garde. TCP permet également de spécifier la qualité de service attendue (cf. RFC 2474, et la section 4.2.4.2 du RFC 1123). Comme envoyer un paquet IP par caractère tapé serait peu efficace, TCP permet, pour les sessions interactives, genre SSH, d'attendre un peu pour voir si d'autres caractères n'arrivent pas (algorithme de Nagle, section 4.2.3.4 du RFC 1123). Et il y a encore d'autres possibilités moins connues, comme le User Timeout du RFC 5482. Je vous passe des détails, n'hésitez pas à lire le RFC pour approfondir, TCP a d'autres possibilités à offrir aux applications.
Après TCP, notre RFC se penche sur MPTCP. MPTCP crée plusieurs connexions TCP vers le pair, utilisant des adresses IP différentes, et répartit le trafic sur ces différents chemins (cf. RFC 6182). Le modèle qu'il présente aux applications est très proche de celui de TCP. MPTCP est décrit dans les RFC 6824 et son API est dans le RFC 6897. Ses primitives sont logiquement celles de TCP, plus quelques extensions. Parmi les variations par rapport à TCP, la primitive d'ouverture de connexion a évidemment une option pour activer ou pas MPTCP (sinon, on fait du TCP normal). Les extensions permettent de mieux contrôler quelles adresses IP on va utiliser pour la machine locale.
SCTP est le principal concurrent de TCP sur le créneau « protocole de transfert avec fiabilité de la distribution des données ». Il est normalisé dans le RFC 9260. Du point de vue de l'application, SCTP a trois différences importantes par rapport à TCP :
Il a aussi une terminologie différente, par exemple on parle d'associations et plus de connexions.
Certaines primitives sont donc différentes : par exemple,
Associate
au lieu du
Open
TCP. D'autres sont identiques, comme
Send
et Receive
. SCTP
fournit beaucoup de primitives, bien plus que TCP.
Et UDP ? Ce protocole sans connexion, et sans fiabilité, ainsi que sa variante UDP-Lite, est analysé dans un RFC séparé, le RFC 8304.
Nous passons ensuite à LEDBAT (RFC 6817). Ce n'est pas vraiment un protocole de transport, mais plutôt un système de contrôle de la congestion bâti sur l'idée « décroissante » de « on n'utilise le réseau que si personne d'autre ne s'en sert ». LEDBAT est pour les protocoles économes, qui veulent transférer des grandes quantités de données sans gêner personne. Un flot LEDBAT cédera donc toujours la place à un flot TCP ou compatible (RFC 5681). N'étant pas un protocole de transport, LEDBAT peut, en théorie, être utilisé sur les protocoles existants comme TCP ou SCTP (mais les extensions nécessaires n'ont hélas jamais été spécifiées). Pour l'instant, les applications n'ont pas vraiment de moyen propre d'activer ou de désactiver LEDBAT.
Bien, maintenant, passons à la deuxième passe (section 4 du RFC). Après la description des protocoles lors de la première passe, cette deuxième passe liste explicitement les primitives, rapidement décrites en première passe, et les met dans différentes catégories (alors que la première passe classait par protocole). Notons que certaines primitives, ou certaines options sont exclues. Ainsi, TCP a un mécanisme pour les données urgentes (RFC 793, section 3.7). Il était utilisé pour telnet (cf. RFC 854) mais n'a jamais vraiment fonctionné de manière satisfaisante et le RFC 6093 l'a officiellement exclu pour les nouvelles applications de TCP. Ce paramètre n'apparait donc pas dans notre RFC, puisque celui-ci vise le futur.
La première catégorie de primitives concerne la gestion de connexion. On y trouve d'abord les primitives d'établissement de connexion :
CONNECT.TCP
: établit une connexion
TCP,CONNECT.SCTP
: idem pour SCTP,CONNECT.MPTCP
: établit une connexion
MPTCP,CONNECT.UDP
: établit une « connexion »
UDP sauf, que, UDP étant sans connexion, il s'agit cette fois
d'une opération purement locale, sans communication avec une
autre machine.
Cette catégorie comprend aussi les primitives permettant de
signaler qu'on est prêt à recevoir une connexion,
LISTEN.TCP
, celles de maintenance de la
connexion, qui sont bien plus variées, et celles permettant de
mettre fin à la connexion (comme CLOSE.TCP
et
CLOSE.SCTP
, ou comme le signal qu'un délai de
garde a été dépassé, TIMEOUT.TCP
).
Dans les primitives de maintenance, on trouve aussi bien les
changements du délai de garde
(CHANGE_TIMEOUT.TCP
et
SCTP
), le débrayage de
l'algorithme de Nagle, pour TCP et SCTP,
l'ajout d'une adresse IP locale à l'ensemble des adresses
utilisables (ADD_PATH.MPTCP
et
ADD_PATH.SCTP
), la notification d'erreurs,
l'activation ou la désactivation de la somme de
contrôle (UDP seulement, la somme de contrôle étant
obligatoire pour les autres)… La plupart sont spécifiques à SCTP,
le protocole le plus riche.
La deuxième catégorie rassemble les primitives liées à la
transmission de données. On y trouve donc
SEND.TCP
(idem pour SCTP et UDP),
RECEIVE.TCP
(avec ses équivalents SCTP et
UDP), mais aussi les signalements d'échec comme
SEND_FAILURE.UDP
par exemple quand on a reçu
un message ICMP d'erreur.
Venons-en maintenant à la troisième passe (section 5 du RFC). On va cette fois lister les primitives sous forme d'un concept de haut niveau, en notant les protocoles auxquels elles s'appliquent. Ainsi, pour la gestion des connexions, on aura de quoi se connecter : une primitive Connect, qui s'applique à plusieurs protocoles (TCP, SCTP et UDP, même si Connect ne se réalise pas de la même façon pour tous ces protocoles). Et de quoi écouter passivement : une primitive Listen. Pour la maintenance, on aura une primitive Change timeout, qui s'applique à TCP et SCTP, une Disable Nagle qui s'applique à TCP et SCTP, une Add path qui a un sens pour MPTCP et SCTP, une Disable checksum qui ne marche que pour UDP, etc.
Pour l'envoi de données, on ne fusionne pas les opérations
d'envoi (SEND.PROTOCOL
) de la passe
précédente car elles ont une sémantique trop différente. Il y a
Reliably transfer data pour TCP, et
Reliably transfer a message pour SCTP. (Et bien
sûr Unreliably transfer a message avec UDP.)
Voilà, tout ça a l'air un peu abstrait mais ça acquerra davantage de sens quand on passera aux futures descriptions d'API (voir le RFC 8923).
L'annexe B du RFC explique la méthodologie suivie pour l'élaboration de ce document : coller au texte des RFC, exclure les primitives optionnelles, puis les trois passes dont j'ai parlé plus tôt. C'est cette annexe, écrite avant le RFC, qui avait servi de guide aux auteurs des différentes parties (il fallait des spécialistes SCTP pour ne pas faire d'erreurs lors de la description de ce protocole complexe…)
Notez (surtout si vous avez lu le RFC 8095) que TLS n'a pas été inclus, pas parce que ce ne serait pas un vrai protocole de transport (ça se discute) mais car il aurait été plus difficile à traiter (il aurait fallu des experts en sécurité).
Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : P. Quinn (Cisco), U. Elzur
(Intel), C. Pignataro (Cisco)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sfc
Première rédaction de cet article le 14 janvier 2018
Ce Network Service Header est un mécanisme concret pour faire passer sur le réseau les paquets destinés à une SF (Service Function, voir RFC 7665 pour l'architecture et les définitions). On colle un NSH, stockant plusieurs métadonnées, au paquet à traiter, on encapsule ce paquet à traiter et on l'envoie au dispositif de traitement via un réseau overlay. Et on fait l'opération inverse au retour. L'encapsulation peut se faire dans IP (par exemple avec GRE) ou dans un autre protocole.
Les métadonnées mises dans le NSH sont le résultat d'un processus de classification où le réseau décide ce qu'on va faire au paquet. Par exemple, en cas de dDoS, le classificateur décide de faire passer tous les paquets ayant telle adresse source par un équipement de filtrage plus fin, et met donc cette décision dans le NSH (section 7.1). Le NSH contient les informations nécessaires pour le SFC (Service Function Chain, RFC 7665). Sa lecture est donc très utile pour l'opérateur du réseau (elle contient la liste des traitements choisis, et cette liste peut permettre de déduire des informations sur le trafic en cours) et c'est donc une information plutôt sensible (voir aussi le RFC 8165).
Le NSH ne s'utilise qu'à l'intérieur de votre propre réseau (il n'offre, par défaut, aucune authentification et aucune confidentialité, voir section 8 du RFC). C'est à l'opérateur de prendre les mesures nécessaires, par exemple en chiffrant tout son trafic interne. Cette limitation à un seul domaine permet également de régler le problème de la fragmentation, si ennuyeux dès qu'on encapsule, ajoutant donc des octets. Au sein d'un même réseau, on peut contrôler tous les équipements et donc s'assurer que la MTU sera suffisante, ou, sinon, que la fragmentation se passera bien (section 5 du RFC).
Tout le projet SFC (dont c'est le troisième RFC) repose sur une vision de l'Internet comme étant un ensemble de middleboxes tripotant les paquets au passage, plutôt qu'étant un ensemble de machines terminales se parlant suivant le principe de bout en bout. C'est un point important à noter pour comprendre les débats au sein de l'IETF.
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : K. Oku (Fastly)
Expérimental
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 20 décembre 2017
Lorsqu'un serveur HTTP répond, la réponse contient souvent des liens vers d'autres ressources. Un exemple typique est celui de la page Web dont le chargement va déclencher le chargement de feuilles de style, de JavaScript, etc. Pour minimiser la latence, il serait intéressant de prévenir le client le plus tôt possible. C'est le but de ce RFC, qui décrit le code de retour intérimaire 103, qui prévient le client qu'il peut tout de suite commencer à charger ces ressources supplémentaires.
Il existe un type de lien pour cela,
preload
, décrit par ses auteurs et
enregistré dans le registre des types de
liens (cf. RFC 8288). Il peut être
utilisé dans la réponse « normale » :
HTTP/1.1 200 OK Date: Fri, 26 May 2017 10:02:11 GMT Content-Length: 1234 Content-Type: text/html; charset=utf-8 Link: </main.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script"
Mais cela ne fait pas gagner grand'chose : une toute petite fraction de seconde après, le client HTTP verra arriver le source HTML et pourra y découvrir les liens. On voudrait renvoyer tout de suite la liste des ressources à charger, sans attendre que le serveur ait fini de calculer la réponse (ce qui peut prendre du temps, s'il faut dérouler mille lignes de Java et plein de requêtes SQL…)
Le nouveau code de retour, 103, lui, peut être envoyé immédiatement, avec la liste des ressources. Le client peut alors les charger, tout en attendant le code de retour 200 qui indiquera que la ressource principale est prête. (Les codes de retour commençant par 1, comme 103, sont des réponses temporaires, « pour information », en attendant le succès, annoncé par un code commençant par 2. Cf. RFC 7231, sections 6.2 et 6.3.) La réponse HTTP utilisant le nouveau code ressemblera à :
HTTP/1.1 103 Early Hints Link: </main.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script" HTTP/1.1 200 OK Date: Fri, 26 May 2017 10:02:11 GMT Content-Length: 1234 Content-Type: text/html; charset=utf-8 Link: </main.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script"
Les détails, maintenant (section 2 du RFC). 103 indique au client qu'il y aura une série de liens vers des ressources supplémentaires qu'il peut être intéressant, par exemple, de charger tout de suite. Les liens finaux seront peut-être différents (dans l'exemple ci-dessus, ils sont identiques). 103 est juste une optimisation, pas une obligation. (Hint = suggestion.) Les liens qu'il indique ne font pas autorité. Le serveur peut indiquer des liens supplémentaires, ne pas indiquer des liens qui étaient dans la réponse 103, indiquer des liens différents, etc.
Il peut même y avoir plusieurs 103 à la suite, notamment si un relais sur le trajet ajoute le sien, par exemple en se basant sur une réponse qu'il avait gardée en mémoire. 103 n'est en effet pas toujours envoyé par le serveur d'origine de la ressource, il peut l'être par un intermédiaire. Voici un exemple qui donne une idée des variantes possibles :
HTTP/1.1 103 Early Hints Link: </main.css>; rel="preload"; as="style" HTTP/1.1 103 Early Hints Link: </style.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script" HTTP/1.1 200 OK Date: Fri, 26 May 2017 10:02:11 GMT Content-Length: 1234 Content-Type: text/html; charset=utf-8 Link: </main.css>; rel="preload"; as="style" Link: </newstyle.css>; rel="preload"; as="style" Link: </script.js>; rel="preload"; as="script"
On voit que la réponse finale n'est ni la première suggestion, ni la deuxième (ni une union des deux).
Note pour les programmeurs Python/WSGI. Je ne suis pas arrivé à utiliser ce code « intérimaire » avec WSGI, cela ne semble pas possible en WSGI. Mais on trouvera sans doute d'autres implémentations…
Le code 103 est désormais dans le registre IANA des codes de retour.
Date de publication du RFC : Janvier 2018
Auteur(s) du RFC : K. Nichols (Pollere), V. Jacobson, A. McGregor, J. Iyengar (Google)
Expérimental
Réalisé dans le cadre du groupe de travail IETF aqm
Première rédaction de cet article le 6 janvier 2018
Ah, la gestion des files d'attentes… Le cauchemar de plein d'étudiants en informatique. Et cela ne cesse pas quand ils deviennent ingénieurs et qu'il faut construire un routeur pour connecter des réseaux de capacités différentes, et qui aura donc besoin de files d'attente. Bref, dès qu'on n'a pas assez de ressources (et on n'en aura jamais assez), il faut optimiser ses files d'attente. Ce nouveau RFC décrit le mécanisme CoDel (mis en œuvre depuis un certain temps dans le noyau Linux) qui permet notamment de limiter le terrible, l'épouvantable bufferbloat.
L'algorithme naïf pour gérer une file d'attente est le suivant (on prend le cas simple d'un routeur qui n'a que deux interfaces et une seule file d'attente dans chaque direction) : les paquets arrivent au routeur et sont mis dans la file gérée en FIFO. Dès que des ressources suffisantes sont disponibles pour envoyer un paquet (dès que l'interface de sortie est libre), on envoie le paquet et on le retire donc de la file. Si un paquet arrive quand la file est pleine, on le jette : TCP détectera cette perte, réduira son rythme d'envoi, et réémettra les données manquantes.
Avec cet algorithme simpliste, il y a une décision importante à prendre, la taille de la file. Le trafic sur l'Internet est tout sauf constant : des périodes de grand calme succèdent à des pics de trafic impressionnants. Si la file d'attente est trop petite, on ne pourra pas écluser ces pics, et on jettera des paquets. Ça tombe bien, le prix des mémoires baisse, on va pouvoir mettre des files plus grandes, non ? Eh bien non car arrive le fameux bufferbloat. Si la file est trop grande, les paquets y séjourneront très longtemps, et on augmentera ainsi la latence, au grand dam des applications sensibles comme SSH ou les jeux en ligne. Bref, on est coincés, il n'y a pas de solution idéale. Pire, si on ne jette des paquets que lorsque la file est pleine, on risque de tomber dans le cas où l'équilibre se fait avec une file d'attente toujours pleine, et donc avec une mauvaise latence.
Bien sûr, il y a longtemps que les routeurs n'utilisent plus d'algorithme aussi naïf que celui présenté ici. Tout un travail a été fait sur l'AQM : voir par exemple les RFC 7567 et RFC 8033. Mais le problème de la file d'attente toujours pleine est toujours là. Sa première description est dans le RFC 896 en 1984. Plus récemment, on le trouve mentionné dans une présentation de Jim Gettys à l'IETF 80 (« Bufferbloat: Dark Buffers in the Internet ») développé dans un article dans Communications of the ACM (Gettys a beaucoup fait pour la prise de conscience des dangers du bufferbloat.).
Résoudre le problème de l'« obésité du
tampon » (bufferbloat) en
réduisant la taille des files d'attentes ne serait pas une
solution : les tampons sont là pour une bonne raison, pour absorber
les problèmes brefs, lorsque, temporairement,
on reçoit davantage de paquets que ce que l'on peut transmettre. Le
fait que des files plus petites ne sont pas une solution a déjà été
exposé dans le RFC 2309, dans
« A Rant on
Queues » de Van Jacobson,
dans le rapport « RED
in a Different Light » et dans l'article fondateur
de CoDel, « Controlling
Queue Delay » (article complet sur
Sci-Hub, cherchez
10.1145/2209249.2209264
). Le problème n'est
pas tant la taille de la file en soi (ce n'est pas la taille qui
compte), mais si c'est une « bonne » file ou une « mauvaise » file
(au passage, si vous ne connaissez pas la différence entre le bon
chasseur et le mauvais chasseur, ne ratez pas
l'indispensable sketch des
Inconnus).
CoDel est donc une vieille idée. Elle veut répondre, entre autres, aux critères suivants (section 1 du RFC) :
Lorsque CoDel estime nécessaire de prendre des mesures (le trafic entre trop vite), il peut jeter les paquets, ou les marquer avec ECN (RFC 3168).
La section 2 de notre RFC définit la terminologie de CoDel. Parmi les termes importants :
Passons maintenant à une description de haut niveau de CoDel. Son but est de différencier la mauvaise file (qui ne fait qu'ajouter du retard d'acheminement des paquets) de la bonne. Une file d'attente se forme lorsqu'il y a un goulet d'étranglement, parce qu'un lien à forte capacité se déverse dans un lien à faible capacité, ou bien parce que plusieurs liens convergent vers un lien ayant la capacité de seulement l'un d'eux. Une notion importante à ce sujet est celle de BDP, en gros le nombre d'octets en transit pour une connexion donnée, lorsque le débit atteint 100 % de la capacité. Imaginons une connexion TCP dont la fenêtre d'envoi est de 25 paquets (je sais bien que les fenêtres TCP se calculent en octets, pas en paquets, mais c'est le RFC qui fait cet abus de langage, pas moi) et où le BDP est de 20 paquets. En régime permanent, il y aura 5 paquets dans la file d'attente. Si la fenêtre est de 30 paquets, ce seront 10 paquets qui occuperont en permanence la file d'attente, augmentant encore le délai, alors que le débit ne changera pas (20 paquets arriveront par RTT). Au contraire, si on réduit la fenêtre à 20 paquets, la file d'attente sera vide, le délai sera réduit au minimum, alors que le débit n'aura pas changé. Ce résultat contre-intuitif montre que bourrer la connexion de paquets n'est pas forcément le meilleur moyen d'aller « vite ». Et que la taille de la file ne renseigne pas sur le rythme d'envoi des données. Et enfin que les files pleines en permanence n'apportent que du délai. Dans le premier exemple, la file contenant cinq paquets tout le temps était clairement une mauvaise file. Un tampon d'entrée/sortie de 20 paquets est pourtant une bonne chose (pour absorber les variations brutales) mais, s'il ne se vide pas complètement ou presque en un RTT, c'est qu'il est mal utilisé. Répétons : Les bonnes files se vident vite.
CoDel comporte trois composants : un estimateur, un objectif et une boucle de rétroaction. La section 3 de notre RFC va les présenter successivement. Pour citer l'exposé de Van Jacobson à une réunion IETF, ces trois composants sont :
D'abord, l'estimateur. C'est la partie de CoDel qui observe la file d'attente et en déduit si elle est bonne ou mauvaise. Autrefois, la principale métrique était la taille de la file d'attente. Mais celle-ci peut varier très vite, le trafic Internet étant très irrégulier. CoDel préfère donc observer le temps de séjour dans la file d'attente. C'est une information d'autant plus essentielle qu'elle a un impact direct sur le vécu de l'utilisateur, via l'augmentation de la latence.
Bon, et une fois qu'on observe cette durée de séjour, comment en déduit-on que la file est bonne ou mauvaise ? Notre RFC recommande de considérer la durée de séjour minimale. Si elle est nulle (c'est-à-dire si au moins un paquet n'a pas attendu du tout dans la file, pendant la dernière période d'observation), alors il n'y a pas de file d'attente permanente, et la file est bonne.
Le RFC parle de « période d'observation ». Quelle doit être la longueur de cette période ? Au moins un RTT pour être sûr d'écluser les pics de trafic, mais pas moins pour être sûr de détecter rapidement les mauvaises files. Le RFC recommande donc de prendre comme longueur le RTT maximal de toutes les connexions qui empruntent ce tampon d'entrée/sortie.
Et, petite astuce d'implémentation (un routeur doit aller
vite, et utilise souvent des circuits de
calcul plus simples qu'un processeur généraliste), on peut calculer
la durée de séjour minimale avec une seule variable : le temps écoulé
depuis lequel la durée de séjour est inférieure ou supérieure au
seuil choisi. (Dans le pseudo-code de la section 5, et dans le
noyau Linux, c'est à peu près le rôle de first_above_time
.)
Si vous aimez les explications avec images, il y en a plein dans cet excellent exposé à l'IETF.
Ensuite, l'objectif (appelé également référence) : il s'agit de fixer un objectif de durée de séjour dans la file. Apparemment, zéro serait l'idéal. Mais cela entrainerait des « sur-réactions », où on jetterait des paquets (et ralentirait TCP) dès qu'une file d'attente se forme. On va plutôt utiliser un concept dû à l'inventeur du datagramme, Leonard Kleinrock, dans « An Invariant Property of Computer Network Power », la « puissance » (power). En gros, c'est le débit divisé par le délai et l'endroit idéal, sur la courbe de puissance, est en haut (le maximum de débit, pour le minimum de délai). Après une analyse que je vous épargne, le RFC recommande de se fixer comme objectif entre 5 et 10 % du RTT.
Enfin, la boucle de rétroaction. Ce n'est pas tout d'observer, il faut agir. CoDel s'appuie sur la théorie du contrôle, pour un système MIMO (Multi-Input Multi-Output). Au passage, si vous ne comprenez rien à la théorie du contrôle et notamment à la régulation PID, je vous recommande chaudement un article qui l'explique sans mathématiques. Placé à la fin de la file d'attente, au moment où on décide quoi faire des paquets, le contrôleur va les jeter (ou les marquer avec ECN) si l'objectif de durée de séjour est dépassé.
Passons maintenant à la section 4 du RFC, la plus concrète,
puisqu'elle décrit précisement CoDel. L'algorithme a deux
variables, TARGET
et
INTERVAL
(ces noms sont utilisés tels quels
dans le pseudo-code en section 5, et dans l'implémentation dans le
noyau Linux). TARGET
est
l'objectif (le temps de séjour dans la file d'attente qu'on ne
souhaite pas dépasser) et INTERVAL
est la
période d'observation. Ce dernier est également le seul paramètre
de CoDel qu'il faut définir explicitement. Le RFC recommande 100 ms
par défaut, ce qui couvre la plupart des RTT
qu'on rencontre dans l'Internet, sauf si on parle à des gens très
lointains ou si on passe par des satellites
(cf. M. Dischinger, « Characterizing Residential Broadband
Networks », dans les Proceedings of the Internet
Measurement Conference, San Diego, en 2007, si vous
voulez des mesures). Cela permet, par
exemple, de vendre des routeurs bas de gamme, genre
point d'accès Wifi sans imposer aux
acheteurs de configurer CoDel.
Si on est dans un environnement très différent de celui d'un
accès Internet « normal », il peut être nécessaire d'ajuster les
valeurs (CoDel n'est donc pas réellement
« parameterless »). L'annexe A du RFC en donne
un exemple, pour le cas du centre de
données, où les latences sont bien plus faibles (et les
capacités plus grandes) ;
INTERVAL
peut alors être défini en
microsecondes plutôt qu'en millisecondes.
TARGET
, lui, va être déterminé
dynamiquement par CoDel. Une valeur typique sera aux alentours de 5
ms (elle dépend évidemment de INTERVAL
) : si, pendant une durée égale à INTERVAL
, les paquets restent plus longtemps que cela dans la file
d'attente, c'est que c'est une mauvaise file, on jette des
paquets. Au passage, dans le noyau Linux, c'est dans
codel_params_init
que ces valeurs sont
fixées :
params->interval = MS2TIME(100); params->target = MS2TIME(5);
Les programmeurs
apprécieront la section 5, qui contient du
pseudo-code, style
C++, mettant en œuvre CoDel. Le choix de C++
est pour profiter de l'héritage, car CoDel est juste une
dérivation d'un classique algorithme tail-drop. On
peut donc le programmer sous forme d'une classe qui hérite d'une
classe queue_t
, plus générale.
De toute façon, si vous n'aimez pas C++, vous pouvez lire le
code source du noyau
Linux, qui met en œuvre CoDel depuis longtemps (fichier
net/sched/sch_codel.c
, également accessible
via le Web).
(Pour comprendre les exemples de code, notez que packet_t*
pointe vers un
descripteur de paquet, incluant entre autres un champ, tstamp
, qui stocke
un temps, et que le type time_t
est exprimé
en unités qui dépendent de la résolution du système, sachant que
la milliseconde est suffisante, pour du trafic Internet « habituel ».) Presque tout le travail de CoDel est fait au moment où le
paquet sort de la file d'attente. À l'entrée, on se contente
d'ajouter l'heure d'arrivée, puis on appelle le traitement
habituel des files d'attente :
void codel_queue_t::enqueue(packet_t* pkt) { pkt->tstamp = clock(); queue_t::enqueue(pkt); }
Le gros du code est dans le sous-programme
dodequeue
qui va déterminer si le paquet
individuel vient de nous faire changer d'état :
dodequeue_result codel_queue_t::dodequeue(time_t now) { ... // Calcul de *la* variable importante, le temps passé dans la // file d'attente time_t sojourn_time = now - r.p->tstamp; // S'il est inférieur à l'objectif, c'est bon if (sojourn_time_ < TARGET || bytes() <= maxpacket_) { first_above_time_ = 0; } else { // Aïe, le paquet est resté trop longtemps (par rapport à TARGET) if (first_above_time_ == 0) { // Pas de panique, c'est peut-être récent, attendons // INTERVAL avant de décider first_above_time_ = now + INTERVAL; } else if (now >= first_above_time_) { // La file ne se vide pas : jetons le paquet r.ok_to_drop = true; } } return r; }
Le résultat de dodequeue
est ensuite utilisé
par dequeue
qui fait se fait quelques
réflexions supplémentaires avant de jeter réellement le paquet.
Ce code est suffisamment simple pour pouvoir être mis en œuvre dans le matériel, par exemple ASIC ou NPU.
L'annexe A du RFC donne un exemple de déclinaison de CoDel pour le cas d'un centre de données, où les RTT typiques sont bien plus bas que sur l'Internet public. Cette annexe étudie également le cas où on applique CoDel aux files d'attente des serveurs, pas uniquement des routeurs, avec des résultats spectaculaires :
INTERVAL
),ACK
, sans
données) ne sont jamais jetés, car ils sont importants (et de
petite taille, de toute façon).Sur l'Internet public, il serait dangereux de ne jamais jeter les paquets de pur contrôle, ils pourraient être envoyés par un attaquant. Mais, dans le serveur, aucun risque.
Également à lire : un article de synthèse sur le blog de l'IETF.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : M. Nottingham
Chemin des normes
Première rédaction de cet article le 25 octobre 2017
Le lien est à la base du Web. Mais c'est seulement récemment, avec le RFC 5988 que notre RFC remplace, que certains manques ont été comblés :
Bien sûr, des formats normalisés qui permettent des liens, il y en a
plusieurs, et avant tout HTML, avec le fameux
élément <A>
(pour
anchor). Il y a aussi le plus récent
Atom (RFC 4287, notamment
la section 4.2.7). Comme HTML, Atom avait l'idée de registre
des types de liens, mais ces types étaient spécifiques à Atom. L'une
des idées phares du RFC 5988 et de celui-ci, son successeur RFC 8288, est de
généraliser le concept de type de lien et de le rendre accessible à
tous les formats et protocoles qui en ont besoin. Ce RFC
décrit un cadre général pour les types de liens, en partant de celui
d'Atom.
Second apport de cette norme, une (re-)définition de
l'en-tête HTTP Link:
,
utilisant évidemment le nouveau cadre général. Cet en-tête permettant
d'indiquer un lien dans la réponse HTTP, indépendamment du document
servi, avait été normalisé dans le RFC 2068, section 19.6.2.4,
puis, peu utilisé, avait été supprimé par le RFC 2616, avant de faire sa
réapparition ici, sous une forme quasi-identique à l'original. On peut
voir cet en-tête comme une représentation concrète du cadre de notre
RFC. D'autres apparaîtront sans doute.
Pour un exemple réel, regardez les en-têtes
Link:
de mon blog, il y en a un de type
licence
, suivant le RFC 4946. Avec Apache, cela se configure
simplement avec le module headers
et la
directive Header set Link "<http://www.gnu.org/copyleft/fdl.html>; rel=\"license\"; title=\"GFDL\""
.
Donc, qu'est-ce qu'un lien ? La section 2, la principale du RFC, le définit comme une connexion typée entre deux ressources (une ressource étant typiquement une page Web), nommées respectivement contexte et cible. Les deux ressources sont représentées par leur IRI (cf. RFC 3987, en notant que, dans la plupart des cas, les IRI des liens seront des URI). Le lien comprend :
Par exemple, dans le flux de syndication
Atom de mon blog, on trouvera un lien
<atom:link rel="alternate"
href="http://www.bortzmeyer.org/expose-go.html"/>
qui se décompose
en un contexte (l'entrée Atom dont l'IRI est
tag:bortzmeyer.org,2006-02:Blog/expose-go
), un
type (alternate
, qui indique une version
alternative de la ressource, ici une page HTML au lieu d'une entrée
Atom), et une cible (ici http://www.bortzmeyer.org/expose-go.html
). Il n'y a pas dans
cet exemple
d'attributs de la cible mais Atom en permet (par exemple
hfrelang
pour indiquer la langue de la cible ou bien
length
pour indiquer sa longueur - afin de
prévenir d'un long téléchargement, par exemple).
Cette définition du lien ne place aucune limite sur la cardinalité. Il peut y avoir zéro, un ou plusieurs liens partant d'une ressource et c'est la même chose pour les lients entrants.
La section 2 s'arrête là. Puisque ce RFC propose un cadre général, il ne formalise pas une syntaxe unique pour représenter les liens. Chaque format, chaque protocole, aura la sienne, la sérialisation.
Un des points les plus importants de cette définition des liens,
et qui est souvent ignorée des gens qui écrivent des pages Web, est
la notion de type d'un lien (section 2.1). Par
exemple, on a un type copyright
qui associe, via un lien, un document à l'auteur de
celui-ci. Point à retenir : ce type du lien ne doit pas être
confondu avec le type de médium du RFC 6838
comme text/html
ou
audio/ogg
.
Il y a deux sortes de type de lien : enregistrés ou bien
extensions. Les premiers font l'objet de la section 2.1.1. Ils ont
fait l'objet d'un processus formel d'enregistrement et leur liste est publiée sous forme d'un
registre
IANA. On y trouve par exemple via
(RFC 4287) ou hub
(https://github.com/pubsubhubbub
). La politique
d'enregistrement est délibérement ouverte, l'idée étant que si un
type est utilisé sur le Web, il faut vraiment qu'il soit indiqué
dans le registre, sauf s'il est gravement dangereux pour la santé
du Web.
Les extensions sont spécifiées dans la section 2.1.2. L'idée est
que, si on n'a pas envie de se fatiguer à enregistrer un type de
lien, et qu'on veut quand même créer un type
unique, n'ayant pas de risque de collision
avec le travail des autres, on peut simplement se servir d'un URI
(forcément unique) pour indiquer le type. Cette URI peut (mais ce
n'est pas obligé) mener à une page Web qui décrira le type en
question. Ainsi, on pourrait imaginer de réécrire le lien plus haut
en <atom:link
rel="http://www.bortzmeyer.org/reg/my-link-type"
href="http://www.bortzmeyer.org/expose-go.html"/>
(en
pratique, le format Atom ne permet pas actuellement de telles
valeurs pour l'attribut rel
.)
Après le cadre général de description des liens, notre RFC
introduit une syntaxe concrète pour le cas de l'en-tête
Link:
des requêtes HTTP. Les autres formats et
protocoles devront s'ajuster à ce cadre chacun de son côté. Pour
HTTP, la section 3 décrit l'en-tête Link:
. La
cible doit être un URI (et un éventuel IRI doit donc être
transformé en URI), le contexte (l'origine) est la ressource qui
avait été demandée en HTTP et le type est indiqué dans le paramètre
rel
. (Le paramètre rev
qui avait été utilisé dans des vieilles versions est officiellement
abandonné.) Plusieurs attributs sont possibles comme
hreflang
, type
(qui est
le type MIME, pas le type du lien) ou
title
(qui peut être noté
title*
s'il utilise les en-têtes étendus du
RFC 8187). Pour la plupart de ces attributs,
leur valeur est juste une indication, la vraie valeur sera obtenue
en accédant à la ressource cible. Ainsi,
hreflang
dans le lien ne remplace pas
Content-Language:
dans la réponse et
type
ne gagnera pas si un
Content-Type:
différent est indiqué dans la
réponse.
Voici des exemples d'en-têtes, tirés de la section 3.5 du RFC :
Link: <http://www.example.com/MyBook/chapter2> rel="previous"; title="Previous chapter"
Ici, cet en-tête, dans une réponse HTTP, indique que
http://www.example.com/MyBook/chapter2
est une
ressource liée à la ressource qu'on vient de récupérer et que ce lien
est de type previous
, donc précède la ressource
actuelle dans l'ordre de lecture. L'attribut
title
indique un titre relatif, alors que la
vraie ressource
http://www.example.com/MyBook/chapter2
aura
probablement un titre du genre « Chapitre 2 ». En application des
règles de la section 3.4, c'est ce dernier titre qui gagnera au
final.
Un seul en-tête Link:
peut indiquer plusieurs
liens, comme dans l'exemple suivant :
Link: </TheBook/chapter2>; rel="previous"; title*=UTF-8'de'letztes%20Kapitel, </TheBook/chapter4>; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel
Ce dernier montre également les en-têtes complètement
internationalisés du RFC 8187, ici en
allemand (étiquette de langue de
).
Cet en-tête a été enregistré à l'IANA, en application du RFC 3864 dans le registre des en-têtes (section 4.1).
D'autre part, un registre des types de liens existe. La section 4.2 décrit en détail ce registre. Voici, à titre d'exemple, quelques-uns des valeurs qu'on peut y trouver :
blocked-by
indique l'entité qui a exigé
le blocage d'une page Web (avec le fameux code 451, voir RFC 7725),copyright
qui indique
le copyright du document (issu de la norme HTML),edit
qui indique l'URI à utiliser pour une
modification de ce document, comme le permet le protocole
APP (RFC 5023),first
, qui pointe vers le premier document
de la série (défini par ce RFC 8288, même s'il était déjà enregistré),hub
qui indique l'endroit où s'abonner pour
des notifications ultérieures, suivant le protocole
PubSubHubbub),latest-version
qui indique où trouver la
dernière version d'un document versionné (RFC 5829),licence
, qui associe un document à sa
licence d'utilisation (RFC 4946),nofollow
, qui indique qu'on ne recommande
pas la ressource vers laquelle on pointe, et qu'il ne faut donc pas
considérer ce lien comme une approbation,related
, qui indique un document qui a un
rapport avec celui-ci (créé pour Atom, dans le RFC 4287),replies
, qui indique les réponses faites à
ce document (pour mettre en œuvre le
threading, RFC 4685),Ce registre est peuplé par le mécanisme dit Spécification
Nécessaire (cf. RFC 8126), avec exigence
d'un examen par un expert (l'actuel expert est Mark Nottingham, auteur de
plusieurs RFC, dont celui-ci). Pour chaque type, il faudra indiquer le type (aussi nommé
relation, comme par exemple previous
plus haut), une
description et une référence à la norme qui le formalise. Les demandes
d'enregistrement sont reçues par link-relations@ietf.org
.
Attention, ce n'est pas parce qu'il y a un lien qu'il faut le suivre automatiquement. La section 5, sur la sécurité, met en garde contre la confiance accordée à un lien.
L'annexe A.1 contient une discussion de l'utilisation des liens avec
HTML 4, d'où le cadre actuel de définition des liens est issu. Le type
y est indiqué par l'attribut rel
. Un exemple
indiquant la licence en XHTML est donc :
<link rel="license" type="text/html" title="GFDL in HTML format" href="http://www.gnu.org/copyleft/fdl.html"/>
L'annexe A.2
discute, elle, de l'utilisation du cadre de définition des liens en
Atom, qui utilise l'élément
<atom:link>
avec les attributs
href
pour indiquer la cible et
rel
pour le type. Par exemple,
<atom:link rel="license" type="text/html"
title="GFDL in HTML format"
href="http://www.gnu.org/copyleft/fdl.html"/>
indiquera
la licence du flux Atom qui contient cet élément (et, oui, Atom et
XHTML ont quasiment la même syntaxe).
L'annexe C de notre RFC indique les changements depuis son prédécesseur, le RFC 5988. Rien de très crucial :
Ce nouveau RFC a fait l'objet de peu de discussions à l'IETF, mais il y en a eu beaucoup plus sur GitHub.
Notez qu'à Paris Web
2017, le t-shirt officiel
portait une allusion à ce RFC, avec le texte <link
rel="human" ... >
, encourageant les relations entre humains.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : N. ten Oever (Article 19), C. Cath (Oxford Internet Institute)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF hrpc
Première rédaction de cet article le 2 novembre 2017
Ce RFC très politique est le premier du groupe de recherche IRTF HRPC, dont le nom veut dire « Human Rights and Protocol Considerations ». À première vue, il n'y a pas de rapport entre les droits humains et les protocoles réseau. Les premiers relèvent de la politique, les seconds de la pure technique, non ? Mais, justement, le groupe HRPC a été créé sur la base de l'idée qu'il y a de la politique dans le travail de l'IETF, que les protocoles ne sont pas complètement neutres, et qu'il était nécessaire de creuser cette relation complexe entre protocoles et droits humains. Le premier RFC analyse le problème de base : « TCP/IP est-il politique ? »
Si vous êtes un concepteur de protocoles, plutôt porté sur le concret, et que les discussions politiques vous gonflent, vous pouvez passer directement à la section 6 du RFC, qui est la check-list Droits Humains pour votre prochain protocole ou format. En la suivant, vous pourrez plus facilement vérifier que votre création n'a pas trop d'effets néfastes, question droits humains. (Depuis, il vaut mieux consulter le RFC 9620, qui la remplace.)
Ce RFC n'est pas le premier RFC « politique », et il ne faudrait pas croire que les ingénieur·e·s qui participent à l'IETF sont tou·ṫe·s des nerds asociaux avec la conscience politique d'un poisson rouge. Parmi les RFC politiques, on peut citer le RFC 1984 (refus d'affaiblir la cryptographie), le RFC 7258 (post-Snowden, RFC affirmant que la surveillance de masse est une attaque contre l'Internet, et qu'il faut déployer des mesures techniques la rendant plus difficile), et bien sûr l'excellent RFC 6973, sur la vie privée, qui sert largement de modèle à notre RFC 8280.
Le groupe de recherche IRTF HRPC va donc travailler sur deux axes (section 3 de notre RFC) :
Ce RFC particulier a eu une gestation de plus de deux ans. Deux des étapes importantes avaient été la réunion IETF 92 à Dallas, et la réunion IETF 95 à Buenos Aires. À la seconde, vu les opinions politiques de beaucoup des participant·e·s, l'après-réunion s'est tenu dans un restaurant végétarien. En Argentine…
La section 1 du RFC rappelle les nombreux débats qui ont agité le monde de l'Internet sur le rôle politique de ce réseau. Deux belles citations ouvrent cette section, une de Tim Berners-Lee qui dit « There's a freedom about the Internet: As long as we accept the rules of sending packets around, we can send packets containing anything to anywhere. » et un extrait du RFC 3935 « The Internet isn't value-neutral, and neither is the IETF. ». Et le RFC 3935 continue : « We want the Internet to be useful for communities that share our commitment to openness and fairness. We embrace technical concepts such as decentralized control, edge-user empowerment and sharing of resources, because those concepts resonate with the core values of the IETF community. These concepts have little to do with the technology that's possible, and much to do with the technology that we choose to create. ». Le succès immense de l'Internet, contre tous les prophètes de malheur qui prétendaient que ce réseau, qui n'avait pas été conçu par des Messieurs Sérieux, ne pourrait jamais marcher, fait que l'impact social et politique des techniques de la famille TCP/IP est énorme. On trouve donc logiquement de nombreux textes qui affirment que « ce grand pouvoir donne à l'Internet de grandes responsabilités », par exemple cette résolution des Nations Unies, ou bien la déclaration de NETmundial. Une position plus radicale est qu'il faut défendre et renforcer l'Internet, car il serait intrinsèquement un outil aux services des droits humains.
En effet, la connectivité de bout en bout, tout le monde peut parler à tous ceux qui veulent bien, Alice et Bob peuvent échanger sans autorisation, est à la fois un principe fondamental de l'Internet (cf. RFC 1958) et un puissant soutien aux droits humains. Pour citer Benjamin Bayart, « L’imprimerie a permis au peuple de lire, Internet va lui permettre d’écrire. » L'architecture de l'Internet est ouverte (je me souviens de techniciens d'un opérateur de télécommunications historique qui avaient poussé des cris d'horreur quand je leur avais montré traceroute, au début des années 1990. Ils avaient tout de suite demandé comment empêcher le client de regarder l'intérieur du réseau de l'opérateur.) Les normes techniques de l'Internet sont développées selon un processus ouvert, et sont librement distribuées (ce que ne font toujours pas les dinosaures de la normalisation comme l'AFNOR ou l'ISO). En prime, une bonne partie de l'infrastructure de l'Internet repose sur du logiciel libre.
L'Internet a prouvé qu'il pouvait continuer à fonctionner en environnement hostile (RFC 1984 et RFC 3365). Les RFC politiques cités plus haut défendent tous des valeurs qui vont dans le sens des droits humains (par exemple la vie privée, dans les RFC 6973 et RFC 7258). Cela ne va pas de soi : une organisation comme l'UIT n'en a jamais fait autant et développe au contraire des technologies hostiles aux droits humains comme les techniques de surveillance dans le NGN.
On pourrait peut-être même dire que non seulement l'Internet défend les droits humains, mais que ceux-ci sont à la base de l'architecture de l'Internet. (Cf. Cath, C. and L. Floridi, « The Design of the Internet's Architecture by the Internet Engineering Task Force (IETF) and Human Rights », 2017.) On peut citer ici Bless, R. et C. Orwat, « Values and Networks » : « to a certain extent, the Internet and its protocols have already facilitated the realization of human rights, e.g., the freedom of assembly and expression. In contrast, measures of censorship and pervasive surveillance violate fundamental human rights. » ou bien Denardis, L., « The Internet Design Tension between Surveillance and Security » « Since the first hints of Internet commercialization and internationalization, the IETF has supported strong security in protocol design and has sometimes served as a force resisting protocol-enabled surveillance features. ».
Mais la question reste chaudement débattue à l'IETF. Nombreux sont les techniciens qui grommelent « tout ça, c'est de la politique, cela ne nous concerne pas », voire reprennent l'argument classique de la neutralité de la technique « un outil est neutre, c'est l'usage qu'on en fait qui compte, le fabricant du couteau n'est pas responsable d'un meurtre qu'on commet avec ce couteau, donc on ne doit pas se poser la question des droits humains ». Avant Snowden, c'était sans doute l'opinion dominante à l'IETF, mais cela a changé depuis.
Mais, au fait, ce sont quoi, les Droits Humains avec leur majuscule ? Ce sont des droits universels, indivisibles et inaliénables, formalisés dans des textes comme la Déclaration Universelle des Droits de l'Homme ou comme le pacte international relatif aux droits civils et politiques ou le Pacte international relatif aux droits économiques, sociaux et culturels. La section 2 du RFC, sur la terminologie, discute en détail cette définition. Si vous voulez un document unique sur les droits humains, la DUDH citée plus haut est une lecture recommandée. Le fait qu'ils soient universels est important : on entend régulièrement des dirigeants ou des lécheurs de bottes des dirigeants prétendre que les droits humains ne sont pas bons pour le peuple qu'ils oppriment, qu'ils seraient uniquement pour certaines catégories de l'humanité. Si c'était le cas, il serait en effet inutile de discuter des droits humains sur l'Internet, puisque celui-ci connecte tout le monde. Mais, bien sûr, cette soi-disant relativité des droits humains est de la pure rhétorique malhonnête au service de dictateurs locaux.
On notera que, à l'époque de la rédaction de la DUDH, le seul risque de violation envisagée était l'œuvre des États, mais que l'idée s'est imposée depuis que des acteurs non-étatiques pouvaient également être concernés.
Cela ne veut pas dire que les textes comme la DUDH citée plus haut sont parfaits. Ce ne sont pas des textes sacrés, mais le résultat d'un processus politique. Comme toute œuvre humaine, ils peuvent être améliorés, mais il faut juste garder en tête que ceux qui les critiquent ne cherchent pas en général à les améliorer, mais à les affaiblir, voire à les détruire.
Par contre, les droits humains ne sont pas absolus. Un exemple de ce caractère non-absolu des droits humains est qu'ils peuvent être en conflit entre eux. Par exemple, le droit à la liberté d'expression peut rentrer en contradiction avec le droit de ne pas être insulté ou harcelé. Ou avec le droit à la vie privée. Les droits humains ne pourront donc pas être mis en algorithmes.
La section 2 de notre RFC est consacrée à la terminologie. Sujet très difficile car elle est souvent floue dans les domaines liés à la sécurité. Je ne vais pas la reproduire en entier ici (la section est longue, en partie en raison du caractère transversal de notre RFC, cf. section 5.2.1.3), juste noter quelques définitions qui ont fait des histoires (listées dans l'ordre alphabétique de l'original en anglais). Notez que notre RFC 8280 ne fait souvent que reprendre des définitions de RFC précédents. Ainsi, la définition de « connectivité Internet complète » vient du RFC 4084 (et est nécessaire car bien des malhonnêtes vendent comme « accès Internet » des offres plus ou moins bridées). De même le RFC 4949, sur le vocabulaire de la sécurité, et le RFC 6973, sur la vie privée, sont très mis à contribution.
En parlant de vie privée, la définition d'« anonymat » est un des premiers problèmes de terminologie. Le terme est utilisé à tort et à travers dans les médias (« Bitcoin est une monnaie anonyme ») et souvent confondu avec pseudonymat. À leur décharge, il faut dire que les définitions du RFC 4949 et du RFC 6973 sont très abstraites.
Parmi les autres définitions plutôt vagues, notons celle de « neutralité par rapport au contenu » (content agnosticism). C'est bien sûr un concept très important, d'autant plus que cette neutralité est menacée, mais la définition ne va pas très loin. Ce n'est pas mieux pour un autre concept important mais flou, et difficile à saisir, celui de décentralisation, un peu utilisé à toutes les sauces aujourd'hui (cf. mon article pour JRES).
Passons maintenant au principe de bout en bout. C'est un des concepts clés de l'Internet (RFC 2775) : l'« intelligence » (les traitements compliqués) doit être aux extrémités, pas dans le réseau. Plusieurs raisons militent en faveur de ce principe mais, pour en rester aux droits humains, notons surtout que ce principe se traduit par « touche pas à mes données ».
Autre sujet difficile à définir, les « normes ouvertes » (open standards). Il y a plein de SDO, dont le degré d'ouverture varie considérablement. Par exemple, l'ISO ou l'IEEE ne publient pas leurs normes en ligne et, même si on les acquiert, on n'a pas le droit de les redistribuer. L'UIT ne permet de participer que si vous êtes gouvernement ou grande entreprise. L'IETF, sans doute la SDO la plus ouverte, n'a pas de définition claire de ce qu'est une norme ouverte (cf. RFC 2026), à part dans le RFC 6852, qui est surtout un document politicien (et hypocrite).
Un concept important de l'Internet est celui d'« innovation sans autorisation ». Pour le comprendre, regardons l'invention du World-Wide Web. Tim Berners-Lee, Robert Cailliau et les autres ont pu inventer le Web et le déployer, sans rien demander à personne. Aucun comité Machin, aucun gouvernement, n'a été sollicité pour donner son avis ou son autorisation. Au contraire, dans les réseaux de télécommunication pré-Internet, il fallait l'accord préalable de l'opérateur pour tout déploiement d'une application. Sans l'« innovation sans autorisation », nous n'aurions pas le Web.
Et la « vie privée », on la définit comment ? Le RFC 4949 la définit comme le droit à contrôler ce qu'on expose à l'extérieur. C'est actuellement un des droits humains les plus menacés sur l'Internet, en raison des possibilités de surveillance massive que permet le numérique, possibilités largement utilisées par les États. Or, ce droit est lui-même à la base de nombreux autres droits. Ainsi, la liberté d'expression est sérieusement en danger si on n'a pas de droit à la vie privée, par exemple parce que des gens hésiteront à lire certains textes s'ils savent que leurs habitudes de lecture sont surveillées.
La section 4 du RFC est consacrée à un examen du débat (très ancien) sur la neutralité de la technique, et sur les relations entre technique et politique. La littérature scientifique et philosophique dans ce domaine est riche ! (À une réunion de HRPC, la discussion avait tourné à la pure philosophie, et on y avait abondemment cité Foucault, Heidegger, Wittgenstein, Derrida et Kant, ce qui est plutôt rare à l'IETF.)
Les deux opinions extrêmes à ce sujet sont :
Il n'est pas compliqué de trouver plein d'exemples de luttes politiques autour des protocoles Internet, dans les RFC cités plus haut comme le RFC 7258, ou bien dans des articles comme celui de Denardis « The Internet Design Tension between Surveillance and Security ». Les participants à l'IETF ne vivent pas dans une bulle, ils vivent dans un contexte politique, social, historique, culturel, et cela affecte certainement leurs décisions.
Notre RFC cite un grand nombre de publications sur ces sujets, de Francesca Musiani « Giants, Dwarfs and Decentralized Alternatives to Internet-based Services » à Lawrence Lessig, Jonathan Zittrain (« The future of the Internet ») et Milton Mueller. Si vous avez quelques mois de libres devant vous, je vous encourage à lire tous ces livres et articles.
Il y a aussi des études plus spécifiques au rôle des SDO, parmi lesquelles « Engineering 'Privacy by Design' in the Internet Protocols - Understanding Online Privacy both as a Technical and a Human Rights Issue in the Face of Pervasive Monitoring » ou le célèbre article de Clark et ses collègues, « Tussle in Cyberspace ».
Le RFC dégage cinq opinions différentes sur les relations entre le travail des ingénieurs et les droits humains, et sur la question de savoir si les droits humains doivent être intégrés dans les protocoles Internet. La première est celle citée dans l'article de Clark et al., qu'on peut résumer par « ce serait dangereux d'essayer de faire respecter les droits humains par les protocoles » :
L'article résume en disant que les ingénieurs doivent concevoir le terrain, pas le résultat du match.
Une deuxième position est que certaines valeurs universelles, dont les droits humains tels que formalisés dans la DUDH, devraient être incluses dans l'architecture même du réseau. (Cf. l'article « Should Specific Values Be Embedded In The Internet Architecture? », et attention, c'est un article collectif, avec plusieurs points de vue. Celui résumé ici est celui de Brown.) L'idéal serait que le réseau lui-même protège ces droits. En effet, les techniciens, de part le pouvoir qu'ils ont, ont une obligation « morale » de faire tout ce qui est possible pour faire respecter les droits humains.
Une troisième position, qui part sur un plan différent, est d'estimer qu'on ne peut pas inclure le respect des droits humains dans les protocoles, mais que c'est bien dommage et, qu'à défaut, il faudrait déclarer clairement que le réseau est un bien commun, et que toute tentative de l'utiliser pour le mal est en soi une violation des droits humains. Si on suit ces auteurs (« The public core of the Internet. An international agenda for Internet governance »), l'Internet lui-même, et les protocoles tels que normalisés dans les RFC, seraient un bien commun qu'on ne peut pas tripoter, comme un parc naturel, par exemple. Si le DNS était inclus comme « bien commun », des manipulations comme les résolveurs menteurs deviendraient illégales ou en tout cas immorales.
Les auteurs de « Values and Networks » sont plus prudents. Ils estiment que les protocoles Internet ont effectivement des conséquences sur les droits humains, mais qu'on n'est pas sûrs de quelles conséquences exactement, et qu'il est important de poursuivre les recherches. Cette quatrième position va donc tout à fait dans le sens de la constitution de HRPC comme groupe de recherche de l'IRTF.
Enfin, cinquième possibilité (et vous avez vu qu'elles ne sont pas forcément incompatibles), Berners-Lee et Halpin disent que l'Internet crée également de nouveaux droits. Ainsi, dans une société connectée où ne pas avoir l'Internet est un handicap social, le droit à un accès Internet devient un droit humain.
Quel bilan tirer de cette littérature scientifique et philosophique existante ? D'abord, d'un point de vue pratique, on ne sait pas si créer un réseau qui, par construction, assurerait le respect des droits humains est faisable (avant même de savoir si c'est souhaitable). Mais, au moins, on peut arrêter de croire que la technique est complètement neutre, étudier les conséquences des protocoles sur les droits humains (ce que fait la section 5 de notre RFC) et essayer d'améliorer ces protocoles à la lumière de cette analyse (la section 6 du RFC).
Voyons donc une série d'étude de cas de protocoles Internet existants, et en quoi ils affectent les droits humains (section 5). Une anecdote personnelle au passage : les premières versions de ces études de cas comportaient d'énormes erreurs techniques. Il est en effet difficile de trouver des gens qui sont à la fois sensibilisés aux droits humains et compétents techniquement. Comme le note le RFC, un travail interdisciplinaire est nécessaire. Le travail collectif à l'IRTF fait que cette section 5 est maintenant correcte.
Avant les études de cas techniques, le point de départ est une analyse des discours (selon la méthodologie présentée dans l'article de Cath). Elle s'est faite à la fois informellement (discussion avec des auteurs de RFC, interviews de participants à l'IETF) et formellement, par le biais d'un outil d'analyse automatique. Ce dernier, écrit en Python avec Big Bang, a permis de déterminer les « éléments de langage » importants dans les normes Internet. Et cela donne de jolis graphes.
La partie informelle s'est surtout faite pendant la réunion IETF 92 à Dallas, et a donné le film « Net of Rights ». Mais il y a eu aussi l'observation des groupes de travail IETF en action.
Les protocoles Internet sont bâtis en utilisant des concepts techniques (connectivité, confidentialité, accessibilité, etc) et la section 5.2.2 met en correspondance ces concepts avec les droits humains tels que définis dans la DUDH. Par exemple, le droit de s'assembler s'appuie sur la connectivité, mais aussi sur la résistance à la censure, et sur la sécurité en général.
Maintenant, place à la première partie technique de notre RFC, la section 5.2.3. Elle étudie en détail les conséquences de divers protocoles pour les droits humains. Attention, la conception d'un protocole est une activité complexe, avec des cahiers de charges épais où le respect des droits humains (quand il est présent…) n'est qu'une partie. Et le travail d'ingéniérie nécessite toujours des compromis. Le RFC prévient donc que ce travail est forcément étroit : on n'examine les protocoles que sous l'angle des droits humains, alors qu'une évaluation complète de ces protocoles nécessiterait la prise en compte de bien d'autres aspects. Comme exemple de compromis auquel il faut parfois se résoudre, avoir un serveur distinct de la machine de l'utilisat·eur·rice, possiblement géré par un tiers (c'est le cas de SMTP et XMPP), est certainement mauvais pour la résistance à la censure, car il va fournir un point de contrôle évident, sur lequel des autorités peuvent taper. D'un autre côté, sans un tel serveur, comment communiquerait-on avec des utilisat·eur·rice·s qui ne sont pas connecté·e·s en permanence ou qui sont coincé·e·s derrière un réseau qui interdit les connexions entrantes ? Bref, les protocoles qui sont souvent vertement critiqués par la suite ne sont pas forcément mauvais, encore moins délibérement mauvais. L'idée de cette section est de bien illustrer, sur des cas concrets, que les décisions techniques ont des conséquences politiques. (Ce point avait fait l'objet de vives discussions à l'IETF, des gens estimant que le RFC était trop négatif, et qu'il aurait également fallu indiquer les aspects positifs de l'Internet.)
Donc, pour commencer la série, évidemment, IP lui-même, plus précisement IPv4 (RFC 791). Malgré la normalisation d'IPv6, IPv4 est toujours le principal protocole du réseau. C'est un succès fou, connectant des centaines de millions de machines (et bien plus via les systèmes de traduction d'adresses). Il est conçu pour en faire le moins possible : l'intelligence doit être dans les machines terminales, pas dans le réseau, pas dans la couche 3. (Cf. RFC 3724.) En pratique, toutefois, on voit des intermédiaires agir au niveau IP et, par exemple, ralentir certains types de trafic, ou bien bloquer certaines machines. IP expose en effet certaines informations qui peuvent faciliter ce genre de violations de la neutralité du réseau.
Par exemple, les adresses IP source et destination sont visibles en clair (même si tout le reste du paquet est chiffré) et à un endroit fixe du paquet, ce qui facilite la tâche des routeurs mais aussi des dispositifs de blocage. Avant que vous ne me dites « ben, évidemment, sinon le réseau ne pourrait pas marcher », faites attention. L'adresse IP de destination est effectivement nécessaire aux routeurs pour prendre des décisions de transmission, mais ce n'est pas le cas de l'adresse source. En outre, IP expose le protocole de transport utilisé, encore une information dont les routeurs n'ont pas besoin, mais qui peut aider des intermédiaires à traiter certains types de trafic différemment.
Aujourd'hui, beaucoup de décisions de blocage sont prises sur la base des adresses IP ainsi exposées, ce qui illustre les conséquences d'une décision apparemment purement technique. (Pour les amateurs d'histoire alternative, X.25 n'exposait pas obligatoirement les adresses source et destination dans chaque paquet. Même le serveur final ne les voyait pas forcément. X.25 avait plein de défauts, mais cette anecdote montre que d'autres choix étaient possibles. Il faut juste se rappeler qu'ils avaient leurs propres inconvénients.) Si vous êtes enseignant·e en réseaux informatiques, voici un exercice intéressant faire faire à vos étudiant·e·s : « concevoir un réseau qui n'expose pas à tous des identificateurs uniques mondiaux ». Des alternatives au mécanisme d'IP ont été conçues (comme Hornet ou APIP) mais aucune n'a connu de déploiement significatif. Le routage par la source (combiné avec de la triche sur les adresses IP) aurait également permis de limiter l'exposition des adresses IP sur le trajet mais il pose bien d'autres problèmes. La principale solution employée aujourd'hui, lorsqu'on veut dissimuler les adresses IP des machines qui communiquent, est Tor.
Une autre particularité d'IPv4, qui n'était pas présente à ses débuts, est l'utilisation massive de la traduction d'adresses (RFC 3022). Elle est très répandue. Mais elle casse le modèle de bout en bout, et met le routeur qui fait la traduction dans une position privilégiée (par exemple, beaucoup refusent de faire passer d'autres protocoles de transport que TCP ou UDP). C'est donc une sérieuse limite à la connectivité et donc aux droits humains qui en dépendent.
Et le DNS ? Voilà un protocole dont la relation aux droits humains a été largement discutée. Comme toute opération sur l'Internet commence par une requête DNS, il est un point de contrôle évident. On peut notamment l'utiliser pour la censure. Autre question politique liée au DNS et qui a fait s'agiter beaucoup d'électrons, le pouvoir des organismes qui gèrent les TLD et, bien sûr, la racine du DNS. On peut dire sans exagérer que l'essentiel des débats sur la « gouvernance de l'Internet » ont tourné sur la gestion de la racine du DNS, qui ne représente pourtant pas, et de loin, le seul enjeu politique.
Pourquoi est-ce un enjeu pour les droits humains ? Le DNS a une
structure arborescente, avec
l'ICANN à la racine. Le contrôle de l'ICANN
fait donc saliver bien du monde. Ensuite, les
TLD, qu'ils soient contrôlés par l'ICANN
(les gTLD) ou pas, ont un rôle
politique important, via leur politique d'enregistrement. Celle-ci
varie selon les TLD. Les gTLD historiques
comme .com
ont une politique déterminée par
des organisations états-uniennes, l'ICANN et leur registre
(Verisign dans le cas de
.com
). Les nouveaux
gTLD ont des registres de nationalité différentes mais dépendent
tous des règles ICANN (cf. l'excellente étude de
l'EFF comparant ces politiques dans l'optique des droits
humains). Les ccTLD, eux, dépendent
de lois nationales très variables. Elles sont par exemple plus ou
moins protectrices de la liberté d'expression. (Voir le fameux
cas
lybien.)
Est-ce que les centaines de nouveaux
gTLD créés depuis quelques années ont amélioré les choses
ou pas, pour cette liberté d'expression ? Certains disent que non
car beaucoup de ces nouveaux TLD ont une politique
d'enregistrement restrictive (cf. le rapport de l'EFF cité plus
haut), d'autres disent que oui car ces nouveaux TLD ont élargi le
choix. Et que la liberté d'association peut ne pas bien s'entendre
avec la liberté d'expression (la première peut justifier des
règles restrictives, pour que des minorités discriminées puissent
se rassembler sans être harcelées). Une chose est sûre, les débats
ont été chauds, par exemple autour d'une éventuelle création du
.gay
(un rapport
du Conseil de l'Europe détaille cette question « TLD et
droits humains »).
Le DNS soulève plein d'autres questions liées aux droits humains. Par exemple, il est indiscret (RFC 7626), et des solutions partielles comme le RFC 9156 semblent très peu déployées.
Et, comme trop peu de zones DNS sont protégées par DNSSEC (et, de toute façon, DNSSEC ne protège pas contre toutes les manipulations), il est trop facile de modifier les réponses envoyées. C'est aujourd'hui une des techniques de censure les plus déployées, notamment en Europe (voir à ce sujet le très bon rapport du Conseil Scientifique de l'AFNIC). Parmi les moyens possibles pour censurer via les noms de domaine :
Le RFC étudie ensuite le cas de HTTP, le protocole vedette de l'Internet (RFC 7230 et suivants). Sa simplicité et son efficacité ont largement contribué à son immense succès, qui a à son tour entrainé celui de l'Internet tout entier. On voit même aujourd'hui des tas de services non-Web utiliser HTTP comme substrat. Du fait de cette utilisation massive, les conséquences des caractéristiques de HTTP pour les droits humains ont été beaucoup plus étudiées que le cas du DNS.
Premier problème, HTTP est par défaut peu sûr, avec des communications en clair, écoutables et modifiables. Si la solution HTTPS est très ancienne (le RFC 2828 a dix-sept ans…, et SSL avait été décrit et mis en œuvre avant), elle n'a été massivement déployée que depuis peu, essentiellement grâce au courage d'Edward Snowden.
En attendant ce déploiement massif de HTTPS, d'innombrables équipements réseau de censure et de détournement de HTTP ont été fabriqués et vendus (par exemple par Blue Coat mais ils sont loin d'être les seuls). Celui qui veut aujourd'hui empêcher ou perturber les communications par HTTP n'a pas besoin de compétences techniques, les solutions toutes prêtes existent sur le marché.
Un autre RFC qui touchait directement aux droits humains et qui avait fait pas mal de bruit à l'IETF est le RFC 7725, qui normalise le code d'erreur 451, renvoyé au client lorsque le contenu est censuré. Il permet une « franchise de la censure », où celle-ci est explicitement assumée.
Les discussions à l'IETF avaient été chaudes en partie parce que l'impact politique de ce RFC est évident, et en partie parce qu'il y avait des doutes sur son utilité pratique. Beaucoup de censeurs ne l'utiliseront pas, c'est clair, soit parce qu'ils sont hypocrites, soit parce que les techniques de censure utilisées ne reposent pas sur HTTP mais, par exemple, sur un filtrage IP. Et, lorsque certains l'utilisent, quelle utilité pour les programmes ? Notre RFC explique que le principal intérêt est l'étude du déploiement de la « censure honnête » (ou « censure franche »). C'est le cas de projets comme Lumen. Du code est d'ailleurs en cours de développement pour les analyses automatiques des 451 (on travaillera là-dessus au hackathon de la prochaine réunion IETF).
Outre la censure, l'envoi du trafic en clair permet la surveillance massive, par exemple par les programmes Tempora ou XKeyscore. Cette vulnérabilité était connue depuis longtemps mais, avant les révélations de Snowden, la possibilité d'une telle surveillance de masse par des pays supposés démocratiques était balayée d'un revers de main comme « paranoïa complotiste ». Pour la France, souvenons-nous qu'une société française vend des produits d'espionnage de leur population à des dictatures, comme celle du défunt Khadafi.
D'autre part, l'attaque active, la modification des données en transit, ne sert pas qu'à la censure. Du trafic HTTP changé en route peut être utilisé pour distribuer un contenu malveillant (possibilité utilisée dans QUANTUMINSERT/FOXACID) ou pour modifier du code envoyé lors d'une phase de mise à jour du logiciel d'une machine. Cela semble une attaque compliquée à réaliser ? Ne vous inquiétez pas, jeune dictateur, des sociétés vous vendent ce genre de produits clés en main.
HTTPS n'est évidemment pas une solution magique, qui assurerait la protection des droits humains à elle seule. Pour ne citer que ses limites techniques, sa technologie sous-jacente, TLS (RFC 5246) a été victime de plusieurs failles de sécurité (sans compter les afaiblissements délibérés comme les célèbres « algorithmes pour l'exportation »). Ensuite, dans certains cas, un·e utilisat·eur·rice peut être incité·e à utiliser la version en clair (attaque par repli, contre laquelle des techniques comme celles du RFC 6797 ont été mises au point).
HTTPS n'étant pas obligatoire, la possibilité d'une attaque par repli existe toujours. Pour HTTP/2, il avait été envisagé d'imposer HTTPS, pour qu'il n'y ait plus de version non sûre, mais le RFC 7540 n'a finalement pas entériné cette idée (que le RFC 8164 a partiellement ressorti depuis.)
Autre protocole étudié, XMPP (RFC 6120). Un de ses principes est que le logiciel client (par exemple pidgin) ne parle pas directement au logiciel du correspondant, mais passe forcément par un (ou deux) serveur(s). Cette architecture présente des avantages pratiques (si le correspondant est absent, son serveur peut indiquer cette absence à l'appelant) mais aussi en matière de protection (on ne voit pas l'adresse IP de l'appelant). Ces serveurs sont fédérés entre eux, XMPP, contrairement à des protocoles inférieurs comme Slack ne peut donc pas être arrêté par décision supérieure.
Mais XMPP a aussi des inconvénients. Les utilisat·eurs·rices sont
identifiés par un JID comme
bortzmeyer@example.com/home
qui comprend une
« ressource » (le terme après la barre oblique) qui, en pratique,
identifie souvent une machine particulière ou un lieu
particulier. En général, ce JID est présenté tel quel aux
correspondants, ce qui n'est pas idéal pour la vie privée. D'autre
part, les communications sont en clair par défaut, mais peuvent
être chiffrées, avec TLS. Sauf que l'utilisat·eur·rice ne sait pas si
son serveur chiffre avec le serveur suivant, ou bien le serveur
final avec son correspondant. Sans possibilité d'évaluation de la
sécurité, il faut donc faire une confiance
aveugle à tous les serveurs pour prendre des précautions. Et
espérer qu'ils suivront tous le « XMPP
manifesto ».
Si XMPP lui-même est fédéré et donc relativement résistant à la censure, les salles collectives de discussion sont centralisées. Chaque salle est sur un serveur particulier, une sorte de « propriétaire », qui peut donc contrôler l'activité collective, même si aucun des participants n'a de compte sur ce serveur. (En prime, ces salles sont une extension du protocole, spécifiée dans le XEP-0045, pas mise en œuvre de manière identique partout, ce qui est un problème non-politique fréquent avec XMPP.)
Et le pair-à-pair, lui, quelles sont ses implications pour les droits humains ? D'abord, il faut évidemment noter que ce terme ne désigne pas un protocole particulier, qu'on pourrait analyser en détail, mais une famille de protocoles très divers (RFC 5694). L'application la plus connue du pair-à-pair est évidemment l'échange de fichiers culturels, mais le pair-à-pair est une architecture très générale, qui peut servir à plein de choses (Bitcoin, par exemple).
À l'époque des GAFA, monstres centralisés qui contrôlent toutes les interactions entre utilisat·eur·rice·s, le pair-à-pair est souvent présenté comme la solution idéale à tous les problèmes, notamment à la censure. Mais la situation est plus compliquée que cela.
D'abord, les réseaux en pair-à-pair, n'ayant pas d'autorité centrale de certification des contenus, sont vulnérables aux diverses formes d'empoisonnement des données. On se souvient des faux MP3 sur eDonkey, avec un nom prometteur et un contenu décevant. Un attaquant peut aussi relativement facilement corrompre, sinon les données, en tout cas le routage qui y mène.
Comme les protocoles pair-à-pair représentent une bonne part du trafic Internet, et qu'ils sont souvent identifiables sur le réseau, le FAI peut être tenté de limiter leur trafic.
Plus gênant, question droits humains, bien des protocoles pair-à-pair ne dissimulent pas l'adresse IP des utilisat·eur·rice·s. En BitTorrent, si vous trouvez un pair qui a le fichier qui vous intéresse, et que vous le contactez, ce pair apprendra votre adresse IP. Cela peut servir de base pour des lettres de menace ou pour des poursuites judiciaires (comme avec la HADOPI en France). Il existe des réseaux pair-à-pair qui déploient des techniques de protection contre cette fuite d'informations personnelles. Le plus ancien est Freenet mais il y a aussi Bitmessage. Ils restent peu utilisés.
Autre danger spécifique aux réseaux pair-à-pair, les attaques Sybil. En l'absence d'une vérification que l'identité est liée à quelque chose de coûteux et/ou difficile à obtenir, rien n'empêche un attaquant de se créer des millions d'identités et de subvertir ainsi des systèmes de vote. L'attaque Sybil permet de « bourrer les urnes » virtuelles. (Ne ratez pas l'article de Wikipédia sur l'astroturfing.)
C'est pour lutter contre cette attaque que Bitcoin utilise la preuve de travail et que CAcert utilise des certifications faites pendant des rencontres physiques, avec vérification de l'identité étatique. Le RFC note qu'on n'a pas actuellement de solution générale au problèmes des attaques Sybil, si on exige de cette solution qu'elle soit écologiquement durable (ce que n'est pas la preuve de travail) et entièrement pair-à-pair (ce que ne sont pas les systèmes d'enrôlement typiques, où un acteur privilégié vérifie les participants à l'entrée). Quant aux solutions à base de « réseaux de connaissances » (utilisées dans le Web of Trust de PGP), elles sont mauvaises pour la vie privée, puisqu'elles exposent le graphe social des participants.
Bref, le pair-à-pair n'est pas actuellement la solution idéale à tous les problèmes de droits humains, et les recherches doivent se poursuivre.
Un autre outil est souvent présenté comme solution pour bien des problèmes de respect des droits humains, notamment pour la sécurité de ceux qui vivent et travaillent dans des pays dictatoriaux, le VPN. On entend parfois des discussions entre militants des droits humains, ou bien entre journalistes, sur les avantages comparés de Tor et du VPN pour regarder le Web en toute sécurité. En fait, les deux ne fournissent pas réellement le même service et, pire, les propriétés du VPN sont souvent mal comprises. Le VPN fonctionne en établissant une liaison sécurisée (authentifiée, chiffrée) avec un fournisseur, qui va ensuite vous connecter à l'Internet. Il existe plusieurs systèmes techniques ouverts pour cela (IPsec, OpenVPN) mais la question centrale et difficile est le choix du fournisseur. Les VPN sont très populaires, et il existe donc une offre commerciale abondante. Mais, en général, il est impossible d'évaluer sa qualité, aussi bien technique (même si le protocole est standard, le fournisseur impose souvent un logiciel client à lui, binaire non auditable, et des failles ont déjà été découvertes dans certains VPN) que politique (ce fournisseur de VPN qui dit ne pas garder de journaux dit-il la vérité ?) On est très loin de l'attention qui a été portée à la sécurité de Tor, et des innombrables évaluations et analyses dont Tor a fait l'objet !
Il existe aussi des attaques plus sophistiquées (et pas à la portée de la première police venue) comme la corrélation de trafic (entre ce qui entre dans le VPN et ce qui en sort) si l'attaquant peut observer plusieurs points du réseau (la NSA le fait).
Donc, un rappel à tou·te·s les utilisat·eur·rices·s de VPN : la question la plus importante est celle de votre fournisseur. Le VPN peut vous promettre l'anonymat, vous ne serez pas pour autant anonyme vis-à-vis de votre fournisseur. Celui-ci peut vous trahir ou, tout simplement, comme il est situé dans un pays physique, être forcé par les autorités de ce pays de vous dénoncer.
Une question bien plus délicate avait fait l'objet de nombreux débats à l'IETF, celle d'une possibilité de considérer certaines attaques dDoS comme « légitimes ». C'est par exemple un point de vue qui a été défendu par Richard Stallman. La position classique de l'IETF est qu'au contraire toutes les attaques dDoS sont négatives, impactant l'infrastructure (y compris des tas d'innocents) et sont au bout du compte une attaque contre la liberté d'expression. En simplifiant, il existe trois types d'attaques dDoS, les volumétriques (on envoie le plus de paquets ou d'octets possibles, espérant épuiser les ressources du réseau), les attaques sur les protocoles intermédiaires (comme les SYN flood ou comme le très mal nommé ping of death), attaques qui permettent à l'assaillant de n'envoyer que peu de paquets/octets, et enfin les attaques applicatives, visant les failles d'une application. Une attaque faite par LOIC tient de l'attaque volumétrique (on envoie le plus de requêtes HTTP possibles) et de l'attaque applicative, puisqu'elle ne fonctionne que parce que l'application n'arrive pas à suivre (sur la plupart des sites Web, où il faut exécuter des milliers de lignes de code PHP ou Java pour afficher la moindre page, l'application craque avant le réseau).
Dans les trois cas, cette possibilité d'attaque est avant tout une menace contre les médias indépendants, contre les petites associations ou les individus qui ne peuvent pas ou ne veulent pas payer la « protection » (le mot a un double sens en anglais…) des sociétés spécialisées. Et les attaques dDoS peuvent faciliter la tâche des censeurs hypocrites : il suffit de déguiser une censure en une attaque par déni de service. Une des principales raisons pour lesquelles on ne peut pas comparer l'attaque dDoS à une manifestation est que, dans une attaque dDoS, la plupart des participants ne sont pas volontaires, ce sont des zombies. Lorsque des gens manifestent dans la rue, ils donnent de leur temps, et parfois prennent des risques personnels. Lorsqu'une organisation puissante loue les services d'un botnet pour faire taire par dDoS un gêneur, elle ne dépense qu'un peu d'argent.
Il y a bien sûr quelques exceptions (l'opération Abibil ou bien le Green Movement) mais elles sont rares. Il est donc parfaitement justifié que l'IETF fasse tout son possible pour rendre les attaques dDoS plus difficiles (RFC 3552, section 4.6). Dans la discussion menant à ce nouveau RFC 8280, certaines voix se sont élevées pour demander qu'on puisse lutter seulement contre les « mauvaises » dDoS. Mais c'est aussi absurde que l'idée récurrente des ministres de faire de la cryptographie « légale » qui ne pourrait protéger que les gens honnêtes !
Nous en arrivons maintenant à la partie la plus utilitaire de ce RFC, la section 6, qui est la méthodologie qui devrait être suivie lors du développement d'un nouveau protocole, pour comprendre son impact sur les droits humains, et pour essayer de minimiser les conséquences négatives, et maximiser les positives. Cette section 6 (depuis largement remplacée par le RFC 9620) concerne donc surtout les développeurs de protocole, par exemple les auteurs des RFC techniques. (C'est pour cela que le début de la section 6 répète beaucoup de choses dites avant : on pense que pas mal de techniciens ne liront que cette section.) Évidemment, les conséquences (bonnes ou mauvaises) d'un protocole, ne sont pas uniquement dans la norme technique qui le définit. La façon dont le protocole est mis en œuvre et déployé joue un rôle crucial. (Par exemple, la domination excessive de Gmail n'est pas inscrite dans le RFC 5321.)
Un bon exemple d'une telle démarche est donnée par le RFC 6973, sur la protection de la vie privée. La première responsabilité du développeur de protocole est d'examiner les menaces sur les droits humains que ce protocole peut créer ou aggraver. De même qu'il est recommandé de réfléchir aux conséquences d'un nouveau protocole pour la sécurité de l'Internet (RFC 3552), et sur les conditions dans lesquelles ce protocole est utile, de même il faut désormais réfléchir aux conséquences de son protocole sur les droits humains. Notons que notre RFC ne dit pas « voici ce qu'il faut faire pour respecter les droits humains ». Cela serait clairement irréaliste, vu la variété des menaces et la diversité des protocoles. Notre RFC demande qu'on se pose des questions, il ne fournit pas les réponses. Et il n'impose pas d'avoir dans chaque RFC une section « Human Rights Considerations » comme il existe une « Security Considerations » obligatoire.
Bon, maintenant, la liste des choses à vérifier quand vous concevez un nouveau protocole (section 6.2). À chaque fois, il y a une ou plusieurs questions, une explication, un exemple et une liste d'impacts. Par exemple, pour la question de la connectivité, les questions sont « Est-ce que votre protocole nécessite des machines intermédiaires ? Est-ce que ça ne pourrait pas être fait de bout en bout, plutôt ? Est-ce que votre protocole marchera également sur des liens à faible capacité et forte latence ? Est-ce que votre protocole est à état (alors que les protocoles sans état sont souvent plus robustes) ? » L'explication consiste surtout à répéter l'intérêt des systèmes de bout en bout (RFC 1958). L'exemple est évidemment celui des conséquences négatives des middleboxes. Et les impacts sont les conséquences sur la liberté d'expression et la liberté d'association. Bien sûr, tous les protocoles IETF se préoccupent peu ou prou de connectivité, mais ce n'était pas considéré jusqu'à présent comme pouvant impacter les droits humains.
Sur le deuxième point à vérifier, la vie privée, notre RFC renvoie au RFC 6973, qui demandait déjà aux auteurs de protocoles de faire attention à ce point.
Le troisième point est celui de la neutralité vis-à-vis du contenu. Il reste un peu vague, il n'y a pas actuellement d'exemple de protocole IETF qui soit activement discriminant vis-à-vis du contenu.
Quatrième point qui nécessite l'attention du développeur de protocole, la sécurité. Il est déjà largement traité dans de nombreux autres RFC (notamment le RFC 3552), il faut juste rappeler que ce point a des conséquences en matières de droits humains. Si un protocole a une faille de sécurité, cela peut signifier l'emprisonnement, la torture ou la mort pour un dissident.
En prime, le RFC rappelle que, contrairement à une utilisation réthorique fréquente, il n'y a pas une sécurité mais plusieurs services de sécurité. (Et certaines de ses propriétés peuvent avoir des frictions, par exemple la disponibilité et la confidentialité ne s'entendent pas toujours bien.)
Cinquième point que le développeur de protocoles doit vérifier, l'internationalisation (voir aussi le douzième point, sur la localisation). Eh oui, restreindre l'utilisation de l'Internet à ceux qui sont parfaitement à l'aise en anglais n'irait pas vraiment dans le sens des droits humains, notamment des droits à participer à la vie politique et sociale. D'où les questions « Est-ce que votre protocole gère des chaînes de caractères qui seront affichées aux humains ? Si oui, sont-elles en Unicode ? Au passage, avez-vous regardé le RFC 6365 ? » Dans le contexte IETF (qui s'occupe de protocoles et pas d'interfaces utilisateur), l'essentiel du travail d'internationalisation consiste à permettre de l'Unicode partout. Partout ? Non, c'est un peu plus compliqué que cela car l'IETF distingue les textes prévus pour les utilisat·eur·rice·s de ceux prévus pour les programmes (RFC 2277). Seuls les premiers doivent absolument permettre Unicode. (Cette distinction ne marche pas très bien pour les identificateurs, qui sont prévus à la fois pour les utilisat·eur·rice·s et pour les programmes, c'est le cas par exemple des noms de domaine.)
En prime, petite difficulté technique, il ne suffit pas
d'accepter Unicode, il faut encore, si on accepte d'autres
jeux de
caractères et/ou encodages,
l'indiquer (par exemple le charset=
de
MIME), sinon on risque le mojibake. Ou alors, si on n'accepte qu'un
seul jeu de caractères / encodage, ce doit être UTF-8.
Sixième point sur la liste, une question dont les conséquences pour les droits humaines sont évidentes, la résistance à la censure. « Est-ce que votre protocole utilise des identificateurs qui peuvent être associés à des personnes ou à un contenu spécifique ? Est-ce que la censure peut être explicite ? Est-ce que la censure est facile avec votre protocole ? Si oui, ne pourrait-on pas le durcir pour la rendre plus difficile ? »
Un exemple est bien sûr la longue discussion du passé au sujet d'une méthode de fabrication des adresses IPv6. Le mécanisme recommandé à l'origine mettait l'adresse MAC dans l'adresse IP. Outre l'atteinte à la vie privée, cela facilitait la censure, permettant de bloquer un contenu pour seulement certaines personnes. (Ce mécanisme a été abandonné il y a longtemps, cf. RFC 4941.) Quand au cas de rendre la censure explicite, c'est une référence au code 451 (RFC 7725).
Septième point, les « normes ouvertes ». Intuitivement, il est évident qu'il vaut mieux des normes ouvertes que fermées. Mais attention, il n'existe pas de définition claire et largement adoptée, même pas à l'intérieur de l'IETF (qui est certainement une organisation très ouverte). Les questions posées dans ce RFC 8280 donnent une idée des critères qui peuvent permettre de décider si une norme est ouverte ou pas : « Le protocole est-il documenté publiquement ? Sa mise en œuvre peut-elle être faite sans code privateur ? Le protocole dépend t-il d'une technologie contrôlée par une entreprise particulière ? Y a-t-il des brevets (RFC 3979 et RFC 6701) ? »
Ce sont les normes ouvertes de la famille TCP/IP qui ont permis le développement et le déploiement massif de l'Internet. Les appropriations intellectuelles comme le secret industriel ou comme les brevets auraient tué l'Internet dans l'œuf. Il est donc logique que l'IETF soit une organisation particulièrement ouverte : les RFC sont publics et librement redistribuables, bien sûr (ce qui n'est pas le cas des normes d'autres SDO comme l'AFNOR, l'ISO ou l'IEEE), mais l'IETF publie également ses documents temporaires, ses listes de diffusion et ses réunions (ce que ne fait pas, par exemple, l'UIT).
(On note que le RFC 6852 traite également cette question mais c'est un document purement tactique, qui fait du « open washing » en faisant comme si l'IEEE était ouverte.)
Je saute le point huit, sur l'acceptation de l'hétérogénéité du réseau, et j'en arrive à l'important point neuf, sur l'anonymat. Le terme est très galvaudé (« Bitcoin est une monnaie anonyme » et autres erreurs). Il est souvent utilisé par les non-spécialistes comme synonyme de « une autre identité que celle de l'état civil » (ce qui est en fait la définition du pseudonyme, traité au point suivant). Normalement, même si la définition du RFC 4949 est très peu claire, l'anonymat implique la non-traçabilité : si un système est réellement anonyme, il ne doit pas être possible d'attribuer deux actions à la même entité.
Autre erreur courante quand on parle d'anonymat, la considérer comme une propriété binaire. C'est ce qui est fait quand un responsable ignorant affirme « les données sont anonymisées » (cela doit en général déclencher un signal d'alarme). En effet, il existe de nombreuses techniques, et en progrès rapide, pour « désanonymiser », c'est-à-dire pour relier des actions qui ne l'étaient a priori pas.
Cette confusion est d'autant plus dommage que l'anonymat est une propriété essentielle pour la sécurité du citoyen dans un monde numérique. Autrefois, la plupart des actions qu'on faisait dans la journée étaient anonymes, au sens où un observateur extérieur ne pouvait pas facilement les relier entre elles. Aujourd'hui, si vous mettez une photo sur Instagram, achetez un livre sur Amazon, et écrivez un document sur Google Docs, toutes ces actions sont facilement reliables entre elles, même si vos comptes se nomment respectivement « titi75 », « jean.durand » et « le_type_du_coin ». Par défaut, dans le monde numérique, tout est traçable, et il faut déployer des technologies compliquées pour retrouver un peu d'obscurité. En tout cas, rappelez-vous que l'anonymat n'est jamais parfait : c'est un but souhaitable, mais pas forcément atteignable.
Par exemple, la présence de votre adresse IP dans chaque paquet est un moyen simple de relier toutes vos activités (il en existe d'autres). Il est donc tout à fait légitime que l'adresse IP soit regardée comme une donnée personnelle.
Le pseudonymat, dixième point, est une propriété moins forte. C'est simplement le fait d'utiliser une identité persistante qui n'est pas l'identité officielle. On va utiliser un pseudonyme quand on veut masquer son orientation sexuelle, ou sa transidentité, ou l'entreprise où on travaille, mais tout en gardant une identité constante, par exemple pour avoir une réputation en ligne. C'est souvent une protection nécessaire contre le harcèlement, dont les femmes sont particulièrement fréquemment victimes en ligne. Outre les pseudonymes qu'on choisit, la nature du monde numérique fait que plein d'identificateurs attribués automatiquement sont des pseudonymes. Ainsi, une adresse IP est un pseudonyme (elle cesse de l'être dès que votre FAI communique le nom et l'adresse de l'abonné aux autorités). Une adresse Bitcoin est un pseudonyme (Bitcoin est très traçable, c'est nécessaire pour son fonctionnement).
L'auteur·e de protocoles doit donc se méfier des informations qu'ielle expose. Si elles permettent de retrouver la personne à l'origine de la communication, ce sont des données personnelles. Par exemple, si vous exportez des données contenant des adresses IP (exemples en RFC 7011 ou bien pour les journaux d'un serveur), une des façons de brouiller la traçabilité (et donc de passer du pseudonymat à un relatif anonymat) est de ne garder qu'un préfixe assez général. Condenser les adresses IP n'est pas très efficace, un attaquant, un « désanonymiseur » peut condenser toutes les adresses possibles et ainsi retrouver l'information. D'une manière générale, soyez modeste : réellement anonymiser est très difficile.
Le onzième point concerne un sujet dont les conséquences en
matière de droits humains sont claires pour quiconque a suivi les
conférences Paris Web :
l'accessibilité à tou·te·s, y compris en cas
de handicap. L'accessibilité est une propriété nécessaire pour
faire respecter le droit à ne pas être victime de
discrimination. Cela ne concerne a priori pas l'IETF, qui ne fait
pas d'interface utilisateur, mais c'est utile pour d'autres
SDO. Ainsi, le RFC donne l'exemple de
HTML, où l'obligation de mettre un attribut
alt
pour les images, oblige à réfléchir à
l'accessibilité de la page Web aux malvoyants.
Le treizième point porte sur un concept très flou et d'autant plus répété qu'il n'a pas de définition claire : la « décentralisation ». Mon article à JRES sur ce sujet donne une idée de la complexité de la question. Le RFC traite la question par plusieurs questions : « Est-ce que le protocole a un point de contrôle unique ? Est-ce que le protocole ne pourrait pas être fédéré plutôt ? » Ça reste à mon avis trop vague mais, au moins, ça pousse les concepteurs de protocoles à réfléchir. La plupart des protocoles de base de l'Internet sont décentralisés ou fédérés comme vous voulez (et c'est explicitement souhaité par le RFC 3935), mais les services à la mode sont en général centralisés. (XMPP est fédéré mais dans les startups, on est obligés d'utiliser Slack qui est centralisé.)
Passons sur le point 14, la fiabilité (c'est sûr qu'un réseau qui marche, c'est mieux) et voyons le point 15, la confidentialité. Son impact sur les droits humains est clair : si des gens peuvent lire ma correspondance privée, mes droits sont clairement violés. La solution technique la plus évidente est le chiffrement et, aujourd'hui, le RFC 8280 estime à juste titre qu'un protocole sans possibilité de chiffrement (par exemple RFC 3912) est à éviter (RFC 3365). Et, même si le RFC ne le dit pas explicitement, il doit évidemment choisir d'un chiffrement sérieux, donc sans portes dérobées, sans affaiblissement délibéré.
Rédigé il y a trop longtemps, cette section dit que le DNS ne dispose pas de cette possibilité de chiffrement, mais c'est faux, depuis le RFC 7858.
Le RFC note à juste titre que le chiffrement ne protège pas contre les intermédiaires légitimes, comme dans l'exemple de XMPP cité plus haut : le chiffrement entre le client et le premier serveur XMPP ne protège pas contre ce serveur, qui voit tout passer en clair. Mais le RFC oublie de dire qu'il y a également le problème des extrémités : faire du HTTPS avec Gmail ne vous protège pas contre PRISM. Il faut donc également prévoir de la minimisation (envoyer le moins de données possible) pas seulement du chiffrement.
Je saute les points 16 (intégrité), 17 (authentification) et 18 (adaptabilité), je vous laisse les lire dans le RFC. Le dernier point, le dix-neuvième, porte sur la transparence : les utilisat·eur·rice·s peuvent-il·elle·s voir comment fonctionne le protocole et notamment quels sont les acteurs impliqués ? Par exemple, un service qui semble « gratuit » peut l'être parce qu'il y a derrière une grosse activité économique, avec de la publicité ciblée en exploitant vos données personnelles. Bref, si c'est gratuit, c'est peut-être que vous êtes le produit. Et vous ne le voyez peut-être pas clairement.
Voilà, si vous voulez en savoir plus, le RFC a une colossale bibliographie. Bonne lecture. Si vous préférez les vidéos, il y a mon intervention à Radio-France sur ce sujet.
Date de publication du RFC : Novembre 2017
Auteur(s) du RFC : P. Kampanakis (Cisco Systems), M. Suzuki (NICT)
Pour information
Réalisé dans le cadre du groupe de travail IETF mile
Première rédaction de cet article le 20 novembre 2017
Le format IODEF, dont la dernière version est décrite dans le RFC 7970, est un format structuré permettant l'échange de données sur des incidents de sécurité. Cela permet, par exemple, aux CSIRT de se transmettre des données automatiquement exploitables. Ces données peuvent être produites automatiquement (par exemple par un IDS, ou bien issues du remplissage manuel d'un formulaire). IODEF est riche, très riche, peut-être trop riche (certaines classes qu'il définit ne sont que rarement utilisées). Il peut donc être difficile de programmer des outils IODEF, et de les utiliser. (En pratique, il me semble qu'IODEF est peu utilisé.) Ce RFC, officiellement, est donc chargé d'aider ces professionnels, en expliquant les cas les plus courants et les plus importants, et en guidant programmeurs et utilisateurs.
Personnellement, je ne suis pas convaincu du résultat, ce RFC me semble plutôt un pot-pourri de diverses choses qui n'avaient pas été mises dans la norme.
La section 3 du RFC discute de l'utilisation de base d'IODEF. Reprenant la section 7.1 du RFC 7970, elle présente le document IODEF minimum, celui avec uniquement l'information obligatoire :
<?xml version="1.0" encoding="UTF-8"?> <IODEF-Document version="2.00" xml:lang="en" xmlns="urn:ietf:params:xml:ns:iodef-2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.iana.org/assignments/xml-registry/schema/iodef-2.0.xsd"> <Incident purpose="reporting" restriction="private"> <IncidentID name="csirt.example.com">492382</IncidentID> <GenerationTime>2015-07-18T09:00:00-05:00</GenerationTime> <Contact type="organization" role="creator"> <Email> <EmailTo>contact@csirt.example.com</EmailTo> <!-- Pas réellement obligatoire, mais le document serait vraiment sans intérêt sans lui. --> </Email> </Contact> </Incident> </IODEF-Document>
Un tel document, comportant
une instance de la classe Incident
, qui comprend elle-même une
instance de la classe Contact
, serait syntaxiquement correct mais
n'aurait guère d'intérêt pratique. Des documents un peu plus
réalistes figurent dans l'annexe B.
Le programmeur qui génère ou traite des fichiers IODEF n'a pas
forcément à mettre en œuvre la totalité des classes. Il peut se
contenter de ce qui est important pour son ou ses scénarios
d'utilisation. Par exemple, si on travaille sur les
dDoS, la classe Flow
est la plus
importante, puisqu'elle décrit le trafic de l'attaque. (L'annexe
B.2 du RFC contient un fichier IODEF décrivant une attaque faite
avec LOIC. Je l'ai copié ici dans ddos-iodef.xml
.) De même, si
on travaille sur le C&C d'un
logiciel malveillant, les classes
Flow
et ServiceName
sont cruciales. Bref, il faut analyser ce dont on a besoin.
La section 4 du RFC mentionne les extensions à IODEF. Si riche que soit ce format, on peut toujours avoir besoin d'autres informations et c'est pour cela qu'IODEF est extensible. Par exemple, le RFC 5901 décrit une extension à IODEF pour signaler des cas de hameçonnage. Évidemment, on ne doit définir une extension que s'il n'existe pas de moyen existant de stocker l'information dans de l'IODEF standard.
La section 4 rappelle aussi aux développeurs que, certes, IODEF
bénéfice d'un mécanisme d'indication de la
confidentialité (l'attribut
restriction
, qui se trouve dans les deux
exemples que j'ai cité), mais qu'IODEF ne fournit aucun
moyen technique de le faire respecter. Les documents IODEF étant
souvent sensibles, puisqu'ils parlent de problèmes de sécurité, le programmeur qui réalise un système de
traitement de fichiers IODEF doit donc mettre en œuvre des mesures
pratiques de protection de la confidentialité
(chiffrement des fichiers stockés, par
exemple).
Questions mise en œuvre d'IODEF, le RFC 8134 détaille des programmes existants, indique où les récupérer quand ils sont accessibles en ligne, et analyse leurs caractéristiques. C'est le cas par exemple d'iodeflib.
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : J. Brzozowski (Comcast
Cable), G. Van De Velde (Nokia)
Pour information
Réalisé dans le cadre du groupe de travail IETF v6ops
Première rédaction de cet article le 5 décembre 2017
Ce court RFC explique comment (et pourquoi) attribuer un préfixe IPv6 unique à chaque machine, même quand le média réseau où elle est connectée est partagé avec d'autres machines.
Ce RFC s'adresse aux gens qui gèrent un grand réseau de couche 2, partagé par de nombreux abonnés. Un exemple est celui d'un serveur dédié connecté à un Ethernet partagé avec les serveurs d'autres clients. Un autre exemple est celui d'une connexion WiFi dans un congrès ou un café. Dans les deux cas, la pratique sans doute la plus courante aujourd'hui est de donner une seule adresse IPv6 à la machine (ou, ce qui revient au même, un préfixe de 128 bits). C'est cette pratique que veut changer ce RFC. Le but est de mieux isoler les clients les uns des autres, et de bien pouvoir gérer les abonnements et leur utilisation. (Justement le problème de Comcast, dont un des auteurs du RFC est un employé, cf. section 1.) Les clients ne se connaissent en effet pas les autres et il est important que les actions d'un client ne puissent pas trop affecter les autres (et qu'on puisse attribuer les actions à un client précis, pour le cas où ces actions soient illégales). En outre, certaines options d'abonnement sont « par client » (section 3, qui cite par exemple le contrôle parental, ou la qualité de service, qui peut être plus faible pour ceux qui ne paient pas le tarif « gold ».)
Si chaque client a un préfixe IPv6 à lui (au lieu d'une seule adresse IP), toute communication entre clients passera forcément par le routeur géré par l'opérateur, qui pourra ainsi mieux savoir ce qui se passe, et le contrôler. (Les lecteurs férus de routage ont noté que le client, s'il est administrateur de sa machine, peut toujours changer les tables de routage, mais cela n'affectera que le trafic aller, le retour passera toujours par le routeur. De toute façon, je soupçonne que la technique décrite dans ce RFC ne marche que si le réseau donne un coup de main, pour isoler les participants.)
Le RFC affirme que cela protègera contre des attaques comme l'épuisement de cache Neighbor Discovery, les redirections malveillantes faites avec ICMP ou les RAcailles (RFC 6104). Cela éviterait de devoir déployer des contre-mesures comme le RA Guard (RFC 6105). Là aussi, il me semble personnellement que ça n'est complètement vrai que si l'attaquant n'est pas administrateur sur sa machine. Ou alors, il faut que le réseau ne soit pas complètement partagé, qu'il y ait un mécanisme de compartimentage.
Les mécanismes décrits ici supposent que la machine du client utilise SLAAC (RFC 4862) pour obtenir une adresse IP. Cette obtention peut aussi passer par DHCP (RFC 8415) mais c'est plus rare, relativement peu de clients étant capable de demander une adresse en DHCP (RFC 7934).
La section 4 du RFC décrit comment le client obtient ce
préfixe. Il va envoyer un message RS (Router
Solicitation, voir le RFC 4861,
section 3) et écouter les réponses, qui lui diront son adresse IP
mais aussi d'autres informations comme les résolveurs
DNS à utiliser (voir RFC 8106). Pas de changement côté client, donc (ce qui
aurait rendu ce mécanisme difficile à déployer). Par contre, côté
« serveur », il y a de légers changements. Le routeur qui reçoit les RS
et génère des RA (Router Advertisement), qu'ils
aient été sollicités ou pas, va devoir les envoyer uniquement à
une machine (puisque chaque client a un préfixe différent : il ne
faut donc pas diffuser bêtement). Comme le RFC 4861 (sections 6.2.4 et 6.2.6) impose que
l'adresse IP de destination soit
ff02::1
(« tous les nœuds IPv6 »), l'astuce
est d'utiliser comme adresse MAC, non pas
l'adresse multicast habituelle, mais une
adresse unicast (RFC 6085). Ainsi, chaque client ne recevra que
son préfixe.
Ce RA contient le préfixe que l'opérateur alloue à ce client particulier. Les options du RA (RFC 4861, section 4.2) sont :
Le bit A étant mis à un, la machine qui a obtenu le préfixe peut s'attribuer une adresse IP à l'intérieur de ce préfixe, , avec SLAAC, comme indiqué dans le RFC 4862. Elle doit suivre la procédure DAD (Duplicate Address Detection, RFC 4862, section 5.4) pour vérifier que l'adresse IP en question n'est pas déjà utilisée.
Voilà, l'essentiel de ce RFC était là. La section 5 concerne quelques détails pratiques, par exemple ce que peut faire la machine client si elle héberge plusieurs machines virtuelles ou containers (en gros, elle alloue leurs adresses en utilisant le préfixe reçu).
Ce mécanisme de préfixe IP spécifique à chaque client de l'opérateur n'est pas sans poser des questions liées à la vie privée, comme l'explique la section 7 du RFC. (Rappelez-vous la section 1, qui disait qu'un des buts de cette technique était de satisfaire aux « obligations légales », autrement dit de pouvoir suivre à la trace ses utilisateurs.) Bien sûr, la machine cliente peut utiliser le système du RFC 8981, mais, ici, il aurait peu d'impact. Même avec un identificateur d'interface temporaire et imprévisible, le préfixe resterait, et identifierait parfaitement le client. Le RFC mentionne (mais sans l'exiger) qu'on peut limiter les dégâts en changeant le préfixe de temps en temps.
Date de publication du RFC : Novembre 2017
Auteur(s) du RFC : C. Schmitt, B. Stiller (University of Zurich), B. Trammell (ETH Zurich)
Pour information
Première rédaction de cet article le 15 novembre 2017
Le format IPFIX, normalisé dans le RFC 7011, permet à un équipement réseau d'envoyer des données résumées à un collecteur, à des fins d'études ou de supervision. À l'origine, l'idée était que l'équipement réseau soit un routeur, une machine relativement grosse, munie de suffisamment de ressources pour pouvoir s'accomoder d'un protocole un peu compliqué, et qui nécessite l'envoi de pas mal d'octets. L'IPFIX original était donc peu adapté aux engins contraints, par exemple aux capteurs connectés. D'où cette variante d'IPFIX, TinyIPFIX, qui vise à être utilisable par des objets connectés comme ceux utilisant le 6LoWPAN du RFC 4944 (un compteur Linky ?)
Mais prenons plutôt comme exemple un capteur non connecté au réseau électrique (donc dépendant d'une batterie, qu'on ne peut pas recharger tout le temps, par exemple parce que le capteur est dans un lieu difficile d'accès) et n'ayant comme connexion au réseau que le WiFi. L'émission radio coûte cher en terme d'énergie et notre capteur va donc souvent éteindre sa liaison WiFi, sauf quand il a explicitement quelque chose à transmettre. Un protocole de type pull ne peut donc pas convenir, il faut du push, que le capteur envoie ses données quand il le décide. Ces contraintes sont détaillées dans « Applications for Wireless Sensor Networks », par l'auteure du RFC (pas trouvé en ligne, c'est publié dans le livre « Handbook of Research on P2P and Grid Systems for Service-Oriented Computing: Models, Methodologies and Applications », édité par Antonopoulos N.; Exarchakos G.; Li M.; Liotta A. chez Information Science Publishing).
Le RFC donne (section 3) l'exemple de l'IRIS de Crossbow : sa taille n'est que de 58 x 32 x 7 mm, et il a 128 ko de flash pour les programmes (512 ko pour les données mesurées), 8 ko de RAM et 4 d'EEPROM pour sa configuration. On ne peut pas demander des miracles à un objet aussi contraint. (Si c'est vraiment trop peu, le RFC cite aussi l'engin d'Advantic avec ses 48 ko de flash « programme », 1024 ko de flash « données », 10 ko de RAM et 16 d'EEPROM.) Question énergie, ce n'est pas mieux, deux piles AA de 2 800 mAh chacune peuvent donner en tout 30 240 J.
Autre contrainte vécue par ces pauvres objets connectés, les limites du protocole réseau (section 3.3 de notre RFC). 6LoWPAN (RFC 4944) utilise IEEE 802.15.4. Ce protocole ne porte que 102 octets par trame. Ce n'est pas assez pour IPv6, qui veut une MTU minimum de 1 280 octets. Il faut donc utiliser la fragmentation, un mécanisme problématique, notamment parce que, si un seul fragment est perdu (et ces pertes sont des réalités, sur les liaisons radio), il faut retransmettre tout le paquet. Il est donc prudent de s'en tenir à des paquets assez petits pour tenir dans une trame. C'est un des apports de TinyIPFIX par rapport au IPFIX classique : des messages plus petits.
Enfin, dernière contrainte, le protocole de transport. IPFIX impose (RFC 7011, section 10.1) que SCTP soit disponible, même s'il permet aussi UDP et TCP. Mais SCTP (et TCP) ne permettent pas d'utiliser les mécanismes de compression des en-têtes de 6LoWPAN. Et SCTP n'est pas toujours présent dans les systèmes d'exploitation des objets, par exemple TinyOS. TinyIPFIX utilise donc UDP. À noter que, comme le demande la section 6.2 du du RFC 5153, TinyIPFIX sur UDP n'est pas prévu pour l'Internet ouvert, mais uniquement pour des réseaux fermés.
TinyIPFIX est dérivé de IPFIX (RFC 7011) et en hérite donc de la plupart des concepts, comme la séparation des données (Data Sets) et de la description des données (dans des gabarits, transmis en Template Sets).
La section 4 du RFC décrit des scénarios d'usage. Comme TinyIPFIX (comme IPFIX) est undirectionnel (de l'exporteur vers le collecteur), et qu'il tourne sur UDP (où les messages peuvent se perdre), le développeur doit être conscient des limites de ce service. Si on perd un paquet de données, on perd des données. Pire, si on perd un paquet de gabarit (RFC 7011, sections 3.4.1 et 8), on ne pourra plus décoder les paquets de données suivants. On ne doit donc pas utiliser TinyIPFIX pour des systèmes où la perte de données serait critique. Un système d'accusés de réception et de retransmission (refaire une partie de TCP, quoi…) serait trop lourd pour ces engins contraints (il faudrait stocker les messages en attendant l'accusé de réception).
Le RFC recommande de renvoyer les paquets de gabarit de temps en temps. C'est normalement inutile (on n'imagine pas un capteur contraint en ressources changer de gabarit), mais cela permet de compenser le risque de perte. Le collecteur qui, lui, n'a pas de contraintes, a tout intérêt à enregistrer tous les messages, même quand il n'y a pas de gabarit, de manière à pouvoir les décoder quand le gabarit arrivera. (Normalement, on ne fait pas ça avec IPFIX, le gabarit ne peut s'appliquer qu'aux messages reçus après, mais, avec TinyIPFIX, il y a peu de chances que les gabarits changent.)
Le RFC donne un exemple animalier qui conviendrait au déploiement de TinyIPFIX, afin de surveiller des oiseaux (Szewczyk, R., Mainwaring, A., Polastre, J., et D. Culler, « An analysis of a large scale habitat monitoring application ».) Les capteurs notent ce que font ces charmants animaux et le transmettent.
Cet exemple sert à illustrer un cas où TinyIPFIX serait bien adapté : collecte de type push, efficacité en terme de nombre de paquets, perte de paquets non critique, pas de nécessité d'un estampillage temporel des messages (qui existe dans IPFIX mais que TinyIPFIX supprime pour alléger le travail).
La section 5 décrit l'architecture de TinyIPFIX, très similaire à celle d'IPFIX (RFC 5470).
Enfin, la section 6 décrit les aspects concrets de TinyIPFIX, notamment le format des messages. Il ressemble beaucoup à celui d'IPFIX, avec quelques optimisations pour réduire la taille des messages. Ainsi, l'en-tête de message IPFIX fait toujours 16 octets, alors que dans TinyIPFIX, il est de taille variable, avec seulement 3 octets dans le meilleur des cas. C'est ainsi que des champs comme le numéro de version (qui valait 11 pour IPFIX) ont été retirés. De même, l'estampille temporelle (« Export Time » dans IPFIX) est partie (de toute façon, les objets contraints ont rarement une horloge correcte).
Les objets contraints déployés sur le terrain n'ont souvent pas à un accès direct à Internet, à la fois pour des raisons de sécurité, et parce qu'un TCP/IP complet serait trop lourd pour eux. Il est donc fréquent qu'ils doivent passer par des relais qui ont, eux, un vrai TCP/IP, voire un accès Internet. (Cette particularité des déploiements d'objets connectés est une des raisons pour lesquelles le terme d'« Internet des Objets » n'a pas de valeur autre que marketing.)
TinyIPFIX va donc fonctionner dans ce type d'environnement et la section 7 de notre RFC décrit donc le mécanisme d'« intermédiation ». L'intermédiaire peut, par exemple, transformer du TinyIPFIX/UDP en TinyIPFIX/SCTP ou, carrément, du TinyIPFIX en IPFIX. Dans ce dernier cas, il devra ajouter les informations manquantes, comme l'estampille temporelle ou bien le numéro de version.
Côté mise en œuvre, Tiny IPFIX a été mis dans
TinyOS et
Contiki. Voir http://www.net.in.tum.de/pub/cs/TinyIPFIX_GUI_Licenced.zip
et http://www.net.in.tum.de/pub/cs/TinyIPFIX_and_Extentions_Licenced.zip
.
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : L. Velvindron (Hackers.mu), M. Baushke (Juniper Networks)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 6 décembre 2017
Un RFC de moins de quatre pages, boilerplate administratif inclus, pour passer la taille minimum des modules des groupes Diffie-Hellman utilisés par SSH, de 1 024 bits à 2 048.
L'échange Diffie-Hellman dans SSH est décrit dans le RFC 4419, que notre nouveau RFC met à jour. C'est dans le RFC 4419 (sa section 3) que se trouvait la recommandation d'accepter au moins 1 024 bits pour le module du groupe. Or, cette taille est bien faible face aux attaques modernes comme Logjam.
Voilà, c'est tout, on remplace « minimum 1 024 bits » par
« minimum 2 048 » et on peut continuer à utiliser SSH. Si vous
êtes utilisateur d'OpenSSH, la commande de
génération de clés, ssh-keygen
, peut
également générer ces modules (cf. la section Moduli
generation dans le manuel.) Les versions un peu
anciennes ne vous empêchent pas de faire des modules bien trop
petits. Ainsi, sur une version 7.2 :
% ssh-keygen -G moduli-512.candidates -b 512 Fri Oct 20 20:13:49 2017 Sieve next 4177920 plus 511-bit Fri Oct 20 20:14:51 2017 Sieved with 203277289 small primes in 62 seconds Fri Oct 20 20:14:51 2017 Found 3472 candidates % ssh-keygen -G moduli-256.candidates -b 256 Too few bits: 256 < 512 modulus candidate generation failed
Le RGS recommande quant à lui 3 072 bits minimum (la règle exacte est « RègleLogp-1. La taille minimale de modules premiers est de 2048 bits pour une utilisation ne devant pas dépasser l’année 2030. RègleLogp-2. Pour une utilisation au delà de 2030, la taille minimale de modules premiers est de 3072 bits. »)
Enfin, la modification d'OpenSSH pour se conformer à ce RFC est juste un changement de la définition de DH_GRP_MIN.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Saint-Andre (Jabber.org)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF precis
Première rédaction de cet article le 6 octobre 2017
Bien des protocoles Internet manipulent des noms qui doivent être parlants pour les utilisateurs et donc, de nos jours, doivent pouvoir être en Unicode. Les noms purement ASCII appartiennent à un passé révolu. Le groupe de travail PRECIS de l'IETF établit des règles pour ces noms, de manière à éviter que chaque protocole, chaque application, ne soit obligé de définir ses propres règles. Ce RFC contient les règles pour un sous-ensemble de ces noms : les noms qui visent plutôt à communiquer avec un utilisateur humain (par opposition aux noms qui sont indispensables aux protocoles réseaux, traités dans le RFC 8265). Il remplace le RFC 7700 (mais il y a peu de changements).
Ces noms « humains » sont typiquement ceux qui sont présentés aux utilisateurs. Ils doivent donc avant tout être « parlants » et il faut donc qu'ils puissent utiliser la plus grande part du jeu de caractères Unicode, sans restrictions arbitraires (contrairement aux identificateurs formels du RFC 8265, pour lesquels on accepte des limites du genre « pas d'espaces » ou « pas d'emojis »).
Le terme utilisé par le RFC pour ces noms « humains » est nicknames, terme qui vient du monde de la messagerie instantanée. Il n'y a pas de terme standard pour les désigner, certains protocoles (comme le courrier) parlent de display names (par opposition au login name ou account name), d'autres utilisent encore d'autres termes (l'article « An Introduction to Petname Systems » peut vous intéresser). Par exemple, dans un message électronique, on pourrait voir :
From: Valérie Pécresse <vp@les-républicains.fr>
Et, dans cet exemple, vp
serait le nom formel
(mailbox name dans le courrier, login
name pour se connecter), alors que Valérie
Pécresse
est le nickname, le nom
montrable aux humains. (Le concept de display
name dans le courrier est normalisé dans la section 3.4.1
du RFC 5322, son exact équivalent XMPP,
le nickname, est dans XEP-0172.)
Autre exemple, le réseau social Mastodon
où mon nom formel est bortzmeyer@mastodon.gougere.fr
alors que la description, le
terme affiché est « S. Bortzmeyer 🗸 » (avec un symbole à la fin, le U+1F5F8).
Comme l'illustre l'exemple ci-dessus, on veut évidemment que le nom puisse être en Unicode, sauf pour la petite minorité des habitants de la planète qui utilisent une langue qui peut s'écrire uniquement en ASCII.
Ces noms « parlants », ces nicknames, ne servent pas qu'à désigner des humains, ils peuvent aussi être utilisés pour des machines, des sites Web (dans les signets), etc.
On pourrait penser qu'il n'y a rien à spécifier pour permettre leur internationalisation. On remplace juste ASCII par Unicode comme jeu de caractères autorisé et vas-y, poupoule. Mais Unicode recèle quelques surprises et, pour que les nicknames fonctionnent d'une manière qui paraitra raisonnable à la plupart des utilisateurs, il faut limiter légèrement leur syntaxe.
Ces limites sont exposées dans la section 2 de notre RFC, qui
définit un profil de PRECIS. PRECIS,
Preparation, Enforcement, and Comparison of
Internationalized Strings est le sigle qui désigne le
projet « Unicode dans tous les identificateurs » et le groupe de
travail IETF qui réalise ce projet. PRECIS définit (RFC 8264) plusieurs classes d'identificateurs et
les nicknames sont un cas particulier de la
classe FreeformClass
(RFC 8264, section 4.3), la moins restrictive
(celle qui permet le plus de caractères).
Outre les restrictions de FreeformClass
(qui n'est pas complètement laxiste : par exemple, cette classe ne
permet pas les caractères de contrôle), le profil
Nickname
:
Thérèse
" et "Thérèse
" sont le
même nom,À noter qu'un nickname doit avoir une taille non nulle, après l'application des ces règles (autrement, un nickname de trois espaces serait réduit à... zéro).
Une fois ce filtrage et cette canonicalisation faite, les nicknames peuvent être comparés par une simple égalité bit à bit (s'ils utilisent le même encodage, a priori UTF-8). Un test d'égalité est utile si, par exemple, un système veut empêcher que deux utilisateurs aient le même nickname.
La section 3 de notre RFC fournit quelques exemples amusants et instructifs de nicknames :
Foo
" et "foo
" sont
acceptables, mais sont le même nom (en application de la régle
d'insensibilité à la casse),Foo Bar
" est permis (les espaces sont
autorisés, avec les quelques restrictions indiquées plus
haut),Échec au roi ♚
" est permis,
rien n'interdit les symboles comme cette pièce du jeu
d'échecs, le caractère Unicode
U+265A,Henri Ⅳ
" est permis (ouvrez
l'œil : c'est le chiffre romain à la fin, U+2163) mais la
normalisation NFKC (précédée du passage en minuscules) va faire
que ce nom est équivalent à "henri iv
"
(avec, cette fois, deux caractères à la fin).Notez que ces règles ne sont pas idempotentes et le RFC demande qu'elles soient appliquées répétitivement jusqu'à la stabilité (ou, au maximum, jusqu'à ce que trois applications aient été faites).
Comme le rappelle la section 4 de notre RFC, les applications doivent maintenant définir l'utilisation de ces noms parlants, où peut-on les utiliser, etc. L'application peut donc avoir des règles supplémentaires, comme une longueur maximale pour ces nicknames, ou des caractères interdits car ils sont spéciaux pour l'application.
L'application ou le protocole applicatif a également toute
latitude pour gérer des cas comme les duplicatas : j'ai écrit plus
haut que "Foo Bar
" et "foo
bar
" étaient considérés comme le même
nickname mais cela ne veut pas dire que leur
coexistence soit interdite : certaines applications permettent à
deux utilisateurs distincts d'avoir le même
nickname. Même chose avec d'autres règles
« métier » comme la possibilité d'interdire certains noms (par
exemple parce qu'ils sont grossiers).
Le profil Nickname
est désormais ajouté au
registre IANA (section 5 du RFC).
L'annexe A décrit les changements depuis le RFC 7700. Le principal est le remplacement de l'opération
Unicode CaseFold()
par
toLower()
pour assurer l'insensibilité à la
casse. La différence est subtile, et ne change rien pour la
plupart des écritures. Sinon, l'erreur notée #4570
a été corrigée. Le reste n'est que de la maintenance.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Saint-Andre (Jabber.org), A. Melnikov (Isode)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF precis
Première rédaction de cet article le 6 octobre 2017
Ah, les plaisirs de l'internationalisation du logiciel... Quand l'informatique ne concernait que les États-Unis, tout était simple. Un utilisateur ne pouvait avoir un nom (un login) que s'il était composé uniquement des lettres de l'alphabet latin (et même chose pour son mot de passe). Mais de nos jours, il n'est pas acceptable de se limiter au RFC 20. Il faut que Пу́тин ou 艾未未 puissent écrire leur nom dans leur écriture et l'informatique doit suivre. Notre RFC décrit comment le faire (il remplace le RFC 7613, mais il y a peu de changements, rassurez-vous).
Mais pourquoi faut-il standardiser quelque chose ? Pourquoi ne
pas dire simplement « les noms d'utilisateur sont en
Unicode » et basta ? Parce que les
logiciels qui gèrent noms d'utilisateurs et mots de passe ont
besoin de les manipuler, notamment de les
comparer. Si ПУ́ТИН
essaie de se loguer, et
que la base de données contient un utilisateur пу́тин
, il faut bien déterminer si c'est
le même utilisateur (ici, oui, à part la casse). C'est en général assez simple dans l'alphabet
latin (ou dans le cyrillique utilisé pour
les exemples) mais il y a d'autres cas plus vicieux qui
nécessitent quelques règles supplémentaires.
Le cadre général de l'internationalisation des identificateurs est normalisé dans le RFC 8264. Notre nouveau RFC 8265 est l'adaptation de ce RFC 8264 au cas spécifique des noms d'utilisateur et des mots de passe.
Ces noms et ces mots de passe (aujourd'hui, il faudrait plutôt
dire phrases de passe) sont largement
utilisés pour l'authentification, soit
directement (SASL
PLAIN
du RFC 4616, authentification
de base de HTTP du RFC 7617), ou
bien comme entrée d'une fonction de condensation
cryptographique (SASL SCRAM
du
RFC 5802 ou bien authentification HTTP
digest du RFC 7616). L'idée est que
les opérations de base sur ces noms (et sur les mots de passe) ne
surprennent pas excessivement l'utilisateur, quel que soit son
pays, sa langue, sa culture. Un Français ou un Belge ne sera probablement pas trop
étonné que Dupont
soit accepté comme synonyme
de dupont
mais il le serait davantage si
dupond
l'était. Évidemment, la tâche est
impossible (les utilisateurs sont tous différents) mais l'idée est
de ne pas faire un système parfait mais un système qui marche la
plupart du temps.
C'est là qu'intervient le cadre PRECIS (PReparation,
Enforcement, and Comparison of Internationalized
Strings) du RFC 8264. Il évite que
chaque développeur d'un système d'authentification doive lire et
comprendre toutes les conséquences d'Unicode, et lui permet de
s'appuyer sur une analyse déjà faite. Un exemple de piège
d'Unicode (et qui montre pourquoi « on va juste dire que les noms
d'utilisateur peuvent être n'importe quel caractère Unicode » est
sans doute une mauvaise politique) est le chiffre 1 en exposant,
U+00B9 (comme ça : « ¹ » Si vous ne voyez rien, c'est parce
que votre logiciel ne sait pas afficher ce caractère. Vous pouvez
toujours regarder le source HTML pour comprendre l'exemple.). Doit-on
l'autoriser ? Le mettre en correspondance avec le 1 traditionnel
de façon à ce que user¹
et
user1
soient le même nom ? Imaginez un client
XMPP qui vous dise
« user¹@example.com
veut être votre
copain. Je l'autorise ? » et que vous acceptiez en croyant que
c'était user1@example.com
. Bien sûr, on ne
peut pas réellement parler d'« attaque » dans ce cas, une telle
erreur permettrait juste de faire quelques farces mais, pour le
logiciel qui vérifie une identité, des confusions seraient plus
gênantes. Si les « attaques » exploitant la similitude de
caractères Unicode sont surtout un fantasme d'informaticien
anglophone n'ayant jamais réellement accepté
l'internationalisation (plutôt qu'une réalité du terrain), il est
quand même plus prudent de supprimer quelques causes de
cafouillage le plus tôt possible.
(Ce RFC suggère également de séparer le nom d'utilisateur, identificateur formel et n'utilisant qu'un jeu de caractères restreint, et la description (cf. RFC 8266) qui pourrait utiliser davantage de caractères. Twitter ou Mastodon fonctionnent ainsi.)
Notre RFC compte deux sections importantes, décrivant le profil
PRECIS pour les noms d'utilisateur (section 3) et les mots de
passe (section 4). Commençons par les noms d'utilisateur. Un nom
est une chaîne de caractères Unicode
composée de parties séparées par des espaces. Chaque partie doit
être une instance de IdentifierClass
et est
normalisée en NFC. Pourquoi cette notion de « parties
séparées par des espaces » ? Parce que la classe
IdentifierClass
du RFC 8264 ne permet pas les espaces, ce qui est gênant pour
certains identificateurs (« Prénom Nom » par exemple, cf. section
3.5). D'où la grammaire de la section 3.1 :
username = userpart *(1*SP userpart)
qui dit « un nom d'utilisateur est composé d'au moins une partie,
les parties étant séparées par un nombre quelconque
d'espaces ». Une des conséquences de cette grammaire étant que le
nombre d'espaces n'est pas significatif : Jean
Dupont
et Jean Dupont
sont
le même identificateur.
Chaque partie étant une instance de
l'IdentifierClass
du RFC 8264, les caractères interdits par cette classe sont
donc interdits pour les noms d'utilisateurs. Une application
donnée peut restreindre (mais pas étendre) ce profil. Ces noms
d'utilisateurs sont-ils sensibles à la casse ? Certains protocoles ont
fait un choix et d'autres le choix opposé. Eh bien, il y a
deux sous-profils, un sensible et
un insensible (ce dernier étant recommandé). Les
protocoles et applications utilisant ce RFC 8265
devront annoncer clairement lequel ils utilisent. Et les
bibliothèques logicielles manipulant ces utilisateurs auront
probablement une option pour indiquer le sous-profil à
utiliser.
Le sous-profil UsernameCaseMapped
rajoute
donc une règle de préparation des chaînes de caractères : tout
passer en minuscules (avant les comparaisons, les condensations
cryptographiques, etc), en utilisant l'algorithme
toLowerCase d'Unicode (section 3.13 de la norme
Unicode ; et, oui, changer la casse est
bien plus compliqué en Unicode qu'en ASCII). Une
fois la préparation faite, on peut comparer octet par octet, si
l'application a bien pris soin de définir l'encodage.
L'autre sous-profil, UsernameCasePreserved
ne change pas la casse, comme son nom
l'indique. ПУ́ТИН
et
пу́тин
y sont donc deux identificateurs
différents. C'est la seule différence entre les deux
sous-profils. Notre RFC recommande le profil insensible à la
casse, UsernameCaseMapped
, pour éviter des
surprises comme celles décrites dans le RFC 6943 (cf. section 8.2 de notre RFC).
Bon, tout ça est bien nébuleux et vous préféreriez des exemples ? Le RFC nous en fournit. D'abord, des identificateurs peut-être surprenants mais légaux (légaux par rapport à PRECIS : certains protocoles peuvent mettre des restrictions supplémentaires). Attention, pour bien les voir, il vous faut un navigateur Unicode, avec toutes les polices nécessaires :
juliet@example.com
: le @ est
autorisé donc un JID (identificateur
XMPP) est légal.fussball
: un nom d'utilisateur
traditionnel, purement ASCII, qui passera
partout, même sur les systèmes les plus antédiluviens.fußball
: presque le même mais
avec un peu d'Unicode. Bien qu'en
allemand, on traite en général ces deux
identificateurs comme identiques, pour PRECIS, ils sont
différents. Si on veut éviter de la confusion aux germanophones,
on peut interdire la création d'un des deux identificateurs si
l'autre existe déjà : PRECIS ne donne que des règles miminales,
on a toujours droit à sa propre politique derrière.π
: entièrement en Unicode, une
lettre.Σ
: une lettre
majuscule.σ
: la même en
minuscule. Cet identificateur et le précédent seront identiques
si on utilise le profil UsernameCaseMapped
et différents si on utilise le profil UsernameCasePreserved
.ς
: la même, lorsqu'elle est en
fin de mot. Le cas de ce sigma final est
compliqué, PRECIS ne tente pas de le résoudre. Comme pour le
eszett plus haut, vous pouvez toujours
ajouter des règles locales.Par contre, ces noms d'utilisateurs ne sont pas valides :
HenriⅣ
: le chiffre romain 4 à
la fin est illégal (car il a ce qu'Unicode appelle « un
équivalent en compatibilité », la chaîne « IV »).♚
: seules les lettres sont
acceptées, pas les symboles (même règle que pour les noms de domaine).Continuons avec les mots de passe (section 4). Comme les noms,
le mot de passe est une chaîne de caractères
Unicode. Il doit
être une instance de FreeformClass
. Cette
classe autorise bien plus de caractères que pour les noms
d'utilisateur, ce qui est logique : un mot de passe doit avoir
beaucoup d'entropie. Peu de caractères sont donc interdits (parmi
eux, les caractères de
contrôle, voir l'exemple plus bas). Les
mots de passe sont sensibles à la casse.
Des exemples ? Légaux, d'abord :
correct horse battery staple
: vous
avez reconnu un fameux dessin de XKCD.Correct Horse Battery Staple
: les
mots de passe sont sensibles à la casse, donc c'est un mot de
passe différent du précédent.πßå
: un mot de passe en Unicode ne pose
pas de problème.Jack of ♦s
: et les symboles sont
acceptés, contrairement à ce qui se passe pour les noms d'utilisateur.foo bar
: le truc qui
ressemble à un trait est
l'espace de l'Ogham, qui doit normalement
être normalisé en un espace ordinaire, donc ce mot de passe est
équivalent à foo bar
.Par contre, ce mot de passe n'est pas valide :
my cat is a by
: les caractères
de contrôle, ici une tabulation, ne sont pas autorisés.Rappelez-vous que ces profils PRECIS ne spécifient que des règles minimales. Un protocole utilisant ce RFC peut ajouter d'autres restrictions (section 5). Par exemple, il peut imposer une longueur minimale à un mot de passe (la grammaire de la section 4.1 n'autorise pas les mots de passe vides mais elle autorise ceux d'un seul caractère, ce qui serait évidemment insuffisant pour la sécurité), une longueur maximale à un nom d'utilisateur, interdire certains caractères qui sont spéciaux pour ce protocole, etc.
Certains profils de PRECIS suggèrent d'être laxiste en acceptant certains caractères ou certaines variantes dans la façon d'écrire un mot (accepter strasse pour straße ? C'est ce qu'on nomme le principe de robustesse.) Mais notre RFC dit que c'est une mauvaise idée pour des mots utilisés dans la sécurité, comme ici (voir RFC 6943).
Les profils PRECIS ont été ajoutés au registre IANA (section 7 de notre RFC).
Un petit mot sur la sécurité et on a fini. La section 8 de notre RFC revient sur quelques points difficiles. Notamment, est-ce une bonne idée d'utiliser Unicode pour les mots de passe ? Ça se discute. D'un côté, cela augmente les possibilités (en simplifiant les hypothèses, avec un mot de passe de 8 caractères, on passe de 10^15 à 10^39 possibilités en permettant Unicode et plus seulement ASCII), donc l'entropie. D'un autre, cela rend plus compliquée la saisie du mot de passe, surtout sur un clavier avec lequel l'utilisateur n'est pas familier, surtout en tenant compte du fait que lors de la saisie d'un mot de passe, ce qu'on tape ne s'affiche pas. Le monde est imparfait et il faut choisir le moindre mal...
L'annexe A du RFC décrit les changements (peu nombreux) depuis
l'ancien RFC 7613. Le principal est le
passage de la conversion de casse de la fonction Unicode
CaseFold()
à
toLowerCase()
. Et
UTF-8 n'est plus obligatoire, c'est
désormais à l'application de décider (en pratique, cela ne
changera rien, UTF-8 est déjà l'encodage recommandé dans
l'écrasante majorité des applications). Sinon, il y a une petite
correction dans l'ordre des opérations du profil
UsernameCaseMapped
, et quelques nettoyages et
mises à jour.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Saint-Andre (Jabber.org), M. Blanchet (Viagenie)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF precis
Première rédaction de cet article le 6 octobre 2017
Dans la longue marche vers une plus grande internationalisation de l'Internet, la question des identificateurs (comme par exemple les noms de domaine) a toujours été délicate. Ce sont en effet à la fois des éléments techniques, traités automatiquement par les programmes, et des marqueurs d'identité, vus par les humains (par exemple sur des cartes de visite) et manipulés par eux. Plutôt que de laisser chaque protocole internationaliser ses identificateurs (plus ou moins bien), l'approche de ce RFC est unificatrice, en élaborant des règles qui peuvent servir à de larges classes d'identificateurs, pour de nombreux protocoles différents. Il remplace le premier RFC qui avait suivi cette voie, le RFC 7564, désormais dépassé (mais les changements sont peu importants, c'est juste de la maintenance).
Cette opposition entre « éléments techniques » et « textes
prévus pour l'utilisateur » est au cœur du RFC 2277 qui pose comme principe politique qu'on
internationalise les seconds, mais pas les premiers. Cet excellent
principe achoppe souvent sur la question des identificateurs, qui
sont les deux à la fois. D'un côté, les programmes doivent les
traiter (les identificateurs doivent donc être clairement définis,
sans ambiguïté), de l'autre les humains les voient, les
communiquent, les échangent (les identificateurs doivent donc
permettre, sinon toutes les constructions du langage humain, en
tout cas un sous-ensemble d'Unicode qui
parait raisonnable aux humains ordinaires : pas question d'imposer
stephane
comme nom de login
à un utilisateur nommé Stéphane, avec un accent sur le E). C'est
cette double nature des identificateurs (ainsi, il est vrai, que
l'énorme couche de bureaucratie qui gère les noms de domaine) qui explique la durée et
la vivacité des discussions sur les IDN.
Maintenant que ces IDN existent (depuis plus de quatorze ans, RFC 3490), que faire avec les autres identificateurs ? Une possibilité aurait été que chaque protocole se débrouille avec ses propres identificateurs, comme l'a fait le DNS avec les noms de domaine. Mais cela menait à une duplication du travail (et tous les auteurs de protocole ne sont pas des experts Unicode) et surtout à un risque de choix très différents : certains protocoles autoriseraient tel caractère Unicode et d'autres pas, sans que l'utilisateur ordinaire puisse comprendre clairement les raisons de cette différence. L'idée de base du groupe PRECIS était donc d'essayer de faire des règles qui s'appliqueraient à beaucoup de protocoles, épargnant aux concepteurs de ces protocoles de les concevoir eux-mêmes, et offrant à l'utilisateur une certaine homogénéité. Ce RFC 8264 est le cadre de définition des identificateurs internationalisés. Ce cadre permet la manipulation d'identificateurs internationalisés (par exemple leur comparaison, comme lorsqu'un utilisateur tape son nom et son mot de passe, et qu'il faut les vérifier, cf. RFC 6943.)
Certaines personnes, surtout en Anglosaxonnie, pourraient estimer que c'est bien compliqué tout cela, et qu'il vaudrait mieux limiter les identificateurs à ASCII. Certes, Unicode est compliqué, mais sa complexité ne fait que refléter celle des langues humaines (section 6.1 de notre RFC). On ne peut pas simplifier Unicode, sauf à éliminer une partie de la diversité humaine.
Le nom du groupe de travail PRECIS reflète les trois fonctions essentielles que font les programmes qui manipulent les identificateurs internationalisés :
A priori, les serveurs Internet seront responsables de l'application, les clients n'ayant à faire qu'une préparation. Ayant souvent moins de ressources, les clients pourraient en effet avoir du mal à faire certaines opérations Unicode complexes (section 3).
Les principes du travail de PRECIS sont :
Si tout va bien, ces principes permettront l'indépendance vis-à-vis des versions d'Unicode (ou, plus exactement, la possibilité de passer à une nouvelle version d'Unicode sans faire une nouvelle norme incompatible), le partage des tables et du logiciel entre applications (par exemple par le biais de bibliothèques communes, utilisables si toutes les applications reposent sur PRECIS), et moins de surprises pour les utilisateurs, qui auraient été bien embêtés si chaque protocole Internet avait eu une manière complètement différente de prévoir l'internationalisation.
Bien, quelles sont donc les classes prévues par PRECIS (section 4) ? L'idée est de faire un compromis entre expressivité et sécurité. Qu'est-ce que la sécurité vient faire là dedans ? Personne ne le sait trop (le RFC utilise plusieurs fois safe et safety sans expliquer face à quels risques) mais Unicode fait souvent peur aux informaticiens anglo-saxons et il est donc courant d'estimer qu'il existe des caractères dangereux.
Il y a donc deux classes en tout et pour tout dans PRECIS :
IdentifierClass
et
FreeformClass
. La première classe servira à
identifier utilisateurs, machines, pièces de discussions en
messagerie instantanée, fichiers, etc, et ne
permettra que les lettres, les nombres et quelques symboles (comme
le ! ou le +, car ils étaient dans ASCII). C'est contraignant mais l'idée est qu'on veut des
désignations simples et sans ambiguïté, pas écrire des romans. La
seconde classe servira à tout le reste (mots de passe, textes
affichés comme la description d'une pièce XMPP, nom complet d'une
machine, etc). Par exemple, une imprimante sera
imprimante-jean-et-thérèse
pour les protocoles
qui demandent un nom de la classe
IdentifierClass
et Imprimante de
Jean & Thérèse
lorsqu'on pourra utiliser un nom
FreeformClass
.
Les classes sont définies par les caractères inclus et exclus. Plus précisément, un caractère peut être, pour une classe donnée (voir aussi la section 8) :
La classe IdentifierClass
se définit donc par :
JoinControl
du RFC 5892, section 2.8.La classe Freeform Class
se définit, elle,
par :
JoinControl
plus quelques exceptions.Ces listes de caractères autorisés ou interdits ne sont pas suffisantes. Il y a d'autres points à régler (section 5), ce qui se fait typiquement dans les profils. Ainsi, un profil doit définir :
target0
et target0
soient considérés différents (le fameux POLA, principe de
la moindre surprise).toLowerCase()
(section 3.13 de la norme
Unicode). Attention, cette méthode Unicode ne gère pas des cas qui
dépendent de la langue, dont le plus fameux est le i sans point du
turc (U+0131 c'est-à-dire ı). Le
changement de casse est évidemment déconseillé pour les mots de
passe (puisqu'il diminue l'entropie). Notez aussi que ce cas
illustre le fait que les transformations PRECIS ne sont pas sans
perte : si on met tout en minuscules, Poussin
ne se distingue plus de poussin
.Un profil peut également interdire certains caractères normalement autorisés (mais pas l'inverse).
Au passage, pour la comparaison, le RFC (section 7) impose un ordre à ces opérations. Par exemple, la mise en correspondance de la version large sur la version normale doit se faire avant l'éventuel changement de casse. C'est important car ces opérations ne sont pas commutatives entre elles.
Les profils sont enregistrés à l'IANA. Le RFC met bien en garde contre leur multiplication : toute l'idée de PRECIS est d'éviter que chaque protocole ne gère l'internationalisation des identificateurs de manière différente, ce qui empêcherait la réutilisation de code, et perturberait les utilisateurs. Si on avait un cadre commun mais des dizaines de profils différents, on ne pourrait pas dire qu'on a atteint cet objectif. Par exemple, en matière d'interface utilisateur, PRECIS essaie de s'en tenir au POLA (Principle of Least Astonishment) et ce principe serait certainement violé si chaque application trouvait rigolo d'interdire un caractère ou l'autre, sans raison apparente. Le RFC estime d'ailleurs (section 5.1) qu'il ne devrait y avoir idéalement que deux ou trois profils. Mais ce n'est pas possible puisque les protocoles existent déjà, avec leurs propres règles, et qu'on ne peut pas faire table rase de l'existant (tous les protocoles qui ont déjà définis des caractères interdits, comme IRC, NFS, SMTP, XMPP, iSCSI, etc).
Un petit point en passant, avant de continuer avec les
applications : vous avez noté que la classe
IdentifierClass
interdit les espaces (tous les
espaces Unicode, pas seulement le U+0020 d'ASCII), alors que
certaines applications acceptent les espaces dans les
identificateurs (par exemple, Unix les
accepte sans problèmes dans les noms de fichier,
Apple permet depuis longtemps de les
utiliser pour nommer iTrucs et imprimantes, etc). La section
5.3 explique cette interdiction :
françoise durand
avec françoisedurand
.C'est embêtant (toute contrainte est embêtante) mais le compromis a semblé raisonnable au groupe PRECIS. Tant pis pour les espaces.
Passons maintenant aux questions des développeurs d'applications (section 6 du RFC). Que doivent-ils savoir pour utiliser PRECIS correctement ? Idéalement, il suffirait de lier son code aux bonnes bibliothèques bien internationalisées et tout se ferait automatiquement. En pratique, cela ne se passera pas comme ça. Sans être obligé de lire et de comprendre tout le RFC, le développeur d'applications devra quand même réflechir un peu à l'internationalisation de son programme :
UsernameCaseMapped
de la classe
IdentifierClass
» (cf. RFC 8265).Sur ce dernier point, il faut noter que la frontière est mince entre « interdire plusieurs caractères normalement autorisés par le profil » et « définir un nouveau profil ». La possibilité d'interdire des caractères supplémentaires est surtout là pour s'accomoder des protocoles existants (comme dans l'exemple du courrier ci-dessus), et pour éviter d'avoir un profil par application existante.
Votre application pourra avoir besoin de constructions au-dessus
des classes existantes. Par exemple, si un nom d'utilisateur, dans
votre programme, peut s'écrire « Prénom Nom », il ne peut pas être
une instance de la classe IdentifierClass
, qui
n'accepte pas les espaces pour la raison indiquée plus haut. Il
faudra alors définir un concept « nom d'utilisateur », par exemple
en le spécifiant comme composé d'une ou plusieurs instances de
IdentifierClass
, séparées par des espaces. En
ABNF :
username = userpart *(1*SP userpart) userpart = ... ; Instance d'IdentifierClass
La même technique peut être utilisée pour spécifier des identificateurs
qui ne seraient normalement pas autorisés par
IdentifierClass
comme
stéphane@maçonnerie-générale.fr
ou /politique/séries/Game-of-Thrones/saison05épisode08.mkv
.
On a vu plus haut qu'un des principes de PRECIS était de définir les caractères autorisés de manière algorithmique, à partir de leur propriétés Unicode, et non pas sous la forme d'une liste figée (qu'il faudrait réviser à chaque nouvelle version d'Unicode). Les catégories de caractères utilisées par cet algorithme sont décrites en section 9. Par exemple, on y trouve :
LettersDigits
qui rassemble les chiffres et
les lettres. (Rappelez-vous qu'on utilise Unicode : ce ne sont pas
uniquement les lettres et les chiffres d'ASCII.)ASCII7
, les caractères
d'ASCII, à l'exception des caractères de
contrôle,Spaces
, tous les espaces possibles (comme
le U+200A, HAIR SPACE, ainsi appelé en raison de sa
minceur),Symbols
, les symboles, comme U+20A3
(FRENCH FRANC SIGN, ₣) ou U+02DB
(OGONEK, ˛),Plusieurs registres IANA sont nécessaires pour stocker toutes les données nécessaires à PRECIS. La section 11 les recense tous. Le plus important est le PRECIS Derived Property Value, qui est recalculé à chaque version d'Unicode. Il indique pour chaque caractère s'il est autorisé ou interdit dans un identificateur PRECIS. Voici sa version pour Unicode 6.3 (on attend avec impatience une mise à jour…).
Les deux autres registres stockent les classes et les profils (pour l'instant, ils sont quatre). Les règles d'enregistrement (section 11) dans le premier sont strictes (un RFC est nécessaire) et celles dans le second plus ouvertes (un examen par un expert est nécessaire). La section 10 explique aux experts en question ce qu'ils devront bien regarder. Elle note que l'informaticien ordinaire est en général très ignorant des subtilités d'Unicode et des exigences de l'internationalisation, et que l'expert doit donc se montrer plus intrusif que d'habitude, en n'hésitant pas à mettre en cause les propositions qu'il reçoit. Dans beaucoup de RFC, les directives aux experts sont d'accepter, par défaut, les propositions, sauf s'il existe une bonne raison de les rejeter. Ici, c'est le contraire : le RFC recommande notamment de rejeter les profils proposés, sauf s'il y a une bonne raison de les accepter.
La section 12 est consacrée aux problèmes de sécurité qui, comme
toujours lorsqu'il s'agit
d'Unicode, sont plus imaginaires que réels. Un des problèmes
envisagés est celui du risque de confusion entre deux caractères
qui sont visuellement proches. Le problème existe déjà avec le seul
alphabet latin (vous voyez du premier coup
la différence entre google.com
et
goog1e.com
?) mais est souvent utilisé comme
prétexte pour retarder le déploiement d'Unicode. PRECIS se voulant
fidèle au principe POLA, le risque de confusion est
considéré comme important. Notez que le risque réel dépend de la
police
utilisée. Unicode normalisant des caractères et non pas des
glyphes, il n'y a pas de
solution générale à ce problème dans Unicode (les écritures
humaines sont compliquées : c'est ainsi). Si le texte
ᏚᎢᎵᎬᎢᎬᏒ ressemble
à STPETER, c'est que vous utilisez une police qui ne distingue pas
tellement l'alphabet
cherokee de l'alphabet latin. Est-ce que ça a des
conséquences pratiques ? Le RFC cite le risque accru de
hameçonnage, sans citer les nombreuses
études qui montrent le contraire (cf. le Unicode Technical Report
36, section 2, « the use of visually
confusable characters in spoofing is often overstated »,
et la FAQ de
sécurité d'Unicode).
Quelques conseils aux développeurs concluent cette partie : limiter le nombre de caractères ou d'écritures qu'on accepte, interdire le mélange des écritures (conseil inapplicable : dans la plupart des alphabets non-latins, on utilise des mots entiers en alphabet latin)... Le RFC conseille aussi de marquer visuellement les identificateurs qui utilisent plusieurs écritures (par exemple en utilisant des couleurs différentes), pour avertir l'utilisateur.
C'est au nom de ce principe POLA que la classe
IdentifierClass
est restreinte à un ensemble
« sûr » de caractères (notez que ce terme « sûr » n'est jamais
expliqué ou défini dans ce RFC). Comme son nom l'indique,
FreeformClass
est bien plus large et peut donc
susciter davantage de surprises.
PRECIS gère aussi le cas des mots de passe en Unicode. Un bon
mot de passe doit être difficile à deviner ou à trouver par force
brute (il doit avoir beaucoup d'entropie). Et
il faut minimiser les risques de faux positifs (un mot de passe
accepté alors qu'il ne devrait pas : par exemple, des mots de passe
insensibles à la
casse seraient agréables pour les utilisateurs mais
augmenteraient le risque de faux positifs). L'argument de
l'entropie fait que le RFC déconseille de créer des profils
restreints de FreeformClass
, par exemple en
excluant des catégories comme la ponctuation. Unicode permet des
mots de passe vraiment résistants à la force brute, comme
« 𝃍🐬ꢞĚΥਟዶᚦ⬧ »... D'un
autre côté, comme le montre l'exemple hypothétique de mots de passe
insensibles à la casse, il y a toujours une tension entre la
sécurité et l'utilisabilité. Laisser les utilisateurs choisir des
mots de passe comportant des caractères « exotiques » peut poser
bien des problèmes par la suite lorsqu'utilisateur tentera de les
taper sur un clavier peu familier. Il faut noter aussi que les mots
de passe passent parfois par des protocoles intermédiaires (comme
SASL, RFC 4422, ou comme RADIUS, RFC 2865) et
qu'il vaut donc mieux que tout le monde utilise les mêmes règles
pour éviter des surprises (comme un mot de passe refusé par un
protocole intermédiaire).
Enfin, la section 13 de notre RFC se penche sur l'interopérabilité. Elle rappele qu'UTF-8 est l'encodage recommandé (mais PRECIS est un cadre, pas un protocole complet, et un protocole conforme à PRECIS peut utiliser un autre encodage). Elle rappelle aussi qu'il faut être prudent si on doit faire circuler ses identificateurs vers d'autres protocoles : tous ne savent pas gérer Unicode, hélas.
Il existe une mise en œuvre de PRECIS en Go : https://godoc.org/golang.org/x/text/secure/precis
et une en
JavaScript, precis-js.
Les changements depuis le précédent RFC, le RFC 7564, sont résumés dans l'annexe A. Rien de crucial, le
principal étant le remplacement de
toCaseFold()
par
toLower()
pour les opérations insensibles à la
casse. Ces deux fonctions sont définies dans la norme
Unicode (section 4.1). La principale différence est que
toCaseFold()
ne convertit pas forcément en
minuscules (l'alphabet
cherokee est converti en majuscules). Grâce à Python, dont la version 3
fournit casefold
en plus de
lower()
et upper()
,
voici d'abord la différence sur l'eszett :
>>> "ß".upper() 'SS' >>> "ß".lower() 'ß' >>> "ß".casefold() 'ss'
Et sur l'alphabet cherokee (pour le cas, très improbable, où vous ayiez la police pour les minuscules, ajoutées récemment) :
>>> "ᏚᎢᎵᎬᎢᎬᏒ".upper() 'ᏚᎢᎵᎬᎢᎬᏒ' >>> "ᏚᎢᎵᎬᎢᎬᏒ".lower() 'ꮪꭲꮅꭼꭲꭼꮢ' >>> "ᏚᎢᎵᎬᎢᎬᏒ".casefold() 'ᏚᎢᎵᎬᎢᎬᏒ'
Date de publication du RFC : Novembre 2017
Auteur(s) du RFC : M. Tuexen (Muenster Univ. of Appl. Sciences), R. Stewart (Netflix), R. Jesup (WorldGate Communications), S. Loreto (Ericsson)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tsvwg
Première rédaction de cet article le 16 novembre 2017
Le protocole de transport SCTP est normalement prévu pour tourner directement sur IP. Pour diverses raisons, il peut être utile de le faire tourner sur un autre protocole de transport comme UDP (ce que fait le RFC 6951) ou même sur un protocole qui offre des services de sécurité comme DTLS (ce que fait notre nouveau RFC).
SCTP est défini dans le RFC 4960. C'est un concurrent de TCP, offrant un certain nombre de services que TCP ne fait pas. DTLS, normalisé dans le RFC 6347, est un protocole permettant d'utiliser les services de sécurité de TLS au-dessus d'UDP. Il est notamment très utilisé par WebRTC (RFC 8827), lui donnant ainsi une sécurité de bout en bout.
En théorie, SCTP peut fonctionner directement sur IP. Hélas, dans l'Internet actuel, très ossifié, plein d'obstacles s'y opposent. Par exemple, les routeurs NAT tel que la box de M. Michu à la maison, n'acceptent en général que TCP et UDP. Et bien des pare-feux bloquent stupidement les protocoles que leurs auteurs sont trop ignorants pour connaître. En pratique, donc, SCTP ne passe pas partout. L'encapsuler dans un autre protocole de transport, comme UDP (directement, ou bien via DTLS), est souvent la seule solution pour avoir une connectivité. L'intérêt de DTLS est qu'on a toute la sécurité de TLS, notamment la confidentialité via le chiffrement.
Cette encapsulation est simple (section 3) : on met le paquet SCTP, avec ses en-têtes et sa charge utile, dans les données du paquet DTLS.
Il y a toutefois quelques détails à prendre en compte (section 4 de notre RFC). Par exemple, comme toute encapsulation prend quelques octets, la MTU diminue. Il faut donc un système de PMTUD. Comme l'encapsulation rend difficile l'accès à ICMP (voir la section 6) et que les middleboxes pénibles dont je parlais plus haut bloquent souvent, à tort, ICMP, cette découverte de la MTU du chemin doit pouvoir se faire sans ICMP (la méthode des RFC 4821 et RFC 8899 est recommandée).
Cette histoire de MTU concerne tout protocole encapsulé. Mais il y a aussi d'autres problèmes, ceux liés aux spécificités de SCTP (section 6) :
Cette norme est surtout issue des besoins de WebRTC, dont les implémenteurs réclamaient un moyen facile de fournir sécurité et passage à travers le NAT. Elle est mise en œuvre depuis longtemps, dans des clients WebRTC comme Chrome, Firefox ou Opera.
Date de publication du RFC : Décembre 2017
Auteur(s) du RFC : T. Bray (Textuality)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF jsonbis
Première rédaction de cet article le 14 décembre 2017
Il existe une pléthore de langages pour décrire des données structurées. JSON, normalisé dans ce RFC (qui succède au RFC 7159, avec peu de changements), est actuellement le plus à la mode. Comme son concurrent XML, c'est un format textuel, et il permet de représenter des structures de données hiérarchiques.
À noter que JSON doit son origine, et son nom complet (JavaScript Object Notation) au langage de programmation JavaScript, dont il est un sous-ensemble (enfin, approximativement). La norme officielle de JavaScript est à l'ECMA, dans ECMA-262. JSON est dans la section 24.5 de ce document mais est aussi dans ECMA-404, qui lui est réservé. Les deux normes, ce RFC et la norme ECMA, sont écrites de manière différente mais, en théorie, doivent aboutir au même résultat. ECMA et l'IETF sont censés travailler ensemble pour résoudre les incohérences (aucune des deux organisations n'a, officiellement, le dernier mot).
Contrairement à JavaScript, JSON n'est
pas un langage de programmation, seulement un langage de
description de données, et il ne peut donc pas servir de véhicule
pour du code
méchant (sauf si on fait des bêtises comme de
soumettre du texte JSON à eval()
, cf. section
12 et erratum
#3607 qui donne des détails sur cette vulnérabilité).
Voici un exemple, tiré du RFC, d'un objet exprimé en JSON :
{ "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "http://www.example.com/image/481989943", "Height": 125, "Width": "100" }, "IDs": [116, 943, 234, 38793] } }
Les détails de syntaxe sont dans la section 2 du RFC. Cet objet
d'exemple a un seul champ, Image
, qui est un
autre objet (entre { et }) et qui a plusieurs champs. (Les objets
sont appelés dictionnaires ou maps
dans d'autres langages.) L'ordre des éléments de l'objet n'est pas
significatif (certains analyseurs JSON le conservent, d'autres
pas). Un de ces champs, IDs
, a pour valeur un
tableau (entre [ et ]). Les éléments d'un
tableau ne sont pas forcément du même type (section 5).
Un texte JSON n'est pas forcément un objet ou un tableau, par exemple :
"Hello world!"
est un texte JSON légal (composé d'une chaîne de caractères en tout
et pour tout). Une des conséquences est qu'un lecteur de JSON qui lit au fil de l'eau peut
ne pas savoir si le texte est fini ou pas (il ne suffit pas de
compter les crochets et accolades). À part les objets, les tableaux
et les chaînes de caractères, un texte JSON peut être un nombre, ou
bien un littéral, false
,
true
ou null
.
Et quel encodage utiliser pour les textes JSON (section 8) ? Le RFC 4627 était presque muet à ce sujet. Cette question est désormais plus développée. Le jeu de caractères est toujours Unicode et l'encodage est obligatoirement UTF-8 dès qu'on envoie du JSON par le réseau (bien des mises en œuvre de JSON ne peuvent en lire aucun autre). Les textes JSON transmis par le réseau ne doivent pas utiliser de BOM.
Lorsqu'on envoie du JSON par le réseau, le type MIME à utiliser est application/json
.
Autre problème classique d'Unicode, la comparaison de chaînes de
caractères. Ces comparaisons doivent se faire selon les caractères
Unicode et pas selon les octets (il y a plusieurs façons de
représenter la même chaîne de caractères, par exemple
foo*bar
et foo\u002Abar
sont la même chaîne).
JSON est donc un format simple, il n'a même pas la possibilité de commentaires dans le fichier... Voir sur ce sujet une intéressante compilation.
Le premier RFC décrivant JSON était le RFC 4627, remplacé ensuite par le RFC 7159. Quels changements apporte cette troisième révision (annexe A) ? Elle corrige quelques erreurs, résout quelques incohérences avec le texte ECMA, et donne des avis pratiques aux programmeurs. Les principaux changements :
Voici un exemple d'un programme Python pour écrire un objet Python en JSON (on notera que la syntaxe de Python et celle de JavaScript sont très proches) :
import json objekt = {u'Image': {u'Width': 800, u'Title': u'View from Smith\'s, 15th Floor, "Nice"', u'Thumbnail': {u'Url': u'http://www.example.com/image/481989943', u'Width': u'100', u'Height': 125}, u'IDs': [116, 943, 234, 38793], u'Height': 600}} # Example from RFC 4627, lightly modified print(json.dumps(objekt))
Et un programme pour lire du JSON et le charger dans un objet Python :
import json # One backslash for Python, one for JSON objekt = json.loads(""" { "Image": { "Width": 800, "Height": 600, "Title": "View from Smith's, 15th Floor, \\\"Nice\\\"", "Thumbnail": { "Url": "http://www.example.com/image/481989943", "Height": 125, "Width": "100" }, "IDs": [116, 943, 234, 38793] } } """) # Example from RFC 4267, lightly modified print(objekt) print("") print(objekt["Image"]["Title"])
Le code ci-dessus est très simple car Python (comme Perl ou
Ruby ou, bien sûr,
JavaScript) a un typage complètement
dynamique. Dans les langages où le typage est plus statique, c'est
moins facile et on devra souvent utiliser des méthodes dont
certains programmeurs se méfient, comme des conversions de types à
l'exécution. Si vous voulez le faire en Go, il existe un bon
article d'introduction au paquetage standard
json
. Un exemple en Go figure plus loin, pour
analyser la liste des stations de la RATP.
Pour Java, qui a le même « problème » que Go, il existe une quantité impressionnante de bibliothèques différentes pour faire du JSON (on trouve en ligne plusieurs tentatives de comparaison). J'ai utilisé JSON Simple. Lire un texte JSON ressemble à :
import org.json.simple.*; ... Object obj=JSONValue.parse(args[0]); if (obj == null) { // May be use JSONParser instead, it raises an exception when there is a problem System.err.println("Invalid JSON text"); System.exit(1); } else { System.out.println(obj); } JSONObject obj2=(JSONObject)obj; // java.lang.ClassCastException if not a JSON object System.out.println(obj2.get("foo")); // Displays member named "foo"
Et le produire :
JSONObject obj3=new JSONObject(); obj3.put("name","foo"); obj3.put("num",new Integer(100)); obj3.put("balance",new Double(1000.21)); obj3.put("is_vip",new Boolean(true));
Voyons maintenant des exemples réels avec divers outils de traitement de JSON. D'abord, les données issues du service de vélos en libre-service Vélo'v. C'est un gros JSON qui contient toutes les données du système. Nous allons programmer en Haskell un programme qui affiche le nombre de vélos libres et le nombre de places disponibles. Il existe plusieurs bibliothèques pour faire du JSON en Haskell mais Aeson semble la plus utilisée. Haskell est un langage statiquement typé, ce qui est loin d'être idéal pour JSON. Il faut déclarer des types correspondant aux structures JSON :
data Velov = Velov {values :: [Station]} deriving Show instance FromJSON Velov where parseJSON (Object v) = Velov <$> (v .: "values") data Station = Station {stands :: Integer, bikes :: Integer, available :: Integer} deriving Show data Values = Values [Station]
Mais ça ne marche pas : les nombres dans le fichier JSON ont été
représentés comme des chaînes de caractères ! (Cela illustre un
problème fréquent dans le monde de JSON et de
l'open data : les données
sont de qualité technique très variable.) On doit donc les
déclarer en String
:
data Station = Station {stands :: String, bikes :: String, available :: String} deriving Show
Autre problème, les données contiennent parfois la chaîne de
caractères None
. Il faudra donc filtrer avec
la fonction Haskell filter
. La fonction
importante filtre les données, les convertit en entier, et en fait
la somme grâce à foldl
:
sumArray a = show (foldl (+) 0 (map Main.toInteger (filter (\i -> i /= "None") a)))
Le programme
complet est velov.hs
. Une fois compilé, testons-le :
% curl -s https://download.data.grandlyon.com/ws/rdata/jcd_jcdecaux.jcdvelov/all.json | ./velov "Stands: 6773" "Bikes: 2838" "Available: 3653"
Je n'ai pas utilisé les dates contenues dans ce fichier mais on peut noter que, si elles sont exprimées en ISO 8601 (ce n'est hélas pas souvent le cas), c'est sans indication du fuseau horaire (celui en vigueur à Lyon, peut-on supposer).
Un autre exemple de mauvais fichier JSON est donné par
Le Monde avec la base
des députés français. Le fichier
est du JavaScript, pas du JSON (il commence par une déclaration
JavaScript var datadep = {
…) et il contient
plusieurs erreurs de syntaxe (des apostrophes qui n'auraient pas dû
être échappées).
Voyons maintenant un traitement avec le programme spécialisé
dans JSON, jq. On va servir du service de
tests TLS
, dont les résultats sont
consultables avec un navigateur Web, mais également téléchargeables
en JSON. Par exemple,
https://tls.imirhil.fr/
https://tls.imirhil.fr/https/www.bortzmeyer.org.json
donne accès aux résultats des tests pour la version HTTPS de ce blog :
% curl -s https://tls.imirhil.fr/https/www.bortzmeyer.org.json| jq '.date' "2017-07-23T14:10:25.760Z"
Notons qu'au moins une clé d'un objet JSON n'est pas nommée
uniquement avec des lettres et chiffres, la clé
$oid
. jq n'aime pas cela :
% curl -s https://tls.imirhil.fr/https/www.bortzmeyer.org.json| jq '._id.$oid' jq: error: syntax error, unexpected '$', expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1: ._id.$oid jq: 1 compile error
Il faut mettre cette clé entre guillemets :
% curl -s https://tls.imirhil.fr/https/bortzmeyer.org.json| jq '."_id"."$oid"' "596cb76c2525939a3b34120f"
Toujours avec jq, les données de la Deutsche
Bahn, en
. C'est
du GeoJSON (RFC 7946),
un profil de JSON. Ici, on cherche la gare de Ratisbonne :
http://data.deutschebahn.com/dataset/data-streckennetz
% jq '.features | map(select(.properties.geographicalName == "Regensburg Hbf"))' railwayStationNodes.geojson [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 12.09966625451, 49.011754555481 ] }, "properties": { "id": "SNode-1492185", "formOfNode": "railwayStop", "railwayStationCode": "NRH", "geographicalName": "Regensburg Hbf", ...
Toujours avec jq, on peut s'intéresser aux données officielles
états-uniennes en
https://catalog.data.gov/dataset?res_format=JSON
. Prenons
les données
sur la délinquance à Los
Angeles (j'ai bien dit délinquance et pas criminalité,
celui qui traduit crime par crime ne connait pas
l'anglais, ni le
droit). https://data.lacity.org/api/views/y8tr-7khq/rows.json?accessType=DOWNLOAD
est un très gros fichier (805 Mo) et jq
n'y arrive pas :
% jq .data la-crime.json error: cannot allocate memory
Beaucoup de programmes qui traitent le JSON ont ce problème (un
script Python produit un MemoryError
) : ils
chargent tout en mémoire et ne peuvent donc pas traiter des données
de grande taille. Il faut donc éviter de produire de trop gros
fichiers JSON.
Si vous voulez voir un vrai exemple en Python, il y a mon article sur le traitement de la base des codes postaux. Cette base peut évidemment aussi être examinée avec jq. Et c'est l'occasion de voir du GeoJSON :
% jq '.features | map(select(.properties.nom_de_la_commune == "LE TRAIT"))' laposte_hexasmal.geojson [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ 0.820017087099, 49.4836816397 ] }, "properties": { "nom_de_la_commune": "LE TRAIT", "libell_d_acheminement": "LE TRAIT", "code_postal": "76580", "coordonnees_gps": [ 49.4836816397, 0.820017087099 ], "code_commune_insee": "76709" } } ]
J'avais promis plus haut un exemple écrit en
Go. On va utiliser la liste des positions
géographiques des stations RATP, en
https://data.ratp.fr/explore/dataset/positions-geographiques-des-stations-du-reseau-ratp/table/?disjunctive.stop_name&disjunctive.code_postal&disjunctive.departement
.
Le programme Go read-ratp.go
va afficher le nombre
de stations et la liste :
% ./read-ratp < positions-geographiques-des-stations-du-reseau-ratp.json 26560 stations Achères-Ville Alésia Concorde ...
Comme déjà indiqué, c'est plus délicat en Go que dans un langage très dynamique comme Python. Il faut construire à l'avance des structures de données :
type StationFields struct { Fields Station } type Station struct { Stop_Id int Stop_Name string }
Et toute violation du « schéma » des données par le fichier JSON (quelque chose qui arrive souvent dans la nature) plantera le programme.
Si on veut beaucoup de fichiers JSON, le service de données
ouvertes officielles data.gouv.fr permet de
sélectionner des données par format. Ainsi,
donnera tous les fichiers en JSON. Prenons au hasard les frayères du centre de la France,
https://www.data.gouv.fr/fr/datasets/?format=JSON
https://www.data.gouv.fr/fr/datasets/points-de-frayere-des-especes-de-linventaire-frayeres-des-regions-centre-et-poitou-charentes/
.
Il est encodé en ISO-8859-1, ce qui est
explicitement interdit par le RFC. Bref, il faut encore rappeler
qu'on trouve de tout dans le monde JSON et que l'analyse de
fichiers réalisés par d'autres amène parfois des surprises.
On peut aussi traiter du JSON dans
PostgreSQL. Bien sûr, il est toujours
possible (et sans doute parfois plus avantageux) d'analyser le JSON
avec une des bibliothèques présentées plus haut, et de mettre les
données dans
une base PostgreSQL. Mais on peut aussi mettre le JSON directement
dans PostgreSQL et ce SGBD fournit un
type de données JSON et quelques
fonctions permettant de l'analyser. Pour les données, on va
utiliser les centres de santé en Bolivie,
en
http://geo.gob.bo/geoserver/web/?wicket:bookmarkablePage=:org.geoserver.web.demo.MapPreviewPage
. On
crée la table :
CREATE TABLE centers ( ID serial NOT NULL PRIMARY KEY, info json NOT NULL );
Si
on importe le fichier JSON bêtement dans PostgreSQL (psql -c "copy centers(info) from stdin" mydb < centro-salud.json
), on récupère
un seul enregistrement. Il faut donc éclater le fichier JSON en
plusieurs lignes. On peut utiliser les extensions à SQL de
PostgreSQL pour cela, mais j'ai préféré me servir de jq :
% jq --compact-output '.features | .[]' centro-salud.json | psql -c "copy centers(info) from stdin" mydb COPY 50
On peut alors faire des requêtes dans le JSON, avec l'opérateur
->
. Ici, le nom des centres (en jq, on
aurait écrit .properties.nombre
) :
mydb=> SELECT info->'properties'->'nombre' AS Nom FROM centers; nom ------------------------------------------ "P.S. ARABATE" "INSTITUTO PSICOPEDAGOGICO" "HOSPITAL GINECO OBSTETRICO" "HOSPITAL GASTROENTEROLOGICO" "C.S. VILLA ROSARIO EL TEJAR" "C.S. BARRIO JAPON" "C.S. SAN ANTONIO ALTO (CHQ)" "C.S. SAN JOSE (CHQ)" "C.S. SAN ROQUE" ...
Bon, sinon, JSON dispose d'une page Web officielle, où vous trouverez plein d'informations. Pour tester dynamiquement vos textes JSON, il y a ce service.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : S. Bensley, D. Thaler, P. Balasubramanian (Microsoft), L. Eggert (NetApp), G. Judd (Morgan Stanley)
Pour information
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 19 octobre 2017
DCTCP (Datacenter TCP), décrit dans ce RFC (qui n'est pas une norme, attention), est une variante de TCP avec un contrôle de congestion moins prudent. Elle est conçue pour le cas particulier des centres de données et ne doit pas être utilisée sur l'Internet public.
DCTCP repose sur la technique ECN du RFC 3168. Normalement, cette technique permet de signaler la congestion plus tôt qu'avec la méthode traditionnelle d'attendre les pertes de paquets. L'ECN est binaire : il y a eu de la congestion ou il n'y en a pas eu. DCTCP va plus loin et utilise ECN pour estimer le nombre d'octets qui ont rencontré de la congestion. DCTCP va ensuite refermer sa fenêtre (le nombre d'octets qu'on peut envoyer avant d'avoir reçu des accusés de réception) plus lentement que le TCP classique (et c'est pour cela que la concurrence entre eux est inégale, et que DCTCP ne doit pas être activé dans l'Internet, mais seulement dans des environnements fermés).
Quelles sont les particularités du réseau dans un centre de données ? Il faut beaucoup de commutateurs pour relier ces machines. Il est tentant d'utiliser pour cela des commutateurs bon marché. Mais ceux-ci ont des tampons de taille limitée, et le risque de congestion est donc plus élevé. Dans un centre de données, les flots sont de deux types : des courts et des longs. Les courts veulent en général une faible latence et les longs sont souvent davantage intéressés par une forte capacité. Enfin, le trafic est souvent très synchronisé. Si on fait du MapReduce, tous les serveurs vont voir de l'activité réseau en même temps (quand le travail est réparti, et quand il se termine).
Le cahier des charges des commutateurs est donc plein de contradictions :
Avec le TCP traditionnel (pré-ECN), l'indicateur de congestion est la perte de paquets, détectée par l'absence d'accusé de réception. (Voir le RFC 5681 pour une bonne synthèse sur le contrôle de congestion dans TCP.) Attendre la perte de paquets pour ralentir n'est pas très efficace : pour un flot court qui rencontre de la congestion au début, la majorité des paquets aura été jetée avant que TCP ne puisse ralentir. D'où l'invention d'ECN (RFC 3168). ECN permet de réagir avant qu'on perde des paquets. Mais, comme expliqué plus haut, il est binaire : il détecte la congestion, pas son importance. Il va donc souvent mener TCP à refermer trop énergiquement la fenêtre d'envoi.
La section 3 du RFC présente les algorithmes à utiliser. Les
commutateurs/routeurs
détectent la congestion et la signalent via
ECN (RFC 3168). Les
récepteurs des données renvoient l'ECN à l'émetteur et celui-ci
réduit sa fenêtre de congestion (cwnd
pour
Congestion WiNDow, cf. RFC 5681, section 2). Tout ceci est
le fonctionnement classique d'ECN. C'est surtout dans la dernière étape,
le calcul de la réduction de la fenêtre, que DCTCP apporte des
nouveautés. Mais, avant, quelques détails sur les deux premières
étapes.
D'abord, la décision des commutateurs et·ou routeurs de considérer qu'il y a congestion. Fondamentalement, c'est une décision locale, qui n'est pas standardisée. En général, on décide qu'il y a congestion dès que le temps de séjour des paquets dans les tampons du commutateur/routeur augmente « trop ». On n'attend donc pas que les files d'attente soient pleines (si elles sont grandes - bufferbloat - la latence va augmenter sérieusement bien avant qu'elles ne soient pleines). Une fois que l'engin décide qu'il y a congestion, il marque les paquets avec ECN (bit CE - Congestion Experienced, cf. RFC 3168).
Le récepteur du paquet va alors se dire « ouh là, ce paquet a rencontré de la congestion sur son trajet, il faut que je prévienne l'émetteur de se calmer » et il va mettre le bit ECE dans ses accusés de réception. Ça, c'est l'ECN normal. Mais pour DCTCP, il faut davantage de détails, puisqu'on veut savoir précisément quels octets ont rencontré de la congestion. Une possibilité serait d'envoyer un accusé de réception à chaque segment (paquet TCP), avec le bit ECE si ce segment a rencontré de la congestion. Mais cela empêcherait d'utiliser des optimisations très utiles de TCP, comme les accusés de réception retardés (on attend un peu de voir si un autre segment arrive, pour pouvoir accuser réception des deux avec un seul paquet). À la place, DCTCP utilise une nouvelle variable booléenne locale chez le récepteur qui stocke l'état CE du précédent segment. On envoie un accusé de réception dès que cette variable change d'état. Ainsi, l'accusé de réception des octets M à N indique, selon qu'il a le bit ECE ou pas, que tous ces octets ont eu ou n'ont pas eu de congestion.
Et chez l'émetteur qui reçoit ces nouvelles notifications de
congestion plus subtiles ? Il va s'en servir pour déterminer quel
pourcentage des octets qu'il a envoyé ont rencontré de la
congestion. Les détails du calcul (dont une partie est laissé à
l'implémenteur, cf. section 4.2) figurent en section 3.3. Le résultat est stocké
dans une nouvelle variable locale,
DCTCP.Alpha
.
Une fois ces calculs faits et cette variable disponible,
lorsque la congestion apparait, au lieu de diviser brusquement sa
fenêtre de congestion, l'émetteur la fermera plus doucement, par
la formule cwnd = cwnd * (1 - DCTCP.Alpha /
2)
(où cwnd
est la taille de la
fenêtre de congestion ; avec l'ancien algorithme, tout se passait
comme si tous les octets avaient subi la congestion, donc
DCTCP.Alpha
= 1).
La formule ci-dessus était pour la cas où la congestion était signalée par ECN. Si elle était signalée par une perte de paquets, DCTCP se conduit comme le TCP traditionnel, divisant sa fenêtre par deux. De même, une fois la congestion passée, Datacenter TCP agrandit sa fenêtre exactement comme un TCP normal.
Voilà, l'algorithme est là, il n'y a plus qu'à le mettre en œuvre. Cela mène à quelques points subtils, que traite la section 4. Par exemple, on a dit que DCTCP, plus agressif qu'un TCP habituel, ne doit pas rentrer en concurrence avec lui (car il gagnerait toujours). Une implémentation de DCTCP doit donc savoir quand activer le nouvel algorithme et quand garder le comportement conservateur traditionnel. (Cela ne peut pas être automatique, puisque TCP ne fournit pas de moyen de négocier l'algorithme de gestion de la congestion avec son pair.) On pense à une variable globale (configurée avec sysctl sur Unix) mais cela ne suffit pas : la même machine dans le centre de données peut avoir besoin de communiquer avec d'autres machines du centre, en utilisant DCTCP, et avec l'extérieur, où il ne faut pas l'utiliser. Il faut donc utiliser des configurations du genre « DCTCP activé pour les machines dans le même /48 que moi ».
Une solution plus rigolote mais un peu risquée, serait d'activer DCTCP dès que la mesure du RTT indique une valeur inférieure à N millisecondes, où N est assez bas pour qu'on soit sûr que seules les machines de la même tribu soient concernées.
Après le programmeur en section 4, l'administrateur réseaux en section 5. Comment déployer proprement DCTCP ? Comme on a vu que les flots TCP traditionnels et DCTCP coexistaient mal, la section 5 recommande de les séparer. Par exemple, l'article « Attaining the Promise and Avoiding the Pitfalls of TCP in the Datacenter » décrit un déploiement où le DSCP (RFC 2474) d'IPv4 est utilisé pour distinguer les deux TCP, ce qui permet d'appliquer de l'AQM (RFC 7567) à DCTCP et des méthodes plus traditionnelles (laisser tomber le dernier paquet en cas de congestion) au TCP habituel. (Il faut aussi penser au trafic non-TCP, ICMP, par exemple, quand on configure ses commutateurs/routeurs.)
Aujourd'hui, DCTCP est déjà largement déployé et ce RFC ne fait que prendre acte de ce déploiement On trouve DCTCP dans Linux (cf. ce commit de 2014, notez les mesures de performance qui accompagnent sa description), dans FreeBSD (ce commit, et cette description de l'implémentation), et sur Windows (cette fois, on ne peut pas voir le source mais il y a une documentation). Sur Linux, on peut voir la liste des algorithmes de gestion de la congestion qui ont été compilés dans ce noyau :
% sysctl net.ipv4.tcp_available_congestion_control net.ipv4.tcp_available_congestion_control = cubic reno
Si DCTCP manque, c'est peut-être parce qu'il faut charger le module :
% modprobe tcp_dctcp % sysctl net.ipv4.tcp_available_congestion_control net.ipv4.tcp_available_congestion_control = cubic reno dctcp
Si DCTCP se trouve dans la liste, on peut l'activer (c'est une activation globale, par défaut) :
% sysctl -w net.ipv4.tcp_congestion_control=dctcp
Pour le faire uniquement vers certaines destinations (par exemple à l'intérieur du centre de données) :
% ip route add 192.168.0.0/16 congctl dctcp
Le choix des algorithmes de gestion de la congestion peut
également être fait par chaque application (setsockopt(ns, IPPROTO_TCP, TCP_CONGESTION, …
).
Enfin, la section 6 du RFC rassemble quelques problèmes non résolus avec DCTCP :
Enfin, la section 7 du RFC, portant sur la sécurité, note que DCTCP hérite des faiblesses de sécurité d'ECN (les bits ECN dans les en-têtes IP et TCP peuvent être modifiés par un attaquant actif) mais que c'est moins grave pour DCTCP, qui ne tourne que dans des environnements fermés.
Si vous aimez lire, l'article original décrivant DCTCP en 2010 est celui de Alizadeh, M., Greenberg, A., Maltz, D., Padhye, J., Patel, P., Prabhakar, B., Sengupta, S., et M. Sridharan, « Data Center TCP (DCTCP) ». Le dinosaure ACM ne le rendant pas disponible librement, il faut le récupérer sur Sci-Hub (encore merci aux créateurs de ce service).
Merci à djanos pour ses nombreuses corrections sur la gestion de DCTCP dans Linux.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : N. Tomkinson, N. Borenstein (Mimecast)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF slim
Première rédaction de cet article le 10 octobre 2017
Dernière mise à jour le 20 mars 2018
La norme MIME permet d'étiqueter un
message en
indiquant la langue, avec l'en-tête
Content-Language:
(RFC 4021). Mais
comment faire si on veut envoyer le même message en plusieurs
langues, pour s'adapter à une audience variée, ou bien si on n'est
pas sûr des langues parlées par le destinataire ? C'est ce que
permet le nouveau type de
message multipart/multilingual
qui permet d'étiqueter les messages multilingues.
C'est le premier RFC du groupe de travail SLIM, chargé de créer des normes pour indiquer la langue utilisée dans le courrier et pour les communications synchrones (téléphonie sur IP, par exemple, couverte depuis par le RFC 8373), dans le cas où plusieurs langues sont en présence.
Le type de premier niveau multipart/
(RFC 2046, section 5.1, enregistré à
l'IANA) permet d'indiquer un message (ou une partie de
message, en cas de récursivité) composé de plusieurs
parties. Ainsi, multipart/mixed
(RFC 2046, section 5.1.3) indique un message dont
les dfférentes parties sont indépendantes (un texte et une image,
par exemple). Alors que multipart/alternative
(RFC 2046, section 5.1.4) est utilisé pour
le cas où les différentes parties veulent dire la même chose, mais
avec des formats différentes (une version texte
seul et une version HTML, par
exemple, ou bien une image en JPEG et la
même en PNG). Normalement, le lecteur de courrier ne va afficher qu'une
seule des parties d'une
multipart/alternative
, celle qui convient le
mieux, selon les capacités du logiciel de lecture et les
préférences de l'utilisateur. Ce sera la même chose avec ce
nouveau multipart/multilingual
: l'émetteur
enverra le message en plusieurs langues, le logiciel de lecture
n'affichera que celle qui colle le mieux aux choix de
l'utilisateur. Ce type est désormais enregistré
à l'IANA (section 9 du RFC).
Rappelez-vous que MIME est
récursif : un
multipart/
peut contenir un autre
multipart/
. Voici, par exemple, vu par le
MUA mutt, un
multipart/mixed
, dont la première partie est
un multipart/alternative
et la seconde un
fichier PDF :
I 1 <no description> [multipart/alternative, 7bit, 45K] I 2 ├─><no description> [text/plain, 8bit, utf-8, 1,8K] I 3 └─><no description> [text/html, 8bit, utf-8, 43K] A 4 35378031645-672029836-registration.pdf [application/pdf, base64, 84K]
Cette première partie du corps du message, de type
multipart/alternative
, a à son tour deux
parties, une en texte brut et l'autre en HTML. Comme ma
configuration mutt inclut la directive alternative_order
text/plain
, seule la version texte sera affichée, le
HTML étant ignoré.
Revenons aux messages multilingues. Avant ce RFC 8255, la solution la plus fréquente, quand on envoyait un message à quelqu'un dont on n'était pas sûr des préférences linguistiques, était de mettre toutes les versions dans un même message. Du genre :
(English translation at the end) Bonjour, ceci est un message. Hello, this is a message.
(La première ligne ayant pour but d'éviter que le lecteur anglophone se décourage en ne voyant au début que du français.) Cette solution n'est pas satisfaisante : elle passe mal à l'échelle dès qu'il y a plus de deux langues. Et elle ne permet pas la sélection automatique de la langue par le logiciel du destinataire.
Le type MIME d'un message est mis dans un en-tête
Content-Type:
. Par exemple, un message de
plusieurs parties représentant le même contenu serait :
Content-Type: multipart/alternative; boundary="------------E6043EF6F3B557567F3B18F4"
Où boundary
indique le texte qui sera le
séparateur entre les parties (RFC 2046,
section 5.1). La section 2 du RFC décrit ce qu'on peut désormais
mettre dans le Content-Type:
, avec le nouveau
type multipart/multilingual
.
Il ressemble beaucoup au
multipart/alternative
et, comme tous les
multipart/quelquechose
(RFC 2046, section 5.1.1), a une chaîne de
caractères qui indique le passage d'une partie à une autre :
Content-Type: multipart/multilingual; boundary=01189998819991197253
Chacune des parties d'un message en plusieurs langues doit
indiquer la langue de la partie, avec l'en-tête
Content-Language:
. Mais attention, comme il
faut tenir compte des vieux clients de messagerie qui ne
connaissent pas ce RFC, la première partie ne doit
pas avoir de
Content-Language:
, elle sert de solution de
repli, car c'est elle qui sera affichée en premier par les logiciels qui ne
connaissent pas multipart/multilingual
. Comme
elle sert de secours, il est recommandé qu'elle soit dans le
format le plus simple, donc text/plain
, en
UTF-8 (car il faut pouvoir représenter
diverses langues). Cette partie se nomme la préface. En la
lisant, l'utilisateur pourra donc comprendre qu'il doit mettre à
jour vers un logiciel plus récent.
Les parties suivantes, après la préface, vont être écrites dans des langues différentes. Le RFC recommande de les mettre dans l'ordre, avec celles qui ont le plus de chance d'être pertinentes en premier. (Ainsi, une société française ayant une activité européenne, avec une majorité de clients français, et envoyant un message en anglais, allemand et français, mettra sans doute le français en premier, suivi de l'anglais.)
Chacune de ces parties aura un en-tête
Content-Language:
, pour indiquer la langue,
et Content-Type:
pour indiquer le type
MIME. (Rappelez-vous toujours que MIME est récursif.) Il est
recommandé que chaque partie soit un message complet (avec
notamment le champ Subject:
, qui a besoin
d'être traduit, lui aussi, et le champ From:
,
dont une partie peut être utilement traduite). Le type conseillé est donc
message/rfc822
(RFC 2046, section 5.2.1), mais on peut aussi utiliser le
plus récent message/global
(RFC 6532).
Notez bien que ce RFC ne spécifie évidemment pas comment se fera la traduction : il ne s'occupe que d'étiqueter proprement le résultat.
Le message peut se terminer par une section « indépendante de
la langue » (par exemple une image ne comportant pas de
texte, si le message peut être porté par une image). Dans ce cas, son Content-Language:
doit indiquer zxx
, ce qui veut dire
« information non pertinente » (RFC 5646,
section 4.1). C'est elle qui sera sélectionnée si aucune partie ne
correspond aux préférences de l'utilisateur.
Maintenant, que va faire le client de
messagerie qui reçoit un tel message multilingue ? La
section 4 de notre RFC décrit les différents cas. D'abord, avec
les logiciels actuels, le client va afficher les différentes
parties de multipart/multilingual
dans
l'ordre où elles apparaissent (donc, en commençant par la
préface).
Mais le cas le plus intéressant est évidemment celui d'un client plus récent, qui connait les messages multilingues. Il va dans ce cas sauter la préface (qui n'a pas de langue indiquée, rappelez-vous) et sélectionner une des parties, celle qui convient le mieux à l'utilisateur.
Un moment. Arrêtons-nous un peu. C'est quoi, la « meilleure » version ? Imaginons un lecteur francophone, mais qui parle anglais couramment. Il reçoit un message multilingue, en français et en anglais. S'il choisissait manuellement, il prendrait forcément le français, non ? Eh bien non, car cela dépend de la qualité du texte. Comme peut le voir n'importe quel utilisateur du Web, les différentes versions linguistiques d'un site Web ne sont pas de qualité égale. Il y a le texte original, les traductions faites par un professionnel compétent, les traductions faites par le stagiaire, et celles faites par un programme (en général, les plus drôles). Sélectionner la meilleure version uniquement sur la langue n'est pas une bonne idée, comme le montre la mauvaise expérience de HTTP. Ici, pour reprendre notre exemple, si la version en anglais est la version originale, et que le français est le résultat d'une mauvaise traduction par un amateur, notre francophone qui comprend bien l'anglais va sans doute préférer la version en anglais.
Il est donc crucial d'indiquer le type de traduction effectuée,
ce que permet le Content-Translation-Type:
exposé plus loin, en section 6. (Les premières versions du projet
qui a mené à ce RFC, naïvement, ignoraient complètement ce
problème de la qualité de la traduction, et de la version
originale.)
Donc, le mécanisme de sélection par le MUA de la « meilleure » partie dans un message multilingue n'est pas complètement spécifié. Mais il va dépendre des préférences de l'utilisateur, et sans doute des règles du RFC 4647.
Si aucune partie ne correspond aux choix de l'utilisateur, le RFC recommande que le MUA affiche la partie indépendante de la langue, ou, si elle n'existe pas, la partie après la préface. Le MUA peut également proposer le choix à l'utilisateur (« Vous n'avez indiqué qu'une langue, le français. Ce message est disponible en anglais et en chinois. Vous préférez lequel ? »)
La section 5 du RFC présente en détail l'utilisation de l'en-tête
Content-Language:
. Il doit suivre la norme
existante de cet en-tête, le RFC 3282, et sa valeur doit
donc être une étiquette de langue du RFC 5646. Des exemples (en pratique, il ne faut
évidemment en mettre qu'un) :
Content-Language: fr Content-Language: sr-Cyrl Content-Language: ay Content-Language: aaq
Le premier exemple concerne le français, le second le serbe écrit en alphabet cyrillique, le troisième l'aymara et le quatrième l'abénaqui oriental.
La section décrit le nouvel en-tête
Content-Translation-Type:
qui indique le type
de traduction réalisé. Il peut prendre trois valeurs,
original
(la version originale),
human
(traduit par un humain) et
automated
(traduit par un programme). Notez
que les humains (les programmes aussi, d'ailleurs) varient
considérablement dans leurs compétences de traducteur. J'aurais
personnellement préféré qu'on distingue un traducteur
professionnel d'un amateur, mais la traduction fait partie des
métiers mal compris, où beaucoup de gens croient que si on parle
italien et allemand, on peut traduire de l'italien en allemand
correctement. C'est loin d'être le cas. (D'un autre côté, comme
pour tous les étiquetages, si on augmente le nombre de choix, on
rend l'étiquetage plus difficile et il risque d'être incorrect.)
Voici un exemple complet, tel qu'il apparait entre deux logiciels de messagerie. Il est fortement inspiré du premier exemple de la section 8 du RFC, qui comprend également des exemples plus complexes. Dans ce message, l'original est en anglais, mais une traduction française a été faite par un humain.
From: jeanne@example.com To: jack@example.com Subject: Example of a message in French and English Date: Thu, 7 Apr 2017 21:28:00 +0100 MIME-Version: 1.0 Content-Type: multipart/multilingual; boundary="01189998819991197253" --01189998819991197253 Content-Type: text/plain; charset="UTF-8" Content-Disposition: inline Content-Transfer-Encoding: 8bit This is a message in multiple languages. It says the same thing in each language. If you can read it in one language, you can ignore the other translations. The other translations may be presented as attachments or grouped together. Ce message est disponible en plusieurs langues, disant la même chose dans toutes les langues. Si vous le lisez dans une des langues, vous pouvez donc ignorer les autres. Dans votre logiciel de messagerie, ces autres traductions peuvent se présenter comme des pièces jointes, ou bien collées ensemble. --01189998819991197253 Content-Type: message/rfc822 Content-Language: en Content-Translation-Type: original Content-Disposition: inline From: Manager <jeanne@example.com> Subject: Example of a message in French and English Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Hello, this message content is provided in your language. --01189998819991197253 Content-Type: message/rfc822 Content-Language: fr Content-Translation-Type: human Content-Disposition: inline From: Directrice <jeanne@example.com> Subject: =?utf-8?q?Message_d=27exemple=2C_en_fran=C3=A7ais_et_en_anglais?= Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit MIME-Version: 1.0 Bonjour, ce message est disponible dans votre langue. --01189998819991197253--
On note qu'il y a une préface (la première partie, celle qui
commence par « This is a message in multiple languages »), que
chacun des deux parties étiquetées a un
From:
(pour pouvoir adapter le titre de
l'expéditrice) et un sujet (celui en français étant encodé selon
le RFC 2047). Les deux dernières parties
ont un Content-Translation-Type:
.
Actuellement, il ne semble pas qu'il existe de
MUA qui gère cette nouvelle norme. Si vous
voulez envoyer des messages à ce format, vous pouvez copier/coller
l'exemple ci-dessus, ou bien utiliser le programme Python send-multilingual.py
.
Voici le message d'exemple cité plus haut affiché par mutt (version NeoMutt 20170113 (1.7.2)) :
Thunderbird affiche à peu près la même chose.
Gmail a stupidement décidé de mettre ce message dans le dossier Spam. Une fois le message sorti de ce purgatoire, voici ce que ça donnait :
Notez l'existence d'une FAQ sur ce format.
Une mise en œuvre dans
mutt a été faite au hackathon
de la réunion IETF 101 par Alexandre Simon. Elle a été soumise en tant que pull
request à mutt. Outre ce travail sur un
MUA, le même auteur a écrit un réflecteur de
courrier permettant de tester ce RFC. Vous envoyez un
message ordinaire en français à
reflector+sl_fr+tl_zh@rfc8255.as-home.fr
(sl
= Source Language, tl = Target
Language, ici, on dit que le message est en français, et
on veut une réponse en chinois), et il répond avec
un message conforme au RFC 8255, traduit par Google Translate. Voici par exemple une demande :
To: reflector+sl_fr+tl_zh@rfc8255.as-home.fr MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit User-Agent: NeoMutt/20170113 (1.7.2) Subject: Test je parle la France From: Stephane Bortzmeyer <stephane@bortzmeyer.org> Alors, ça donne quoi ? Surtout s'il y a plusieurs lignes.
Et sa réponse :
Content-Type: multipart/multilingual; boundary="----------=_1521485603-32194-0" Content-Transfer-Encoding: binary MIME-Version: 1.0 To: Stephane Bortzmeyer <stephane@bortzmeyer.org> Sender: <reflector@idummy.as-home.fr> Subject: Test je parle la France From: Stephane Bortzmeyer <stephane@bortzmeyer.org> This is a multi-part message in MIME format... ------------=_1521485603-32194-0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: binary Alors, ça donne quoi ? Surtout s'il y a plusieurs lignes. ------------=_1521485603-32194-0 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: binary Content-Language: fr Content-Translation-Type: original Content-Type: text/plain; charset=utf-8 From: Stephane Bortzmeyer <stephane@bortzmeyer.org> Subject: Test je parle la France Alors, ça donne quoi ? Surtout s'il y a plusieurs lignes. ------------=_1521485603-32194-0 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: binary Content-Language: zh Content-Translation-Type: automated Content-Type: text/plain; charset=utf-8 From: Stephane Bortzmeyer <stephane@bortzmeyer.org> Subject: =?UTF-8?B?5rWL6K+V5oiR6K6y5rOV5Zu9?= 那么,这给了什么? 特别是如果有几条线。 ------------=_1521485603-32194-0 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: binary Content-Language: zxx Content-Translation-Type: human Content-Type: text/plain; charset=utf-8 /////| ///// | ///// | |~~~| M | |===| U | | | L | | R | T | | F | I | | C | L | | 8 | I | | 2 | N | | 5 | G | | 5 | U | | | A | | | L | | | | | | / | | / |===|/ '---' ------------=_1521485603-32194-0--
On notera la dernière partie, qui utilise la langue
zxx
, code qui veut dire « non spécifiée », et
qui sert à mettre du texte qui sera affiché dans tous les
cas. Notez aussi que le message originel est inclus.
Ce message est ainsi affiché par un mutt actuel :
Et par le mutt patché pendant le hackathon :
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : J. Klensin, J. Hakala (The National Library of Finland)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF urnbis
Première rédaction de cet article le 21 octobre 2017
Pour avoir un espace de noms d'URN, autrefois, il fallait un examen par l'IETF, ce qui était un peu contraignant (et risquait de mener au « squat », avec des gens utilisant un espace de noms qu'ils ne se sont pas donné la peine d'enregistrer). Le RFC 8141 ayant changé cette politique d'enregistrement pour celle, plus facile, d'examen par un expert, il a fallu clarifier quelques processus, ce que fait ce RFC. Dans la foulée, ce RFC change le statut de quelques RFC précédents, qui avaient servi de base à l'enregistrement de certains espaces de noms. Bref, que de la procédure et de la bureaucratie (mais pas inutile).
Un des buts des URN (désormais normalisés dans le RFC 8141) était d'intégrer des systèmes d'identificateurs existants. C'est ainsi que le RFC 2288 avait décrit un espace de noms pour les ISBN, mais sans l'enregistrer formellement à l'IANA, en partie parce que le mécanisme d'enregistrement n'était pas clair à l'époque. Par la suite, le RFC 3187 a remplacé le RFC 2288 et comblé ce manque (dans sa section 5). Idem pour les ISSN dans le RFC 3044. Ces deux RFC 3187 et RFC 3044 sont reclassés comme étant désormais d'intérêt historique uniquement (section 2 de notre RFC), les enregistrements de ces espaces de noms ayant été refaits selon les nouvelles règles (section 5). Voyez le nouvel enregistrement d'isbn et celui d'issn.
Pour ISBN, le principal changement est que l'espace de noms URN fait désormais référence à la nouvelle version de la norme, ISO 2108:2017 (cf. la page officielle et, comme toutes les normes du dinosaure ISO, elle n'est pas disponible en ligne.) Elle permet les ISBN de treize caractères (les ISBN avaient dix caractères au début). Pour ISSN, la norme du nouvel enregistrement est ISO 3297:2007, qui permet notamment à un ISSN de référencer tous les médias dans lesquels a été fait une publication (il fallait un ISSN par média, avant).
Et les NBN (National
Bibliography Number, section 3) ? C'est un cas rigolo,
car NBN ne désigne pas une norme spécifique, ni même une famille
d'identificateurs, mais regroupe tous les mécanismes
d'identificateurs utilisés par les bibliothèques nationales
partout dans le monde. En effet, les bibibliothèques peuvent
recevoir des documents qui n'ont pas d'autre identificateur. Si la
bibliothèque nationale de Finlande reçoit
un tel document, son identificateur national pourrait être,
mettons, fe19981001
, et, grâce à l'espace de
noms URN nbn
, il aura un URN,
urn:nbn:fi-fe19981001
.
Les NBN sont spécifiés dans le RFC 3188, pour lequel il n'y a pas encore de mise à jour prévue, contrairement aux ISBN et ISSN.
Outre les NBN, il y a bien d'autres schémas d'URN qui mériteraient une mise à jour, compte-tenu des changements du RFC 8141 (section 4). Mais il n'y a pas le feu, même si leur enregistrement actuel à l'IANA n'est pas tout à fait conforme au RFC 8141. On peut continuer à les utiliser.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : JM. Valin (Mozilla
Corporation), K. Vos (vocTone)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF codec
Première rédaction de cet article le 22 octobre 2017
Le codec Opus, normalisé dans le RFC 6716 a une particularité rare à l'IETF : il est spécifié par un programme, pas par une description écrite en langue naturelle. Les programmes ont des bogues et ce nouveau RFC corrige quelques petites bogues pas graves trouvées dans le RFC 6716.
C'est un vieux débat dans le monde de la normalisation : faut-il décrire un protocole ou un format par une description en langue naturelle (vecteur souvent ambigu) ou par une mise en œuvre dans un langage de programmation (qui fournira directement un programme utilisable), dans laquelle il sera difficile de distinguer ce qui est réellement obligatoire et ce qui n'est qu'un détail de cette mise en œuvre particulière ? Presque tout le temps, à l'IETF, c'est la voie de la description en anglais qui est choisie. Mais le RFC 6716, qui normalisait Opus, avait choisi une autre voie, celle du code source, écrit en C. C'est ce code qui est la loi.
Dans les deux cas, en anglais ou en C, les humains qui rédigent les normes font des erreurs. D'où ce RFC de correction, qui répare le RFC 6716 sur des points mineurs (la compatibilité est maintenue, il ne s'agit pas d'une nouvelle version d'Opus).
Chaque section du RFC est ensuite consacrée à une des erreurs. La section 3, par exemple, corrige un simple oubli dans le code de réinitialiser l'état du décodeur lors d'un changement. Le patch ne fait que deux lignes. Notez qu'il change le résultat produit par le décodeur, mais suffisamment peu pour que les vecteurs de test de l'annexe A.4 du RFC 6716 soient inchangés.
L'erreur en section 9 est également une erreur de logique dans la programmation. Sa correction, par contre, nécessite de changer les vecteurs de tests (cf. section 11).
Le reste des bogues, en revanche, consiste en erreurs de programmation
C banales. Ainsi, en section 4, il y a un
débordement d'entier si les données font
plus de 2^31-1 octets, pouvant mener à lire en dehors de la mémoire. En
théorie, cela peut planter le décodeur (mais, en pratique, le
code existant n'a pas planté.) Notez que cela ne peut pas arriver
si on utilise Opus dans RTP, dont les
limites seraient rencontrées avant qu'Opus ne reçoive ces données
anormales. Cette bogue peut quand même avoir des conséquences de
sécurité et c'est pour cela qu'elle a reçu un
CVE, CVE-2013-0899. Le
patch est très court. (Petit
rappel de C : la norme de ce
langage ne spécifie pas ce qu'il faut faire lorsqu'on
incrémente un entier qui a la taille maximale. Le résultat dépend
donc de l'implémentation. Pour un entier
signé, comme le type
int
, le comportement le plus courant est de
passer à des valeurs négatives.)
Notez qu'une autre bogue, celle de la section 7, a eu un CVE, CVE-2017-0381.
En section 5, c'est un problème de typage : un entier de 32 bits utilisé au lieu d'un de 16 bits, ce qui pouvait mener une copie de données à écraser partiellement les données originales.
Dans la section 6 du RFC, la bogue était encore une valeur trop grande pour un entier. Cette bogue a été découverte par fuzzing, une technique très efficace pour un programme traitant des données externes venues de sources qu'on ne contrôle pas !
Bref, pas de surprise : programmer en C est difficile, car de trop bas niveau, et de nombreux pièges guettent le programmeur.
La section 11 du RFC décrit les nouveaux vecteurs de test rendus nécessaires par les corrections à ces bogues. Ils sont téléchargeables.
Une version à jour du décodeur normatif figure désormais sur le site officiel. Le patch traitant les problèmes décrits dans ce RFC est en ligne. Ce patch global est de petite taille (244 lignes, moins de dix kilo-octets, ce qui ne veut pas dire que les bogues n'étaient pas sérieuses).
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : N. Cam-Winget (Cisco), L. Lorenzin (Pulse Secure)
Pour information
Réalisé dans le cadre du groupe de travail IETF sacm
Première rédaction de cet article le 25 septembre 2017
Le groupe de travail SACM de l'IETF bosse sur une architecture, un modèle de données, et des protocoles pour suivre l'état de la sécurité de ses machines. Ceci est son deuxième RFC, qui vise à définir le cahier des charges de la future solution.
Le cœur de cette idée est l'évaluation de la sécurité sur chaque machine du réseau. Il faut la déterminer, et acheminer le résultat jusqu'aux responsables de la sécurité. Cela nécessite donc un modèle de données (comment on décrit « la sécurité d'une machine » ?) puis un protocole permettant de gérer un très grand nombre de machines. Parmi les informations à récolter sur chaque machine, on trouvera évidemment la liste des logiciels installés, les logiciels serveurs en activité qui écoutent sur le réseau, l'état de la mise à jour (« pas pu joindre le dépôt officiel de logiciels depuis N jours »), etc. Le cahier des charges décrit dans ce RFC part des scénarios d'usage du RFC 7632. Ce sont souvent de grandes généralités. On est loin de mécanismes concrets.
La section 2 forme l'essentiel de ce RFC, avec la liste des exigences du cahier des charges. Je ne vais en citer que quelques unes. La première, nommée G-001 (« G » préfixe les exigences générales sur l'ensemble du système SACM), demande que toute la solution SACM soit extensible, pour les futurs besoins. G-003 est la possibilité de passage à l'échelle. Par exemple, les messages échangés peuvent aller de quelques lignes à, peut-être plusieurs gigaoctets si on fait une analyse détaillée d'une machine. Et il peut y avoir beaucoup de machine avec des échanges fréquents (le RFC n'est pas plus précis sur les chiffres).
Les données traitées sont évidemment sensibles, et G-006 demande que la solution permette évidemment de préserver la confidentialité des données.
Les exigences sur l'architecture de la future solution sont préfixées de « ARCH ». Ainsi, ARCH-009 insiste sur l'importance d'une bonne synchronisation temporelle de toutes les machines. Un journal des connexions/déconnexions des utilisateurs, par exemple, n'aurait guère d'intérêt si l'horloge qui sert à l'estampiller n'est pas exacte.
D'autres sections décrivent les exigences pour le modèle de données et pour les protocoles, je vous laisse les découvrir.
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : P. McManus (Mozilla)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 16 septembre 2017
Lorsqu'un serveur HTTP renvoie une
réponse à un client, il peut indiquer une durée de vie maximale de
la ressource transmise, avec l'en-tête
Cache-Control:
(RFC 9111). Mais cela ne donne qu'une durée
maximale. La ressource peut quand même être
modifiée avant la fin de cette période. Un client HTTP prudent va
donc revalider la fraîcheur de cette
ressource de temps en temps. L'extension décrite dans ce
RFC permet de se dispenser de cette
revalidation, en indiquant une durée
minimale, pendant laquelle on est sûrs et
certains que la ressource ne sera pas modifiée.
Le RFC prend l'exemple (section 1) d'un journal dont la page d'accueil indique une durée de vie maximale d'une heure :
Cache-Control: max-age=3600
Le client HTTP qui reçoit cet en-tête dans la réponse sait qu'il doit recontacter le serveur au bout d'une heure. Mais la page sera peut-être modifiée (par exemple en raison d'un évènement soudain et imprévu) avant qu'une heure soit écoulée. Le client est donc partagé entre la revalidation (qui risque de consommer des ressources pour rien, même si elle est moins coûteuse qu'un téléchargement complet, grâce aux trucs du RFC 7232) et le risque de servir à son utilisateur une ressource qui n'est désormais plus à jour. Le problème est d'autant plus ennuyeux que la revalidation d'une bête page Web qui comprend des photos peut générer beaucoup de requêtes, dont la plupart produiront sans doute un 304 (ressource non modifiée, la revalidation était inutile).
Notez que dans certains cas, le contenu pointé par un URL peut changer, mais dans d'autres cas, il est réellement immuable. Par exemple, si on publie chaque version de sa feuille de style sous un URL différent, la CSS n'a pas besoin d'être revalidée. (La mention d'immuabilité est donc intéressante pour les URL faits à partir du RFC 6920.)
Bref, il y avait un besoin de pouvoir indiquer l'immuabilité
d'une ressource. C'est désormais fait (section 2 de ce RFC) avec
une extension à Cache-Control:
:
Cache-Control: max-age=3600, immutable
Avec le mot-clé immutable
, le serveur indique
que la ressource ne sera pas modifiée pendant la durée de vie
indiquée, le client peut donc se dispenser de
vérifier. (« Client » ici désignant aussi bien le client final,
par exemple le navigateur Web, qu'un
intermédiaire.)
Par exemple, comme les RFC sont immuables (on ne les change
jamais même d'une virgule, même en cas d'erreur), la ressource qui désigne ce RFC,
https://www.rfc-editor.org/rfc/rfc8246.txt
,
pourrait parfaitement renvoyer cet en-tête (elle ne le fait
pas). Cela pourrait être :
Last-Modified: Thu, 14 Sep 2017 23:11:35 GMT Cache-Control: max-age=315576000, immutable
(Oui, dix ans…)
Voilà, c'est tout, la directive a été ajoutée au registre
IANA. Mais la section 3 du RFC se penche encore
sur quelques questions de sécurité. Indiquer qu'une ressource est
immuable revient à la fixer pour un temps potentiellement très
long, et cela peut donc servir à pérenniser une attaque. Si un
méchant pirate un site Web, et sert son contenu piraté avec un
en-tête d'immuabilité, il restera bien plus longtemps dans les
caches. Pire, sans
HTTPS, l'en-tête avec
immutable
pourrait être ajouté par un
intermédiaire (le RFC déconseille donc de tenir compte de cette
option si on n'a pas utilisé HTTPS).
Les navigateurs Web ont souvent deux options pour recharger une
page, « douce » et « dure » (F5 et Contrôle-F5 dans
Firefox sur Unix). Le RFC conseille que, dans le second cas
(rechargement forcé), le immutable
soit ignoré (afin de
pouvoir recharger une page invalide).
Enfin, toujours question sécurité, le RFC recommande aux
clients HTTP de ne tenir compte de l'en-tête d'immuabilité que
s'ils sont sûrs que la ressource a été transmise proprement
(taille correspondant à Content-Length:
par
exemple).
Si vous voulez comparer deux ressources avec et sans
immutable
, regardez
(sans http://www.bortzmeyer.org/files/maybemodified.txt
immutable
) et
(avec). Si le client HTTP gère l'extension d'immuabilité, un
rechargement « doux » ne fera pas de requête HTTP. Sinon, il y aura
revalidation et le serveur HTTP renverra un 304 :
http://www.bortzmeyer.org/files/forever.txt
[2001:db8:abcd:1234:acd8:9bd0:27c9:3a7f]:45452 - - [16/Sep/2017:17:53:29 +0200] "GET /files/forever.txt HTTP/1.1" 304 - "-" "Mozilla/5.0 (X11; Linux i686; rv:52.0) Gecko/20100101 Firefox/52.0" www.bortzmeyer.org
Apparemment, Firefox gère cette extension mais, ici, le Firefox était sans doute trop vieux (normalement, cela aurait dû arriver avec la version 49, puisque Mozilla était premier intéressé). Les auteurs de Squid ont annoncé qu'ils géreraient cette extension. Côté serveur, notons que Facebook envoie déjà cette extension pour, par exemple, le code JavaScript (qui est versionné et donc jamais changé) :
% curl -v https://www.facebook.com/rsrc.php/v3iCKe4/y2/l/fr_FR/KzpL-Ycd5sN.js * Connected to www.facebook.com (2a03:2880:f112:83:face:b00c:0:25de) port 443 (#0) ... < HTTP/2 200 < content-type: application/x-javascript; charset=utf-8 < cache-control: public,max-age=31536000,immutable ...
Idem pour une mise en œuvre d'IPFS.
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : T. Lemon
(Nominum), R. Droms, W. Kumari
(Google)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 20 octobre 2017
Le RFC 6761 créait un
registre de « noms de
domaines d'usage spécial », où l'IETF
enregistrait des noms qui pouvaient nécessiter un traitement
spécial par une des entités de la chaîne d'avitaillement et de
résolution des noms de domaines. Un exemple est celui des noms de
domaine qui n'utilisaient pas le DNS, comme le
.onion
du RFC 7686. Certaines personnes ont émis des
réserves sur ce registre, par exemple parce qu'il marchait sur une
pelouse que l'ICANN considère comme sienne. Ce
nouveau RFC,
très controversé, fait la liste de tous les reproches qui ont été
faits au RFC 6761 et à son registre des noms
spéciaux.
La résolution de noms - partir d'un nom de domaine et obtenir des informations
comme l'adresse IP - est une des fonctions
cruciales de l'Internet. Il est donc normal
de la prendre très au sérieux. Et les noms ont une valeur pour les
utilisateurs (la vente record semble être celle de
business.com
). Souvent, mais pas
toujours, cette résolution se fait avec le
DNS. Mais, je l'ai dit, pas toujours : il
ne faut pas confondre les noms de domaines (une organisation
arborescente, une syntaxe, les composants séparés par des
points, par exemple
_443._tcp.www.bortzmeyer.org.
,
réussir-en.fr.
ou
sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion.
) et le DNS, un
protocole réseau particulier (section 2 du RFC, pour la
terminologie). La section 2 de ce RFC, suivant la définition du
RFC 8499, ne considère pas qu'un nom de
domaine utilise forcément le DNS. (Le problème n° 20, en section 3 revient sur
cette question.)
Le registre
des noms de domaines spéciaux a été créé en 2013 par le
RFC 6761. Contrairement à ce que croient
beaucoup de gens, il ne contient pas que des TLD (par exemple, il
a aussi a.e.f.ip6.arpa
ou bien
example.org
). Depuis sa création, plusieurs
noms ont été ajoutés, le plus spectaculaire ayant été le
.onion
en 2015, via le
RFC 7686. Beaucoup d'autres noms ont été
proposés, sans être explicitement rejetés mais sans être acceptés
non plus (voir les
Internet-Drafts
draft-grothoff-iesg-special-use-p2p-gns
et draft-chapin-additional-reserved-tlds
). Ces
enregistrements ne se sont pas très bien passés, avec beaucoup
d'engueulades, notamment pour les TLD.
En effet, la bagarre a toujours fait rage pour la gestion des noms dans la racine du DNS. Il y a cinq sortes de TLD :
.bo
,
.net
,
.guru
,
etc. Contrairement à ce que dit le RFC, ils ne sont pas
forcément délégués par l'ICANN
(.de
a été créé
bien avant l'ICANN, et
.su
résiste depuis
longtemps à toutes les tentatives de l'ICANN de le supprimer)..arpa
. Les domaines
du registre des noms spéciaux en font partie. Les fanas de
gouvernance noteront
qu'ils ne passent jamais par l'ICANN..eth
pour Ethereum ou
.bit
pour
Namecoin. Cela inclut aussi des noms
très utilisés localement comme .home
ou .corp
..macron
sera
utilisé par les fans du Président ?Cette classification est imparfaite, comme toutes les classifications. Par exemple, un nom peut être sur la liste noire de l'ICANN et être quand même utilisé par des organisations locales, ou via un logiciel particulier.
Ce RFC veut présenter une liste exhaustive des problèmes posés lors de l'enregistrement de ces noms spéciaux. Comme elle est exhaustive (« an unfiltered compilation of issues »), elle n'est pas consensuelle, loin de là ! Plusieurs des problèmes ne sont pas considérés comme tels par tout le monde, ce que le RFC note bien dans les sections 1 et 3.
Le gros du RFC est donc cette liste de problèmes, en section 3. Je ne vais pas tous les citer. Notez tout de suite que ce RFC décrit des problèmes, ou perçus comme tels par certains, pas des solutions. Il ne propose pas de réforme du RFC 6761. Notez aussi que les problèmes ne sont pas classés (leur ordre d'exposition, et le numéro qu'ils reçoivent, n'est pas un ordre d'importance.)
Premier problème cité, la coordination avec
l'ICANN. Les cinq catégories de noms de
TLD cités
plus haut coexistent dans le même espace de noms. S'il y a deux
.macron
, il y aura un problème (voir par
exemple le
rapport SAC 090, mais en se rappelant que c'est l'ICANN qui
s'auto-défend). L'IETF et
l'ICANN ont un mécanisme d'information
réciproque mais pas de processus bureaucratique formel de
coordination. Si l'IETF veut réserver .home
(RFC 7788), il n'y a officiellement pas de
mécanisme pour communiquer cette décision à l'ICANN et obtenir
d'elle la confirmation que ce nom n'est pas par ailleurs en cours
de réservation. (Cf. 4.1.4 qui revient sur ce mécanisme
- « liaison » en anglais.)
(Personnellement, cet argument me fait plutôt rire : beaucoup de gens sont actifs à la fois à l'IETF et à l'ICANN, les deux organisations opèrent de manière assez publique, surtout l'IETF, et la possibilité qu'elles réservent chacune un nom sans savoir ce qu'a fait l'autre est purement théorique.)
Le problème 2 est celui de la définition de « pour des raisons techniques », introduit dans le RFC 2860, et qui fonde le droit de l'IETF à réserver des TLD sans passer par la caisse de l'ICANN (« assignments of domain names for technical uses [...] are not considered to be policy issues, and shall remain subject to the provisions of this Section 4 »). Le problème est que ces raisons techniques ne sont définies nulle part et que personne ne sait trop ce que cela veut dire.
Les problèmes 3 et 4 sont qu'il n'y a pas de Directeur Chef de
l'Internet. Ni l'ICANN, ni l'IETF (ni évidemment
l'UIT) ne sont reconnus, ni en droit, ni en
fait, comme ayant une autorité quelconque (le raccourci
journalistique de présenter l'ICANN comme « le régulateur mondial
de l'Internet » est ridiculement faux). Imaginons qu'une
développeuse crée un nouveau logiciel qui utilise le TLD
.zigzag
pour son nommage, et diffuse le
logiciel en question : personne ne peut l'en empêcher, même si
l'ICANN souffre de la perte de revenus potentiels, même si des
grognons à l'IETF murmurent bruyamment que c'est du
squatting (voir aussi le début de la section 4). Et heureusement que c'est ainsi :
c'est le côté « innover sans autorisation »
(« permissionless innovation » qui a été si
essentiel pour le succès de l'Internet). Un autre exemple est bien sûr le
projet .42, aujourd'hui abandonné mais qui
illustrait ce côté décentralisé de l'Internet.
La définition du problème 5 note que les organisations (ou les individus) utilisent des TLD (ou des domaines quelconques, mais c'est surtout sur les TLD que se focalise la discussion) sans suivre les procédures. Elles ont plusieurs raisons pour agir ainsi :
.corp
et .home
sont des TLD locaux très
populaires et il y a des candidatures (actuellement gelées
de facto) pour ces TLD à
l'ICANNEnsuite, le problème 6 : il y a plusieurs protocoles de résolution de
nom, le DNS n'étant que le principal. En l'absence de
métadonnées indiquant le protocole à
utiliser (par exemple dans les URL),
se servir du TLD comme « aiguillage » est une solution
tentante (if tld == "onion" then use Tor elsif tld ==
"gnu" then use GnuNet else use DNS
…)
Le registre des noms de domaines spéciaux est essentiellement
en texte libre, sans grammaire formelle. Cela veut dire que le
code spécifique qui peut être nécessaire pour traiter tous ces
domaines spéciaux (un résolveur doit, par exemple, savoir que
.onion
n'utilise pas
le DNS et qu'il ne sert donc à rien de l'envoyer à la racine) doit
être fait à la main, il ne peut pas être automatiquement dérivé du
registre (problème 7). Résultat, quand un nouveau domaine est
ajouté à ce registre, il sera traité « normalement » par les
logiciels pas mis à jour, et pendant un temps assez long. Par
exemple, les requêtes seront envoyées à la racine, ce qui pose des
problèmes de vie privée (cf. RFC 7626).
Comme noté plus haut, certains candidats à un nom de domaine
spécial sont inquiets du temps que prendra l'examen de leur
candidature, et du risque de rejet (problème 8). Ils n'ont pas
tort. L'enregistrement du
.local
(RFC 6762) a pris dix ans, et les efforts de Christian Grothoff
pour enregistrer .gnu
se sont enlisés dans
d'épaisses couches de bureaucratie. Ce n'est peut-être pas un
hasard si Apple a fini par avoir son
.local
(non sans mal) alors que des projets
de logiciel libre
comme GNUnet se sont vu fermer la porte au
nez.
C'est que l'IETF n'est pas toujours facile et qu'un certain nombre de participants à cette organisation ont souvent une attitude de blocage face à tout intervenant extérieur (problème 9). Ils n'ont pas tort non plus de se méfier des systèmes non-DNS, leurs raisons sont variées :
.zkey
? »). De plus, un certain nombre de
participants à l'IETF estiment qu'il ne faut de toute façon
qu'un seul protocole de résolution, afin de limiter la
complexité..onion
, cela peut durer
longtemps et coûter cher. L'ICANN court le même risque mais,
elle, elle a des avocats en quantité, et de l'argent pour les payer.Le problème 11 est davantage lié au RFC 6761 lui-même. Ce RFC a parfois été compris de travers,
comme pour l'enregistrement de ipv4only.arpa
(RFC 7050, corrigé ensuite dans le RFC 8880) et surtout celui de
.home
(RFC 7788, qui
citait le RFC 6761 mais n'avait tenu aucune
de ses obligations). Voir aussi le problème 16.
Les problèmes 12 et 13 concernent les TLD existants (au sens où ils sont utilisés) mais pas enregistrés officiellement (voir par exemple le rapport de l'ICANN sur les « collisions »). Cela serait bien de documenter quelque part cette utilisation, de façon à, par exemple, être sûrs qu'ils ne soient pas délégués par l'ICANN. Mais cela n'a pas marché jusqu'à présent.
Le problème 14 concerne le fait que des noms de domaine spéciaux sont parfois désignés comme spéciaux par leur écriture dans le registre, mais parfois simplement par leur délégation dans le DNS.
Le problème 15 est celui de la différence entre enregistrer un
usage et l'approuver. Avec la règle « géré par l'IETF pour un
usage technique » du RFC 2860, il n'y a pas
moyen d'enregistrer un nom sans une forme d'« approbation ». (Pas
mal d'articles sur l'enregistrement de .onion
avaient ainsi dit, à tort, que « l'IETF approuvait officiellement
Tor ».)
Le problème 17 est un bon exemple du fait que la liste
de Prévert qu'est ce RFC est en effet « non filtrée » et que
toute remarque soulevée y a été mise, quels que soient ses
mérites. Il consiste à dire que l'utilisation du registre des noms
spéciaux est incohérente, car les enregistrements donnent des
règles différentes selon le nom. Cela n'a rien d'incohérent,
c'était prévu dès le départ par le RFC 6761 (section 5) :
il n'y a pas de raison de traiter de la même façon
.onion
(qui n'utilise pas le DNS du tout) et
.bit
(passerelle entre le DNS et
Namecoin).
Le problème 19 découle du fait que les noms de domaine ne sont
pas de purs identificateurs techniques comme le sont, par exemple,
les adresses MAC. Ils
ont un sens pour les utilisateurs. Bien que, techniquement
parlant, les développeurs de Tor auraient
pu choisir le nom .04aab3642f5
ou onion.torproject.org
comme suffixe pour les services en oignon,
ils ont préféré un nom d'un seul composant, et compréhensible,
.onion
. Ce désir
est bien compréhensible (une
proposition à l'IETF est de reléguer tous les noms spéciaux sous
un futur TLD .alt
, qui ne connaitra
probablement aucun succès même s'il est créé un jour). Mais il
entraine une pression accrue sur la racine des noms de domaine :
si deux projets réservent .zigzag
, lequel des
deux usages faut-il enregistrer ?
Enfin, le dernier problème, le 21, est davantage technique : il
concerne DNSSEC. Si un TLD est enregistré
comme domaine spécial, faut-il l'ajouter dans la racine du DNS et,
si oui, faut-il que cette délégation soit signée ou pas ? S'il n'y
a pas de délégation, le TLD sera considéré comme invalide par les
résolveurs validants. Par exemple, si je fais une requête pour
quelquechose.zigzag
, la racine du DNS va
répondre :
% dig @k.root-servers.net A quelquechose.zigzag ; <<>> DiG 9.10.3-P4-Debian <<>> @k.root-servers.net A quelquechose.zigzag ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 7785 ;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;quelquechose.zigzag. IN A ;; AUTHORITY SECTION: zero. 86400 IN NSEC zip. NS DS RRSIG NSEC zero. 86400 IN RRSIG NSEC 8 1 86400 ( 20171017050000 20171004040000 46809 . wyKfrNEygGCbDscCu6uV/DFofs5DKYiV+jJd2s4xkkAT ... . 86400 IN NSEC aaa. NS SOA RRSIG NSEC DNSKEY . 86400 IN RRSIG NSEC 8 0 86400 ( 20171017050000 20171004040000 46809 . kgvHoclQNwmDKfgy4b96IgoOkdkyRWyXYwohW+mpfG+R ... . 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. ( 2017100400 ; serial 1800 ; refresh (30 minutes) 900 ; retry (15 minutes) 604800 ; expire (1 week) 86400 ; minimum (1 day) ) . 86400 IN RRSIG SOA 8 0 86400 ( 20171017050000 20171004040000 46809 . GnTMS7cx+XB+EmbMFWt+yEAg29w17HJfUaOqvPsTn0eJ ... ;; Query time: 44 msec ;; SERVER: 2001:7fd::1#53(2001:7fd::1) ;; WHEN: Wed Oct 04 12:58:12 CEST 2017 ;; MSG SIZE rcvd: 1036
Et l'enregistrement NSEC prouvera qu'il n'y a
rien entre .zero
et
.zip
, amenant le résolveur validant à
considérer que .zigzag
ne peut pas
exister. Si le nom devait être traité par le DNS (et, par exemple,
résolu localement comme ceux du RFC 6303,
c'est une réponse correcte : la requête n'aurait pas dû aller à la
racine). Dans d'autres cas, cela peut être gênant. De toute façon,
le débat est assez théorique : l'IETF n'a aucun pouvoir sur la
racine du DNS, et aucun moyen d'y ajouter un nom.
Après cet examen des possibles et potentiels problèmes, la section 4 du RFC examine les pratiques existantes. Plusieurs documents plus ou moins officiels examinent déjà ces questions. Mais je vous préviens tout de suite : aucun ne répond complètement au·x problème·s.
Commençons par le RFC 2826 sur la
nécessité d'une racine unique. C'est un document
IAB et qui n'engage donc pas l'IETF, même
s'il est souvent cité comme texte sacré. Sa thèse principale est
qu'il faut une racine unique, et que .zigzag
,
.pm
ou
.example
doivent donc avoir la même signification partout, sous peine de
confusion importante chez l'utilisateur. Cela n'interdit pas des
noms à usage local à condition que cela reste bien local. Comme
les utilisateurs ne vont pas faire la différence, ces noms locaux
vont forcément fuiter tôt ou tard. (Par exemple, un utilisateur
qui regarde http://something.corp/
ne va pas
réaliser que ce nom ne marche qu'en utilisant les résolveurs de
l'entreprise, et va essayer d'y accéder depuis chez lui. Un autre
exemple serait celui d'un utilisateur qui essaierait de taper
ping sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion
depuis la ligne
de commande, sans se rendre compte que ping
ne connait pas Tor.)
Bref, le RFC 2826 dit clairement qu'une racine unique est nécessaire, et que les noms locaux sont casse-gueule.
Le second RFC à lire est évidemment le RFC 6761, qui a créé le registre des noms de domaine spéciaux. Lui, c'est un document IETF sur le Chemin des Normes. Quelques points qui sont souvent oubliés par les lecteurs du RFC 6761 méritent d'être répétés :
in-addr.arpa
,
par exemple, et rappelez-vous bien à son sujet que les noms de
domaine spéciaux ne sont pas forcément des TLD).10.in-addr.arpa
(pour les adresses IP
du RFC 1918), qui doit être traité par le
résolveur, sans jamais interroger la racine (cf. RFC 6303)..onion
(qui doit
utiliser Tor) ou de
.local
(qui doit utiliser mDNS, RFC 6762).Notez qu'à l'heure actuelle, tous les noms enregistrés comme noms de domaine spéciaux sont des TLD ou bien sont résolus avec le DNS.
Troisième RFC à lire absolument avant de dire des bêtises sur les noms de domaine spéciaux, le RFC 2860, qui fixe le cadre des relations compliquées entre l'IETF et l'ICANN. En gros, la règle par défaut est que l'ajout (ou le retrait) de TLD dans la racine est une prérogative de l'ICANN sauf les « noms de domaine à usage technique » (notion non définie…) où l'IETF décide. Notez que ce document concerne uniquement les relations entre l'IETF et l'ICANN. Si le W3C, ou la développeuse du logiciel ZigZag, veut créer un TLD, que se passe-t-il ? Ce point n'est pas traité dans le RFC 2860. Certains exégètes estiment que cela veut dire que ces tiers sont implicitement exclus.
Il y a aussi d'autres documents mais moins cruciaux. Le RFC 6762 qui normalise mDNS est celui qui a
réservé .local
et c'est donc un
exemple d'un enregistrement réussi (mais qui fut laborieux, plus
de douze années de développement furent nécessaires, cf. l'annexe
H du RFC 6762).
Autre exemple réussi, le RFC 7686 sur le
.onion
. .onion
était
utilisé depuis longtemps quand le RFC 6761 a
créé le registre des noms de domaine spéciaux. L'enregistrement
a
posteriori a réussi, malgré de
vigoureuses oppositions mais il faut noter que le consensus
approximatif de l'IETF a été facilité par une décision du
CA/B Forum de ne plus allouer
de certificats pour des TLD internes.
Encore un autre RFC à lire, le RFC 6303,
qui décrit les noms qui devraient idéalement être résolus
localement, c'est-à-dire par le résolveur de l'utilisateur, sans
demander aux serveurs faisant autorité. C'est par exemple le cas
des in-addr.arpa
correspondant aux adresses IPv4 privées du RFC 1918. Il ne sert à rien de demander à la racine
l'enregistrement PTR de
3.2.1.10.in-addr.arpa
: ces adresses IP étant
purement locales, il ne peut pas y avoir de réponse intelligente
de la racine. Les noms en 10.in-addr.arpa
doivent donc être résolus localement, et sont donc, eux aussi, des
« noms de domaine spéciaux ». Par contre, contrairement à
.local
ou à .onion
, ils
sont résolus par le DNS.
Pas fatigué·e·s ? Encore envie de lire ? Il y aussi l'étude
d'Interisle sur les « collisions ». Derrière ce nom
sensationnaliste conçu pour faire peur, il y a un vrai problème,
le risque qu'un TLD récent masque, ou soit masqué par, un TLD
alloué localement sans réfléchir (comme .dev
). L'étude
montrait par exemple que .home
était celui
posant le plus de risques.
Sur un sujet proche, il y a aussi une étude du SSAC, un comité ICANN.
On a dit plus haut que les noms de domaine « spéciaux »
n'étaient pas forcément des TLD. C'est par exemple le cas d'un nom
utilisé pour certaines manipulations IPv6,
ipv4only.arpa
, créé par le RFC 7050, mais qui, par suite d'un cafouillage dans le
processus, n'avait pas été ajouté immédiatement au registre des noms de domaine
spéciaux. Dommage : ce nom, n'étant pas un TLD et n'ayant pas de
valeur particulière, n'avait pas posé de problème et avait été
accepté rapidement.
Enfin, un dernier échec qu'il peut être utile de regarder, est
la tentative d'enregistrer comme noms de domaine spéciaux des TLD
très souvent alloués localement, et qu'il serait prudent de ne pas
déléguer dans la racine, comme le .home
cité
plus haut. Un projet
avait été rédigé en ce sens, mais n'avait jamais abouti,
enlisé dans les sables procéduraux.
Si vous n'avez pas mal à la tête à ce stade, vous pouvez encore
lire la section 5, qui rappelle l'histoire tourmentée de ce
concept de noms de domaine spéciaux. Lorsque le DNS a été produit
(RFC 882 et RFC 883)
pour remplacer l'ancien système HOSTS.TXT
(RFC 608), la transition ne s'est pas faite sans
douleur, car plusieurs systèmes de résolution coexistaient (le
plus sérieux étant sans doute les Yellow
Pages sur Unix, mais il y avait aussi
NetBIOS name
service/WINS, qui ne tournait pas que sur
Windows). Encore aujourd'hui, des anciens
systèmes de résolution fonctionnent toujours. Le
HOSTS.TXT
survit sous la forme du
/etc/hosts
d'Unix (et de son équivalent
Windows). Les systèmes d'exploitation ont en général
un « commutateur » qui permet d'indiquer quel mécanisme de
résolution utiliser pour quel nom. Voici un exemple d'un
/etc/nsswitch.conf
sur une machine
Debian qui, pour résoudre un nom de
domaine, va utiliser successivement
/etc/hosts
, LDAP, puis
le DNS :
hosts: files ldap dns
Le concept de TLD privé, connu uniquement en local, a été (bien à tort) recommandé par certaines entreprises comme Sun ou Microsoft. Il a survécu à la disparition des technologies qui l'utilisaient, comme Yellow Pages. Aujourd'hui, c'est une source d'ennuis sans fin, et bien des administrateurs réseau ont maudit leur prédécesseur pour avoir configuré ainsi tout le réseau local, allumant ainsi une bombe qui allait exploser quand le TLD « privé » s'est retrouvé délégué.
La discussion à l'IETF, notamment dans son groupe de travail
DNSOP a été très chaude. Un premier document avait été
élaboré, draft-adpkja-dnsop-special-names-problem
,
puis l'ancêtre de ce RFC avait été écrit, le premier document
étant abandonné (il était très proche du point de vue de
l'ICANN comme quoi seule l'ICANN devrait
pouvoir créer des TLD, les autres acteurs n'étant que de vilains
squatteurs).
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : H. Tschofenig (ARM Limited), S. Farrell (Trinity College Dublin)
Pour information
Première rédaction de cet article le 12 septembre 2017
La mode des objets connectés a mené à l'existence de nombreux « objets », qui sont en fait des ordinateurs (avec leurs problèmes, comme le fait que leur logiciel ait des failles de sécurité), mais qui ne sont pas considérés comme des ordinateurs par leurs propriétaires, et ne sont donc pas gérés (pas d'administrateur système, pas de mises à jour des logiciels). L'attaque contre Dyn du 21 octobre 2016, apparemment menée par ces objets, a bien illustré le risque que cette profusion irresponsable crée. Ce nouveau RFC est le compte-rendu d'un atelier sur la mise à jour des objets connectés, atelier de réflexion qui s'est tenu à Dublin les 13 et 14 juin 2016. Il fait le point sur la (difficile) question.
La question de base était « que peut-on faire pour que ces foutus objets soient mis à jour, au moins pour patcher leurs failles de sécurité ? » L'atelier réuni à Trinity College n'a évidemment pas fourni une réponse parfaite, mais a au moins permis de clarifier le problème. Il me semble toutefois, d'après le compte-rendu, qu'il y avait un gros manque : les droits du propriétaire de l'objet. La plupart des solutions discutées tournaient autour de l'idée de mises à jour systématiques et automatiques du logiciel via les serveurs du vendeur, et les éventuelles conséquences néfastes pour le propriétaire (comme l'arrivée de nouvelles fonctions de surveillance, par exemple) ne sont guère mentionnées.
Aujourd'hui, un grand nombre des machines ayant accès, même indirect, à l'Internet, est composé de trucs qualifiés d'objets, qui ont en commun qu'on ne les appelle pas « ordinateurs » (alors que c'est bien cela qu'ils sont). Ces objets sont très divers, allant d'engins qui ont les capacités matérielles d'un ordinateur (une télévision connectée, par exemple) à des petits machins très contraints (CPU et mémoire limités, batterie à la capacité finie, etc). Le risque que font peser ces objets « irresponsables » (pas gérés, pas supervisés) sur l'Internet est connu depuis longtemps. Le RFC cite l'article de Schneier, « The Internet of Things Is Wildly Insecure And Often Unpatchable », qui tirait la sonnette d'alarme en 2014. Il notait que le logiciel de ces objets n'était déjà plus à jour quand l'objet était sorti de sa boite la première fois (les fabricants d'objets connectés aiment bien utiliser des versions antédiluviennes des bibliothèques). Or, des objets peuvent rester branchés et actifs pendant des années, avec leur logiciel dépassé et jamais mis à jour, plein de failles de sécurité que les craqueurs ne vont pas manquer d'exploiter (cf. le logiciel Mirai).
Vers la même époque, un rapport de la FTC, « FTC Report on Internet of Things Urges Companies to Adopt Best Practices to Address Consumer Privacy and Security Risks », et un autre du groupe Article 29, « Opinion 8/2014 on the on Recent Developments on the Internet of Things » faisaient les mêmes remarques.
Donc, le problème est connu depuis des années. Mais il n'est pas facile à résoudre :
Le risque n'est donc pas seulement de l'absence de système de mise à jour. Il peut être aussi d'un système de mise à jour bogué, vulnérable, ou non documenté, donc non maintenable.
Le problème de la mise à jour automatique des logiciels est évidemment ancien, et plusieurs systèmes d'exploitation ont des solutions opérationnelles depuis un certain temps (comme pacman ou aptitude). Le RFC se focalise donc sur les objets les plus contraints, ceux limités dans leurs capacités matérielles, et donc dans les logiciels qu'ils peuvent faire tourner.
Après cette introduction, le RFC fait un peu de terminologie, car le choix des mots a suscité une discussion à l'atelier. D'abord, la notion de classe. Les « objets connectés » vont de systèmes qui ont les capacités matérielles et l'alimentation électrique d'un ordinateur de bureau (une télévision connectée, par exemple) et qui peuvent donc utiliser les mêmes techniques, jusqu'à des objets bien plus contraints dans leurs capacités. Pour prendre un exemple chez les systèmes populaires auprès des geeks, un Raspberry Pi fait tourner un système d'exploitation « normal » et se met à jour comme un ordinateur de bureau, un Arduino ne le peut typiquement pas. Il faudra donc sans doute développer des solutions différentes selon la classe. Ou, si on veut classer selon le type de processeur, on peut séparer les objets ayant plus ou moins l'équivalent d'un Cortex-A (comme le Pi) de ceux ayant plutôt les ressources du Cortex-M (comme l'Arduino, mais notez que cette catégorie est elle-même très variée).
Le RFC définit également dans cette section 2 la différence entre mise à jour du logiciel et mise à jour du firmware. La distinction importante est que les engins les plus contraints en ressources n'ont typiquement qu'une mise à jour du firmware, qui change tout, système et applications, alors que les engins de classe « supérieure » ont des mises à jour modulaires (on peut ne mettre à jour qu'une seule application).
Dernier terme à retenir, hitless (« sans impact »). C'est une propriété des mises à jour qui ne gênent pas le fonctionnement normal. Par exemple, s'il faut arrêter l'engin pour une mise à jour, elle ne sera pas hitless. Évidemment, la mise à jour du logiciel d'une voiture ne sera probablement pas hitless et nécessitera donc des précautions particulières.
Maintenant, le gros morceau du RFC, la section 3, qui regroupe notamment les exigences issues de l'atelier. C'est assez dans le désordre, et il y a davantage de questions que de réponses. Pour commencer, que devraient faire les fabricants d'objets connectés (en admettant qu'ils lisent les RFC et aient le sens des responsabilités, deux paris hasardeux) ? Par exemple, le RFC note que les mises à jour globales (on remplace tout) sont dangereuses et recommande que des mises à jour partielles soient possibles, notamment pour limiter le débit utilisé sur le réseau (comme avec bsdiff ou courgette). Idéalement, on devrait pouvoir mettre à jour une seule bibliothèque, mais cela nécessiterait du liage dynamique et de code indépendant de la position, ce qui peut être trop demander pour les plus petits objets. Mais au moins un système d'exploitation plutôt conçu pour les « classe M », les objets les plus contraints (équivalents au Cortex-M), le système Contiki, a bien un lieur dynamique.
Certains dispositifs industriels ont plusieurs processeurs, dont un seul gère la connexion avec le reste du monde. Il peut être préférable d'avoir une architecture où ce processeur s'occupe de la mise à jour du logiciel de ses collègues qui ne sont pas directement connectés.
Un problème plus gênant, car non exclusivement technique, est celui des engins qui ont plusieurs responsables potentiels. Si une machine sert à plusieurs fonctions, on risque d'avoir des cas où il n'est pas évident de savoir quel département de l'entreprise a « le dernier mot » en matière de mise à jour. Pour les ordinateurs, cette question sociale a été réglée depuis longtemps dans la plupart des organisations (par exemple en laissant le service informatique seul maître) mais les objets connectés redistribuent les cartes. Si le service Communication a une caméra connectée, qui peut décider de la mettre à jour (ou pas) ?
Un cas proche est celui où un appareil industriel inclut un ordinateur acheté à un tiers (avec le système d'exploitation). Qui doit faire la mise à jour ? Qui est responsable ? Pensons par exemple au problème d'Android où les mises à jour doivent circuler depuis Google vers le constructeur du téléphone, puis (souvent) vers l'opérateur qui l'a vendu. En pratique, on constate qu'Android est mal mis à jour. Et ce sera pire pour un réfrigérateur connecté puisque sa partie informatique sera fabriquée indépendamment, et avant le frigo complet. Lorsque celui-ci démarrera pour la première fois, son logiciel aura sans doute déjà plusieurs mois de retard.
Et la sécurité ? Imaginons un objet connecté dont le fabricant soit assez incompétent et assez irresponsable pour que les mises à jour se fassent en récupérant du code en HTTP, sans aucune authentification d'aucune sorte. Un tel objet serait très vulnérable à des attaques visant à remplacer le code authentique par un code malveillant. D'où l'exigence de DAO (Data Origin Authentication). Sans DAO, tout serait fichu. Deux solutions évidentes utilisent la cryptographie, authentifier le serveur qui distribue les mises à jour (par exemple avec HTTPS) ou bien authentifier le code téléchargé, via une signature numérique (notez que l'IETF a un format CMS pour cela, décrit dans le RFC 4108, mais qui semble peu utilisé.) Mais signer et vérifier est plus facile à dire qu'à faire ! D'abord, si la cryptographie n'est pas un problème pour les engins « classe A », comme une télévision connectée ou bien une voiture connectée, elle peut être bien coûteuse pour des objets plus limités. La cryptographie symétrique est typiquement moins coûteuse, mais bien moins pratique. Le problème de fond est que la cryptographie symétrique n'authentifie pas un envoyeur mais une classe d'envoyeurs (tous ceux qui ont la clé). Il faudrait donc une clé différente par objet, ce qui semble ingérable. Faut-il lister comme exigence de base d'utiliser la cryptographie asymétrique ?
Son coût en opérations de calcul n'est pas le seul problème. Par exemple, la clé privée qui signe les mises à jour peut être volée, ou bien tout simplement devenir trop faible avec les progrès de la cryptanalyse (certains objets peuvent rester en production pendans dix ans ou plus). Il faut donc un moyen de mettre la clé publique à jour. Comment le faire sans introduire de nouvelles vulnérabilités (imaginez un attaquant qui trouverait le moyen de subvertir ce mécanisme de remplacement de clé, et changerait la clé de signature pour la sienne). Bref, les méthodes « il n'y a qu'à… » du genre « il n'y a qu'à utiliser des signatures cryptographiques » ne sont pas des solutions magiques.
Et si l'objet utilise du code venant de plusieurs sources ? Faut-il plusieurs clés, et des règles compliquées du genre « l'organisation A est autorisée à mettre à jour les composants 1, 3 et 4 du système, l'organisation B peut mettre à jour 2 et 3 » ? (Les systèmes fermés sont certainement mauvais pour l'utilisateur, mais présentent des avantages en matière de sécurité : une seule source. Pensez à tous les débats autour de l'utilisation du magasin libre F-Droid, par exemple l'opinion de Whisper.)
Un problème plus fondamental est celui de la confiance : à qui un objet connecté doit-il faire confiance, et donc quelle clé publique utiliser pour vérifier les mises à jour ? Faire confiance à son propriétaire ? À son fabricant ? Au sous-traitant du fabricant ?
On n'en a pas fini avec les problèmes, loin de là. Les objets sont conçus pour être fabriqués et distribués en grande quantité. Cela va donc être un difficile problème de s'assurer que tous les objets dont on est responsable soient à jour. Imaginez une usine qui a des centaines, voire des milliers d'objets identiques, et qui veut savoir s'ils sont tous à la même version du logiciel.
On a parlé plus haut de confiance. On peut décider de faire confiance au fabricant d'un objet, parce qu'on juge l'organisation en question honnête et compétente. Mais cette évaluation ne s'étend pas forcément à la totalité de ses employés. Comment s'assurer qu'un employé méchant ne va pas compromettre le processus de mise à jour du logiciel, par exemple en se gardant une copie de la clé privée, ou bien en ajoutant aux objets produits une clé publique supplémentaire (il aurait alors une porte dérobée dans chaque objet, ce qui illustre bien le fait que la mise à jour est elle-même source de vulnérabilités).
On a parlé des risques pour la sécurité de l'objet connecté. Les précautions prises sont actuellement proches de zéro. Espérons que cela va s'améliorer. Mais, même en prenant beaucoup de précautions, des piratages se produiront parfois. Ce qui ouvre la question de la récupération : comment récupérer un objet piraté ? Si on a dix grille-pains compromis, va t-il falloir les reflasher tous les dix, manuellement ? (Avant cela, il y a aussi l'intéressante question de savoir comment détecter qu'il y a eu piratage, sachant que la quasi-totalité des organisations ne lisent pas les messages leur signalant des comportements suspects de leurs machines.)
La cryptographie apporte clairement des solutions intéressantes à bien des problèmes de sécurité. Mais elle nécessite en général une horloge bien à l'heure (par exemple pour vérifier la date d'expiration d'un certificat). Avoir une horloge maintenue par une batterie pendant l'arrêt de la machine et pendant les coupures de courant n'est pas trivial :
Il n'est donc pas évident qu'il y ait une bonne solution à ce problème. (Le RFC cite Roughtime comme une approche prometteuse.)
Et si on a une bonne solution pour distribuer les mises à jour à des milliers ou des millions d'objets connectés, comment ceux-ci vont-ils être informés de l'existence d'une mise à jour. Push ou pull ? Envoyer les mises à jour depuis le serveur est la solution la plus rapide mais elle peut être délicate si l'objet est derrière un pare-feu qui interdit les connexions entrantes (il faudrait que l'objet contacte le serveur de mise à jour, et reste connecté). Et le cas d'objets qui ne sont pas joignables en permanence complique les choses. De l'autre côté, le cas où l'objet contacte le serveur de temps en temps pour savoir s'il y a une mise à jour pose d'autres problèmes. Par exemple, cela consomme de l'électricité « pour rien ».
Vous trouvez qu'il y a assez de problèmes ? Mais ce n'est pas fini. Imaginez le cas d'une mise à jour qui contienne une bogue, ce qui arrivera inévitablement. Comment revenir en arrière ? Refaire une mise à jour (la bogue risque d'empêcher ce processus) ? L'idéal serait que l'objet stocke deux ou trois versions antérieures de son logiciel localement, pour pouvoir basculer vers ces vieilles versions en cas de problème. Mais cela consomme de l'espace de stockage, qui est très limité pour la plupart des objets connectés.
Le consensus de l'atelier a été que les signatures des logiciels (avec vérification par l'objet), et la possibilité de mises à jour partielles, étaient importants. De même, les participants estimaient essentiels la disponibilité d'une infrastructure de mise à jour, qui puisse fonctionner pour des objets qui n'ont pas un accès complet à l'Internet. Par exemple, il faut qu'une organisation puisse avoir une copie locale du serveur de mise à jour, à la fois pour des raisons de performances, de protection de la vie privée, et pour éviter de donner un accès Internet à ses objets.
En parlant de vie privée (RFC 6973), il faut noter que des objets se connectant à un serveur de mise à jour exposent un certain nombre de choses au serveur, ce qui peut ne pas être souhaitable. Et certains objets sont étroitement associés à un utilisateur (tous les gadgets à la maison), aggravant le problème. Le RFC note qu'au minimum, il ne faut pas envoyer un identificateur unique trop facilement, surtout au dessus d'un lien non chiffré. Mais le RFC note à juste titre qu'il y a un conflit entre la vie privée et le désir des vendeurs de se faire de l'argent avec les données des utilisateurs.
On a surtout parlé jusqu'à présent des risques pour l'objet si le serveur des mises à jour est méchant ou piraté. Certains acteurs du domaine vont ajouter des risques pour le serveur : par exemple, si le logiciel est non-libre, certains peuvent souhaiter authentifier l'objet, pour ne pas distribuer leur précieux logiciel privateur à « n'importe qui ».
On en a déjà parlé mais cela vaut la peine d'y revenir : un gros problème de fond est celui de l'autorisation (section 4 du RFC). Qui peut accepter ou refuser une mise à jour ? Oui, il y a de bonnes raisons de refuser : des mises à jour obligatoires sont une forme de porte dérobée, elles permettent d'ajouter des fonctions qui n'étaient pas là avant et qui peuvent être malveillantes. Le cas avait été cité lors de l'affaire de l'iPhone de San Bernardino (des gens proposaient une mise à jour que le téléphone appliquerait, introduisant une porte dérobée). Et le RFC rappelle le cas d'une mise à jour forcée d'imprimantes HP qui avait supprimé des fonctions utiles mais que HP ne voulait plus (la possibilité d'accepter des cartouches d'encre fournies par les concurrents). Un cas analogue révélé la veille de la publication du RFC est celui de Tesla activant une nouvelle fonction à distance (ici, pour améliorer la voiture, mais il est facile de voir que cela pourrait être en sens inverse). Et il y a bien sûr le cas fameux d'Amazon détruisant des livres à distance.
Prenez un téléphone Android programmé par Google, fabriqué par LG, vendu par Orange à la société Michu qui le confie à son employé M. Dupuis-Morizeau. Qui va décider des mises à jour ?
On peut séparer les mises à jour qui corrigent une faille de sécurité et les autres : seules les premières seraient systématiquement appliquées. (Tous les fournisseurs de logiciel ne font pas cette séparation, qui les oblige à gérer deux lignes de mises à jour.) Ceci dit, en pratique, la distinction n'est pas toujours facile, et la correction d'une faille peut en entrainer d'autres.
D'autre part, certains objets connectés sont utilisés dans un environnement très régulé, où tout, matériel et logiciel, doit subir un processus de validation formel avant d'être déployé (c'est le cas des objets utilisés dans les hôpitaux, par exemple). Il y aura forcément une tension entre « faire la mise à jour en urgence car elle corrige une vulnérabilité » et « ne pas faire la mise à jour avant d'avoir tout revalidé, car cet objet est utilisé pour des fonctions vitales ».
Autre problème rigolo, et qui a de quoi inquiéter, le risque élevé d'une cessation du service de mise à jour logicielle (section 5 du RFC). Les vendeurs de logiciel ne sont pas un service public : ils peuvent décider d'arrêter de fournir un service pour des produits qui ne les intéressent plus (ou qui fait concurrence à un produit plus récent, qui offre une meilleure marge). Ou bien ils peuvent tout simplement faire faillite. Que faire face à cet échec du capitalisme à gérer les produits qu'il met sur le marché ? Le problème est d'autant plus fréquent que des objets connectés peuvent rester en service des années, une éternité pour les directions commerciales. Outre le cas du Nabaztag cité plus haut, le RFC mentionne l'exemple d'Eyefi. Un objet un peu complexe peut incorporer des parties faites par des organisations très différentes, dont certaines seront plus stables que d'autres.
Faut-il envisager un service de reprise de la maintenance, par des organisations spécialisées dans ce ramassage des orphelins ? Si ces organisations sont des entreprises à but lucratif, quel sera leur modèle d'affaires ? (Le RFC ne mentionne pas la possibilité qu'il puisse s'agir d'un service public.) Et est-ce que l'entreprise qui abandonne un produit va accepter ce transfert de responsabilité (aucune loi ne la force à gérer ses déchets logiciels) ?
Pour le logiciel, une grosse différence apparait selon qu'il s'agisse de logiciel libre ou pas. Un projet de logiciel libre peut se casser la figure (par exemple parce que l'unique développeur en a eu marre), mais il peut, techniquement et juridiquement, être repris par quelqu'un d'autre. Ce n'est qu'une possibilité, pas une certitude, et des tas de projets utiles ont été abandonnés car personne ne les a repris.
Néanmoins, si le logiciel libre n'est pas une condition suffisante pour assurer une maintenance sur le long terme, c'est à mon avis une condition nécessaire. Parfois, un logiciel est libéré lorsqu'une société abandonne un produit (ce fut le cas avec Little Printer, qui fut un succès, la maintenance étant repris par des développeurs individuels), parfois la société ne fait rien, et le logiciel est perdu. Bref, un système de séquestre du code, pour pouvoir être transmis en cas de défaillance de l'entreprise originale, serait une bonne chose.
Comme souvent en sécurité, domaine qui ne supporte pas le « ya ka fo kon », certaines exigences sont contradictoires. Par exemple, on veut sécuriser les mises à jour (pour éviter qu'un pirate ne glisse de fausses mises à jour dans le processus), ce qui impose des contrôles, par exemple par des signatures numériques. Mais on veut aussi assurer la maintenance après la fin de la société originelle. Si un prestataire reprend la maintenance du code, mais qu'il n'a pas la clé privée permettant de signer les mises à jour, les objets refuseront celles-ci… Le séquestre obligatoire de ces clés peut être une solution, mais elle introduit d'autres risques (vol des clés chez le notaire).
Une solution possible serait que les appareils connectés soient programmés pour cesser tout service s'ils n'arrivent pas à faire de mise à jour pendant N mois, ou bien si le serveur des mises à jour leur dit que le contrat de support est terminé. De telles bombes temporelles seraient bonnes pour la sécurité, et pour le business des fabricants. Mais elles changeraient le modèle d'achat d'équipement : on ne serait plus propriétaire de sa voiture ou de son grille-pain, mais locataire temporaire. De la vraie obsolescence programmée ! De telles bombes dormantes peuvent aussi interférer avec d'autres fonctions de sécurité. Par exemple, un téléphone doit pouvoir passer des appels d'urgence, même si l'abonnement a expiré. Il serait logique qu'il soit en mesure d'appeler le 112, même si son logiciel n'a pas pu être mis à jour depuis longtemps (cf. RFC 7406 pour une discussion sur un compromis similaire.)
Dans une logique libertarienne, ce RFC parle peu de la possibilité d'obligations légales (comme il y a un contrôle technique pour les voitures, il pourrait y avoir obligation de mesures de sécurité quand on vend des objets connectés). Comme il n'y a aucune chance que des entreprises capitalistes fassent spontanément des efforts pour améliorer la sécurité de tous, si on n'utilise pas de contraintes légales, il reste la possibilité d'incitations, discutée dans la section 6. Certaines sont utopiques (« il est dans l'intérêt de tous d'améliorer la sécurité, donc il faut expliquer aux gentils capitalistes l'intérêt de la sécurité, et ils le feront gratuitement »). Une possibilité est celle d'un abonnement : ayant acheté un objet connecté, on devrait payer régulièrement pour le maintenir à jour. Du point de vue de la sécurité, c'est absurde : il est dans l'intérêt de tous que toutes les machines soient à jour, question sécurité. Avec un abonnement, certains propriétaires choisiront de ne pas payer, et leurs engins seront un danger pour tous. (Comparons avec la santé publiqué : il est dans l'intérêt des riches que les pauvres se soignent, sinon, les maladies contagieuses se répandront et frapperont tout le monde. Les vaccinations gratuites ne sont donc pas de la pure générosité.)
Un des problèmes du soi-disant « Internet des objets » est qu'on ne sait pas grand'chose de ce qui s'y passe. Combien de constructeurs d'objets connectés peuvent dire combien ils ont d'objets encore actifs dans la nature, et quel pourcentage est à jour, question logiciel ? La section 7 du RFC se penche sur les questions de mesures. Un des articles présentés à l'atelier notait que, treize ans après la fin de la distribution d'un objet connecté, on en détectait encore des exemplaires actifs.
Garder trace de ces machines n'est pas trivial, puisque certaines ne seront allumées que de manière très intermittente, et que d'autres seront déployées dans des réseaux fermés. Mais ce serait pourtant utile, par exemple pour savoir combien de temps encore il faut gérer des mises à jour pour tel modèle, savoir quels problèmes ont été rencontrés sur le terrain, savoir quelles machines ont été compromises, etc.
La solution simple serait que l'objet ait un identifiant unique et contacte son fabricant de temps en temps, en donnant des informations sur lui-même. Évidemment, cela soulève de gros problèmes de préservation de la vie privée, si le fabricant de brosses à dents connait vos habitudes d'hygiène (« sa brosse à dents n'a pas été allumée depuis six mois, envoyons-lui des pubs pour un dentiste »). Il existe des techniques statistiques qui permettent de récolter des données sans tout révéler, comme le système RAPPOR ou EPID.
Quelle que soit l'efficacité des mises à jour, il ne fait pas de doute que, parfois, des machines seront piratées. Comment les gérer, à partir de là (section 9) ? Car ce ne sera pas seulement un ou deux objets qu'il faudra reformater avec une copie neuve du système d'exploitation. En cas de faille exploitée, ce seront au moins des centaines ou des milliers d'objets qui auront été piratés par un logiciel comme Mirai. (Notez qu'un travail - qui a échoué - avait été fait à l'IETF sur la gestion des machines terminales et leur sécurité, NEA.)
Rien que signaler à l'utilisateur, sans ouvrir une nouvelle voie d'attaque pour le hameçonnage, n'est pas évident (le RFC 6561 propose une solution, mais peu déployée). Bref, pour l'instant, pas de solution à ce problème. Quand on sait que les organisations à but lucratif ne réagissent jamais quand on leur signale un bot dans leur réseau, on imagine ce que ce sera quand on préviendra M. Michu que son grille-pain est piraté et participe à des DoS (comme celles avec SNMP ou bien celle contre Brian Krebs).
Avant d'arriver à une conclusion très partielle, notre RFC, dans sa section 10, traite quelques points divers :
La section 11 rassemble les conclusions de l'atelier. On notera qu'il y a peu de consensus.
L'annexe B du RFC liste tous les papiers présentés à l'atelier qui sont, évidemment en ligne. Sinon, vous pouvez regarder le site officiel de l'atelier, et un bon résumé de présentation. Je recommande aussi cet article de l'IETF Journal, et un très bon article critique sur les objets connectés.
Date de publication du RFC : Août 2017
Auteur(s) du RFC : A. Freytag
Pour information
Première rédaction de cet article le 23 août 2017
Le RFC 7940 définissait un langage, LGR (Label Generation Ruleset), pour exprimer des politiques d'enregistrement de noms écrits en Unicode, avec les éventuelles variantes. Par exemple, avec LGR, on peut exprimer une politique disant que les caractères de l'alphabet latin et eux seuls sont autorisés, et que « é » est considéré équivalent à « e », « ç » à « c », etc. Ce nouveau RFC donne quelques conseils aux auteurs LGR. (Personnellement, je n'ai pas trouvé qu'il apportait grand'chose par rapport au RFC 7940.)
LGR peut s'appliquer à tous les cas où on a des identificateurs en Unicode mais il a surtout été développé pour les IDN (RFC 5890). Les exemples de ce RFC 8228 sont donc surtout pris dans ce domaine d'application. Un ensemble de règles exprimées dans le langage du RFC 7940 va définir deux choses :
google
vs. goog1e
), mais c'est une notion bien trop
floue, et qui dépend en outre de la police de
caractères utilisée (avec
Courier, google
et goog1e
sont difficilement distinguables). Le RFC ajoute qu'il y a
également le cas de la similarité sémantique (le cas de
theatre vs. theater, ou
bien celui des sinogrammes traditionnels vs. les
simplifiés ou encore, mais c'est
compliqué, le cas des accents en
français, avec « café » et « cafe » mais
aussi avec « interne » et « interné »).L'idée est que le registre utilise ensuite ces règles pour accepter ou refuser un nom et, si le nom est accepté, pour définir quelles variantes deviennent ainsi bloquées, ou bien attribuées automatiquement au même titulaire. Notez une nouvelle fois qu'il s'agit d'une décision politique et non technique. Le RFC dit d'ailleurs prudemment dans la section 1 qu'il ne sait pas si ce concept de variante est vraiment utile à quoi que ce soit. (J'ajoute qu'en général ce soi-disant risque de confusion a surtout été utilisé comme FUD anti-Unicode, par des gens qui n'ont jamais digéré la variété des écritures humaines.)
J'ai dit qu'une variante était « le même » que le nom de
base. Du point de vue mathématique, la relation « est une
variante de » doit donc être symétrique et
transitive (section 2). « Est une variante
de » est noté avec un tiret donc
A - B
signifie « A est une variante de B »
(et réciproquement, vu la symétrie). Notez que le RFC 7940 utilisait un langage fondé sur
XML alors que ce RFC 8228
préfère une notation plus compacte. La section 18 de notre RFC
indique la correspondance entre la notation du RFC 7940 et celle du nouveau RFC.
Mais toutes les relations entre noms « proches » ne sont pas forcément symétriques et transitives. Ainsi, la relation « ressemble à » (le chiffre 1 ressemble à la lettre l minuscule) est non seulement vague (cela dépend en effet de la police) mais également non transitive (deux noms ressemblent chacun à un nom dont la forme est quelque part entre eux, sans pour autant se ressembler entre eux).
Le RFC 7940 permet de définir des relations qui ne sont ni symétriques, ni transitives. Mais ce nouveau RFC 8228 préfère les relations d'équivalence.
Bon, et maintenant, comment on crée les variantes d'un nom ou d'un composant d'un nom (section 6) ? On remplace chaque caractère du nom originel par toutes ses variantes possibles (le fait qu'un caractère correspond à un autre est noté -->). Ainsi, si on a décidé que « e » et « é » étaient équivalents (é --> e), le nom « interne » aura comme variantes possibles « interné », « intérne » et « intérné » (oui, une seule de ces variantes a un sens en français, je sais). Une fois cette génération de variantes faites, le registre, selon sa politique, pourra l'utiliser pour, par exemple, refuser l'enregistrement d'« interné » si « interne » est déjà pris (je le répète parce que c'est important : ce RFC ne décrit pas de politique, l'exemple ci-dessus n'est qu'un exemple).
En pratique, travailler caractère par caractère n'est pas toujours réaliste. Il y a des cas où c'est un groupe de caractères qui poserait problème. La section 7 introduit une amélioration où on peut étiqueter les correspondances de manière asymétrique. Le caractère x veut dire que l'allocation du nom est interdite (détails en section 9), le a qu'elle est autorisée (détails en section 8). On pourrait donc avoir « e x--> é » et « é a--> e » ce qui veut dire que la réservation de « interne » bloquerait celle d'« interné » mais pas le contraire.
Le monde des identificateurs étant très riche et complexe, on ne s'étonnera pas que des règles trop simples soient souvent prises en défaut. Ainsi, la variante peut dépendre du contexte : dans certaines écritures (comme l'arabe), la forme d'un caractère dépend de sa position dans le mot, et un même caractère peut donc entrainer de la confusion s'il est à la fin d'un mot mais pas s'il est au début. Il faut donc étiqueter les relations avec ce détail (par exemple, « final: C --> D » si le caractère noté C peut se confondre avec le caractère D mais uniquement si C est en fin de mot, cf. section 15 du RFC).
Si vous développez du LGR, au moins deux logiciels peuvent aider, celui de Viagénie, lgr-crore et lgr-django et un logiciel de l'auteur de ce RFC, développé pour le « ICANN Integration Panel work » mais qui ne semble pas publié.
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : A. Sullivan (Dyn)
Pour information
Réalisé dans le cadre du groupe de travail IETF dnssd
Première rédaction de cet article le 22 septembre 2017
DNS-SD (DNS-based Service Discovery, normalisé dans le RFC 6763) permet de découvrir des services dans le réseau, via le DNS, mais aussi via d'autres systèmes de résolution de noms, comme mDNS (RFC 6762). Ces différents systèmes de résolution ayant des propriétés différentes, il peut se poser des problèmes d'interopérabilité. Ce RFC documente ces problèmes et est donc une lecture recommandée pour les développeurs DNS-SD.
Rappelons un peu de contexte (section 1 du RFC) : les applications qui utilisent le DNS imposaient fréquemment une syntaxe réduite aux noms manipulés, la syntaxe « LDH » ([ASCII] Letters, Digits and Hyphen), décrite dans le RFC 952. (Contrairement à ce qu'on lit souvent, le DNS n'impose pas une telle limite, elle est purement dans les applications.) Du fait de ces règles des applications, il a fallu, pour utiliser des lettres Unicode, un système spécial, IDN (RFC 5890). IDN ajoute ses propres contraintes, comme le fait que les « non-lettres » (par exemple les emojis) ne sont pas acceptés.
Le DNS accepte tout à fait du binaire quelconque dans les noms (donc, par exemple, des caractères non-ASCII avec des encodages comme Latin-1). mDNS (RFC 6762) autorise également les caractères non-ASCII, mais impose l'encodage UTF-8. mDNS autorise des caractères qui ne sont pas permis en IDN, du banal espace aux symboles les plus rigolos. DNS-SD recommande même leur usage (RFC 6763, section 4.1.3). La seule contrainte est de se limiter au format Unicode du réseau, décrit dans le RFC 5198.
Bien des développeurs d'application ne sont pas au courant de ces subtilités. Ils manipulent des noms de services comme des chaînes de caractères, et ne se soucient pas du système de résolution sous-jacent et de ses particularités. Or, potentiellement, un même nom peut être accepté par certains de ces systèmes de résolution et refusés par d'autres.
Si le développeur est au courant, et s'il prend soin de faire en sorte que les noms « avancés » ne soient pas envoyés aux systèmes de résolution les plus anciens, ça peut marcher. Mais il ne faut pas se faire d'illusion, les fuites se produisent et tout nom utilisé sur une machine connectée à l'Internet finira tôt ou tard par arriver au DNS, comme le montre le trafic que reçoivent les serveurs de noms de la racine.
Notez enfin que les utilisateurs de DNS-SD, encore plus que ceux des noms classiques, ne tapent que très rarement les noms : presque toujours, ils les choisissent dans une liste.
Maintenant, pourquoi y a-t-il un problème ? (Section 2 du RFC.) Comme je l'ai indiqué plus haut, le DNS accepte n'importe quoi dans un nom. Pourquoi ne pas juste dire « envoyons le nom "Joe's printer, first floor" directement dans le DNS, et ça marchera » ? Il y a deux problèmes avec cette approche. D'abord, l'application qui ne ferait pas attention et enverrait un nom non-ASCII en disant « le DNS s'en tirera de toute façon », cette application oublie qu'il peut y avoir sur le trajet de sa requête une couche logicielle qui fait de l'IDN et va donc encoder en Punycode (transformation du U-label en A-label). Le « vrai » système de résolution ne verra donc pas le nom original. Un problème du même genre arrive avec certains logiciels qui se mêlent de politique, par exemple les navigateurs Web comme Firefox qui se permettent d'afficher certains noms en Unicode et d'autres en ASCII, selon la politique du TLD. Bref, le trajet d'un nom dans les différents logiciels est parsemé d'embûches si on est en Unicode.
En outre, l'approche « j'envoie de l'UTF-8 sans me poser de questions » (suggérée par la section 4.1.3 du RFC 6763) se heurte au fait que la racine du DNS et la plupart des TLD ne permettent de toute façon par d'enregistrer directement de l'UTF-8. (Au passage, le RFC oublie un autre problème de l'UTF-8 envoyé directement : les serveurs DNS ne font pas de normalisation Unicode, et l'insensibilité à la casse du DNS n'est pas évidente à traduire en Unicode.)
La section 3 de notre RFC propose donc de travailler à un profil permettant une meilleure interopérabilité. Un profil est une restriction de ce qui est normalement permis (un dénominateur commun), afin de passer à peu près partout. Un exemple de restriction serait l'interdiction des majuscules. En effet, le DNS est insensible à la casse mais IDN interdit les majuscules (RFC 5894, sections 3.1.3 et 4.2) pour éviter le problème de conversion majuscules<->minuscules, qui est bien plus difficile en Unicode qu'en ASCII.
Notez que notre RFC ne décrit pas un tel profil, il propose sa création, et donne quelques idées. Il y a donc encore du travail.
Pour rendre le problème plus amusant, les noms utilisés par DNS-SD sont composés de trois parties, avec des règles différentes (section 4 de notre RFC, et RFC 6763, section 4.1) :
L'instance risque d'être « interceptée » par le traitement IDN et, à tort, transformée en Punycode. Limiter le nom de l'instance aux caractères acceptés par IDN serait horriblement restrictif. La seule solution est probablement de mettre en place du code spécifique qui reconnait les noms utilisés par DNS-SD, pour ne jamais les passer au DNS. Quant à la partie « Domaine », l'idée de la traiter comme un nom de domaine normal, et donc sujet aux règles des RFC 5890 et suivants, est restrictive :
Date de publication du RFC : Octobre 2017
Auteur(s) du RFC : P. Wouters (Red
Hat), D. Migault, J. Mattsson
(Ericsson), Y. Nir (Check
Point), T. Kivinen
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF ipsecme
Première rédaction de cet article le 18 octobre 2017
Le protocole de cryptographie IPsec vient avec une liste d'obligations concernant les algorithmes cryptographiques qu'il faut inclure. Cette liste figure dans ce RFC 8221 (qui remplace l'ancien RFC 7321, avec beaucoup de changements). Ainsi, les différentes mises en œuvre d'IPsec sont sûres d'avoir un jeu d'algorithmes corrects en commun, assurant ainsi l'interopérabilité. Cette nouvelle version marque notamment l'officialisation de l'algorithme ChaCha20.
Ce nouveau RFC concerne les deux services d'IPsec, ESP (Encapsulating Security Payload, RFC 4303) et AH (Authentication Header, RFC 4302). Les RFC normatifs sur IPsec se veulent stables, alors que la cryptographie évolue. D'où le choix de mettre les algorithmes dans un RFC à part. Par exemple, la section 3.2 du RFC 4303 note « The mandatory-to-implement algorithms for use with ESP are described in a separate RFC, to facilitate updating the algorithm requirements independently from the protocol per se » (c'était à l'époque le RFC 4305, remplacé depuis par le RFC 4835, puis par le RFC 7321, puis par notre RFC 8221, trois ans après son prédécesseur).
Ce RFC « extérieur » à IPsec spécifie les algorithmes obligatoires, ceux sur lesquels on peut toujours compter que le pair IPsec les comprenne, ceux qui ne sont pas encore obligatoires mais qu'il vaut mieux mettre en œuvre car ils vont sans doute le devenir dans le futur, et ceux qui sont au contraire déconseillés, en général suite aux progrès de la cryptanalyse, qui nécessitent de réviser régulièrement ce RFC (voir section 1.1). Cette subtilité (différence entre « obligatoire aujourd'hui » et « sans doute obligatoire demain ») mène à une légère adaptation des termes officiels du RFC 2119 : MUST- (avec le signe moins à la fin) est utilisé pour un algorithme obligatoire aujourd'hui mais qui ne le sera sans doute plus demain, en raison des avancées cryptanalytiques, et SHOULD+ est pour un algorithme qui n'est pas obligatoire maintenant mais le deviendra sans doute.
Les section 5 et 6 donnent la liste des algorithmes. Je ne les répète pas intégralement ici, d'autant plus que tous les algorithmes ne sont pas cités, seulement ceux qu'il faut mettre en œuvre, car ils sont fiables, et ceux qu'il ne faut pas mettre en œuvre, car trop cassés, et qui doivent donc être documentés pour mettre les programmeurs en garde. Parmi les points à noter :
NULL
,
c'est-à-dire l'absence de chiffrement (RFC 2410 ; on
peut utiliser ESP pour l'authentification seule, d'où cet
algorithme). Il y a aussi plusieurs algorithmes notés MUST
NOT, comme DES-CBC
(RFC 2405). Ils ne doivent pas être programmés, afin
d'être sûr qu'on ne s'en serve pas.La section 4 donne des conseils sur l'utilisation d'ESP et
AH. AH ne fournit que l'authentification, alors qu'ESP peut fournir
également le chiffrement. Bien sûr, le chiffrement sans
l'authentification ne sert pas à grand'chose, puisqu'on risque
alors de parler à l'homme du milieu sans le savoir (voir l'article
de Bellovin, S. « Problem areas for the IP security
protocols » dans les Proceedings of the Sixth
Usenix Unix Security Symposium en 1996). Certaines
combinaisons d'algorithmes ne sont pas sûres, par exemple,
évidemment, ESP avec les algorithmes de chiffrement et
d'authentification tous les deux à NULL
(voir
par exemple l'article de Paterson, K. et J. Degabriele,
« On the (in)security of IPsec in MAC-then-encrypt
configurations » à l'ACM Conference on Computer
and Communications Security en 2010). Si on veut de
l'authentification/intégrité sans chiffrement, le RFC recommande
d'utiliser ESP avec le chiffrement NULL
,
plutôt que AH. En fait, AH est rarement utile, puisque ESP en est
presque un sur-ensemble, et il y a même eu des propositions de
le supprimer. AH avait été prévu pour une époque où le
chiffrement était interdit d'utilisation ou d'exportation dans
certains pays et un logiciel n'ayant que AH posait donc moins de
problèmes légaux. Aujourd'hui, la seule raison d'utiliser encore AH
est si on veut protéger certains champs de l'en-tête IP, qu'ESP ne défend pas.
Le chiffrement intègre/authentifié d'un algorithme comme AES-GCM (RFC 5116 et RFC 4106) est la solution recommandée dans la plupart des cas. L'intégration du chiffrement et de la vérification d'intégrité est probablement la meilleure façon d'obtenir une forte sécurité.
Triple DES et DES, eux, ont des défauts connus et ne doivent plus être utilisés. Triple DES a une taille de bloc trop faible et, au-delà d'un gigaoctet de données chiffrées avec la même clé, il laisse fuiter des informations à un écoutant, qui peuvent l'aider dans son travail de décryptage. Comme, en prime, il est plus lent qu'AES, il n'y a vraiment aucune raison de l'utiliser. (DES est encore pire, avec sa clé bien trop courte. Il a été cassé avec du matériel dont les plans sont publics.)
Triple DES étant sorti, la « solution de remplacement », si un gros problème est découvert dans AES, sera ChaCha20. Il n'y a aucune indication qu'une telle vulnérabilité existe mais il faut toujours garder un algorithme d'avance.
Pour l'authentification/intégrité, MD5, ayant des vulnérabilités connues (RFC 6151), question résistance aux collisions, est relégué en MUST NOT. (Notez que des vulnérabilités à des collisions ne gênent pas forcément l'utilisation dans HMAC.) SHA-1 a des vulnérabilités analogues mais qui ne concernent pas non plus son utilisation dans HMAC-SHA1, qui est donc toujours en MUST- (le moins indiquant son futur retrait). Les membres de la famille SHA-2 n'ont pas ces défauts, et sont désormais MUST pour SHA-256 et SHOULD pour SHA-512.
Voilà, c'est fini, la section 10 sur la sécurité rappelle juste quelques règles bien connues, notamment que la sécurité d'un système cryptographique dépend certes des algorithmes utilisés mais aussi de la qualité des clés, et de tout l'environnement (logiciels, humains).
Ce RFC se conclut en rappelant que, de même qu'il a remplacé ses prédécesseurs comme le RFC 7321, il sera probablement à son tour remplacé par d'autres RFC, au fur et à mesure des progrès de la recherche en cryptographie.
Si vous voulez comparer avec un autre document sur les algorithmes cryptographiques à choisir, vous pouvez par exemple regarder l'annexe B1 du RGS, disponible en ligne.
Merci à Florian Maury pour sa relecture acharnée. Naturellement, comme c'est moi qui tiens le clavier, les erreurs et horreurs restantes viennent de ma seule décision. De toute façon, vous n'alliez pas vous lancer dans la programmation IPsec sur la base de ce seul article, non ?
La section 8 du RFC résume les changements depuis le RFC 7321. Le RFC a été sérieusement réorganisé, avec une seule section regroupant les algorithmes avec AEAD et sans (j'ai gardé ici la présentation séparée de mon précédent article). Les sections d'analyse et de discussion ont donc été très modifiées. Par exemple, le RFC 7321 s'inquiétait de l'absence d'alternative à AES, problème qui a été résolu par ChaCha20. Parmi les principaux changements d'algorithmes :
En pratique, ce RFC est déjà largement mis en œuvre, la plupart des algorithmes cités sont présents (quand ils sont MUST ou SHOULD) ou absents (quand ils sont MUST NOT) dans les implémentations d'IPsec. Une exception est peut-être ChaCha20, qui est loin d'être présent partout. Malgré cela, ce RFC n'a pas suscité de controverses particulières, l'IETF avait un consensus sur ces nouveaux choix.
Date de publication du RFC : Août 2017
Auteur(s) du RFC : R. Pantos (Apple), W. May (Major
League Baseball Advanced Media)
Pour information
Première rédaction de cet article le 1 septembre 2017
Ce RFC décrit la version 7 du protocole HTTP Live Streaming, qui permet de distribuer des flux multimédia, typiquement de la vidéo.
Utiliser HTTP, c'est bien, ça passe
quasiment tous les pare-feux, et ça permet
de réutiliser l'infrastructure existante, comme les serveurs, et les caches. (Au contraire, un protocole
comme RTP, RFC 3550, a souvent du mal à passer les
différentes
middleboxes.) La manière triviale de distribuer une vidéo avec
HTTP est d'envoyer un fichier vidéo au bon
format, avec des méthodes HTTP standard, typiquement en réponse à
un GET
. Mais cette méthode a des tas de
limites. Par exemple, elle ne permet pas de
s'adapter à une capacité variable du
réseau. Par contre, le protocole décrit dans ce RFC, quoique plus
compliqué qu'un simple GET
, permet cet
ajustement (et plein d'autres).
HTTP Live Streaming (ou simplement Live Streaming, ou encore HLS) existe depuis longtemps, la première spécification a été publiée (par Apple) en 2009, mais c'est la première fois qu'il se retrouve dans un RFC, ce qui devrait permettre d'améliorer encore l'interopérabilité.
Donc, brève description de HTTP Live Streaming : une ressource multimédia qu'on veut diffuser est représentée par un URI. Ceui-ci désigne une playlist, qui est un fichier texte contenant les URI des données multimedia et diverses informations. Les données multimédia figurent dans plusieurs segments ; en jouant tous les segments successivement, on obtient la vidéo souhaitée. Le client n'a pas ainsi à tout charger, il peut récupérer les segments au fur et à mesure de ses besoins. (Au lieu de « segments », mmu_man suggère de parler d'un « émincé de fichiers ».) Voici l'exemple de playlist que donne le RFC :
#EXTM3U #EXT-X-TARGETDURATION:10 #EXTINF:9.009, http://media.example.com/first.ts #EXTINF:9.009, http://media.example.com/second.ts #EXTINF:3.003, http://media.example.com/third.ts
Ce format de playlist est dérivé du format
M3U et est généralement nommé « M3U étendu ».
On y trouve successivement l'identificateur du format (si ça
commence par #EXTM3U
, c'est bien une
playlist de notre RFC), la durée maximale d'un
segment en secondes, puis les trois segments, avec la durée de
chacun. Cette durée, indiquée par l'étiquette
#EXTINF
, est un des rares éléments
obligatoires dans une playlist. Il y a bien sûr plein d'autres choses qu'on peut mettre
dans un fichier playlist, la section 4 du RFC
en donne la liste complète. Ainsi,
#EXT-X-BYTERANGE
permettra d'indiquer qu'on
ne veut jouer qu'une partie d'une vidéo. Des exemples plus
complets sont donnés dans la section 8 du RFC.
Ça, c'était la media playlist, qui se limite à lister des ressources multimédias. La master playlist, dont le format est le même, est plus riche, et permet de spécifier des versions alternatives d'un même contenu. Ces variantes peuvent être purement techniques (« variant stream », deux encodages d'une même vidéo avec différents formats) ou porter sur le fond : par exemple deux pistes audio dans des langues différentes, ou deux vidéos d'un même évènement filmé depuis deux points de vue distincts. Les variantes techniques servent au client à s'ajuster aux conditions réelles du réseau, les variantes de fond (nommées renditions) servent au client à donner à l'utilisateur ce qu'il préfère. Ici, par exemple, on a dans cette master playlist trois variantes techniques (trois résolutions, de la plus basse, à la plus élevée, qui va nécessiter une bonne capacité réseau), et trois « renditions », correspondant à trois points de vue (centerfield et dugout sont des termes de baseball, ce qui est logique vu l'employeur d'un des deux auteurs du RFC) :
#EXTM3U #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main", \ DEFAULT=YES,URI="low/main/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield", \ DEFAULT=NO,URI="low/centerfield/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout", \ DEFAULT=NO,URI="low/dugout/audio-video.m3u8" #EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",VIDEO="low" low/main/audio-video.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main", \ DEFAULT=YES,URI="mid/main/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield", \ DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout", \ DEFAULT=NO,URI="mid/dugout/audio-video.m3u8" #EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",VIDEO="mid" mid/main/audio-video.m3u8 #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main", \ DEFAULT=YES,URI="hi/main/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield", \ DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8" #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout", \ DEFAULT=NO,URI="hi/dugout/audio-video.m3u8" #EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",VIDEO="hi" hi/main/audio-video.m3u8
Si vous voulez le voir en vrai, et que vous êtes abonné à Free, la Freebox distribue sa playlist sous ce format (mais avec le mauvais type MIME) :
% curl -s http://mafreebox.freebox.fr/freeboxtv/playlist.m3u #EXTM3U #EXTINF:0,2 - France 2 (bas débit) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=ld #EXTINF:0,2 - France 2 (HD) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=hd #EXTINF:0,2 - France 2 (standard) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201&flavour=sd #EXTINF:0,2 - France 2 (auto) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=201 #EXTINF:0,3 - France 3 (standard) rtsp://mafreebox.freebox.fr/fbxtv_pub/stream?namespace=1&service=202&flavour=sd ...
(On me dit qu'avec certaines Freebox, il faut ajouter
-L
à curl, en raison d'une redirection HTTP.)
Un client comme vlc peut prendre comme
argument l'URI de la playlist, la télécharger et jouer ensuite les
vidéos.
(Tous les moyens sont bons pour télécharger la playlist, le RFC n'impose pas un
mécanisme particulier.)
Télécharger chacun des segments se fait a priori avec
HTTP, RFC 7230, mais
vous voyez plus haut que, sur la Freebox,
c'est RTSP, RFC 7826. Le serveur, lui, est un serveur HTTP
ordinaire. L'auteur a « juste » eu à découper sa vidéo en segments (ou
la garder en un seul morceau si elle est assez courte), à
fabriquer un fichier playlist, puis à le servir
avec le type MIME application/vnd.apple.mpegurl
. (La
section 6 du RFC décrit plus en détail ce que doivent faire client
et serveur.)
Il est recommandé que chaque segment soit auto-suffisant, c'est-à-dire puisse être décodé sans avoir besoin des autres segments. Ainsi, si les segments sont en H.264, chacun doit avoir un Instantaneous Decoding Refresh (IDR).
Plusieurs formats sont évidemment possibles dans une playlist HTTP Live Streaming, MPEG-2, MPEG-4 fragmenté, WebVTT pour avoir les sous-titres, et un format pour l'audio (qui accepte entre autres MP3).
HTTP Live Streaming permet aussi de servir du direct. Prenons par exemple le flux vidéo de l'Assemblée Nationale, qui permet de suivre les débats parlementaires :
% curl -s http://videos-diffusion-live.assemblee-nationale.fr/live/live1/stream1.m3u8 #EXTM3U #EXT-X-VERSION:3 #EXT-X-ALLOW-CACHE:NO #EXT-X-TARGETDURATION:11 #EXT-X-MEDIA-SEQUENCE:382 #EXTINF:9.996, media_1_4805021_382.ts #EXTINF:11.483, media_1_4805021_383.ts ...
Un peu plus tard, les fichiers ont changé, le client HLS doit recharger la playlist régulièrement (section 6.2.2 du RFC), et charger de nouveaux fichiers vidéo :
% curl -s http://videos-diffusion-live.assemblee-nationale.fr/live/live1/stream1.m3u8 #EXTM3U #EXT-X-VERSION:3 #EXT-X-ALLOW-CACHE:NO #EXT-X-TARGETDURATION:11 #EXT-X-MEDIA-SEQUENCE:390 #EXTINF:10.039, media_1_4805021_390.ts #EXTINF:11.242, media_1_4805021_391.ts ...
(Notez également l'incrémentation du #EXT-X-MEDIA-SEQUENCE
.)
Ainsi, l'utilisateur a l'impression d'une vidéo continue, alors
que le logiciel client passe son temps à charger des segments différents et à les
afficher à la suite.
Notez que HTTP Live Streaming n'est pas le seul mécanisme dans ce domaine, son principal concurrent est MPEG-DASH.
Si vous voulez lire davantage :
Date de publication du RFC : Août 2017
Auteur(s) du RFC : T. Anderson (Redpill Linpro)
Chemin des normes
Première rédaction de cet article le 18 septembre 2017
Ce très court RFC documente la
réservation du préfixe IPv6
64:ff9b:1::/48
, pour les divers mécanismes de
traduction entre IPv6 et IPv4.
L'adressage de ces mécanismes de traduction est décrit dans le
RFC 6052. Il réservait le préfixe
64:ff9b::/96
, qui s'est avéré insuffisant et
est désormais complété par notre RFC 8215.
Ouvrez bien l'œil : ces deux préfixes sont différents, même si leurs quatre premiers octets sont identiques (c'est un problème fréquent en IPv6 : il peut être difficile de distinguer deux adresses du premier coup.)
La section 3 de notre RFC expose le problème : depuis la
publication du RFC 6052, plusieurs
mécanismes de traduction IPv4<->IPv6 ont été proposés (par
exemple dans les RFC 6146 et RFC 7915). Ils ne sont pas inconciliables (ils couvrent
parfois des scénarios différents ) et un opérateur peut donc
vouloir en déployer plusieurs à la fois. Le seul préfixe
64:ff9b::/96
ne va alors plus suffire. Il
faut donc un préfixe plus grand, et dont l'usage n'est pas
restreint à une seule technologie de traduction.
Pourquoi 64:ff9b:1::/48
, d'ailleurs ? La
section 4 répond à la question. La longueur de 48 bits a été
choisie pour permettre plusieurs mécanismes de traduction, chacun
utilisant un préfixe plus spécifique. Par exemple, si chaque
mécanisme utilise un /64, le préfixe réservé pour les englober
tous devait être un /48, ou plus général (on se limite aux
multiples de 16 bits, car ils permettent que les préfixes se
terminent sur un deux-points, facilitant la
vie de l'administrateur réseaux qui devra les manipuler).
Ensuite, pourquoi cette valeur
64:ff9b:1::
? Parce qu'elle est proche
(presque adjacente) du
64:ff9b::/96
, minimisant la consommation
d'adresses IPv6. (64:ff9a:ffff::/48
est
complètement adjacent, de l'autre côté, mais l'agrégation avec
64:ff9b::/96
en un seul préfixe est beaucoup
moins efficace. Et puis 64:ff9b:1::
est plus
court.)
À part son usage, 64:ff9b:1::/48
est un
préfixe IPv6 normal et les routeurs, ou autres machines qui
manipulent des adresses IPv6, ne doivent pas le traiter
différemment des autres.
Le nouveau préfixe est désormais enregistré dans le registre des adresses IPv6 spéciales (section 7 de notre RFC).
Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : J. Mauch (Akamai), J. Snijders
(NTT), G. Hankins (Nokia)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF grow
Première rédaction de cet article le 10 juillet 2017
Ce RFC est très court, mais concerne un problème fréquent sur l'Internet : les fuites de routes BGP. Traditionnellement, un routeur BGP acceptait par défaut toutes les routes, et annonçait par défaut à ses voisins toutes les routes qu'il connaissait. Il fallait une configuration explicite pour ne pas le faire. En cas d'erreur, ce comportement menait à des fuites (RFC 7908). Notre RFC normalise désormais le comportement inverse : un routeur BGP ne doit, par défaut, rien accepter et rien annoncer. Il faut qu'il soit configuré explicitement si on veut le faire.
Avec l'ancien comportement, la configuration de certains routeurs BGP, les plus imprudents, indiquait les pairs avec qui on échangeait de l'information, plus un certain nombre de routes qu'on n'envoyait pas. Si une erreur faisait qu'on recevait tout à coup des routes imprévues, on les acceptait, et on les renvoyait telles quelles, propageant la fuite (RFC 7908). Des cas fameux de fuites ne manquent pas (voir par exemple celle d'un opérateur malaisien). L'idée derrière ce comportement était d'assurer la connectivité du graphe de l'Internet. Aujourd'hui, on est plutôt plus sensibles aux risques de sécurité qu'à ceux de partitionnement du graphe, et les bonnes pratiques demandent depuis longtemps qu'on indique explicitement ce qu'on accepte et ce qu'on envoie. Voyez par exemple les recommandations de l'ANSSI.
En pratique, ce très court RFC ajoute juste deux paragraphes à la norme BGP, le RFC 4271. Dans sa section 9.1, les paragraphe en question disent désormais qu'il ne doit pas y avoir du tout d'exportation ou d'importation de routes, par défaut. Notez donc que cela ne change pas le protocole BGP, juste le comportement local.
Du moment qu'on change le comportement par défaut, il va y avoir un problème de transition (et ce point a soulevé des discussions à l'IETF). Si le logiciel du routeur s'adaptait au nouveau RFC, certains opérateurs seraient bien surpris que leurs routes ne soient tout coup plus annoncées. L'annexe A du RFC recommande une stratégie en deux temps :
Et pour finir sur une note d'humour, à une réunion IETF (IETF 97), le projet qui a finalement mené à ce RFC était illustré… de photos de préservatifs. Pratiquez donc le BGP safe, l'Internet vous remerciera.
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : R. Bush (Internet Initiative
Japan), R. Austein (Dragon Research
Labs)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 28 septembre 2017
Le protocole décrit dans ce RFC fait partie de la grande famille des RFC sur la RPKI, un ensemble de mécanismes permettant de sécuriser le routage sur l'Internet. Il traite un problème bien particulier : comme la validation des annonces de route, potentiellement complexe en cryptographie, se fait typiquement sur une machine spécialisée, le cache validateur, comment communiquer le résultat de ces validations au routeur, qui va devoir accepter ou rejeter des routes ? C'est le rôle du protocole RTR (RPKI/Router Protocol), un protocole très simple. Ce RFC décrit une nouvelle version de RTR, la version 1, la première (version 0) était dans le RFC 6810.
L'opérateur Internet a donc deux acteurs qui font du RTR entre eux, le cache validateur qui récupère les ROA (Route Origin Authorization, cf. RFC 6482) par rsync, les valide cryptographiquement et garde en mémoire le résultat de cette validation (pour des exemples d'utilisation de deux programmes mettant en œuvre cette fonction, voir mon article sur les logiciels de la RPKI), et le routeur (Cisco, Juniper, Unix avec Quagga, etc). Ce dernier joue un rôle critique et on ne souhaite pas trop charger la barque en lui imposant les opérations assez lourdes du validateur. Dans la plupart des déploiements de la RPKI, le routeur ne fait donc que récupérer des résultats simples (« ce préfixe IP peut être annoncé par cet AS »). Le jeu de données que connait le cache validateur a un numéro de série, qui est incrémenté dès qu'il y a une modification (RFC 1982). Le cache peut donc facilement savoir si un de ses clients routeur est à jour en comparant son numéro de série avec celui du routeur. Le numéro de série est spécifique à un cache donné, il n'a pas de signification globale. Le routeur ne validant pas, le cache qu'il utilise doit donc être une machine de confiance.
Un autre acteur, externe à l'opérateur, est la RPKI, l'ensemble des informations de routage publiées (RFC 6481), et qui sont transportées en général en rsync et validées par les signatures cryptographiques qu'elles portent.
Le routeur va ouvrir une connexion avec un ou plusieurs caches et transmettre le numéro de série de ses données. Le cache va lui envoyer les données plus récentes que ce numéro (ou bien rien, si le routeur est à jour). Le cache dispose aussi d'un mécanisme de notification pour dire à tous les routeurs connectés « j'ai du nouveau ». La section 10 décrit plus en détail le processus avec plusieurs caches.
La section 5 décrit le format des paquets : chacun, outre le classique champ version (1 dans ce RFC, c'était 0 dans le RFC précédent), a un type, indiqué par un entier de 8 bits (requête, réponse, etc), le numéro de série de l'envoyeur et un identifiant de session, qui identifie le serveur du cache (pour détecter une remise à zéro d'un cache, par exemple), la longueur du préfixe annoncé et bien sûr le préfixe lui-même.
Les types de paquets les plus utiles (la liste complète est dans un registre IANA) :
Le dialogue typique entre un routeur et un cache validateur est décrit en section 8 (le RFC détaille aussi le dialogue de démarrage, ici, je ne montre que celui de fonctionnement régulier) :
À noter que puisqu'il y a désormais deux versions de RTR dans la nature, il a fallu mettre en place un système de négociation, décrit dans la section 7. Lorsqu'un cache/validateur connait la version 1 (celle de notre nouveau RFC), et reçoit une requête de version 0 du routeur, il doit se rabattre sur la version 0 (s'il la connait, et veut bien la parler), ou bien envoyer un message d'erreur (code 4 « Unsupported Protocol Version »).
Si c'est l'inverse (routeur récent parlant à un vieux cache/validateur), deux cas, qui dépendent de la politique du validateur. S'il répond avec une erreur, cela peut être intéressant de réessayer avec le protocole 0, si le routeur le connait. Si, par contre, le validateur répond avec des messages de version 0, le routeur peut décider de continuer en version 0.
Les codes d'erreur possibles sont décrits dans la section 12 et font l'objet d'un registre IANA. On y trouve par exemple 0 (données corrompues), 1 (erreur interne dans le client ou le serveur), 2 (pas de nouvelles données ; ce n'est pas à proprement parler une erreur).
La question du transport des données entre le cache et le routeur est traitée dans la section 9. Le principal problème est le suivant : comme le but de l'opération RPKI+ROA est d'augmenter la sécurité du routage, il faut que chaque maillon de la chaîne soit sécurisé. Il ne servirait pas à grand'chose de déployer une telle usine à gaz de validation si la liaison entre le routeur et le cache permettait à un intermédiaire de changer les données en cours de route. Il était donc tentant de normaliser une technique de sécurité particulière pour le transport, comme par exemple TLS. Malheureusement, il n'existe pas à l'heure actuelle de technique accessible sur tous les routeurs. L'IETF ayant toujours soin de normaliser des protocoles réalistes, qui puissent être mis en œuvre et déployés, le choix a été fait de ne pas imposer une technique de sécurisation particulière. L'interopérabilité en souffre (un cache et un routeur peuvent ne pas avoir de protocole de sécurité en commun) mais c'était la seule option réaliste. (J'ajoute que, si le but de la séparation du routeur et du validateur était de dispenser ce dernier des calculs cryptographiques, il ne serait pas logique de lui imposer ensuite une session protégée cryptographiquement avec le validateur.)
Le RFC se contente donc de donner une préférence à AO (TCP Authentication Option, cf. RFC 5925) en signalant qu'elle sera la technique préférée, dès lors qu'elle sera disponible sur tous les routeurs (ce qui est très loin d'être le cas en 2017, les progrès sont faibles depuis le précédent RFC sur RTR). Les algorithmes du RFC 5926 doivent être acceptés.
En attendant AO, et pour s'assurer qu'il y aura au moins un protocole commun, le RFC spécifie que routeurs et caches doivent pouvoir se parler en utilisant du TCP nu, sans sécurité, vers le port 323. Dans ce cas, le RFC spécifie que routeur et cache validateur doivent être sur le même réseau (idéalement, sur le même segment, cf. section 13) et que des mécanismes, par exemple de contrôle d'accès physiques, empêchent les méchants de jouer avec ce réseau.
Au cas où AO ne soit pas disponible (la quasi-totalité des routeurs
aujourd'hui) et où le transport non sécurisé soit jugé nettement trop
faible, le RFC présente plusieurs transports sécurisés possibles. Par
exemple, SSH (RFC 4252), avec une sécurisation des clés
obtenue par un moyen externe (entrée manuelle des clés, par
exemple). Le sous-système SSH à utiliser est nommé
rpki-rtr
. Notez que, si SSH n'a pas été rendu
obligatoire, alors que quasiment tous les routeurs ont un programme
SSH, c'est parce que ce programme n'est pas toujours disponible sous
forme d'une bibliothèque, accessible aux
applications comme RTR.
Autre alternative de sécurisation évidente,
TLS. Le RFC impose une authentification par un
certificat client, comportant l'extension
subjectAltName
du RFC 5280,
avec une adresse IP (celle du routeur qui se connecte, et que
le serveur RTR, la cache validateur, doit vérifier). Le client RTR,
lui (le routeur), utilisera le nom de
domaine du serveur RTR (le cache) comme entrée pour l'authentification (cf. RFC 6125). N'importe quelle
AC peut être utilisée mais notre RFC estime
qu'en pratique, les opérateurs créeront leur propre AC, et mettront
son certificat dans tous leurs routeurs.
Notez que ce problème de sécurité ne concerne que le transport entre le routeur et le cache validateur. Entre les caches, ou bien entre les caches et les serveurs publics de la RPKI, il n'y a pas besoin d'une forte sécurité, l'intégrité des données est assurée par la validation cryptographique des signatures sur les objets.
Ce RFC spécifie un protocole, pas une politique. Il pourra y avoir plusieurs façons de déployer RTR. La section 11 donne un exemple partiel de la variété des scénarios de déploiement, notamment :
Pour limiter la charge sur les serveurs rsync de la RPKI (voir RFC 6480), il est recommandé d'organiser les caches de manière hiérarchique, avec peu de caches tapant sur les dépôts rsync publics, et les autres caches se nourrissant (et pas tous en même temps) à partir de ces caches ayant un accès public.
Bien que ce protocole RTR ait été conçu dans le cadre de la RPKI, il peut parfaitement être utilisé avec d'autres mécanismes de sécurisation du routage, puisque RTR se contente de distribuer des autorisations, indépendemment de la manière dont elles ont été obtenues.
Et puis un petit point opérationnel : sur le validateur/cache, n'oubliez pas de bien synchroniser l'horloge, puisqu'il faudra vérifier des certificats, qui ont une date d'expiration…
Quels sont les changements par rapport au RFC original, le RFC 6810, qui normalisait la version 0 de RTR ? Ces changements sont peu nombreux, mais, du fait du changement de version, les versions 0 et 1 sont incompatibles. La section 1.2 résume ce qui a été modifié entre les deux RFC :
Aujourd'hui, qui met en œuvre RTR ? Il existe en logiciel libre une bibliothèque en C, RTRlib. Elle a fait l'objet d'un bon article de présentation (avec schéma pour expliquer RTR). Elle peut tourner sur TCP nu mais aussi sur SSH. RTRlib permet d'écrire, très simplement, des clients RTR en C, pour déboguer un cache/validateur, ou bien pour extraire des statistiques. Un client simple en ligne de commande est fourni avec, rtrclient. Il peut servir d'exemple (son code est court et très compréhensible) mais aussi d'outil de test simple. rtrclient se connecte et affiche simplement les préfixes reçus. Voici (avec une version légèrement modifiée pour afficher la date) son comportement (en face, le RPKI Validator du RIPE-NCC, dont il faut noter qu'il ne marche pas avec Java 9, il faut reculer vers une version plus vieille, et qu'il ne gère pas encore la version 1 de RTR) :
% rtrclient tcp localhost 8282 ... 2012-05-14T19:27:42Z + 195.159.0.0 16-16 5381 2012-05-14T19:27:42Z + 193.247.205.0 24-24 15623 2012-05-14T19:27:42Z + 37.130.128.0 20-24 51906 2012-05-14T19:27:42Z + 2001:1388:: 32-32 6147 2012-05-14T19:27:42Z + 2001:67c:2544:: 48-48 44574 2012-05-14T19:27:42Z + 178.252.36.0 22-22 6714 2012-05-14T19:27:42Z + 217.67.224.0 19-24 16131 2012-05-14T19:27:42Z + 77.233.224.0 19-19 31027 2012-05-14T19:27:42Z + 46.226.56.0 21-21 5524 2012-05-14T19:27:42Z + 193.135.240.0 21-24 559 2012-05-14T19:27:42Z + 193.247.95.0 24-24 31592 ...
Autre bibliothèque pour développer des clients RTR, cette fois en Go, ma GoRTR. Le programmeur doit fournir une fonction de traitement des données, qui sera appelée chaque fois qu'il y aura du nouveau. GoRTR est fournie avec deux programmes d'exemple, un qui affiche simplement les préfixes (comme rtrclient plus haut), l'autre qui les stocke dans une base de données, pour étude ultérieure. Par exemple, on voit ici dans cette base de données que la majorité des préfixes annoncés autorisent une longueur de préfixe égale à la leur (pas de tolérance) :
essais=> SELECT count(id) FROM Prefixes WHERE serialno=(SELECT max(serialno) FROM Prefixes) AND maxlength=masklen(prefix); count ------- 2586 (1 row) essais=> SELECT count(id) FROM Prefixes WHERE serialno=(SELECT max(serialno) FROM Prefixes) AND maxlength>masklen(prefix); count ------- 1110 (1 row)
Et voici une partie de évenements d'une session RTR, stockée dans cette base:
tests=> select * from events; id | time | server | event | serialno ----+----------------------------+---------------------------------+---------------------------------+---------- ... 12 | 2017-06-24 13:31:01.445709 | rpki-validator.realmv6.org:8282 | (Temporary) End of Data | 33099 13 | 2017-06-24 13:31:01.447959 | rpki-validator.realmv6.org:8282 | Cache Response, session is 4911 | 33099 14 | 2017-06-24 13:31:25.932909 | rpki-validator.realmv6.org:8282 | (Temporary) End of Data | 33099 15 | 2017-06-24 13:31:38.64284 | rpki-validator.realmv6.org:8282 | Serial Notify #33099 -> #33100 | 33099 16 | 2017-06-24 13:31:38.741937 | rpki-validator.realmv6.org:8282 | Cache reset | 33099 17 | 2017-06-24 13:31:38.899752 | rpki-validator.realmv6.org:8282 | Cache Response, session is 4911 | 33099 18 | 2017-06-24 13:32:03.072801 | rpki-validator.realmv6.org:8282 | (Temporary) End of Data | 33100 ...
Notez que GoRTR gère désormais les deux versions du protocole mais ne connait pas la négociation de version de la section 7 du RFC : il faut indiquer explicitement quelle version on utilise.
Autrement, Cisco et Juniper ont tous les deux annoncé que leurs routeurs avaient un client RTR, et qu'il a passé des tests d'interopérabilité avec trois mises en œuvre du serveur (celles de BBN, du RIPE et de l'ISC). Ces tests d'interopérabilité ont été documentés dans le RFC 7128. Quant aux serveurs, un exemple est fourni par le logiciel RPKI du RIPE-NCC qui fournit un serveur RTR sur le port 8282 par défaut.
En 2013, un client RTR récupèrait 3696 préfixes (ce qui est plus que le nombre de ROA puisqu'un ROA peut comporter plusieurs préfixes). C'est encore très loin de la taille de la table de routage globale et il reste donc à voir si l'infrastructure suivra. (Sur ma station de travail, le validateur, écrit en Java, rame sérieusement.)
Pour les curieux, pourquoi est-ce que l'IETF n'a pas utilisé Netconf (RFC 6241) plutôt que de créer un nouveau protocole ? Les explications sont dans une discussion en décembre 2011. Le fond de la discussion était « les informations de la RPKI sont des données ou de la configuration » ? La validation est relativement statique mais reste quand même plus proche (fréquence de mise à jour, notamment) des données que de la configuration.
Si vous voulez tester un client RTR, il existe un serveur public
rpki-validator.realmv6.org:8282
. Voici le genre de choses
qu'affiche rtrclient (test fait avec un vieux serveur, qui n'est plus disponible) :
% rtrclient tcp rtr-test.bbn.com 12712 ... 2012-05-14T19:36:27Z + 236.198.160.184 18-24 4292108787 2012-05-14T19:36:27Z + 9144:8d7d:89b6:e3b8:dff1:dc2b:d864:d49d 105-124 4292268291 2012-05-14T19:36:27Z + 204.108.12.160 8-29 4292339151 2012-05-14T19:36:27Z + 165.13.118.106 27-28 4293698907 2012-05-14T19:36:27Z + 646:938e:20d7:4db3:dafb:6844:f58c:82d5 8-82 4294213839 2012-05-14T19:36:27Z + fd47:efa8:e209:6c35:5e96:50f7:4359:35ba 20-31 4294900047 ...
Question mise en œuvre, il y a apparemment plusieurs logiciels de validateurs/cache qui mettent en œuvre cette version 1, mais je n'ai pas trouvé lesquels. (Le RPKI Validator du RIPE-NCC ne le fait pas, par exemple, ni le serveur public indiqué plus haut, tous les deux ne connaissent que la version 0.)
Wireshark ne semble pas décoder ce
protocole. Si vous voulez vous y mettre, un
pcap est disponible en rtr.pcap
.
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : R. Bush (Internet Initiative
Japan)
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 28 septembre 2017
Le mécanisme de sécurisation de BGP nommé BGPsec, normalisé dans le RFC 8205 n'a pas encore de déploiement important. Mais le bon sens, l'expérience d'autres mécanismes de sécurité, et les expérimentations déjà faites avec BGPsec permettent de dégager quelques bonnes pratiques opérationnelles, que ce RFC décrit. Le RFC est court : on manque encore de pratique pour être sûr de toutes les bonnes pratiques.
Bien sûr, ces pratiques évolueront. À l'heure de la publication de ce RFC, il y a encore très peu d'expérience concrète avec BGPsec. Au fait, c'est quoi, BGPsec ? Le protocole BGP, la norme pour les annonces de routes sur l'Internet, souffre d'une faiblesse de sécurité : par défaut, n'importe qui peut annoncer n'importe quelle route, même vers un préfixe IP qu'il ne contrôle pas. Il existe plusieurs systèmes pour limiter les dégâts : les IRR, les systèmes d'alarme, et, plus récemment, les ROA (Route Origin Authorizations, cf. RFC 6482). BGPsec est la technique la plus efficace, mais aussi la plus difficile à déployer. Comme les ROA, il repose sur la RPKI. Contrairement aux ROA, il ne valide pas que l'AS d'origine mais tout le chemin d'AS, par une signature de chaque AS. Il introduit donc la cryptographie dans le routage, ce qui causera sans doute quelques perturbations.
Donc, avant de lire ce RFC 8207, c'est sans doute une bonne idée de lire au moins les RFC 4271 sur BGP et RFC 6480 sur la RPKI. Comme BGPsec dépend de la RPKI, il est également bon de lire le RFC 7115, qui parle des questions opérationnelles des ROA (qui reposent également sur la RPKI). Et enfin il faut lire le RFC sur le protocole BGPsec, le RFC 8205.
Avec BGPsec, chaque routeur de bordure de l'AS va avoir une paire {clé privée, clé publique}. On peut avoir une seule paire pour tous ses routeurs (et donc publier un seul certificat dans la RPKI) ou bien une paire par routeur (section 4 du RFC). La première solution est plus simple mais, si un routeur est piraté, il faudra changer la configuration de tous les autres routeurs. Le RFC suggère aussi de pré-publier des clés de secours dans la RPKI, pour qu'elles soient bien distribuées partout le jour où on en aura besoin.
Tous vos routeurs ne sont pas censés parler BGPsec, seulement ceux au bord de l'AS, face au grand monde inconnu du méchant Internet (section 5). Si vous n'avez pas le temps ou l'envie d'activer BGPsec sur tous vos routeurs, commencez par ceux qui font face aux « maillons faibles », les pairs ou les transitaires que vous suspectez d'être le plus capable d'envoyer ou de relayer des annonces erronées ou mensongères. Évidemment, ce sont aussi ceux qui ont le moins de chance de déployer BGPsec…
Si vous ne vous contentez pas de vérifier les signatures BGPsec, mais que vous agissez sur la base de cette vérification (en ignorant les annonces invalides), attendez-vous, comme avec toute technique de sécurité BGP, à ce que le trafic se déplace, par exemple vers d'autres pairs. Un pré-requis au déploiement de BGPsec est donc sans doute un bon système de métrologie.
Attention aux limites de BGPsec : la signature couvre le chemin d'AS, pas les communautés (RFC 1997). Ne vous fiez donc pas à elles.
Si BGPsec vous semble bien compliqué à déployer, et que vous hésitez devant le travail que cela représente, la section 6 vous rassurera : la majorité des AS sont des stubs, ils ne fournissent de connectivité à personne et n'ont donc pas forcément besoin de valider les annonces. Par contre, vous pouvez demander à vos transitaires s'ils utilisent BGPsec (début 2017, la réponse était forcément non).
Les lect·eur·rice·s subtil·e·s ont peut-être noté une
différence avec les ROA. Alors que l'état de la validation ROA
(RFC 6811)
donne un résultat ternaire (pas de ROA, un ROA et valide, un ROA
et invalide), BGPsec est binaire (annonce valide, ou
invalide). C'est parce que les chemins d'AS sécurisés BGPsec_path
ne
sont propagés qu'entre routeurs BGPsec, qui signent et, a priori,
valident. Si on reçoit une annonce BGPsec, c'est forcément que
tout le chemin d'AS gérait BGPsec. L'équivalent du « pas de ROA »
est donc une annonce BGP traditionnelle, avec
AS_PATH
mais sans
BGPsec_path
.
Quant à la décision qu'on prend lorsqu'une annonce est
invalide, c'est une décision locale. Ni
l'IETF, ni l'ICANN,
ni le CSA ne peuvent prendre cette décision
à votre place. Notre RFC recommande (mais c'est juste une
recommandation) de jeter les annonces BGPsec invalides. Certains
opérateurs peuvent se dire « je vais accepter les annonces
invalides, juste leur mettre une préférence plus basse » mais cela
peut ne pas suffire à empêcher leur usage même lorsqu'il existe
des alternatives. Imaginons un routeur qui reçoive une annonce
valide pour 10.0.0.0/16
et une invalide pour
10.0.666.0/24
(oui, je sais que ce n'est pas
une adresse IPv4 correcte, mais c'est le RFC qui a commencé, ce
n'est pas moi). Le routeur installe les deux routes, avec une
préférence basse pour 10.0.666.0/24
. Mais les
préférences ne jouent que s'il s'agit de préfixes identiques. Ici,
lorsqu'il faudra transmettre un paquet, c'est la route la plus
spécifique qui gagnera, donc la mauvaise (666 = chiffre
de la Bête). Notez que l'article « Are We There Yet?
On RPKI’s Deployment and Security » contient plusieurs autres exemples où le rejet d'une annonce
invalide a des conséquences surprenantes.
La même section 7 couvre aussi le cas des serveurs de routes, qui est un peu particulier car ces routeurs n'insèrent pas leur propre AS dans le chemin, ils sont censés être transparents.
Enfin, la section 8 traite de quelques points divers, comme le rappel que la RPKI (comme d'ailleurs le DNS) n'est pas cohérente en permanence, car pas transmise de manière synchrone partout. Par exemple, si vous venez d'obtenir un certificat pour un AS, ne l'utilisez pas tout de suite : les annonces BGP se propagent plus vite que les changements dans la RPKI et vos annonces signées risquent donc d'être considérées comme invalides par certains routeurs.
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : M. Lepinski (NCF), K. Sriram
(NIST)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidr
Première rédaction de cet article le 28 septembre 2017
Ce RFC s'inscrit dans la longue liste des efforts de normalisation d'une solution de sécurité pour BGP. Actuellement, il est trop facile de détourner le routage Internet via BGP (cf. mon article « La longue marche de la sécurité du routage Internet »). Le RFC 6482 introduisait une solution partielle, les ROA (Route Origin Authorizations). S'appuyant sur une infrastructure de clés publiques et de certificats prouvant que vous êtes détenteur légitime d'une ressource Internet (comme un numéro d'AS ou bien un préfixe IP), infrastructure nommée la RPKI (Resource Public Key Infrastructure), ces ROA permettent de valider l'origine d'une annonce BGP, c'est-à-dire le premier AS du chemin. C'est insuffisant pour régler tous les cas. Ainsi, lors de la fuite de Telekom Malaysia en juin 2015, les annonces avaient une origine normale. Avec les ROA, on ne détectait donc pas le problème. D'où cette nouvelle étape, BGPsec. Il s'agit cette fois de valider le chemin d'AS complet de l'annonce BGP, chaque routeur intermédiaire signant cryptographiquement l'annonce avant de la transmettre. BGPsec permet donc de détecter bien plus d'attaques, mais il est aussi bien plus lourd à déployer.
Donc, pour résumer BGPsec en une phrase : un nouvel attribut BGP
est créé, BGPsec_Path
, il est transporté dans les
annonces de route BGP, il offre la possibilité de vérifier, via les
signatures, que chaque AS listé dans ce
chemin a bien autorisé l'annonce. BGPsec ne remplace pas les ROA, il
les complète (et, comme elles, il s'appuie sur la
RPKI). Deux bonnes lectures : le modèle de
menace de BGPsec, dans le RFC 7132, et le
cahier des charges de BGPsec, dans le RFC 7353.
Le reste, ce n'est que du détail technique. Contrairement aux ROA, qui sont transportés en dehors de BGP, BGPsec est une modification du protocole BGP. Deux routeurs qui font du BGPsec doivent donc le négocier lors de l'établissement de la session (section 2 de notre RFC). Cela se fait par le mécanisme des capacités du RFC 5492. La capacité annonçant qu'on sait faire du BGPsec est BGPsec Capability, code n° 7. Notez que cette capacité dépend de celle de gérer des AS de quatre octets (normalisée dans le RFC 6793).
Le nouvel attribut est décrit en section 3. (Le concept
d'attributs BGP est dans la section 5 du RFC 4271.) Les annonces BGP avaient
traditionnellement un attribut nommé AS_PATH
, qui
indiquait le chemin d'AS suivi par l'annonce. (Deux choses
importantes à se rappeler : lorsqu'on écrit un chemin d'AS, il se lit
de droite à gauche, et vous n'avez aucune garantie que les
paquets IP suivront effectivement ce chemin « the data plane does not always follow
the control plane ».)
Le nouvel attribut remplace
AS_PATH
. Il se nomme
BGPsec_path (code 33) et est optionnel et non-transitif,
ce qui veut dire qu'il ne sera pas passé tel quel aux pairs. Au
contraire, le routeur est supposé créer un nouvel attribut
BGPsec_path
, ajoutant son AS
(signé) et celui de l'AS à qui il transmet l'annonce. On
n'envoie une annonce portant cet attribut à un pair que si celui-ci a
signalé sa capacité à gérer BGPsec. (Sinon, on lui envoie un
AS_PATH
ordinaire, non sécurisé, formé en
extrayant les AS du BGPsec_path
, cf. section 4.4.)
La signature contient entre autres un identifiant de l'algorithme utilisé. La liste des algorithmes possibles est dans un registre IANA (RFC 8208).
Pour pouvoir mettre sa signature dans le
BGPsec_path
, le routeur doit évidemment disposer
d'une clé
privée. La clé publique correspondante doit être dans un
certificat de la RPKI, certificat valable pour
l'AS qui signe.
Un routeur qui transmet une annonce à un pair BGPsec ne garantit pas forcément qu'il a validé le chemin sécurisé. (Même s'il le garantissait, pourquoi lui faire confiance ?) C'est à chaque routeur de vérifier l'intégrité et l'authenticité du chemin d'AS (section 5 du RFC).
C'est dans cette section 5 qu'est le cœur du RFC, les détails de la validation d'un chemin d'AS. Cette validation peut être faite par le routeur, ou bien par une machine spécialisée dans la validation, avec laquelle le routeur communique via le protocole RTR (RPKI To Router protocol, cf. RFC 8210). Bien sûr, si on valide, c'est sans doute pour que la validation ait des conséquences sur la sélection de routes, mais notre RFC n'impose pas de politique particulière : ce que l'on fait avec les annonces BGPsec mal signées est une décision locale, par chaque routeur (et peut-être, sur un routeur donné, différente pour chaque pair BGP).
L'algorithme de validation est simple :
BGPsec_path
.À ce stade, on peut déjà rejeter les annonces qui ont échoué à l'un de ces tests, et les traiter comme signifiant un retrait de la route citée (RFC 7606). Ensuite, on passe aux vérifications cryptographiques :
Notez qu'on ne négocie pas les algorithmes cryptographiques au moment de l'établissement de la session BGP, comme cela se fait, par exemple, en TLS. En effet, il ne suffit pas de connaitre l'algorithme de ses pairs, il faut valider tout le chemin, y compris des AS lointains. C'est pour cela que le RFC 8208 liste des algorithmes obligatoires, que tout le monde est censé connaître (actuellement ECDSA avec la courbe P-256 et SHA-256).
On note que l'attribut BGPsec_path
avec ses
signatures n'est « transmis » qu'aux routeurs qui comprennent
BGPsec (pour les autres, on envoie un AS_PATH
classique). Au début du déploiement, ces attributs ne survivront donc pas
longtemps, et les îlots BGPsec seront donc rares (section 7 du
RFC).
Notez que ce protocole est en développement depuis plus de six ans, et qu'il s'est parfois nommé PathSec dans le passé.
BGPsec est apparemment mis en œuvre au moins dans BIRD et dans Quagga (mais pas dans les versions officielles).
Quelques bonnes lectures sur BGPsec :
Et merci à Guillaume Lucas pour m'avoir rappelé que BGPsec est compliqué et que je suis loin de le maitriser.
Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : J. McCann (Digital Equipment
Corporation), S. Deering
(Retired), J. Mogul (Digital Equipment
Corporation), R. Hinden (Check Point
Software)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 16 juillet 2017
Ce RFC est l'adaptation à IPv6 du protocole décrit dans le RFC 1191, protocole qui permet de découvrir la MTU du chemin entre deux machines reliées par Internet. Il remplace le RFC 1981.
Avec IPv4, déterminer la MTU maximale du chemin est très utile pour optimiser les performances. Mais elle devient presque indispensable en IPv6, où les routeurs n'ont pas le droit de fragmenter les paquets trop gros (toute fragmentation doit être faite dans la machine de départ). PMTU fait donc quasi-obligatoirement partie d'IPv6. Une alternative est de n'envoyer que des paquets suffisamment petits pour passer partout. C'est moins efficace (on ne tirera pas profit des MTU plus importantes), mais ça peut être intéressant pour des mises en œuvre légères d'IPv6, par exemple pour des objets connectés. (La section 4 du RFC précise en effet que mettre en œuvre cette découverte de la MTU du chemin n'est pas obligatoire.)
L'algorithme (section 3 de notre RFC) est le même qu'en v4, envoyer des paquets, et voir si on reçoit des paquets ICMP Packet too big (RFC 4443, section 3.2) qui contiennent en général la MTU maximale du lien suivant. (Pas de bit DF - Don't fragment - en IPv6 puisque la fragmentation n'est possible qu'à la source. L'annexe A détaille les différences avec l'algorithme IPv4 décrit dans le RFC 1191.)
Voici par exemple un de ces paquets ICMP, vu par
tcpdump (déclenché par une commande
ping -s 1500 …
) :
18:18:44.813927 IP6 2a02:8400:0:3::a > 2605:4500:2:245b::bad:dcaf: ICMP6, packet too big, mtu 1450, length 1240
Il ne faut pas s'arrêter au premier paquet Packet too big reçu, car il peut y avoir des liens réseau avec une MTU encore plus basse plus loin. En outre, la MTU du chemin change avec le temps et il faut donc rester vigilant, on peut recevoir des Packet too big si le trafic passe désormais par un lien de MTU plus faible que la précédente MTU du chemin. Plus délicat, il faut aussi de temps en temps réessayer avec des paquets plus gros, pour voir si la MTU du chemin n'aurait pas augmenté. Comme cela va faire perdre des paquets, s'il n'y a pas eu d'augmentation, il ne faut pas le faire trop souvent. (Dix minutes, dit la section 5.)
La section 4 du RFC précise les exigences qu'il normalise. (Je
donne quelques exemples de leur mise en œuvre dans le noyau
Linux, dans net/ipv6/route.c
.) Par
exemple, le nœud IPv6 doit vérifier le contenu
des paquets ICMP Packet Too
Big qu'il reçoit (le Message Body,
cf. RFC 4443, où le routeur émetteur est censé
placer des données du paquet original), pour éviter de croire des faux messages
ICMP envoyés par un attaquant (rien n'authentifie un paquet ICMP, à
part son contenu).
Nouveauté de ce RFC, par rapport à son prédecesseur, un nœud IPv6
doit ignorer les messages Packet Too
Big indiquant une MTU inférieur aux 1 280 octets minimum
d'IPv6 (le RFC 8021 explique pourquoi). C'est le
but de l'instruction mtu = max_t(u32, mtu,
IPV6_MIN_MTU);
dans le code du noyau Linux, qui ne garde
pas la MTU indiquée si elle est inférieur au minimum.
Et la machine IPv6 ne doit jamais augmenter la
MTU du chemin suite à la réception d'un Packet Too
Big indiquant une MTU supérieure à l'actuelle. Un tel
message ne peut être qu'une erreur ou une attaque. Voir les
commentaires au début de la fonction
rt6_mtu_change_route
dans le code Linux.
Comme avec IPv4, l'algorithme PMTU marche mal en pratique car beaucoup de sites filtrent stupidement tous les paquets ICMP et la machine qui tente de faire de la Path MTU discovery n'aura jamais de réponse. IPv6, comme IPv4, devra donc plutôt utiliser la technique du RFC 4821.
La section 5 du RFC rassemble diverses questions de mise en œuvre de cette découverte de la MTU du chemin. D'abord, il y a l'interaction entre IP (couche 3) et les protocoles au dessus, comme TCP mais aussi des protocoles utilisant UDP, comme le DNS. Ces protocoles de transport ont une MMS_S (maximum send transport-message size, voir le RFC 1122) qui est à l'origine la MTU moins la taille des en-têtes. Les protocoles de transport peuvent diminuer cette MMS_S pour ne pas avoir à faire de fragmentation.
Pour le DNS, par exemple, on peut diminuer l'usage de la découverte de la MTU du chemin en diminuant la taille EDNS. Par exemple, avec le serveur nsd, on mettrait :
ipv6-edns-size: 1460
Et, avec 1 460 octets possibles, on serait alors raisonnablement sûr de ne pas avoir de fragmentation donc, même si la découverte de la MTU du chemin marche mal, tout ira bien.
Une fois la découverte de la MTU du chemin faite, où faut-il garder l'information, afin d'éviter de recommencer cette découverte à chaque fois ? Idéalement, cette information est valable pour un chemin donné : si le chemin change (ou, a fortiori, si la destination change), il faudra découvrir à nouveau. Mais la machine finale ne connait pas en général tout le chemin suivi (on ne fait pas un traceroute à chaque connexion). Le RFC suggère donc (section 5.2) de stocker une MTU du chemin par couple {destination, interface}. Si on n'a qu'une seule interface réseau, ce qui est le cas de la plupart des machines terminales, on stocke la MTU du chemin par destination et on maintient donc un état pour chaque destination (même quand on ne lui parle qu'en UDP, qui n'a normalement pas d'état). Les deux exemples suivants concernent une destination où la MTU du chemin est de 1 450 octets. Pour afficher cette information sur Linux pour une adresse de destination :
% ip -6 route get 2a02:8428:46c:5801::4 2a02:8428:46c:5801::4 from :: via 2001:67c:1348:7::1 dev eth0 src 2001:67c:1348:7::86:133 metric 0 cache expires 366sec mtu 1450 pref medium
S'il n'y a pas de PMTU indiquée, c'est que le cache a expiré, réessayez (en UDP, car TCP se fie à la MSS). Notez que, tant qu'on n'a pas reçu un seul Packet Too Big, il n'y a pas de MTU affichée. Donc, pour la plupart des destinations (celles joignables avec comme PMTU la MTU d'Ethernet), vous ne verrez rien. Sur FreeBSD, cet affichage peut se faire avec :
% sysctl -o net.inet.tcp.hostcache.list | fgrep 2a02:8428:46c:5801::4 2a02:8428:46c:5801::4 1450 0 23ms 34ms 0 4388 0 0 23 4 3600
Pour d'autres systèmes, je vous recommande cet article.
Notez qu'il existe des cas compliqués (ECMP) où cette approche de stockage de la PMTU par destination peut être insuffisante. Ce n'est donc qu'une suggestion, chaque implémentation d'IPv6 peut choisir sa façon de mémoriser les MTU des chemins.
Si on veut simplifier la mise en œuvre, et la consommation mémoire, on peut ne mémoriser qu'une seule MTU du chemin, la MTU minimale de tous les chemins testés. Ce n'est pas optimal, mais ça marchera.
Si on stocke la MTU du chemin, comme l'Internet peut changer, il faut se préparer à ce que l'information stockée devienne obsolète. Si elle est trop grande, on recevra des Packet too big (il ne faut donc pas s'attendre à n'en recevoir que pendant la découverte initiale). Si elle est trop petite, on va envoyer des paquets plus petits que ce qu'on pourrait se permettre. Le RFC suggère donc (section 5.3) de ne garder l'information sur la MTU du chemin que pendant une dizaine de minutes.
Le RFC demande enfin (section 5.5) que les mises en œuvre d'IPv6 permettent à l'ingénieur système de configurer la découverte de la MTU du chemin : la débrayer sur certaines interfaces, fixer à la main la MTU d'un chemin, etc.
Il ne faut pas oublier les problèmes de sécurité (section 6 du RFC) : les Packet Too Big ne sont pas authentifiés et un attaquant peut donc en générer pour transmettre une fausse information. Si la MTU indiquée est plus grande que la réalité, les paquets seront jetés. Si elle est plus petite, on aura des sous-performances.
Autre problème de sécurité, le risque que certains administrateurs réseau incompétents ne filtrent tous les messages ICMP, donc également les Packet Too Big (c'est stupide, mais cela arrive). Dans ce cas, la découverte de la MTU du chemin ne fonctionnera pas (cf. RFC 4890 sur les recommandations concernant le filtrage ICMP). L'émetteur devra alors se rabattre sur des techniques comme celle du RFC 4821.
L'annexe B de notre RFC résume les changements faits depuis le RFC 1981. Rien de radical, les principaux portent sur la sécurité (ce sont aussi ceux qui ont engendré le plus de discussions à l'IETF) :
Le texte a également subi une actualisation générale, les références (pré-)historiques à BSD 4.2 ou bien à TP4 ont été supprimées.
Enfin, pour faire joli, un exemple de
traceroute avec recherche de la MTU du chemin
activée (option --mtu
). Notez la réception d'un
Packet Too Big à l'étape 12, annonçant une MTU de
1 450 octets :
% sudo traceroute -6 --mtu --udp --port=53 eu.org traceroute to eu.org (2a02:8428:46c:5801::4), 30 hops max, 65000 byte packets ... 3 vl387-te2-6-paris1-rtr-021.noc.renater.fr (2001:660:300c:1002:0:131:0:2200) 2.605 ms 2.654 ms 2.507 ms 4 2001:660:7903:6000:1::4 (2001:660:7903:6000:1::4) 5.002 ms 4.484 ms 5.494 ms 5 neuf-telecom.sfinx.tm.fr (2001:7f8:4e:2::178) 4.193 ms 3.682 ms 3.763 ms 6 neuf-telecom.sfinx.tm.fr (2001:7f8:4e:2::178) 14.264 ms 11.927 ms 11.509 ms 7 2a02-8400-0000-0003-0000-0000-0000-1006.rev.sfr.net (2a02:8400:0:3::1006) 12.760 ms 10.446 ms 11.902 ms 8 2a02-8400-0000-0003-0000-0000-0000-1c2e.rev.sfr.net (2a02:8400:0:3::1c2e) 10.524 ms 11.477 ms 11.896 ms 9 2a02-8400-0000-0003-0000-0000-0000-1c2e.rev.sfr.net (2a02:8400:0:3::1c2e) 13.061 ms 11.589 ms 11.915 ms 10 2a02-8400-0000-0003-0000-0000-0000-117e.rev.sfr.net (2a02:8400:0:3::117e) 10.446 ms 10.420 ms 10.361 ms 11 2a02-8400-0000-0003-0000-0000-0000-000a.rev.sfr.net (2a02:8400:0:3::a) 10.460 ms 10.453 ms 10.517 ms 12 2a02-8428-046c-5801-0000-0000-0000-00ff.rev.sfr.net (2a02:8428:46c:5801::ff) 12.495 ms F=1450 12.102 ms 11.203 ms 13 2a02-8428-046c-5801-0000-0000-0000-0004.rev.sfr.net (2a02:8428:46c:5801::4) 12.397 ms 12.217 ms 12.507 ms
La question de la fragmentation en IPv6 a suscité d'innombrables articles. Vous pouvez commencer par celui de Geoff Huston.
Merci à Pierre Beyssac pour avoir fourni le serveur de test configuré comme il fallait, et à Laurent Frigault pour la solution sur FreeBSD.
Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : S. Deering (Retired), R. Hinden (Check Point Software)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 16 juillet 2017
Ce RFC est la nouvelle norme du protocole IPv6. IP est le protocole de base de l'Internet, la version 6 y est minoritaire mais est bien plus répandue qu'elle ne l'était lors de la sortie de la précédente norme, qui était le RFC 2460, norme que notre nouveau RFC remplace (et cela fait passer IPv6 au statut de norme Internet, la précédente étant officiellement une proposition de norme).
Pas de changements cruciaux, la norme est nouvelle, mais le protocole reste largement le même. Ce RFC 8200 continue à présenter IPv6 comme « a new version of the Internet Protocol (IP) ». Comme la première norme IPv6 est sortie en 1995, l'adjectif « new » n'est vraiment pas sérieux. Comme, malheureusement, la plupart des formations réseau ne parlent que d'IPv4 et traitent IPv6 de manière bâclée à la fin, le RFC présente IPv6 en parlant de ses différences par rapport à IPv4 (section 1 du RFC) :
Notez que ce RFC 8200 ne spécifie que le format des paquets IPv6. D'autres points très importants sont normalisés dans d'autres RFC, les adresses dans le RFC 4291, et ICMP dans le RFC 4443.
La section 3 présente le format des paquets IPv6 :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| Traffic Class | Flow Label | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Payload Length | Next Header | Hop Limit | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | | + Source Address + | | + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + | | + Destination Address + | | + + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
(D'ailleurs, si quelqu'un sait pourquoi l'adresse IP source est avant la destination ? Il me semblerait plus logique que ce soit l'inverse, puisque tous les routeurs sur le trajet doivent examiner l'adresse destination, alors qu'on n'a pas toujours besoin de l'adresse source.) Le numéro de version vaut évidemment 6, le « traffic class » est présenté en section 7, le « flow label » en section 6.
Le champ « Next header » remplace le « Protocol » d'IPv4. Il peut indiquer le protocole de transport utilisé (la liste figure dans un registre IANA et est la même pour IPv4 et IPv6) mais aussi les en-têtes d'extension, une nouveauté d'IPv6, présentée en section 4 de notre RFC.
Le champ « Hop limit » remplace le « Time to Live » d'IPv4. En fait, les deux ont exactement la même sémantique, qui est celle d'un nombre maximal de routeurs utilisés sur le trajet (le but est d'éviter les boucles infinies dans le réseau). Autrefois, dans IPv4, il était prévu que ce champ soit réellement une durée, mais aucune mise en œuvre d'IPv4 ne l'avait jamais utilisé comme ceci. Le renommage dans IPv6 fait donc correspondre la terminologie avec une réalité ancienne (cf. aussi la section 8.2). Notez que c'est ce champ qui est utilisé par traceroute.
Voici une simple connexion HTTP en IPv6,
vue avec tcpdump et
tshark. Le client a demandé
/robots.txt
et a obtenu une réponse négative
(404). Si vous voulez, le pcap complet est
ipv6-http-connection.pcap
. Voici d'abord avec
tcpdump avec ses options par défaut :
15:46:21.768536 IP6 2001:4b98:dc2:43:216:3eff:fea9:41a.37703 > 2605:4500:2:245b::42.80: Flags [S], seq 3053097848, win 28800, options [mss 1440,sackOK,TS val 1963872568 ecr 0,nop,wscale 7], length 0
On voit les deux adresses IPv6, tcpdump n'affiche rien d'autre de
l'en-tête de couche 3, tout le reste est du
TCP, le protocole de transport utilisé par HTTP. Avec l'option -v
de tcpdump :
15:46:21.768536 IP6 (hlim 64, next-header TCP (6) payload length: 40) 2001:4b98:dc2:43:216:3eff:fea9:41a.37703 > 2605:4500:2:245b::42.80: Flags [S] [...]
Cette fois, on voit le « Hop limit » (64),
l'en-tête suivant (TCP, pas d'en-tête d'extension) et la longueur
(40 octets). Pour avoir davantage, il faut passer à
tshark (décodage complet en ipv6-http-connection.txt
) :
Internet Protocol Version 6, Src: 2001:4b98:dc2:43:216:3eff:fea9:41a, Dst: 2605:4500:2:245b::42 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic class: 0x00 (DSCP: CS0, ECN: Not-ECT) .... 0000 00.. .... .... .... .... .... = Differentiated Services Codepoint: Default (0) .... .... ..00 .... .... .... .... .... = Explicit Congestion Notification: Not ECN-Capable Transport (0) .... .... .... 0000 0000 0000 0000 0000 = Flow label: 0x00000 Payload length: 40 Next header: TCP (6) Hop limit: 64 Source: 2001:4b98:dc2:43:216:3eff:fea9:41a [Source SA MAC: Xensourc_a9:04:1a (00:16:3e:a9:04:1a)] Destination: 2605:4500:2:245b::42 [Source GeoIP: France] [Source GeoIP Country: France] [Destination GeoIP: United States] [Destination GeoIP Country: United States]
On a cette fois tout l'en-tête IPv6 : notons le « Flow Label » (j'avais bien dit que peu de gens s'en servaient, il est nul dans ce cas).
La section 4 de notre RFC est dédiée à une nouveauté d'IPv6, les en-têtes d'extension. Au lieu d'un champ « Options » de taille variable (donc difficile à analyser) comme en IPv4, IPv6 met les options IP dans des en-têtes supplémentaires, chaînés avec l'en-tête principal. Par exemple, si un paquet UDP a un en-tête « Destination Options », le champ « Next header » de l'en-tête principal vaudra 60 (pour « Destination Options »), il sera suivi de l'en-tête d'extension « Destination Options » qui aura, lui, un « Next header » de 17 pour indiquer que ce qui suit est de l'UDP. (Je rappelle que les valeurs possibles pour « Next Header » sont dans un registre IANA.) Il peut y avoir zéro, un ou davantage d'en-têtes d'extension chaînés entre eux.
Notez qu'analyser cette chaîne d'en-têtes est compliqué car les en-têtes n'ont pas tous le même format (le RFC 6564 a créé un format unique, mais cela ne concerne que les futurs en-têtes.) Il est donc exagéré de dire que la suppression du champ « Options » de taille variable a simplifié les choses.
Les différents en-têtes ne sont pas tous traités pareillement par les routeurs. Il existe notamment un en-tête, « Hop-by-hop Options » qui doit être examiné par tous les routeurs du trajet (cette obligation, jamais respectée, a de toute façon été relâchée par rapport au RFC 2460, puis re-spécifiée de manière plus réaliste dans le RFC 9673). C'est pour cela qu'il doit être placé au début des en-têtes, juste après l'en-tête principal. Les autres en-têtes d'extension doivent être ignorés par les routeurs.
Comme il est compliqué de rajouter un nouveau modèle d'en-tête (il faudrait modifier toutes les machines IPv6), une solution légère existe pour les options simples : utiliser les en-têtes d'options, « Hop-by-hop Options » et « Destination Options ». Tous les deux sont composés d'une série d'options encodées en TLV. En outre, le type de l'option indique, dans ses deux premiers bits, le traitement à appliquer au paquet si le système ne connait pas cette option. Si les deux premiers bits sont à zéro, on ignore l'option et on continue. Autrement, on jette le paquet (les trois valeurs restantes, 01, 10 et 11, indiquent si on envoie un message d'erreur ICMP et lequel). Ainsi, l'option pour la destination de numéro 0x07 (utilisée par le protocole de sécurité du RFC 5570) est facultative : elle a les deux premiers bits à zéro et sera donc ignorée silencieusement par les destinataires qui ne la connaissent pas (cf. registre IANA.)
« Destination Options », comme son nom l'indique, n'est examinée que par la machine de destination. Si vous voulez envoyer des paquets avec cet en-tête, regardez mon article.
Outre les en-têtes « Hop-by-hop Options » et « Destination Options », il existe des en-têtes :
La fragmentation est différente de celle
d'IPv4. En IPv6, seule la machine émettrice peut fragmenter, pas
les routeurs intermédiaires. Si le paquet est plus grand que la
MTU, on le découpe en fragments, chaque
fragment portant un « Fragment Header ». Cet
en-tête porte une identification (un nombre sur 32 bits qui doit
être unique parmi les paquets qui sont encore dans le réseau),
un décalage (offset) qui indique à combien
d'octets depuis le début du paquet original se situe ce fragment
(il vaut donc zéro pour le premier fragment) et un bit qui indique
si ce fragment est le dernier. À la destination, le paquet est
réassemblé à partir des fragments. (Il est désormais interdit que
ces fragments se recouvrent, cf. RFC 5722.) Voici un exemple de fragmentation. La sonde Atlas n° 6271 a
interrogé un serveur DNS de la racine Yeti avec le type de
question ANY
qui signifie « envoie-moi tout
ce que tu peux / veux ». La réponse, plus grande que la MTU (plus
de quatre kilo-octets !), a été
fragmentée en trois paquets (le pcap
complet est en ipv6-dns-frag.pcap
) :
16:14:27.112945 IP6 2001:67c:217c:4::2.60115 > 2001:4b98:dc2:45:216:3eff:fe4b:8c5b.53: 19997+ [1au] ANY? . (28) 16:14:27.113171 IP6 2001:4b98:dc2:45:216:3eff:fe4b:8c5b > 2001:67c:217c:4::2: frag (0|1232) 53 > 60115: 19997*- 35/0/1 NS bii.dns-lab.net., NS yeti.bofh.priv.at., NS yeti.ipv6.ernet.in., NS yeti.aquaray.com., NS yeti.mind-dns.nl., NS dahu1.yeti.eu.org., NS dahu2.yeti.eu.org., NS yeti1.ipv6.ernet.in., NS ns-yeti.bondis.org., NS yeti-ns.ix.ru., NS yeti-ns.lab.nic.cl., NS yeti-ns.tisf.net., NS yeti-ns.wide.ad.jp., NS yeti-ns.conit.co., NS yeti-ns.datev.net., NS yeti-ns.switch.ch., NS yeti-ns.as59715.net., NS yeti-ns1.dns-lab.net., NS yeti-ns2.dns-lab.net., NS yeti-ns3.dns-lab.net., NS xn--r2bi1c.xn--h2bv6c0a.xn--h2brj9c., NS yeti-dns01.dnsworkshop.org., NS yeti-dns02.dnsworkshop.org., NS 3f79bb7b435b05321651daefd374cd.yeti-dns.net., NS ca978112ca1bbdcafac231b39a23dc.yeti-dns.net., RRSIG, NSEC, RRSIG[|domain] 16:14:27.113187 IP6 2001:4b98:dc2:45:216:3eff:fe4b:8c5b > 2001:67c:217c:4::2: frag (1232|1232) 16:14:27.113189 IP6 2001:4b98:dc2:45:216:3eff:fe4b:8c5b > 2001:67c:217c:4::2: frag (2464|637)
On note que tcpdump n'a interprété qu'un seul fragment comme étant
du DNS, la premier, puisque c'était le seul qui portait l'en-tête
UDP, avec le numéro de port 53 identifiant du DNS. Dans le
résultat de tcpdump, après le mot-clé frag
,
on voit le décalage du fragment par rapport au début du paquet
original (respectivement 0, 1232 et 2464 pour les trois
fragments), et la taille du fragment (respectivement 1232, 1232 et
637 octets). Vu par tshark (l'analyse complète est en ipv6-dns-frag.txt
), le premier fragment contient :
Type: IPv6 (0x86dd) Internet Protocol Version 6, Src: 2001:4b98:dc2:45:216:3eff:fe4b:8c5b, Dst: 2001:67c:217c:4::2 Payload length: 1240 Next header: Fragment Header for IPv6 (44) Fragment Header for IPv6 Next header: UDP (17) Reserved octet: 0x00 0000 0000 0000 0... = Offset: 0 (0 bytes) .... .... .... .00. = Reserved bits: 0 .... .... .... ...1 = More Fragments: Yes Identification: 0xcbf66a8a Data (1232 bytes)
On note le « Next Header » qui indique qu'un en-tête d'extension, l'en-tête « Fragmentation », suit l'en-tête principal. Le bit M (« More Fragments ») est à 1 (ce n'est que le premier fragment, d'autres suivent), le décalage (« offset ») est bien sûr de zéro. L'identificateur du paquet est de 0xcbf66a8a. Le dernier fragment, lui, contient :
Internet Protocol Version 6, Src: 2001:4b98:dc2:45:216:3eff:fe4b:8c5b, Dst: 2001:67c:217c:4::2 Payload length: 645 Next header: Fragment Header for IPv6 (44) Fragment Header for IPv6 Next header: UDP (17) Reserved octet: 0x00 0000 1001 1010 0... = Offset: 308 (2464 bytes) .... .... .... .00. = Reserved bits: 0 .... .... .... ...0 = More Fragments: No Identification: 0xcbf66a8a
Cette fois, le « Next Header » indique que c'est de l'UDP qui suit, le décalage est de 2464, et le bit M est à zéro (plus d'autres fragments). L'identificateur est le même, c'est celui du paquet original, et c'est grâce à lui que la machine de destination saura réassembler les fragments. Notez qu'à la fin de l'analyse par tshark figure un réassemblage complet du paquet, ce qui permet une analyse DNS complète.
Et si je ne suis pas satisfait des en-têtes d'extension existants et que je veux créer le mien ? C'est en général une mauvaise idée. La plupart des cas concrets devraient être résolus avec un des en-têtes déjà normalisés. Notamment, l'en-tête « Destination Options » est là pour la majorité des situations. C'est lui qu'il faut regarder en premier (ce qu'ont fait les RFC 6744, RFC 6788 ou RFC 7837, la liste complète figurant dans un registre IANA). Notre RFC 8200 exige donc que, si vous tenez à créer un nouvel en-tête, vous expliquiez bien pourquoi c'est indispensable, et pourquoi aucun des en-têtes existants ne convient.
Il est également déconseillé de créer de nouvelles options « hop-by-hop » (la liste actuelle est à l'IANA), car ces options doivent être traitées par tous les routeurs du trajet. Il y a un risque sérieux qu'ils laissent tomber les paquets ayant cet en-tête « Hop-by-hop Options », ou bien qu'ils le traitent plus lentement (logiciel du routeur et pas circuits matériels spécialisés). Là aussi, il faudra donc une justification sérieuse.
La section 5 de notre RFC se penche sur un problème délicat, la taille des paquets. IPv6 exige une MTU d'au moins 1 280 octets, mais les paquets sont souvent plus grands (par exemple 1 500 octets si on part d'un Ethernet). Beaucoup de liens ont, en pratique, une MTU de 1 500 octets mais IPv6 étant souvent porté par des tunnels (en raison de l'immobilisme de beaucoup de FAI, qui ne déploient toujours pas IPv6), un certain nombre de liens offrent moins de 1 500 octets. Normalement, l'émetteur d'un paquet IPv6 doit faire de la découverte de la MTU du chemin (RFC 8201) afin de pouvoir fragmenter, si nécessaire. Une mise en œuvre « paresseuse » d'IPv6 pourrait se dispenser de découverte de la MTU du chemin, et se limiter à 1 280 octets par paquet.
Le problème est que la découverte de la MTU du chemin dépend du bon fonctionnement d'ICMP. L'émetteur d'un paquet doit pouvoir recevoir les paquets ICMP « Packet too big » (RFC 4443, section 3.2). Or, un certain nombre de pare-feux, stupidement configurés par des amateurs, bloquent tout ICMP « pour des raisons de sécurité » (c'est d'ailleurs une bonne question pour les entretiens d'embauche d'un administrateur de réseaux : « filtrer ICMP en entrée ou pas ? » S'il répond Oui, on est sûr qu'il est incompétent.) Ne recevant pas les « Packet too big », l'émetteur risque de croire à tort que ses paquets sont passés. En outre, si l'émetteur décide de fragmenter (en général, quand la MTU du chemin est inférieure à la sienne, avec TCP, on réduit la MSS, avec UDP, on fragmente), il faut que les fragments passent à travers le réseau. Et, là encore, un certain nombre de pare-feux bêtement configurés bloquent les fragments. Donc, en pratique, découverte du chemin + fragmentation est un processus fragile, en raison de ce véritable sabotage par des middleboxes.
C'est certainement le plus gros problème pratique lors du déploiement d'IPv6. On peut même penser à prendre des mesures radicales et coûteuses, comme d'abaisser la MTU à 1 280 octets pour être tranquille. Moins violent, il est fréquent de voir des MTU à 1 480 octets. Voici par exemple la configuration de mon routeur Turris Omnia pour passer par l'IPv6 de Free (un tunnel) :
config interface 'lan' option ip6assign '64' option ip6addr '2001:db8:42::1:fe/64' option ip6prefix '2001:db8:42::/64' option ip6gw '2001:db8:42::1' option mtu '1480'
Et le « flow label », dont j'avais parlé plus haut ? Il est décrit dans la (très courte) section 6, qui renvoie surtout au RFC 6437. En pratique, ce champ semble peu utilisé comme on l'a vu dans l'exemple décodé par tshark.
Même chose pour le « traffic class », en section 7 : pour son utilisation pour la différenciation de trafic, voir les RFC 2474 et RFC 3168.
Maintenant qu'IPv6, protocole de couche 3, a été bien défini, le RFC monte vers la couche 4, en consacrant sa section 8 aux problèmes des couches supérieures. Cela concerne notamment la somme de contrôle. Si vous avez fait attention au schéma de l'en-tête IPv6 de la section 3, vous avez noté qu'il n'y avait pas de champ « Header Checksum », contrairement à ce qui existait en IPv4. En IPv6, pas de somme de contrôle en couche 3, c'était une tâche supplémentaire pour les routeurs (pour citer le RFC 791, « Since some header fields change (e.g., time to live), this is recomputed and verified at each point that the internet header is processed. »), tâche dont ils sont désormais dispensés.
Par contre, la somme de contrôle existe pour les en-têtes de couche 4 et elle devient même obligatoire pour UDP (elle était facultative en IPv4, quoique très fortement recommandée). (Voir le RFC 1071, au sujet de cette somme de contrôle.)
Un gros changement de ce RFC par rapport à son prédécesseur, le RFC 2460, concerne la sécurité. La section sur la sécurité est passée d'une annexe de deux lignes (qui se contentait de passer le bébé à IPsec) à une analyse plus détaillée (section 10 du RFC). La question est délicate car la sécurité d'IPv6 a souvent fait l'objet de FUD, visant à justifier l'immobilisme de pas mal d'acteurs. Il est évidemment plus valorisant de dire « nous ne migrons pas vers IPv6 pour des raisons de sécurité » que de reconnaitre « nous sommes trop flemmards et paralysés par la peur du changement ». (Cf. mon exposé sur la sécurité d'IPv6 à l'ESGI.) S'il fallait synthétiser (la deuxième partie de cette synthèse ne figure pas dans le RFC), je dirais :
Bon, assez de stratégie, passons maintenant aux problèmes concrets que décrit cette section 10. Elle rappelle des risques de sécurité qui sont exactement les mêmes qu'en IPv4 mais qu'il est bon de garder en tête, ce sont des problèmes fondamentaux d'IP :
En cohérence avec le RFC 2460 (mais pas avec la réalité du terrain), notre RFC recommande IPsec (RFC 4301) comme solution à la plupart de ces problèmes. Hélas, depuis le temps qu'il existe, ce protocole n'a jamais connu de déploiement significatif sur l'Internet public (il est par contre utilisé dans des réseaux privés, par exemple le VPN qui vous permet de vous connecter avec votre entreprise de l'extérieur utilise probablement une variante plus ou moins standard d'IPsec). Une des raisons de ce faible déploiement est la grande complexité d'IPsec, et la complexité pire encore de ses mises en œuvre. En pratique, même si le RFC ne le reconnait que du bout des lèvres, ce sont les protocoles applicatifs comme SSH ou TLS, qui sécurisent l'Internet.
Pour les attaques par déni de service, par contre, aucune solution n'est proposée : le problème ne peut pas forcément se traiter au niveau du protocole réseau.
La différence la plus spectaculaire entre IPv4 et IPv6 est
évidemment la taille des adresses. Elle rend le
balayage bien plus complexe (mais pas impossible), ce qui
améliore la sécurité (l'Internet IPv4 peut être exploré
incroyablement vite, par exemple avec masscan,
et, si on est trop flemmard pour balayer soi-même, on peut
utiliser des balayages déjà faits, par exemple par
Shodan ou
). Le RFC 7707 fait une très bonne synthèse de l'état de l'art
en matière de balayage IPv6. Par exemple, Shodan, cité plus
haut, doit utiliser des techniques
assez douteuses
pour récolter
des adresses IPv6 à examiner.https://scans.io/
Et qu'en est-il de la vie privée ? L'argument, largement FUDé, a été beaucoup utilisé contre IPv6. Le RFC note qu'IPv6 a entre autre pour but de rendre inutile l'usage du NAT, dont certaines personnes prétendent qu'il protège un peu les utilisateurs. L'argument est largement faux : le NAT (qui est une réponse à la pénurie d'adresses IPv4, pas une technique de sécurité) ne protège pas contre tout fingerprinting, loin de là. Et, si on veut empêcher les adresses IP des machines du réseau local d'être visibles à l'extérieur, on peut toujours faire du NAT en IPv6, si on veut, ou bien utiliser des méthodes des couches supérieures (comme un relais).
Autre question de vie privée avec IPv6, les adresses IP fondées sur l'adresse MAC. Cette ancienne technique, trop indiscrète, a été abandonnée avec les RFC 8981 et RFC 7721, le premier étant très déployé. Il est curieux de constater que cet argument soit encore utilisé, alors qu'il a perdu l'essentiel de sa (faible) pertinence.
Mais il y avait bien des problèmes de sécurité concrets avec le précédent RFC 2460, et qui sont réparés par ce nouveau RFC :
L'annexe B de notre RFC résume les changements depuis le RFC 2460. Pas de choses révolutionnaires, les changements les plus importantes portaient sur la sécurité, comme listé un peu plus haut (section 10 du RFC). On notera comme changements :
Le projet de mise à jour de la norme IPv6 avait été lancé en 2015 (voici les supports du premier exposé).
D'habitude, je termine mes articles sur les RFC par des informations sur l'état de mise en œuvre du RFC. Mais, ici, il y en a tellement que je vous renvoie plutôt à cette liste. Notez que des tests d'interopérabilité ont été faits sur les modifications introduites par ce nouveau RFC et que les résultats publiés n'indiquent pas de problème.
Parmi les publications récentes sur le déploiement d'IPv6, signalons :
Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : K. Fujiwara (JPRS), A. Kato
(Keio/WIDE), W. Kumari (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 26 juillet 2017
Lorqu'un client DNS interroge un résolveur, celui-ci peut répondre très vite si l'information demandée est dans sa mémoire (son « cache ») mais cela peut être beaucoup plus lent s'il faut aller demander aux serveurs faisant autorité. Il est donc essentiel, pour les performances du DNS, d'utiliser le cache le plus possible. Traditionnellement, le résolveur n'utilisait le cache que si la question posée par le client avait une réponse exacte dans le cache. Mais l'arrivée de DNSSEC autorise un usage plus énergique et plus efficace du cache : ce nouveau RFC permet aux résolveurs de synthétiser des réponses à partir des informations DNSSEC.
Comment peut-il faire ? Eh bien prenons par exemple les
enregistrements NSEC. Ils indiquent une
plage où il n'y a pas de noms enregistrés. Ainsi, si une zone
example.com
contient :
albatross IN AAAA 2001:db8:1028::a:1 elephant IN AAAA 2001:db8:1028::a:2 zebra IN AAAA 2001:db8:1028::a:3
Sa signature DNSSEC va ajouter des enregistrements NSEC, notamment :
albatross.example.com. IN NSEC elephant.example.com. AAAA
qui veut dire qu'il n'y a pas de nom entre
albatross
et elephant
. Si le
client interroge le résolveur à propos de
cat.example.com
, le résolveur ira voir les
serveurs faisant autorité, il aura une réponse négative
(NXDOMAIN, ce nom n'existe pas) et l'enregistrement NSEC. Les deux
informations seront renvoyées au client, et pourront être mémorisées dans le cache. Maintenant,
si le client demande dog.example.com
, les
résolveurs traditionnels retourneront demander aux serveurs faisant
autorité. Alors que les résolveurs modernes, conformes à ce nouveau
RFC 8198, pourront déduire du NSEC que
dog.example.com
n'existe pas non plus, et
immédiatement générer un NXDOMAIN pour le client. Cela fera gagner du
temps et des efforts à tout le monde. (Les règles exactes sont
dans la section 5.1 de notre RFC.)
La règle (désormais dépassée) comme quoi le résolveur ne peut répondre immédiatement que s'il a l'information correspondant à la question exacte est spécifiée dans le RFC 2308, pour le cache négatif (mémoriser les réponses NXDOMAIN). Désormais, elle n'est plus obligatoire si (et seulement si, voir section 9) le résolveur valide avec DNSSEC, et si la zone est signée (aucun changement si elle ne l'est pas). Outre le cas simple de NSEC avec des réponses négatives, présenté plus haut, il y a deux cas plus complexes, les enregistrements NSEC3, et les jokers. Si la zone est signée avec NSEC3 au lieu de NSEC, les enregistrements NSEC3 indiqueront des condensats et pas des noms et le résolveur devra donc condenser le nom demandé, pour voir s'il tombe dans un NSEC3 connu, et si on peut synthétiser le NXDOMAIN. Les règles exactes sur les NSEC3 sont dans le RFC 5155 (attention, c'est compliqué), sections 8.4 à 8.7, et dans notre RFC, section 5.2. Évidemment, si la zone est signée avec l'option « opt-out » (c'est le cas de la plupart des TLD), le résolveur ne peut pas être sûr qu'il n'y a pas un nom non signé dans une plage indiquée par un enregistrement NSEC, et ne peut donc pas synthétiser. Tout aussi évidemment, toute solution qui empêchera réellement l'énumération des noms dans une zone signée (comme le projet, désormais abandonné, NSEC5) sera incompatible avec cette solution.
Si la zone inclut des jokers (RFC 1034,
section 4.3.3), par exemple example.org
:
avocado IN A 192.0.2.1 * IN A 192.0.2.2 zucchini IN A 192.0.2.3
Alors, le résolveur pourra également synthétiser des réponses positives. S'il a
déjà récupéré et mémorisé le joker, et que le client lui demande
l'adresse IPv4 de
leek.example.org
, le résolveur pourra tout de
suite répondre 192.0.2.2
. (J'ai simplifié : le
résolveur doit aussi vérifier que le nom
leek.example.org
n'existe pas, ou en tout cas n'a
pas d'enregistrement A. Le joker ne masque en effet pas les noms
existants. Détails en section 5.3 du RFC.)
Pour cette utilisation plus énergique de la mémoire d'un résolveur validant, il a fallu amender légèrement le RFC 4035, dont la section 4.5 ne permettait pas cette synthèse de réponses (la nouvelle rédaction est en section 7). Notez un inconvénient potentiel de cette synthèse : un nom ajouté ne sera pas visible tout de suite. Mais c'est de toute façon une propriété générale du DNS et de ses caches souvent appelée, par erreur, « durée de propagation ».
La durée exacte pendant laquelle le résolveur « énergique » pourra garder les informations qui lui servent à la synthèse des réponses est donnée par le TTL des NSEC.
Quel intérêt à cette synthèse énergique de réponses ? Comme indiqué plus haut, cela permet de diminuer le temps de réponse (section 6). Du fait de la diminution de nombre de questions à transmettre, cela se traduira également par une diminution de la charge, et du résolveur, et du serveur faisant autorité. Sur la racine du DNS, comme 65 % des requêtes entrainent un NXDOMAIN (voir par exemple les statistiques de A-root), le gain devrait être important. Un autre avantage sera pour la lutte contre les attaques dites « random QNAMEs » lorsque l'attaquant envoie plein de requêtes pour des noms aléatoires (générant donc des NXDOMAIN). Si l'attaquant passe par un résolveur, celui-ci pourra écluser la grande majorité des requêtes sans déranger le serveur faisant autorité.
Mais la synthèse énergique entraine aussi un gain en matière de vie privée (cf. RFC 7626) : les serveurs faisant autorité verront encore moins de questions.
Cette technique de « cache négatif énergique » avait été proposée pour la première fois dans la section 6 du RFC 5074. Elle s'inscrit dans une série de propositions visant à augmenter l'efficacité des caches DNS, comme le « NXDOMAIN cut » du RFC 8020. La synthèse énergique de réponses prolonge le RFC 8020, en allant plus loin (mais elle nécessite DNSSEC).
Il semble que Google Public DNS mette déjà en œuvre une partie (les réponses négatives) de ce RFC, mais je n'ai pas encore vérifié personnellement.
Date de publication du RFC : Juillet 2017
Auteur(s) du RFC : H. Schulzrinne (FCC)
Chemin des normes
Première rédaction de cet article le 18 juillet 2017
Voici un RFC qui n'est pas trop long à lire ou à comprendre : il ajoute un code de réponse au protocole SIP (utilisé surtout pour la téléphonie sur IP), le code 607 qui dit explicitement « je n'ai pas envie de répondre à cet appel ».
Sur certaines lignes téléphoniques, la majorité des appels entrants sont du spam (cf. RFC 5039), du genre salons de beauté ou remplacements de fenêtres, voire appels automatiques en faveur d'un candidat politicien. Toute méthode de lutte contre ce spam nécessite d'avoir un retour des utilisateurs, qui déclarent « je ne veux pas de cet appel ». Simplement raccrocher ne suffit pas, il faut permettre à l'utilisateur de déclarer explicitement la spamicité d'un appel. (Les codes de rejet existants comme 603 - RFC 3261, section 21.6.2 - ne sont pas assez spécifiques. 603 indique typiquement un rejet temporaire, du genre je suis à table, ne me dérangez pas maintenant.)
C'est désormais possible avec ce nouveau code de retour 607, enregistré à l'IANA. Le logiciel de l'utilisateur enverra ce code (il aura typiquement un bouton « rejet de ce spam » comme c'est le cas pour les logiciels de courrier), et il pourra être utilisé pour diverses techniques anti-spam (par exemple par un fournisseur SIP pour déterminer que tel numéro ne fait que du spam et peut donc être bloqué). La FCC (pour laquelle travaille l'auteur du RFC) avait beaucoup plaidé pour un tel mécanisme.
Notez que l'interprétation du code sera toujours délicate, car il peut y avoir usurpation de l'identité de l'appelant (section 6 de notre RFC, et aussi RFC 4474) ou tout simplement réaffectation d'une identité à un nouvel utilisateur, innocent de ce qu'avait fait le précédent. Au passage, dans SIP, l'identité de l'appelant peut être un URI SIP ou bien un numéro de téléphone, cf. RFC 3966.
Le code aurait dû être 666 (le nombre de la Bête) mais le code 607 a été finalement choisi pour éviter des querelles religieuses. (Le RFC 7999 n'a pas eu les mêmes pudeurs.)
L'action (ou l'inaction) suivant une réponse 607 dépendra de la politique de chaque acteur. (La discussion animée à l'IETF avait surtout porté sur ce point.) Comme toujours dans la lutte contre le spam, il faudra faire des compromis entre trop de faux positifs (on refuse des messages légitimes) et trop de faux négatifs (on accepte du spam). D'autant plus que l'utilisateur peut être malveillant (rejeter comme spam des appels légitimes dans l'espoir de faire mettre l'appelant sur une liste noire) ou simplement incompétent (il clique un peu au hasard). Les signalements sont donc à prendre avec des pincettes (section 6 du RFC).
Date de publication du RFC : Juin 2017
Auteur(s) du RFC : J. Snijders, J. Heasley
(NTT), M. Schmidt (i3D.net)
Pour information
Réalisé dans le cadre du groupe de travail IETF grow
Première rédaction de cet article le 1 juillet 2017
Le RFC 8092 a normalisé la notion de « grande communauté » BGP, des données attachées aux annonces de routes, plus grandes que les précédentes « communautés » et permettant donc de stocker davantage d'informations. Ce nouveau RFC explique à quoi peuvent servir ces grandes communautés et donne des exemples d'utilisation. Un document concret, qui ravira les opérateurs réseaux.
Le RFC original sur les communautés BGP était le RFC 1997, et il était accompagné d'un RFC 1998 présentant des cas concrets d'utilisation. C'est la même démarche qui est faite ici, de faire suivre le document de normalisation, le RFC 8092, d'un document décrivant des utilisations dans le monde réel (avec des expériences pratiques décrites à NANOG ou NLnog).
Un petit rappel des grandes communautés, d'abord (section 2 du RFC). Chaque communauté se compose de trois champs de quatre octets chacun. Le premier champ se nomme GA, pour Global Administrator, et sa valeur est le numéro de l'AS qui a ajouté cette communauté (rappelez-vous que les numéros d'AS font désormais quatre octets), ce qui assure l'unicité mondiale des communautés. Les deux autres champs portent les noms peu imaginatifs de Local Data Part 1 et Local Data Part 2. La plupart du temps, on se sert du premier pour identifier une fonction, et du second pour identifier les paramètres d'une fonction. Notez qu'il n'existe pas de standard pour ces deux champs locaux : comme leur nom l'indique, chaque AS les affecte comme il veut. Une même valeur peut donc avoir des significations différentes selon l'AS.
Le RFC contient plusieurs exemples, qui utilisent à chaque fois la même topologie : un transitaire, l'AS 65551, avec un client, l'AS 64497, qui est lui-même transitaire pour les AS 64498 et 64499, qui par ailleurs ont une relation de peering (en épais sur l'image) :
Notre RFC classe les communautés en deux catégories : celles d'information et celles d'action. Les premières servent à distribuer des informations qui ne modifieront pas le comportement des routeurs, mais pourront être utiles pour le débogage, ou les statistiques. Un exemple est l'ajout d'une communauté pour indiquer dans quel pays on a appris telle route. Elles sont typiquement ajoutées par l'AS qui a défini ces informations, et il mettra donc son numéro d'AS dans le champ GA. Les secondes, les communautés d'action, servent à indiquer une action souhaitée. Elles sont définies par l'AS qui aura à agir, et mises dans le message BGP par un de ses pairs. Un exemple est une communauté pour indiquer à son pair quelle préférence on souhaite qu'il attribue à telle route.
La section 3 donne des exemples de communautés
d'information. Le premier est celui où l'AS 66497 marque les
routes qu'il a reçues avec une communauté qui indique le pays où
la route a été apprise (le code pays utilisé est celui de
ISO 3166-1). La fonction a reçu le numéro
1. Donc, la présence dans l'annonce
BGP de la communauté 64497:1:528
indique une
route apprise aux Pays-Bas, la communauté
64497:1:392
indique le
Japon, etc. Le même opérateur peut aussi
prévoir une fonction 2 pour indiquer la région (concept plus vaste
que le pays), en utilisant les codes M.49 :
64497:2:2
dit que la route a été apprise en
Afrique, 64497:2:150
en Europe et ainsi de suite. Rappelez-vous
bien que la signification d'une fonction (et donc des paramètres qui
suivent) dépend de l'AS (ou, plus rigoureusement, du champ GA). Il n'y a pas de standardisation des
fonctions. Si vous voyez une communauté
65551:2:2
, cela ne signifie pas forcément que
la route vient d'Afrique : l'AS 65551 peut utiliser la fonction 2
pour tout à fait autre chose.
À part l'origine géographique d'une route, il est souvent utile
de savoir si la route a été apprise d'un pair ou d'un
transitaire. Le RFC donne l'exemple d'une fonction 3 où le
paramètre 1 indique une route interne, 2 une route apprise d'un
client, 3 d'un pair et 4 d'un transitaire. Ainsi, la communauté
64497:3:2
nous dit que la route vient d'un
client de l'AS 64497.
Une même annonce de route peut avoir plusieurs communautés. Une
route étiquetée 64497:1:528
, 64497:2:150
et
64497:3:3
vient donc d'un pair aux Pays-Bas.
Et les communautés d'action (section 4 du
RFC) ? Un premier exemple est celui où on indique à son camarade
BGP de ne pas exporter inconditionnellement une route qu'on lui
annonce (ce qui est le comportement par défaut de BGP). Mettons
que c'est la fonction 4. (Vous noterez qu'il n'existe pas d'espace
de numérotation de fonctions distinct pour les communautés
d'information et d'action. On sait que la fonction 4 est d'action
uniquement si on connait la politique de l'AS en question.)
Mettons encore que l'AS 64497 a défini le paramètre comme étant un
numéro d'AS à qui il ne faut pas exporter la
route. Ainsi, envoyer à l'AS 64497 une route étiquetée
64497:4:64498
signifierait « n'exporte
pas cette route à l'AS 64498 ». De la même
façon, on peut imaginer une fonction 5 qui utilise pour cette
non-exportation sélective le code
pays. 64997:5:392
voudrait dire « n'exporte
pas cette route au Japon ». (Rappelez-vous que BGP est du routage
politique : son but principal est de permettre de faire respecter
les règles du business des opérateurs.)
Je me permets d'enfoncer le clou : dans les communautés d'action, l'AS qui ajoute une communauté ne met pas son numéro d'AS dans le champ GA, mais celui de l'AS qui doit agir.
Un autre exemple de communauté d'action serait un allongement
sélectif du chemin d'AS. On allonge le chemin d'AS en répétant le
même numéro d'AS lorsqu'on veut décourager l'utilisation d'une
certaine route. (En effet, un des facteurs essentiels d'une
décision BGP est la longueur du chemin d'AS : BGP préfère le plus
court.) Ainsi, l'exemple donné utilise la fonction 6 avec comme
paramètre l'AS voisin où appliquer cet allongement
(prepending). 64497:6:64498
signifie « ajoute ton AS quand tu exportes vers 64498 ».
Quand on gère un routeur BGP multihomé, influencer le trafic
sortant est assez simple. On peut par exemple mettre une
préférence plus élevée à la sortie vers tel
transitaire. Influencer le trafic entrant (par exemple si on veut
que le trafic vienne surtout par tel transitaire) est plus
délicat : on ne peut pas configurer directement la politique des
autres AS. Certains partenaires BGP peuvent permettre de définir
la préférence locale (RFC 4271, section
5.1.5), via une communauté. Ici, le RFC donne l'exemple d'une
fonction 8 qui indique « mets la préférence d'une route client [a
priori une préférence élevée] », 10 une route de
peering, 11 de transit
et 12 une route de secours, à n'utiliser qu'en dernier recours
(peut-être parce qu'elle passe par un fournisseur cher, ou bien de
mauvaise qualité). Le paramètre (le troisième champ de la
communauté) n'est pas utilisé. Alors, la communauté
64997:12:0
signifiera « AS 64997, mets à cette route la
préférence des routes de secours [a priori très basse] ».
Le RFC suggère aussi, si le paramètre est utilisé, de le
prendre comme indiquant la région où cette préférence spécifique
est appliquée. Par exemple 64997:10:5
demandera à l'AS 64997 de mettre la préférence des routes de
peering (10) à tous les routeurs en
Amérique du Sud (code 5 de la norme
M.49).
Attention, changer la préférence locale est une arme puissante, et sa mauvaise utilisation peut mener à des coincements (RFC 4264).
Dernier exemple donné par notre RFC, les souhaits exprimés à un
serveur de routes (RFC 7947). Ces serveurs parlent en
BGP mais ne sont pas des routeurs, leur seul rôle est de
redistribuer l'information aux routeurs. Par défaut, tout est
redistribué à tout le monde (RFC 7948), ce
qui n'est pas toujours ce qu'on souhaite. Si on veut une politique
plus sélective, certains serveurs de route documentent des
communautés que les clients du serveur peuvent utiliser pour
influencer la redistribution des routes. Notre RFC donne comme
exemple une fonction 13 pour dire « n'annonce pas » (alors que
cela aurait été le comportement normal) et une fonction 14 pour
« annonce quand même » (si le comportement normal aurait été de ne
pas relayer la route). Ainsi, 64997:13:0
est
« n'annonce pas cette route par défaut » et
64997:14:64999
est « annonce cette route à
l'AS 64999 ».
Vous voudriez des exemples réels ? Netnod avait annoncé que ces grandes communautés étaient utilisables sur leur AS 52005 :
52005:0:0
Do not announce to any peer52005:0:ASx
Do not announce to ASx52005:1:ASx
Announce to ASx if 52005:0:0 is set52005:101:ASx
Prepend peer AS 1 time to ASx52005:102:ASx
Prepend peer AS 2 times to ASx52005:103:ASx
Prepend peer AS 2 times to ASxOn trouve également ces communautés au IX-Denver, documentées ici (sous « Route Server B and C Communities ») ou au SIX (dans leur documentation). Il y a enfin cet exemple à ECIX. (Merci à Job Snijders et Pier Carlo Chiodi pour les exemples.)
Voilà, à vous maintenant de développer votre propre politique de communautés d'information, et de lire la documentation de vos voisins BGP pour voir quelles communautés ils utilisent (communautés d'action).
Date de publication du RFC : Août 2017
Auteur(s) du RFC : T. Burbridge, P. Eardley (BT), M. Bagnulo (Universidad Carlos III de Madrid), J. Schoenwaelder (Jacobs University Bremen)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF lmap
Première rédaction de cet article le 22 août 2017
Ce RFC est l'un de ceux du groupe de travail IETF nommé LMAP, pour « Large-Scale Measurement Platforms ». Ce groupe développe des normes pour les systèmes de mesure à grande échelle, ceux où des centaines ou des milliers de sondes quantifient l'Internet. Il décrit le modèle de données utilisé.
Le projet LMAP est décrit dans les RFC 7536 (qui explique à quoi sert LMAP, avec deux exemples concrets) et RFC 7594 (qui normalise le vocabulaire et le cadre général). Pour résumer très vite, LMAP prévoit un Measurement Agent (la sonde) qui parle à un contrôleur, qui lui dit quoi faire, et à un collecteur, qui ramasse les données. Ce nouveau RFC décrit le modèle de données abstrait utilisé dans ces deux communications.
Rappelons que, dans le cadre défini pour LMAP, chaque MA (Measurement Agent) parle à un et un seul contrôleur, qui lui dit ce qu'il doit mesurer. (LMAP sépare le rôle du contrôleur et du collecteur, rôles que beaucoup de systèmes déployés fusionnent.) Il y a donc trois protocoles en jeu, le protocole de contrôle, entre le contrôleur et les MA, le protocole de collecte, entre les collecteurs et les MA, et enfin les protocoles utilisés dans les tests (ICMP ECHO, par exemple, mais aussi HTTP, DNS…) Enfin, il y a aussi la configuration nécessaire dans tous ces équipements. Tous ces points peuvent faire l'objet d'un travail de normalisation, mais ce RFC 8193 présente uniquement un modèle de données adapté au protocole de collecte et au protocole de contrôle.
La notion de modèle de données est expliquée dans le RFC 3444. En gros, il s'agit d'un modèle abstrait (très abstrait) décrivant quel genre de données sont échangées. Il peut se formaliser de manière un peu plus concrète par la suite, par exemple en utilisant le langage YANG (c'est justement ce que fait le RFC d'accompagnement, le RFC 8194). Un tel modèle sert à guider la normalisation, à faciliter la traduction d'un protocole dans un autre s'ils utilisent le même modèle de données, et à définir les données que doit mesurer et conserver le MA.
Le modèle défini ici utilise une notation formelle, utilisant
des types de données classiques en programmation (section 3 du RFC) :
int
(un entier),
boolean
(une valeur
logique), string
, mais aussi des
types plus riches comme datetime
ou uri
.
Le modèle lui-même est dans la section 4 du RFC. Je rappelle qu'il suit le cadre du RFC 7594. Il a six parties :
Le MA peut également avoir d'autres informations, par exemple des détails sur son type de connectivité.
Pour donner des exemples concrets, je vais souvent citer les sondes RIPE Atlas, même si ce système, conçu avant le RFC, ne suis pas exactement ce modèle. C'est sans doute l'un des plus grands, voire le plus grand réseau de points de mesure dans le monde, avec près de 10 000 sondes connectées. Par exemple, pour la configuration, les informations de pré-configuration d'une sonde Atlas comprennent une adresse MAC et un identificateur, qu'on peut voir sur la sonde :
Les sondes sont également pré-configurées avec le nom du contrôleur (qui sert également de collecteur). Elles acquièrent le reste de leur configuration par DHCP et SLAAC. Regardons par exemple la sonde d'identificateur #21660, installée à Cochabamba, on voit les adresses IP obtenues :
Les sondes Atlas disposent également d'étiquettes (tags) décrivant plus précisement certaines de leurs caractéristiques. Certaines des étiquettes sont attribuées manuellement par leur propriétaire (en bleu sur l'image ci-dessous), d'autres, qui peuvent être déterminées automatiquement, sont mises par le système (en vert sur l'image), et rentrent donc dans le cadre de la partie « état général de la sonde » du modèle :
Les classes utilisées dans le modèle sont, entre autres :
Première partie, la pré-configuration (section 4.1). On y trouve les informations dont le MA aura besoin pour accomplir sa mission. Au minimum, il y aura un identificateur (par exemple l'adresse MAC), les coordonnées de son contrôleur (par exemple un nom de domaine, mais le RFC ne cite que le cas où ces coordonnées sont un URL, pour les protocoles de contrôle de type REST), peut-être des moyens d'authentification (comme le certificat de l'AC qui signera le certificat du contrôleur). URL et moyens d'authentification, ensemble, forment un canal de communication.
Dans le langage formel de ce modèle, cela fait :
object { [uuid ma-preconfig-agent-id;] ma-task-obj ma-preconfig-control-tasks<1..*>; ma-channel-obj ma-preconfig-control-channels<1..*>; ma-schedule-obj ma-preconfig-control-schedules<1..*>; [uri ma-preconfig-device-id;] credentials ma-preconfig-credentials; } ma-preconfig-obj;
Cela se lit ainsi : l'information de pré-configuration (type
ma-preconfig-obj
) comprend
divers attributs dont un identificateur (qui est un
UUID, on a vu que les Atlas utilisaient un
autre type d'identificateurs), des canaux de communication avec le
contrôleur, et des lettres de créance à présenter pour
s'authentifier. Notez que l'identificateur est optionnel (entre
crochets).
Vous avez compris le principe ? On peut passer à la suite plus rapidement (je ne vais pas répéter les déclarations formelles, voyez le RFC). La configuration (section 4.2) stocke les informations volatiles (la pré-configuration étant consacrée aux informations plus stables, typiquement stockées sur une mémoire permanente).
Les instructions (section 4.3) sont les tâches que va accomplir la sonde, ainsi que l'agenda d'exécution, et bien sûr les canaux utilisé pour transmettre les résultats. La journalisation (section 4.4) permet d'enregistrer les problèmes (« mon résolveur DNS ne marche plus ») ou les évènements (« le courant a été coupé »).
Il ne sert évidemment à rien de faire des mesures si on ne transmet pas le résultat. D'où la section 4.6, sur la transmission des résultats de mesure au collecteur. La communication se fait via les canaux décrits en section 4.8.
Une dernière photo, pour clore cet article, une sonde RIPE Atlas en fonctionnement, et une autre sonde, la SamKnows :
Merci à André Sintzoff pour avoir trouvé de grosses fautes.
Date de publication du RFC : Juin 2017
Auteur(s) du RFC : R. Bonica (Juniper
Networks), M. Cotton
(ICANN), B. Haberman (Johns Hopkins
University), L. Vegoda (ICANN)
Première rédaction de cet article le 28 juin 2017
Le RFC 6890 avait créé un registre unique des préfixes d'adresses IP « spéciaux ». Ce nouveau RFC met à jour le RFC 6890, précisant et corrigeant quelques points.
Un préfixe « spécial » est juste un préfixe
IP dédié à un usage inhabituel, et qui peut
nécessiter un traitement particulier. Ainsi, les préfixes
203.0.113.0/24
et
2001:db8::/32
sont dédiés à la documentation,
aux cours et exemples et, bien que n'étant pas traités
différemment des autres préfixes par les routeurs, ne doivent normalement pas
apparaitre dans un vrai réseau. Le RFC 6890
spécifie les deux registres de préfixes spéciaux, un
pour IPv4 et un
pour IPv6. Mais il y avait une ambiguité dans ce RFC, dans
l'usage du terme « global ». Il n'est pas
évident à traduire en français. Dans le langage courant,
global/globalement, a surtout le sens de « le plus
souvent/généralement », alors que, dans le contexte de l'Internet,
ce serait plutôt dans le sens « valable pour tout point du
globe ». Même en anglais, le
terme est polysémique : il peut signifier que
l'adresse IP est unique au niveau mondial ou bien il peut
signifier que l'adresse est potentiellement joignable
mondialement, et qu'un routeur peut donc faire suivre le paquet en
dehors de son propre domaine. (La joignabilité effective, elle,
dépend évidemment de l'état de BGP.) Le
RFC 4291, dans sa section 2.5.4, n'est pas
plus clair.
Reprenons l'exemple des préfixes de documentation. Dans les trois
préfixes IPv4 de documentation (comme
203.0.113.0/24
), les adresses ne sont pas
uniques (chacun les alloue comme il veut et, vu le faible nombre
d'adresses total, il y aura certainement des collisions) et, de
toute façon, n'ont pas de signification en dehors d'un domaine :
cela n'a pas de sens de transmettre des paquets ayant de telles
adresses à ses partenaires. Pour le préfixe de documentation IPv6,
2001:db8::/32
, c'est un peu plus compliqué
car les adresses peuvent être quasi-uniques mondialement (si on
prend la précaution de les tirer au sort dans le préfixe) mais, de
toute façon, n'ont pas vocation à être joignables de partout et on
ne transmet donc pas ces paquets à ses voisins.
Bref, notre nouveau RFC choisit d'être plus précis, renommant l'ancien booléen « global » du RFC 6890 en « globally reachable ».
La nouvelle définition (section 2) est donc bien « le paquet ayant cette adresse comme destination peut être transmis par un routeur à un routeur d'un autre domaine (d'un autre opérateur, en pratique) ». La colonne Global du registre est donc remplacée par Globally reachable. Notez qu'il n'y a pas de colonne « Unicité » (une autre définition possible de global).
Cela a nécessité quelques changements supplémentaires dans le registre (toujours section 2), colonne Globally reachable :
Date de publication du RFC : Septembre 2017
Auteur(s) du RFC : J. Reschke (greenbytes)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 14 septembre 2017
Dernière mise à jour le 16 septembre 2017
Les requêtes et réponses du protocole HTTP incluent des
en-têtes (comme
User-Agent:
ou
Content-Disposition:
) avec des
valeurs, qui, il y a longtemps, ne pouvaient se représenter
directement qu'avec les caractères du jeu ISO
8859-1, voire seulement avec
ASCII (c'était compliqué). Comme MIME, dans le RFC 2231,
prévoyait un mécanisme très riche pour encoder les en-têtes du
courrier électronique, ce RFC 8187
réutilise ce mécanisme pour HTTP (il remplace le RFC 5987, qui avait été le premier à le faire). Pour le corps du
message (voir par exemple le RFC 7578), rien
ne change.
Cette ancienne restriction à Latin-1 (qui n'est plus d'actualité) vient de la norme HTTP, le RFC 2616, dans sa section 2.2, qui imposait l'usage du RFC 2047 pour les caractères en dehors de ISO 8859-1. Le RFC 7230 a changé cette règle depuis (sa section 3.2) mais pas dans le sens d'une plus grande internationalisation (ISO 8859-1 ne convient qu'aux langues européennes), plutôt en supprimant le privilège d'ISO 8859 et en restreignant à ASCII. Et il ne précise pas vraiment comment faire avec d'autres jeux de caractère comme Unicode. Il ne reste donc que la solution du RFC 2231.
Notre nouveau RFC peut être résumé en disant qu'il spécifie un profil du RFC 2231. Ce profil est décrit en section 3, qui liste les points précisés par rapport au RFC 2231. Tout ce RFC n'est pas utilisé, ainsi le mécanisme en section 3 du RFC 2231, qui permettait des en-têtes de plus grande taille, n'est pas importé (section 3.1 de notre RFC).
En revanche, la section 4 du RFC 2231, qui spécifiait
comment indiquer la langue dans laquelle était
écrite la valeur d'un en-tête est repris pour les paramètres dans les
en-têtes. Ainsi, (section 3.2), voici un en-tête (imaginaire :
Information:
n'a pas été enregistré), avec un paramètre
title
traditionnel en pur ASCII :
Information: news; title=Economy
et en voici un avec les possibilités de notre RFC pour permettre les caractères £ et € (« Sterling and euro rates ») :
Information: news; title*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates
Par rapport au RFC 2231 (qui était silencieux sur ce point), un encodage de caractères est décrété obligatoire (c'est bien sûr UTF-8), et il doit donc être géré par tous les logiciels. La mention de l'encodage utilisé est également désormais obligatoire (section 3.2 de notre RFC). La langue elle-même est indiquée par une étiquette, selon la syntaxe du RFC 5646. Du fait de ces possibilités plus riches que celles prévues autrefois pour HTTP, les paramètres qui s'en servent doivent se distinguer, ce qui est fait avec un astérisque avant le signe égal (voir l'exemple ci-dessus). Notez que l'usage de l'astérisque n'est qu'une convention : si on trouve un paramètre inconnu dont le nom se termine par un astérisque, on ne peut pas forcément en déduire qu'il est internationalisé.
La valeur du paramètre inclut donc le jeu de caractères et l'encodage (obligatoire), la langue (facultative, elle n'est pas indiquée dans l'exemple ci-dessus) et la valeur proprement dite.
Voici un exemple incluant la langue, ici
l'allemand (code de
, la
phrase est « Mit der Dummheit kämpfen Götter
selbst vergebens », ou « contre la bêtise, les dieux
eux-mêmes luttent en vain », tirée de la pièce « La pucelle
d'Orléans ») :
Quote: theater; sentence*=UTF-8'de'Mit%20der%20Dummheit%20k%C3%A4mpfen%20G%C3%B6tter%20selbst%20vergebens.
La section 4 couvre ensuite les détails pratiques pour les normes qui décrivent un en-tête qui veut utiliser cette possibilité. Par exemple, la section 4.2 traite des erreurs qu'on peut rencontrer en décodant et suggère que, si deux paramètres identiques sont présents, celui dans le nouveau format prenne le dessus. Par exemple, si on a :
Information: something; title="EURO exchange rates"; title*=utf-8''%e2%82%ac%20exchange%20rates
le titre est à la fois en ASCII pur et en UTF-8, et c'est cette
dernière version qu'il faut utiliser, même si normalement il n'y a
qu'un seul paramètre title
.
Ces paramètres étendus sont mis en œuvre dans Firefox et Opera ainsi que, dans une certaine mesure, dans Internet Explorer.
Plusieurs en-têtes HTTP se réfèrent formellement à cette façon d'encoder les caractères non-ASCII :
Authentication-Control:
, dans le RFC 8053 (« For example, a parameter "username" with
the value "Renee of France" SHOULD be sent as username="Renee of
France". If the value is "Renée of France", it SHOULD be sent as
username*=UTF-8''Ren%C3%89e%20of%20France instead »),Authorization:
(pour l'authentification
HTTP, RFC 7616, avec également un paramètre
username
pour l'ASCII et
username*
pour l'encodage défini dans ce RFC),Content-Disposition:
, RFC 6266,
qui indique sous quel nom enregistrer un fichier et dont le
paramètre filename*
permet tous les caractères Unicode,Link:
, normalisé dans le RFC 5988, où le paramètre title*
permet des
caractères non-ASCII (title
étant pour l'ASCII pur).Les changements depuis le RFC 5987, sont expliqués dans l'annexe A. Le plus spectaculaire est le retrait d'ISO 8859-1 (Latin-1) de la liste des encodages qui doivent être gérés obligatoirement par le logiciel. Cela fera plaisir aux utilisateurs d'Internet Explorer 9, qui avait déjà abandonné Latin-1. Autrement, rien de crucial dans ces changements. Le texte d'introduction a été refait pour mieux expliquer la situation très complexe concernant la légalité (ou pas) des caractères non-ASCII dans les valeurs d'en-tête.
Si vous voulez voir un exemple, essayez de télécharger le fichier
http://www.bortzmeyer.org/files/foobar.txt
. Si
votre client HTTP gère l'en-tête
Content-Disposition:
et le paramètre
internationalisé filename*
, le fichier devrait
être enregistré sous le nom föbàr.txt
.La
configuration d'Apache pour envoyer le
Content-Disposition:
est :
<Files "foobar.txt"> Header set Content-Disposition "attachment; filename=foobar.txt; filename*=utf-8''f%%C3%%B6b%%C3%%A0r.txt" </Files>
Par exemple, Safari ou Firefox enregistrent bien ce fichier sous son nom international.
Ah, et puisque ce RFC parle d'internationalisation, on notera que c'est le premier RFC (à part quelques essais ratés au début) à ne pas comporter que des caractères ASCII. En effet, suivant les principes du RFC 7997, il comporte cinq caractères Unicode : dans les exemples (« Extended notation, using the Unicode character U+00A3 ("£", POUND SIGN) » et « Extended notation, using the Unicode characters U+00A3 ("£", POUND SIGN) and U+20AC ("€", EURO SIGN) »), dans l'adresse (« Münster, NW 48155 ») et dans les noms des contributeurs (« Thanks to Martin Dürst and Frank Ellermann »).
Date de publication du RFC : Mai 2017
Auteur(s) du RFC : Scott Bradner (Harvard
University), Jorge Contreras (University of
Utah)
Première rédaction de cet article le 1 juin 2017
L'appropriation intellectuelle est partout et donc logiquement aussi dans les organismes de normalisation comme l'IETF. C'est l'objet de ce RFC, qui remplace les RFC 3979 et RFC 4879 sur les questions de brevets (nommées, à tort, questions de « propriété intellectuelle », alors que les brevets ne sont pas la même chose que les copyrights, traités dans le RFC 5378).
Donc, sur quels principes repose la politique de l'IETF au sujet des brevets ? L'idée de base est de s'assurer que l'IETF disposera d'information sur les brevets pouvant s'appliquer à une norme donnée, de façon à pouvoir prendre une décision en toute connaissance de cause. Il n'y a par contre pas de mécanisme automatique de décision, par exemple « Ne jamais normaliser des technologies brevetées ». En effet, compte-tenu du fait que l'écrasante majorité des brevets logiciels est futile, enregistrée uniquement parce que les organismes de brevetage ont un intérêt financier à accepter tout et n'importe quoi, une telle politique mènerait à ne rien pouvoir normaliser.
En pratique, tout ce RFC 8179 pourrait donc se résumer à « Tout participant à l'IETF qui connait ou devrait connaitre un brevet pouvant s'appliquer à une technique en cours de discussion doit en informer l'IETF ». C'est tout. Mais il y a quelques détails pratiques.
D'abord, il faut rappeler que ce sont officiellement des individus qui participent à l'IETF, pas des sociétés. Donc l'obligation s'applique à ces individus et ils ne peuvent pas y échapper en prétendant que leur compagnie leur interdit de réveler un brevet sous-marin (brevet sur lequel on fait peu de publicité, pour le ressortir une fois que la technique brevetée a été largement adoptée). Ensuite, le RFC définit ce que signifie contribuer à l'IETF (section 1). Par exemple, écrire sur une liste de diffusion d'un groupe de travail est une contribution. Cette règle est régulièrement rappelée par le fameux Note Well.
La section 1 définit formellement bien d'autres choses. Un concept essentiel mais souvent oublié est le Reasonably and personally known. Il désigne une information que le participant connait ou devrait connaitre, vu sa position dans l'entreprise qui l'emploie. L'idée est que le participant IETF n'est pas obligé de chercher activement dans le portefeuille de brevets de son entreprise, que l'obligation ne s'applique qu'à ce qu'il connait forcément, depuis son poste. Le but de l'ajout reasonably est d'éviter qu'une entreprise ne dissimule un brevet à ses propres employés.
Les principes sont donc :
La section 3 rentre dans le concret, même si elle commence par un bel exercice de langue de bois (« The intent is to benefit the Internet community and the public at large, while respecting the legitimate rights of others. »). C'est elle qui impose que le contributeur à l'IETF ait bien divulgué tous les brevets qu'il connaissait « raisonnablement ». Outre le brevet lui-même, il peut y avoir une licence associée (un droit d'utiliser la technologie brevetée, sous certaines conditions). Si le détenteur du brevet n'indique pas de licence, l'IETF peut poliment lui demander. La licence (RAND, FRAND, RANDZ - c'est-à-dire gratuite …) sera évidemment un des éléments sur lesquels les participants à l'IETF fonderont leur position (cf. RFC 6410).
La section 4
indique ce que l'IETF va en faire, de ces divulgations (nommées
« IPR [Intellectual Property Rights] disclosures ») : indication
du fait qu'il existe des brevets pouvant s'y appliquer et
publication de ces divulgations en http://www.ietf.org/ipr/
. Par exemple,
Verisign a un brevet (brevet
états-unien 8,880,686,
et la
promesse de licence de Verisign)
qu'ils prétendent valable,
et dont ils affirment qu'il couvre la technique décrite dans le RFC 7816. (Sur la page officielle du
RCF, c'est le lien « Find IPR Disclosures from the IETF ».) L'IETF (ou
l'IAB, ou l'ISOC ou
autre) n'ajoutera aucune
appréciation sur la validité du brevet, ou sur les conditions de
licence. Une telle appréciation nécessiterait en effet
un long et coûteux travail juridique.
La note d'information n'est plus à inclure dans chaque RFC comme c'était autrefois le cas. Elle est désormais dans le IETF Trust Legal Provisions (version de 2015 : « The IETF Trust takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in any IETF Document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. »). Comme exemple d'un brevet abusif, on peut citer la divulgation #1154, qui se réclame d'un brevet sur les courbes elliptiques qui s'appliquerait à tous les RFC parlant d'un protocole qui peut utiliser ces courbes, comme le RFC 5246.
Les divulgations ne sont pas incluses dans les RFC eux-mêmes
(section 10) car elles peuvent évoluer dans le temps alors que le RFC
est stable. Il faut donc aller voir ces « IPR
disclosures » en ligne sur http://www.ietf.org/ipr/
.
Les divulgations sont-elles spécifiées plus en détail ? Oui, en
section 5. La 5.1 précise qui doit faire la
divulgation (le participant, en tant que personne physique), la
section 5.2 donne les délais (« aussi vite que possible »), la section
5.4.3 rappelle que la divulgation doit être précise et qu'un
contributeur ne peut pas se contenter de vagues généralités
(« blanket disclosure »). Le tout
est aussi mis en ligne, en http://www.ietf.org/ipr-instructions
.
Et si un tricheur, comme la société RIM, ne respecte pas cette obligation de divulgation ? La section 6 ne prévoit aucune dérogation : si, par exemple, une société empêche ses employés de divulguer les brevets, ces employés ne doivent pas participer à l'IETF (« tu suis les règles, ou bien tu ne joues pas »). Tout participant à l'IETF est censé connaitre cette règle (section 3.3). Le RFC 6701 liste les sanctions possibles contre les tricheurs et le RFC 6702 expose comment encourager le respect des règles.
Bien, donc, arrivé là, l'IETF a ses informations et peut prendre ses décisions. Sur la base de quelles règles ? La section 7 rappelle le vieux principe qu'une technique sans brevets est meilleure ou, sinon, à la rigueur, une technique où le titulaire des brevets a promis des licences gratuites. Mais ce n'est pas une obligation, l'IETF peut choisir une technologie brevetée, même sans promesses sur la licence, si cette technologie en vaut la peine.
La seule exception concerne les techniques de sécurité obligatoires : comme tout en dépend, elles ne doivent être normalisées que s'il n'existe pas de brevet ou bien si la licence est gratuite.
Les règles de bon sens s'appliquent également : s'il s'agit de faire une nouvelle version normalisée d'un protocole très répandu, on évitera de choisir une technologie trop encombrée de brevets, s'il s'agit d'un tout nouveau protocole expérimental, on pourra être moins regardant.
Les changements depuis les RFC précédents, les RFC 3979 et RFC 4879, sont décrits dans la section 13. Pas de révolution, les principes restent les mêmes. Parmi les changements :
Date de publication du RFC : Mai 2017
Auteur(s) du RFC : B. Leiba (Huawei)
Première rédaction de cet article le 19 mai 2017
Dernière mise à jour le 20 mai 2017
Un très court RFC discutant un problème de procédure : dans le RFC 2119, qui décrit les termes précis à utiliser dans les normes, un doute subsistait sur la casse de ces termes.
Ce RFC 2119 est celui qui formalise les fameux MUST, SHOULD et MAY, les termes qu'il faut utiliser dans les normes pour être sûr d'indiquer le niveau exact d'exigence. Suivant l'exemple du RFC 2119, ils sont toujours écrits en CAPITALES pour les distinguer du sens courant en anglais, mais cet usage n'était pas explicite dans le RFC 2119 (qui a juste un vague « These words are often capitalized »). Un oubli que corrige notre RFC 8174. Désormais, MUST n'a le sens du RFC 2119 que s'il est en capitales.
Par exemple, dans le RFC 8120, dans le texte « The client SHOULD try again to construct a req-KEX-C1 message in this case », SHOULD est en capitales et a donc bien le sens précis du RFC 2119 (le client est censé ré-essayer de faire son message, sauf s'il a une très bonne raison), alors que dans le texte « This case should not happen between a correctly implemented server and client without any active attacks », should est en minuscules et a donc bien son sens plus informel qui est usuel en anglais.
Le texte qu'il est recommandé d'inclure dans les RFC qui font référence au RFC 2119 apporte désormais cette précision : « The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119],[RFC 8174] when, and only when, they appear in all capitals, as shown here. » Plusieurs auteurs de RFC, conscients de l'ambiguité, avaient d'ailleurs déjà fait une telle modification dans leur référence au RFC 2119. Ainsi, le RFC 5724 dit « The _capitalized_ [souligné par moi] key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119]. »
Notez que le fait de faire une différence sémantique entre le mot en minuscules et le mot en capitales est assez typique des utilisateurs de l'alphabet latin, et déroute toujours beaucoup les utilisateurs d'écritures qui n'ont pas cette distinction, comme les Coréens.
Date de publication du RFC : Mai 2017
Auteur(s) du RFC : D. Thaler (Microsoft)
Pour information
Première rédaction de cet article le 18 mai 2017
L'Internet existe depuis de nombreuses années (le nombre exact dépend de la façon dont on compte…) et, pendant tout ce temps, les protocoles utilisés ne sont pas restés identiques à eux-mêmes. Ils ont évolué, voire ont été remplacés. Cela soulève un problème : la transition entre l'ancien et le nouveau (le cas le plus fameux étant évidemment le passage d'IPv4 à IPv6…) Beaucoup de ces transitions se sont mal passées, parfois en partie car l'ancien protocole ou l'ancienne version n'avait pas prévu son futur remplacement. Contrairement à ce qu'espèrent souvent les techniciens, il ne suffit pas d'incrémenter le numéro de version pour que tous les acteurs adoptent la nouvelle version. Ce nouveau RFC de l'IAB raconte les leçons tirées, et regarde comment on pourrait améliorer les futures transitions.
Ce RFC se focalise sur les transitions techniques. Ce ne sont évidemment pas les seules (il y a par exemple des transitions organisationnelles) mais ce sont celles qui comptent pour l'IAB et l'IETF. Une transition peut être aussi bien le déploiement d'un tout nouveau protocole, que le passage d'un protocole d'une version à une autre. Le thème de la transition d'un protocole à l'autre est fréquent, et de nombreux RFC ont déjà été consacrés à une transition. C'est le cas de :
Outre les transitions à proprement parler, l'IAB s'est déjà penché sur les principes qui faisaient qu'un protocole pouvait marcher ou pas. C'est notamment le cas de l'excellent RFC 5218 qui étudie les facteurs qui font d'un protocole un échec, un succès, ou un succès fou. Parmi les leçons tirées par ce RFC 5218, les concepteurs d'un protocole devraient s'assurer que :
Le RFC 7305 discutait également des aspects économiques de la transition et notait l'importance de donner une carotte aux premiers à adopter le nouveau protocole, ceux qui font un pari risqué. C'est pour cela qu'il est parfaitement légitime que les premiers à avoir cru dans Bitcoin aient reçu une quantité importante de bitcoins à un prix très faible. Cette décision était une des meilleures prises par Satoshi Nakamoto. Ce RFC note aussi l'importance d'un partenariat avec des organisations qui peuvent aider ou contrarier la transition (comme les RIR ou l'ICANN).
La section 2 de notre RFC rappelle que, de toute façon, le terme « transition » risque d'être mal interprété. Il n'est plus possible depuis longtemps de faire un flag day dans l'Internet, un jour J où on change toutes les machines d'un coup de manière coordonnée. Les transitions sont donc forcément longues, avec une période de co-existence entre l'ancien et le nouveau.
Si l'ancien et le nouveau protocole ne peuvent pas interopérer directement (cas d'IPv4 et d'IPv6), il faudra parfois envisager un mécanisme de traduction (qui ne se situera pas forcément dans la même couche). Un tel traducteur, s'il est situé sur le chemin entre les deux machines, pose souvent d'ennuyeux problèmes de sécurité car il risque fort de casser le modèle de bout en bout.
La section 5 de notre RFC est consacrée aux plans de transition. Ah, les plans… Ils sont évidemment indispensables (on ne va pas se lancer dans une grande transition sans avoir planifié un minimum) mais ils sont aussi très fragiles (comme disent les militaires, « aucun plan ne survit à la première rencontre avec l'ennemi »), et ils terminent souvent au musée des mauvaises idées. Disons qu'il faut avoir un plan, mais ne pas en être esclave.
Quelles sont les qualités d'un bon plan de transition, en s'appuyant sur les expériences ratées et réussies ? D'abord, il faut bien connaitre l'existant. Par exemple, si l'ancien protocole a une fonction optionnelle qui n'a pas d'équivalent, ou un équivalent très différent dans le nouveau protocole, il est bon de savoir si cette fonction est utilisée en pratique (elle peut même ne pas être implémentée du tout, ce qui facilite les choses). De même, il est important de savoir si les logiciels existants mettent réellement en œuvre l'ancien protocole tel qu'il est spécifié, ou bien si, en pratique, ils en dévient, et ont des comportements qui vont poser des problèmes pendant la transition. (Un cas typique est celui de SSL où la plupart des programmes n'avaient pas mis en œuvre correctement le mécanisme de négociation, et plantaient donc lorsqu'une nouvelle version arrivait.)
Un autre élément important d'un plan de transition est d'avoir les idées claires sur les incitations à migrer. Les acteurs de l'Internet utilisent l'ancien protocole. Ça marche pour eux. Pourquoi feraient-ils l'effort de migrer vers un nouveau protocole, ce qui leur coûtera du temps et de l'argent ? Il faut donc des incitations (ou du marketing, qui arrive souvent à faire acheter un gadget inutile). Il n'y a pas que les coûts financiers directs, il faut aussi regarder d'autres problèmes à surmonter (par exemple l'hostilité de certains acteurs, ainsi le chiffrement a du mal à se répandre car les acteurs de l'Internet qui font de la surveillance ont intérêt à continuer à violer la vie privée).
Il y a ensuite le plan proprement dit : une liste des étapes, avec un vague calendrier. Le calendrier est certainement la partie la plus fragile du plan ; l'Internet n'ayant pas de chef, une transition va dépendre des efforts d'un grand nombre d'acteurs non coordonnés, et prédire leurs délais de réaction est à peu près impossible. (Voir le RFC 5211 pour un exemple.)
Un bon plan doit aussi comprendre un moyen de déterminer le succès (ou l'échec). Là aussi, ce n'est pas évident du tout. Certains protocoles sont surtout utilisés dans des réseaux locaux, donc difficiles à mesurer de l'extérieur (comment savoir combien de FAI proposent un résolveur DNS sécurisé par le RFC 7858 ?) Parfois, les critères quantitatifs ne sont pas évidents à établir. Prenons l'exemple d'IPv6 (lisez à ce sujet le rapport de l'ARCEP sur la transition IPv6, qui traite la question en détail). Comment mesure-t-on le succès d'IPv6 ? Le pourcentage de sites Web du Top N d'Alexa qui a une adresse IPv6 ? Le pourcentage d'utilisateurs finaux qui a IPv6 ? Le pourcentage d'octets IPv6 vs. IPv4 ? (Et où ? Chez Google ? Sur un point d'échange comme le France-IX ? Sur le réseau d'un transitaire ? Les valeurs seront très différentes.)
On l'a dit, les plans, même les meilleurs, survivent rarement à la rencontre avec le monde réel. Il faut donc un (ou plusieurs) « plan B », une solution de secours. Souvent, de facto, la solution de secours est la coexistence permanente de l'ancien et du nouveau protocole…
Et puis bien des acteurs de l'Internet ne suivent pas attentivement ce que fait l'IETF, voire ignorent complètement son existence, ce qui ajoute un problème supplémentaire : il faut communiquer le plan, et s'assurer qu'il atteint bien tous les acteurs pertinents (tâche souvent impossible). C'est le but d'opérations de communication comme le World IPv6 Launch Day.
Notre RFC rassemble ensuite (annexe A) quatre études de cas, illustrant des problèmes de transition différents. D'abord, le cas d'ECN. Ce mécanisme, normalisé dans le RFC 3168, permettait aux routeurs de signaler aux machines situées en aval de lui que la congestion menaçait. L'idée est que la machine aval, recevant ces notifications ECN, allait dire à la machine émettrice, située en amont du routeur, de ralentir, avant qu'une vraie congestion n'oblige à jeter des paquets. Les débuts d'ECN, vers 2000-2005, ont été catastrophiques. Les routeurs, voyant apparaitre des options qu'ils ne connaissaient pas, ont souvent planté. C'est un cas typique où une possibilité existait (les options d'IPv4 étaient normalisées depuis le début) mais n'était pas correctement implémentée en pratique. Toute transition qui se mettait à utiliser cette possibilité allait donc se passer mal. Pour protéger les routeurs, des pare-feux se sont mis à retirer les options ECN, ou bien à jeter les paquets ayant ces options, rendant ainsi très difficile tout déploiement ultérieur, même après correction de ces sérieuses failles dans les routeurs.
À la fin des années 2000, Linux et Windows ont commencé à accepter l'ECN par défaut (sans toutefois le réclamer), et la présence d'ECN, mesurée sur le Top Million d'Alexa, a commencé à grimper. De quasiment zéro en 2008, à 30 % en 2012 puis 65 % en 2014. Bref, ECN semble, après un très long purgatoire, sur la bonne voie (article « Enabling Internet-Wide Deployment of Explicit Congestion Notification »).
(Un autre cas, non cité dans le RFC, où le déploiement d'une possibilité ancienne mais jamais testé, a entrainé des conséquences fâcheuses, a été celui de BGP, avec la crise de l'attribut 99.)
L'exemple suivant du RFC est celui
d'IDN. L'internationalisation
est forcément un sujet chaud, vu les sensibilités existantes. Les
IDN résolvent enfin un problème très ancien, l'impossibilité
d'avoir des noms de
domaine dans toutes les écritures du monde. (Voir la
section 3 du RFC 6055, pour la longue et
compliquée histoire des IDN.) Une fois que la norme IDN était
disponible, il restait à effectuer la transition. Elle n'est pas
encore terminée aujourd'hui. En effet, de nombreuses applications
manipulent les noms de domaine et doivent potentiellement être
mises à jour. Bien sûr, elles peuvent toujours utiliser la forme
Punycode, celle-ci est justement conçue
pour ne pas perturber les applications traditionnelles, mais ce
n'est qu'un pis-aller (ஒலிம்பிக்விளையாட்டுகள்.சிங்கப்பூர்
est
quand même supérieur à xn--8kcga3ba7d1akxnes3jhcc3bziwddhe.xn--clchc0ea0b2g2a9gcd
).
Pire, IDN a connu une transition dans la transition, lors du passage de la norme IDN 2003 (RFC 3490) vers IDN 2008 (RFC 5890). IDN 2008 était conçu pour découpler IDN d'une version particulière d'Unicode mais l'un des prix à payer était le cassage de la compatibilité : certains caractères comme le ß étaient traités différemment entre IDN 2003 et IDN 2008.
Le cas d'IDN est aussi l'occasion, pour le RFC, de rappeler que tout le monde n'a pas forcément les mêmes intérêts dans la transition. IDN implique, outre l'IETF, les auteurs de logiciels (par exemple ceux des navigateurs), les registres de noms de domaine, les domaineurs, et bien sûr les utilisateurs. Tous ne sont pas forcément d'accord et le blocage d'une seule catégorie peut sérieusement retarder une transition (diplomatiquement, le RFC ne rappele pas que l'ICANN a longtemps retardé l'introduction d'IDN dans la racine du DNS, pour des pseudo-raisons de sécurité, et que leur introduction n'a pu se faire qu'en la contournant.)
Lorsqu'on parle transition douloureuse, on pense évidemment tout de suite à IPv6. Ce successeur d'IPv4 a été normalisé en 1995 (par le RFC 1833), il y a vingt-deux ans ! Et pourtant, il n'est toujours pas massivement déployé. (Il existe de nombreuses métriques mais toutes donnent le même résultat : IPv6 reste minoritaire, bien que ces dernières années aient vu des progrès certains. Notez que les réseaux visibles publiquement ne sont qu'une partie de l'Internet : plusieurs réseaux internes, par exemple de gestion d'un opérateur, sont déjà purement IPv6.) Il y avait pourtant un plan de transition détaillé (RFC 1933), fondé sur une coexistence temporaire où toutes les machines auraient IPv4 et IPv6, avant qu'on ne démantèle progressivement IPv4. Mais il a clairement échoué, et ce problème est maintenant un sujet de plaisanterie (« l'année prochaine sera celle du déploiement massif d'IPv6 », répété chaque année).
Là encore, un des problèmes était que tout le monde n'a pas les mêmes intérêts. Si les fabricants de routeurs et les développeurs d'applications bénéficient d'IPv6, c'est beaucoup moins évident pour les gérants de sites Web, ce qui explique que plusieurs sites à forte visibilité, comme Twitter, ou bien gérés par des gens pourtant assez branchés sur la technique, comme GitHub, n'aient toujours pas IPv6 (c'est également le cas de la totalité des sites Web du gouvernement français, qui pourtant promeut officiellement l'usage d'IPv6).
L'effet réseau a également joué à fond contre IPv6 : les pionniers n'ont aucune récompense, puisqu'ils seront tout seuls alors que, par définition, le réseau se fait à plusieurs. Bien sûr, IPv6 marche mieux que l'incroyable et branlante pile de techniques nécessaire pour continuer à utiliser IPv4 malgré la pénurie (STUN, TURN, port forwarding, ICE, etc). Mais tout le monde ne ressent pas ce problème de la même façon : le FAI, par exemple, ne supporte pas les coûts liés à la non-transition, alors qu'il paierait ceux de la transition. Ce problème de (non-)correspondance entre les coûts et les bénéfices est celui qui ralentit le plus les nécessaires transitions. Et puis, pour les usages les plus simples, les plus Minitel 2.0, IPv4 et ses prothèses marchent « suffisamment ».
La lenteur de la transition vers IPv6 illustre aussi la difficulté de nombreux acteurs à planifier à l'avance. C'est seulement lorsque l'IANA, puis les RIR sont l'un après l'autre tombés à court d'adresses IPv4 que certains acteurs ont commencé à agir, alors que le problème était prévu depuis longtemps.
Il n'y a évidemment pas une cause unique à la lenteur anormale de la transition vers IPv6. Le RFC cite également le problème de la formation : aujourd'hui encore, dans un pays comme la France, une formation de technicien ou d'ingénieur réseaux peut encore faire l'impasse sur IPv6.
Le bilan du déploiement d'IPv6 est donc peu satisfaisant. Si certains réseaux (réseaux internes d'entreprises, réseaux de gestion) sont aujourd'hui entièrement IPv6, le déploiement reste loin derrière les espérances. Ce mauvais résultat nécessite de penser, pour les futurs déploiements, à aligner les coûts et les bénéfices, et à essayer de fournir des bénéfices incrémentaux (récompenses pour les premiers adoptants, comme l'a fait avec succès Bitcoin).
Dernier cas de transition étudié par notre RFC, HTTP/2 (RFC 7540). Nouvelle version du super-populaire protocole HTTP, elle vise à améliorer les performances, en multiplexant davantage, et en comprimant les en-têtes (RFC 7541). HTTP/2 a vécu la discussion classique lors de la conception d'une nouvelle version, est-ce qu'on résout uniquement les problèmes les plus sérieux de l'ancienne version ou bien est-ce qu'on en profite pour régler tous les problèmes qu'on avait laissés ? HTTP/2 est très différent de HTTP/1. Ses règles plus strictes sur l'utilisation de TLS (algorithmes abandonnés, refus de la renégociation, par exemple) ont d'ailleurs entrainé quelques problèmes de déploiement.
Il y a même eu la tentation de supprimer certaines fonctions de HTTP/1 considérées comme inutiles ou néfastes (les réponses de la série 1xx, et les communications en clair, entre autres). Après un débat très chaud et très houleux, HTTP/2 n'impose finalement pas HTTPS : les communications peuvent se faire en clair même si, en pratique, on voit très peu de HTTP/2 sans TLS.
Et comment négocier l'ancien protocole HTTP/1 ou le nouveau HTTP/2 ? Ce problème du client (le même qu'avec les versions d'IP : est-ce que je dois tenter IPv6 ou bien est-ce que j'essaie IPv4 d'abord ?) peut être résolu par le mécanisme Upgrade de HTTP (celui utilisé par le RFC 6455), mais il nécessite un aller-retour supplémentaire avec le serveur. Pour éviter cela, comme presque toutes les connexions HTTP/2 utilisent TLS, le mécanisme privilégié est l'ALPN du RFC 7301.
Ce mécanisme marche tellement bien que, malgré le conseil du RFC 5218, HTTP/2 prévoit peu de capacités d'extensions du protocole, considérant qu'il vaudra mieux, si on veut l'étendre un jour, passer à un nouvelle version, négociée grâce à ALPN (cf. RFC 6709.)
En conclusion, on peut dire que la conception d'un nouveau protocole (ou d'une nouvelle version d'un protocole existant) pour que la transition se passe vite et bien, reste un art plutôt qu'une science. Mais on a désormais davantage d'expérience, espérons qu'elle sera utilisée dans le futur.
Date de publication du RFC : Mai 2017
Auteur(s) du RFC : T. Hardie
Pour information
Première rédaction de cet article le 14 mai 2017
Ce court RFC déconseille l'insertion de métadonnées dans les paquets IP, si cette insertion est faite en route, par des intermédiaires. Pourquoi ? (Essentiellement pour des raisons de vie privée.)
Le problème de la surveillance de masse que pratiquent la plupart des États (en tout cas ceux qui en ont les moyens financiers) est maintenant bien documenté (par exemple dans les RFC 7258 et RFC 7624). Une solution fréquente pour limiter cette surveillance, la rendre plus coûteuse et moins efficace est de chiffrer ses communications. Dans l'éternelle lutte de l'épée et de la cuirasse, les surveillants réagissent au chiffrement en utilisant davantage les métadonnées, en général non protégées par le chiffrement. Qui met des métadonnées dans les paquets, affaiblissant ainsi l'effet du chiffrement ?
Certaines métadonnées sont absolument indispensables au fonctionnement de l'Internet. Par exemple, l'adresse IP de destination dans un paquet doit être en clair car tous les routeurs situés sur le trajet doivent la voir, pour prendre leurs décisions. Certaines métadonnées sont inutiles au fonctionement de l'Internet, mais difficiles à dissimuler, la taille des paquets, par exemple. (C'est également un exemple d'une métadonnée implicite : contrairement à l'adresse IP, elle n'apparait pas explicitement dans le paquet.) Normalement, pour gêner la surveillance, il faut envoyer le moins de métadonnées possible.
L'Internet est souvent décrit comme
reposant sur une liaison de bout en bout, où seules les deux
machines situées aux extrémités de la communication ont accès à
tout le contenu de la communication. Mais, en pratique, il existe
souvent des équipements intermédiaires qui ont accès à des
informations poour faire leur travail. Si ces
middleboxes ont la
mauvaise idée de mettre ces informations dans les métadonnées d'un
paquet, elles affaiblissent la confidentialité des
échanges. Imaginons par exemple (ce n'est pas forcément fait
aujourd'hui : le RFC met en garde contre une mauvaise idée, pas
toujours contre des pratiques existantes, voir à ce sujet l'examen
par la direction Sécurité), imaginons par exemple un VPN qui
déciderait d'indiquer l'adresse IP originale dans la
communication… Notre RFC mentionne deux exemples qui sont décrits
dans des RFC : le RFC 7239 qui décrit l'en-tête HTTP
Forwarded:
qu'un relais
HTTP peut mettre pour indiquer l'adresse IP d'origine
du client, et bien sûr le RFC 7871, où un
résolveur DNS transmet aux serveurs faisant
autorité l'adresse IP du client original.
La section 4 du RFC est la recommandation concrète : les métadonnées ne doivent pas être mises par les intermédiaires. Si ces informations peuvent être utiles aux destinataires, c'est uniquement au client d'origine de les mettre. Autrement, on trahit l'intimité du client.
Le RFC 7871, par exemple, aurait dû spécifier un mécanisme où l'adresse IP est mise par le client DNS de départ, celui qui tourne sur la machine de l'utilisateur. Cela permettrait un meilleur contrôle de sa vie privée par l'utilisateur.
Et si cette machine ne connait pas sa propre adresse IP publique, par exemple parce qu'elle est coincée derrière un NAT? Dans ce cas, notre RFC 8165 dit qu'il faut utiliser une technique comme STUN (RFC 8489) pour l'apprendre.
Bon, la section 4, c'était très joli, c'était les bons conseils. Mais la cruelle réalité se met parfois sur leur chemin. La section 5 de notre RFC est le « reality check », les problèmes concrets qui peuvent empêcher de réaliser les beaux objectifs précédents.
D'abord, il y a le désir d'aller vite. Prenons l'exemple du
relais HTTP qui ajoute un en-tête
Forwarded:
(RFC 7239), ce qui permet des choses positives (adapter le
contenu de la page Web servie au client) et négatives (fliquer les
clients). Certes, le client HTTP d'origine aurait pu le
faire lui-même, mais, s'il est derrière un routeur
NAT, il faut utiliser
STUN. Même si tous les clients
HTTP décidaient de la faire, cela ne serait pas instantané, et la
longue traine du déploiement des navigateurs Web ferait qu'un
certain nombre de clients n'aurait pas cette fonction. Alors que
les relais sont moins nombreux et plus susceptibles d'être
rapidement mis à jour.
En parlant d'adaptation du contenu au client, il faut noter que c'est une des principales motivations à l'ajout de tas de métadonnées. Or, comme dans l'exemple ci-dessus, si on demande au client de mettre les métadonnées lui-même, beaucoup ne le feront pas. De mon point de vue, ils ont bien raison, et le RFC note qu'une des motivations pour la consigne « ne pas ajouter de métadonnées en route » est justement de rendre le contrôle à l'utilisateur final : il pourra choisir entre envoyer des métadonnées lui permettant d'avoir un contenu bien adapté, et ne pas en envoyer pour préserver sa vie privée. Mais ce choix peut rentrer en conflit avec ds gens puissants, qui exigent, par exemple dans la loi, que le réseau trahisse ses utilisateurs, en ajoutant des informations qu'eux-mêmes ne voulaient pas mettre.
Enfin, il y a l'éternel problème de la latence. L'utilisation de STUN va certainement ralentir le client.
Un dernier point (section 7 du RFC) : si on passe par Internet pour contacter des services d'urgence (pompiers, par exemple, ou autre PSAP), ils ont évidemment besoin du maximum d'informations, et, dans ce cas, c'est peut-être une exception légitime à la règle de ce RFC.
Date de publication du RFC : Mai 2017
Auteur(s) du RFC : M. Nottingham, M. Thomson
(Mozilla)
Intérêt historique uniquement
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 16 mai 2017
Pendant la mise au point de la version 2 du protocole
HTTP (finalement normalisée dans le RFC 7540), un débat très vigoureux avait porté
sur la possibilité de chiffrer les échanges avec
TLS même si le plan de
l'URL demandé était
http:
(et pas
https:
). Certains demandaient le chiffrement
systématique (que l'URL commence par http:
ou
https:
), d'autres voulaient garder la même
sémantique que HTTP version 1 (TLS pour
https:
, en clair pour
http:
). Cette dernière décision l'avait
emporté à l'époque, en gardant la possibilité de permettre une
extension à HTTP/2. Ce RFC décrivait
justement une telle extension : en HTTP/2, on pouvait
utiliser TLS (et donc HTTPS) même pour un
URL de plan http:
. Mais l'expérience a été
abandonnée en décembre 2021 (personne n'utilisait cette extension)
et le RFC reclassé comme d'intérêt historique seulement.
Le problème à résoudre est celui de la surveillance de masse, à laquelle procèdent un certain nombre d'acteurs (les États, bien sûr, mais pas uniquement, certains FAI, certains réseaux locaux, surveillent le trafic de leurs utilisateurs). Cette surveillance de masse est considérée, à juste titre, par l'IETF comme un problème de sécurité, et contre lequel il faut donc trouver des solutions ou au moins des mitigations (RFC 7258). Chiffrer massivement le trafic Web est évidemment indispensable pour diminuer l'efficacité de la surveillance.
Mais le modèle de HTTP version 1 rend cela difficile. En
HTTP/1, on accède à un URL de plan
http:
avec du trafic en clair, passer à
TLS nécessite de changer les URL, donc les
pages Web qui les contiennent, les signets des utilisateurs,
etc. Des logiciels comme HTTPS Everywhere
aident à cela mais ne sont pas une solution parfaite
(rappelez-vous par exemple qu'une bonne partie du trafic HTTP
n'est pas due aux navigateurs
Web).
Il serait tentant de résoudre le problème en disant « le client
HTTP qui tente d'accéder à un URL de plan
http:
n'a qu'à essayer en même temps
HTTPS. Si ça marche, tant mieux. Si ça
rate, au moins on aura essayé. » C'est ce qu'on nomme parfois le
« chiffrement opportuniste » (RFC 7435). Mais cela pose trois problèmes :
https:
pour mon blog car beaucoup de gens
n'ont pas mon AC dans leur magasin
d'autorités.) On note qu'aujourd'hui les alertes de sécurité des
navigateurs Web sont souvent absurdes : si on se connecte en
HTTPS mais avec un certificat expiré (qui a donc été
parfaitement valable), on a des alertes plus
effrayantes que si on se connecte en clair !Bref, pour le HTTP traditionnel, il semble qu'il n'y ait pas de solution.
Celle proposée par notre RFC est d'utiliser le mécanisme des services alternatifs
du RFC 7838. Le serveur va indiquer
(typiquement par un en-tête Alt-Svc:
) qu'il
est accessible par un autre mécanisme (par exemple HTTPS). Cela a
également l'avantage d'éviter les problèmes de contenu
mixte qui surviendraient si on avait mis la page en HTTPS
mais pas tous ses contenus. Par contre, l'indication de service
alternatif n'étant pas forcément bien protégée, ce mécanisme
« opportuniste » reste vulnérable aux attaques actives. En
revanche, ce mécanisme devrait être suffisamment simple pour être
largement déployé assez vite.
Donc, maintenant, les détails concrets (section 2 du RFC). Le
serveur qui accepte de servir des URL http:
avec TLS annonce le service alternatif. Notez que les clients
HTTP/1 n'y arriveront pas, car ils ne peuvent pas indiquer l'URL
complet (avec son plan) dans la requête à un serveur
d'origine (section 5.3.1 du RFC 7230). Cette spécification est donc limitée à HTTP/2 (RFC 7540). Si le client le veut bien, il va alors
effectuer des requêtes chiffrées vers la nouvelle
destination. S'il ne veut pas, ou si pour une raison ou pour une
autre, la session TLS ne peut pas être établie, on se rabat sur du
texte en clair (chose qu'on ne ferai jamais avec un URL https:
).
Si le client est vraiment soucieux de son intimité et ne veut même pas que la première requête soit en clair, il peut utiliser une commande HTTP qui ne révèle pas grand'chose, comme OPTIONS (section 4.3.7 du RFC 7231).
Le certificat client ne servirait à rien
dans ce chiffrement opportuniste et donc, même si on en a un, on
ne doit pas l'envoyer. Par contre, le serveur doit avoir un
certificat, et valide (RFC 2818) pour le
service d'origine (si le service d'origine était en
foo.example
et que le service alternatif est
en bar.example
, le certificat doit indiquer
au moins foo.example
). Ce service
ne permet donc pas de se chiffrer sans authentification, par
exemple avec un certificat expiré, ou avec une
AC inconnue du client, et ne résout donc pas un des
plus gros problèmes de HTTPS. Mais c'est une exigence de la
section 2.1 du RFC 7838, qui exige que le renvoi à un service
alternatif soit « raisonnablement » sécurisé. (Notez que cette
vérification est délicate, comme l'a montré CVE-2015-0799.)
En outre, le client doit avoir fait une
requête sécurisée pour le nom bien connu (RFC 8615,
pour la notion de nom bien connu)
/.well-known/http-opportunistic
. La réponse à
cette requête doit être positive, et doit être en
JSON, et contenir un tableau de chaînes de caractères
dont l'une doit être le nom d'origine (pour être sûr que ce
serveur autorise le service alternatif, car le certificat du serveur
effectivement utilisé prouve une autorisation du serveur
alternatif, et la signature d'une AC, ce qu'on peut trouver
insuffisant). Ce nouveau nom bien connu figure désormais dans
le registre IANA.
La section 4 de notre RFC rappelle quelques trucs de sécurité :
Alt-Svc:
étant envoyé sur
une liaison non sécurisée, il ne faut pas s'y fier aveuglément (d'où
les vérifications faites ci-dessus).Apparemment, Firefox est le seul client
HTTP à mettre
en œuvre ce nouveau service (mais avec une syntaxe
différente pour le JSON, pas encore celle du RFC). Notez que le serveur ne
nécessite pas de code particulier, juste une configuration
(envoyer l'en-tête Alt-Svc:
, avoir le
/.well-known/http-opportunistic
…) Les
serveurs de Cloudflare permettent
ce choix.
Date de publication du RFC : Avril 2017
Auteur(s) du RFC : H. Flanagan (RFC Editor)
Pour information
Première rédaction de cet article le 25 avril 2017
La préservation, sur le long terme, des documents qui ne sont jamais passés par une forme papier, est un défi important de notre époque. Nous pouvons relire aujourd'hui toute la correspondance du ministère des affaires étrangères de Louix XV, pourrons-nous, dans un siècle ou deux, relire les documents numériques du vingtième siècle ? Pourrons-nous relire les RFC ? C'est le but de ce document que d'explorer les pistes permettant de donner aux RFC une meilleure chance de conservation.
Le RFC Editor (RFC 8728) est à la fois l'éditeur et l'archiviste des RFC. Les deux fonctions sont souvent contradictoires : l'éditeur voudrait utiliser les derniers gadgets pour publier des jolis trucs (multimédia, par exemple, ou contenus exécutables), l'archiviste est prudent et conservateur et voudrait des technologies simples. L'éditeur doit produire des documents clairs et lisibles. L'archiviste doit les conserver, et pour une durée potentiellement bien plus longue que les modes technologiques, durée qui peut atteindre des siècles (on est ravis, aujourd'hui, quand on retrouve les textes de lois d'un royaume depuis longtemps oublié, au fin fond de la Mésopotamie, même quand ces lois ont depuis longtemps cessé d'être applicables).
Notez que des organisations comme l'IETF produisent plein de documents (les discussions sur les listes de diffusion, par exemple), mais que ce RFC se focalise sur la préservation des RFC.
Historiquement, les RFC étaient en texte seul. Ce format avait des tas d'avantages. Simple, et auto-documenté (la seule spécification nécessaire pour le comprendre était ASCII), il convenait bien à l'archivage. Régulièrement, des naïfs demandaient que le RFC Editor passe à un format « plus moderne », en général une mode passagère, oubliée quelques années après. Le texte seul a donc tenu très longtemps, et à juste titre.
Mais la roue de l'histoire a fini par tourner et le RFC 6949 a pris acte du fait qu'on n'allait pas rester en texte seul éternellement. Le format officiel des RFC, décrit dans le RFC 7990 est désormais fondé sur XML, avec divers enrichissements comme le jeu de caractères Unicode (RFC 7997) ou les images en SVG (RFC 7996). Cela fait peser une pression plus forte sur l'archivage : si on est certain de pouvoir relire le texte brut en ASCII dans cent ans, qu'en est-il d'images SVG ? L'ancien système d'archivage des RFC ne va donc a priori pas suffire. (Le XML lui-même est relativement auto-documenté. Si on met des documents XML sous les yeux d'un programmeur qui n'en a jamais vu, il pourra probablement rétro-ingénierer l'essentiel. Ce n'est pas forcément le cas des vocabulaires qui utilisent XML, notamment le compliqué SVG.)
Le nouveau système d'archivage suivra le cadre conceptuel d'OAIS (norme ISO 14721, disponible en ligne). Sur OAIS, on peut lire la bonne introduction d'Emmanuelle Bermes. Il faut notamment distinguer deux tâches (section 1.1 de notre RFC) :
Ceci dit, ce problème d'archivage à long terme des fichiers numériques n'est ni nouveau, ni spécifique aux RFC. Il a été largement étudié par de nombreuses organisations. On peut citer la BNF, le projet LIFE en Grande-Bretagne, ou l'étude du cycle de vie faite à la Bibliothèque du Congrès. Des processus pour maintenir sur le long terme les fichiers, avec recopies régulières et nombreuses vérifications, existent.
Les RFC bénéficient depuis un certain temps d'un mécanisme similaire de préservation des bits : les métadonnées (indispensables pour retrouver un document) sont créées et enregistrées. Les fichiers sont recopiés d'un ordinateur à l'autre au fur et à mesure que les anciennes technologies de stockage deviennent dépassées. En outre, depuis 2010, tous les RFC sont imprimés sur du papier, pour avoir « ceinture et bretelles ». Les RFC plus anciens que 2010 subissent également parfois ce traitement, mais il existe des trous (RFC perdus, ou, tout simplement, problèmes avec le droit d'auteur, avant que cette question ne soit explicitement traitée, cf. RFC 8179).
Cette copie papier s'est avérée utile au moins une fois, quand 800 RFC ont dû être été re-saisis à la main, suite à une panne informatique (et une insuffisance des sauvegardes). Un petit détail amusant au passage : le RFC Editor à une époque acceptait des documents qui n'étaient pas des RFC, et qu'il faut aussi sauvegarder, voir l'histoire antique des RFC.
Il n'y a pas actuellement de sauvegarde de l'environnement logiciel utilisé pour lire les RFC, l'idée est que cela n'est pas nécessaire : on pourra toujours lire du texte brut en ASCII dans cent ans (la preuve est qu'on n'a pas de mal à relire le RFC 1, vieux de quarante-huit ans). Le processus de sauvegarde préserve les bits, et on considère que la préservation du contenu ne pose pas de problème, avec un format aussi simple. (Par exemple, l'impression sur le papier ne garde pas les hyperliens mais ce n'est pas un problème puiqu'il n'y en a pas dans le format texte brut.)
Mais, puisque les RFC vont bientôt quitter ce format traditionnel et migrer vers un format plus riche, il faut reconsidérer la question. La section 2 de notre RFC explore en détail les conséquences de cette migration sur chaque étape du cycle de vie. Il faut désormais se pencher sur la préservation des contenus, pas seulement des bits.
Certaines caractéristiques du cycle de vie des RFC facilitent l'archivage. Ainsi, les RFC sont immuables. Même en cas d'erreur dans un RFC, il n'y a jamais de changement (au maximum, on publie un nouveau RFC, comme cela avait été fait pour le RFC 7396). Il n'y a donc pas à sauvegarder des versions successives. D'autres caractéristiques du cycle de vie des RFC compliquent l'archivage. Ainsi, ce n'est pas le RFC Editor qui décide d'approuver ou pas un RFC (RFC 5741). Il n'a donc pas le pouvoir de refuser un document selon ses critères à lui.
Le RFC Editor maintient une base de données (qui n'est pas
directement accessible de l'extérieur) des RFC, avec évidemment
les métadonnées associées (état, auteurs,
date, DOI, liens vers les éventuels errata
puisqu'on ne corrige jamais un RFC, etc). Les pages
d'information sur les RFC sont automatiquement tirées de cette
base (par exemple
,
pour ce RFC).https://www.rfc-editor.org/info/rfc8153
Les RFC citent, dans la bibliographie à la fin, des références
dont certaines sont normatives (nécessaires pour comprendre le
RFC, les autres étant juste « pour en savoir plus »). Idéalement,
les documents ainsi référencés devraient également être archivés
(s'ils ne sont pas eux-même des RFC)
mais ce n'est pas le cas. Notre RFC suggère que l'utilisation de
Perma.cc serait peut-être une
bonne solution. C'est un mécanisme d'archivage des données
extérieures, maintenu par groupe de bibliothèques juridiques de
diverses universités. Pour un exemple, voici la sauvegarde Perma.cc
(https://perma.cc/E7QG-TG98
) de mon article sur le hackathon de l'IETF.
Dans un processus d'archivage, une étape importante est la normalisation, qui va supprimer les détails considérés comme non pertinents. Elle va permettre la préservation du contenu, en évitant de garder des variantes qui ne font que compliquer la tâche des logiciels. Par exemple, bien que XML permette d'utiliser le jeu de caractères de son choix (en l'indiquant dans la déclaration, tout au début), une bonne normalisation va tout passer en UTF-8, simplifiant la tâche du programmeur qui devra un jour, écrire ou maintenir un logiciel de lecture du XML lorsque ce format sera à moitié oublié.
Or, au cours de l'histoire des RFC, le RFC Editor a reçu plein de formats différents, y compris des RFC uniquement sur papier. Aujourd'hui, il y a au moins le format texte brut, et parfois d'autres.
Maintenant qu'il existe un format canonique officiel (celui du RFC 7991), quelles solutions pour assurer la préservation du contenu ?
La première solution, celle qui est utilisée aujourd'hui, n'est plus réaliste depuis le passage au nouveau format. Elle doit être abandonnée. La deuxième solution sauvegarde l'information dans le document, mais pas le document lui-même (et c'est embêtant que le format archivé ne soit pas le format canonique, mais uniquement un des rendus). Et l'avenir de PDF/A-3 est incertain, on n'a pas encore essayé de le lire trente ans après, et les promesses du marketing doivent être considérées avec prudence (d'autant plus qu'il y a toujours peu d'outils PDF/A, par exemple aucun logiciel pour vérifier qu'un document PDF est bien conforme à ce profil restrictif). Pour la troisième solution, cela permettrait de refaire un rendu des RFC de temps en temps, adapté aux outils qui seront modernes à ce moment. Mais c'est aussi la solution la plus chère. Si on imagine un futur où XML, HTML et PDF sont des lointains souvenirs du passé, on imagine ce que cela serait d'avoir préservé un environnement d'exécution complet, les navigateurs, les bibliothèques dont ils dépendent, le système d'exploitation et même le matériel sur lequel il tourne !
Une solution plus légère serait de faire (par exemple tous les ans) un tour d'horizon des techniques existantes et de voir s'il est encore possible, et facile, de visualiser les RFC archivés. Si ce n'est pas le cas, on pourra alors se lancer dans la tâche de regénérer des versions lisibles.
Au passage, il existe déjà des logiciels qui peuvent faciliter certains de ces activités (le RFC cite le logiciel libre de gestion d'archives ArchiveMatica).
Après cette analyse, la section 3 de notre RFC va aux recommandations : l'idée est de sauvegarder le format canonique (XML), un fichier PDF/A-3, le futur outil xml2rfc et au moins deux lecteurs PDF (qui doivent être capables d'extraire le XML embarqué). Les tâches immédiates sont donc :
La version papier, par contre, ne sera plus archivée.
Conclusion (section 4) : les RFC sont des documents importants, qui ont un intérêt pour les générations futures, et qui valent la peine qu'on fasse des efforts pour les préserver sur le long terme.
Date de publication du RFC : Avril 2017
Auteur(s) du RFC : D. Wessels (Verisign), W. Kumari
(Google), P. Hoffman (ICANN)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 19 avril 2017
L'utilisation de DNSSEC implique que le résolveur DNS ait une ou plusieurs clés de départ de la validation (trust anchors). Typiquement, le résolveur aura une clé pour la racine, les autres domaines étant validés en suivant l'arborescence du DNS (cela se configure, même si la plupart des résolveurs viennent avec une pré-configuration pour la clé ICANN de la racine). Seulement, parfois, les clés changent et le gérant d'un domaine aimerait bien savoir, avant de supprimer l'ancienne clé, si les résolveurs ont bien tous reçu la nouvelle. D'où cette nouvelle option EDNS où le résolveur signale au serveur faisant autorité la liste des clés qu'il utilise comme point de départ de la validation. (Le RFC décrit également une autre méthode, non fondée sur EDNS.)
En toute rigueur, il faut dire que le résolveur ne transmet pas les
clés mais les identificateurs courts (key tags
ou key IDs), qui sont un
condensat de 16 bits des clés (section 3.1.6 du RFC 4034, et notez dans l'annexe B du même RFC
que ce ne sont pas des condensats cryptographiques). On trouve cet
identificateur de clé si on utilise l'option
+multi
de dig :
% dig +multi DNSKEY tf ... ;; ANSWER SECTION: tf. 172800 IN DNSKEY 257 3 8 ( ... ) ; KSK; alg = RSASHA256; key id = 12520 tf. 172574 IN DNSKEY 256 3 8 ( ... ) ; ZSK; alg = RSASHA256; key id = 51793 ... tf. 172574 IN RRSIG DNSKEY 8 1 172800 ( 20170524190422 20170325180422 12520 tf. ...
Il est utilisé pour la communication entre humains mais on le trouve aussi dans les enregistrements DS chez le parent :
% dig DS tf ... ;; ANSWER SECTION: tf. 86400 IN DS 12520 8 2 ( 2EC74274DD9AA7FFEA33E695EFF98F17F7C78ABD2D76 EDBBDE4EDD4630D68FA2 ) ...
Ainsi que dans les signatures :
% dig +dnssec SOA tf ... ;; ANSWER SECTION: tf. 172800 IN SOA nsmaster.nic.fr. hostmaster.nic.fr. ( 2222242731 ; serial ... tf. 172800 IN RRSIG SOA 8 1 172800 ( 20170531124004 20170401114004 51793 tf. ...
On voit ici que la clé de
.tf
dans la racine est la 12520, qui signe la clé
51793, qui elle-même signe les enregistrements.
Si vous n'êtes pas parfaitement au point sur la terminologie DNSSEC, lisez la section 3 du RFC. Et, à titre d'exemple, voici la configuration d'un résolveur Unbound pour utiliser comme clés de départ de la validation celles de Yeti :
% cat /etc/unbound/unbound.conf ... server: ... auto-trust-anchor-file: "/var/lib/unbound/yeti.key" ... % cat /var/lib/unbound/yeti.key . 86400 IN DNSKEY 257 3 8 AwE...8uk= ;{id = 59302 (ksk), size = 2048b} ;;state=1 [ ADDPEND ] ;;count=67 ;;lastchange=1488474089 ;;Thu Mar 2 18:01:29 2017 . 86400 IN DNSKEY 257 3 8 AwE...y0U= ;{id = 19444 (ksk), size = 2048b} ;;state=2 [ VALID ] ;;count=0 ;;lastchange=1472139347 ;;Thu Aug 25 17:35:47 2016
On voit deux clés, d'identificateurs 59302 et 19444. Tout contenu signé avec une de ces deux clés sera accepté. (Le fait qu'il y ait deux clés alors qu'une suffirait est dû au fait qu'un changement est en cours, suivant le RFC 5011.)
Voyons maintenant la première façon de signaler ses clés dont
dispose un résolveur, la méthode EDNS
(section 4 de notre RFC, et voir le RFC 6891, pour les détails sur ce qu'est EDNS). On utilise
une nouvelle option EDNS, edns-key-tag
(code 14 dans le
registre IANA). Comme toutes les options EDNS, elle
comprend le code (14), la longueur, puis une suite
d'identificateurs de clés. Par exemple, le résolveur Unbound
montré plus haut enverrait une option {14, 4, 59302, 19444}
(longueur quatre
car il y a deux identificateurs, de deux octets chacun). Il est
recommandé d'utiliser cette option pour toutes les requêtes de
type DNSKEY (et jamais pour les autres).
Notez que le serveur qui reçoit une requête avec cette option n'a rien à faire : elle est juste là pour l'informer, la réponse n'est pas modifiée. S'il le souhaite, le serveur peut enregistrer les valeurs, permettant à son administrateur de voir, par exemple, si une nouvelle clé est largement distribuée (avant de supprimer l'ancienne).
La deuxième méthode de signalisation, celle utilisant le
QNAME (Query Name, le nom indiqué dans la
requête DNS) figure en section 5. La requête de signalisation
utilise le type NULL (valeur
numérique 10), et un nom de
domaine qui commence par « _ta- », suivi de la liste
des identificateurs en hexadécimal (dans cet article, ils
étaient toujours montré en décimal) séparés par des
traits. Le nom de la zone pour laquelle
s'applique ces clés est ajouté à la fin (la plupart du temps, ce
sera la racine, donc il n'y aura rien à ajouter). En reprenant
l'exemple du résolveur Unbound plus haut, la requête sera
_ta-4bf4-e7a6.
. Comme ce nom n'existe pas,
la réponse sera certainement NXDOMAIN.
Le serveur utilise cette requête comme il utilise l'option EDNS : ne rien changer à la réponse qui est faite, éventuellement enregistrer les valeurs indiquées, pour pouvoir informer l'administrateur du serveur.
Voilà, comme vous voyez, c'est tout simple. Reste quelques petites questions de sécurité (section 7) et de vie privée (section 8). Pour la sécurité, comme, par défaut, les requêtes DNS passent en clair (RFC 7626), un écoutant indiscret pourra savoir quelles clés utilise un résolveur. Outre que cela peut permettre, par exemple, de trouver un résolveur ayant gardé les vieilles clés, la liste peut révéler d'autres informations, par exemple sur le logiciel utilisé (selon la façon dont il met en œuvre le RFC 5011). C'est donc un problème de vie privée également.
Notez aussi que le client peut mentir, en mettant de fausses valeurs. Par exemple, il pourrait envoyer de faux messages, avec une adresse IP source usurpée, pour faire croire que beaucoup de clients ont encore l'ancienne clé, de façon à retarder un remplacement.
(Au passage, si vous voulez des informations sur le remplacement des clés DNSSEC de la racine, voyez la page de l'ICANN, et la première expérimentation Yeti ainsi que la deuxième.)
Notez que le mécanisme utilisé a beaucoup varié au cours du développement de ce RFC (section 1.1, sur l'histoire). Au début, il n'y avait que l'option EDNS, en copiant sur le mécanisme du RFC 6975. Mais EDNS a quelques limites :
L'approche concurrente, avec le QNAME, a aussi ses inconvénients :
D'où le choix (chaudement discuté) de fournir les deux méthodes.
À l'heure actuelle, je ne connais qu'une seule mise en œuvre de ce RFC, introduite dans BIND 9.10.5 rc1 (« named now provides feedback to the owners of zones which have trust anchors configured by sending a daily query which encodes the keyids of the configured trust anchors for the zone. This is controlled by trust-anchor-telemetry »).
Date de publication du RFC : Avril 2017
Auteur(s) du RFC : J. Élie
Chemin des normes
Première rédaction de cet article le 25 avril 2017
Ce RFC concernant le protocole NNTP met à jour l'ancien RFC 4642 qui donnait des conseils TLS très spécifiques (activer la compression, utiliser RC4...), conseils qui n'ont pas résisté à l'évolution de la cryptographie. On arrête donc de donner des conseils TLS spécifiques, NNTP a un usage générique de TLS et doit donc se référer au RFC générique BCP 195 (actuellement RFC 7525).
NNTP, le protocole de transport des News, est normalisé dans le RFC 3977. Il peut utiliser TLS (RFC 5246) pour sécuriser la communication entre deux serveurs NNTP, ou bien entre serveur et client. Le RFC 4642, qui décrivait cet usage de TLS, faisait une erreur : il donnait des conseils de cryptographie. Or, d'une part, NNTP ne faisait pas un usage particulier de la cryptographie, et n'avait pas besoin de recommandations spécifiques et, d'autre part, la cryptographie est un domaine qui évolue. Ainsi, les fonctions de compression de données de TLS sont aujourd'hui considérées comme une mauvaise idée, dans la plupart des cas (attaque CRIME, cf. RFC 7525, section 3.3).
L'essentiel de notre nouveau RFC est dans sa section 3 : désormais, il faut suivre le RFC de bonnes pratiques TLS, BCP 195 (actuellement RFC 7525).
De même que le courrier électronique
peut préciser dans un en-tête Received:
que
la connexion SMTP était protégée par TLS,
de même NNTP permet d'ajouter dans le champ
Path:
(section 3.1.5 du RFC 5536) une indication que
le pair a été authentifié (deux points d'exclamation de suite).
La section 2 du RFC résume les changements par rapport au RFC 4642 (la liste complète est dans l'annexe
A). Comme dit plus haut, la compression TLS est désormais fortement déconseillée
(à la place, on peut utiliser l'extension de compression de NNTP,
normalisée dans le RFC 8054). Il est très
nettement recommandé d'utiliser du TLS implicite (connexion sur un
port dédié (le 563 pour les clients, non
spécifié pour les autres serveurs), au lieu de la
directive STARTTLS
, qui est vulnérable à
l'attaque décrite dans la section 2.1 du RFC 7457). Il ne faut évidemment
plus utiliser RC4 (cf. RFC 7465), mais les algorithmes de chiffrement obligatoires
de TLS. Il faut utiliser
l'extension TLS Server Name Indication (RFC 6066, section 3). Et, pour authentifier, il
faut suivre les bonnes pratiques TLS des RFC 5280 et RFC 6125.
Comme la plupart des mises en oœuvre de NNTP-sur-TLS utilisent une bibliothèque TLS générique, elles suivent déjà une bonne partie des recommandations de ce RFC. Après, tout dépend des options particulières qu'elles appellent…
Merci à Julien Élie pour une relecture attentive (j'avais réussi à mettre plusieurs erreurs dans un article aussi court.)
Date de publication du RFC : Avril 2017
Auteur(s) du RFC : P. Saint-Andre
(Filament), J. Klensin
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF urnbis
Première rédaction de cet article le 30 avril 2017
Dans la grande famille des URI, il y a (entre autres) les
URL, et les
URN, comme urn:ietf:params:xml:ns:iodef-2.0
, normalisés
dans ce RFC, qui couvre aussi bien leur
syntaxe (qui était
auparavant dans le RFC 2141) que les
procédures d'enregistrement (autrefois dans le RFC 3406). Il remplace donc ces deux anciens RFC.
Le RFC 3986, qui normalise la syntaxe générique des URI, délègue les détails des familles particulières d'URI à d'autres RFC comme celui-ci. Notre RFC 8141 précise la syntaxe générique pour le cas des URN, des URI dont les propriétés sont a priori la persistence et la non-résolvabilité (donc, plutôt des noms que des adresses, pour reprendre le vocabulaire des RFC 1737 et RFC 3305).
La section 2, URN Syntax décrit à quoi
ressemblent les URN. Un URN est formé avec le plan
urn
(enregistré à l'IANA), un NID (Namespace
IDentifier) qui indique l'organisme qui gère la fin de
l'URN, puis le NSS (Namespace Specific String), tous
séparés par des deux-points. Il existe de très nombreux NID déjà enregistrés, comme ceux du RFC 7207 (messagerie Eurosystem), RFC 7254 (IMEI),
RFC 5165 (normes OGC), RFC 9562 (UUID)… Deux idées essentielles des URN sont que la création de NID est
strictement gérée (il faut documenter précisément le pourquoi du
nouvel NID) et que, dans chaque espace de noms créé par un NID,
l'affectation des NSS est à son tour gérée, avec des règles
rigoureuses, que l'on suit soigneusement. Les URN sont conçus pour
du nommage « sérieux ». Il n'existe pas d'URN à enregistrement libre
et, donc, le fait d'être correct syntaxiquement ne suffit pas pour
qu'un URN soit un vrai URN : il doit en plus être passé par le
processus d'enregistrement.
Par exemple, si on prend les URN néo-zélandais du RFC 4350, le NID est nzl
et un URN
ressemble donc à
urn:nzl:govt:registering:recreational_fishing:form:1-0
. Attention,
le deux-points n'est un séparateur qu'au début,
entre le plan et le NID, puis entre le NID et le NSS. Après, c'est
juste un caractère comme un autre. Donc,
govt:registering:recreational_fishing:form:1-0
n'est pas forcément du nommage arborescent.
Notez que la syntaxe est moins
restrictive qu'elle ne l'était dans le RFC 2141.
Comme avec les autres URI, les caractères considérés comme
« spéciaux » doivent être protégés avec l'encodage pour
cent (cela ne concerne pas le NID qui doit être purement
en ASCII). On ne peut donc hélas pas faire de vrais URN
internationaux. Ainsi, urn:example:café
doit en
fait s'écrire urn:example:caf%E9
.
Notre RFC 8141 introduit un concept nouveau, qui n'était
pas dans le RFC 2141, celui de composants. Ils
sont de trois sortes, les r-components, les
q-components et les
f-components. Les premiers, les
r-components, servent à passer des paramètres à un
éventuel système de résolution. Ils commencent par un point
d'interrogation suivi d'un
plus. Ainsi, dans
urn:example:bar-baz-qux?+CCResolve:cc=fr
, le
r-component est
CCResolve:cc=fr
, indiquant probablement qu'on
souhaite une réponse adaptée à la France (CCResolve
= Country-Code Resolve). La syntaxe est définie
(ainsi que le fait que ces composants doivent être ignorés pour la
comparaison de deux URN) mais la sémantique est pour l'instant laissée
dans le flou.
Contrairement au r-component, prévu pour le
système de résolution, le q-component est prévu
pour la ressource à laquelle on accède. Il commence par un point
d'interrogation suivi d'un égal. Un exemple
pourrait être
urn:example:weather?=op=map&lat=39.56&lon=-104.85
(cherchez pas : c'est près de Denver).
Quant au f-component, introduit par un croisillon, il n'est destiné qu'au client (comme le classique identificateur de fragment des URI).
La section 3, consacrée à l'équivalence
lexicale de deux URN, explique comment on peut
déterminer si deux URN sont égaux ou pas, sans connaitre les règles
particulières de l'organisme qui les enregistre. (Déterminer
l'équivalence sert, par
exemple, pour savoir si un URN a déjà été visité.) Ainsi,
urn:foo:BAR
et urn:FOO:BAR
sont lexicalement équivalents (le NID est insensible à la casse, cf. section 2.1) mais
urn:foo:BAR
et urn:foo:bar
ne le sont pas, le NSS étant, lui, sensible à la casse (les deux URN
sont peut-être fonctionnellement équivalents mais cela dépend de la
politique d'enregistrement de l'organisme désigné par
foo
). Il n'y a pas d'autre normalisation
appliquée avant la comparaison, notamment sur les caractères encodés
pour-cent.
Notez que la définition d'un espace de noms donné peut toujours rajouter des règles (par exemple que le NSS soit insensible à la casse) mais ces règles doivent uniquement créer de nouvelles équivalences, jamais séparer deux URN qui auraient été identiques dans une comparaison générique (par un logiciel qui ne connaissait pas les règles spécifiques de cet espace de noms, voir aussi la section 4.2).
Comme vu plus haut, il y a dans un URN, immédiatement après la
chaîne de caractères urn:
, l'espace de
noms (namespace ou NID), une chaîne de caractères qui identifie le domaine d'une
autorité d'enregistrement. Notre RFC 8141 explique les
procédures de création d'un nouvel espace de noms dans le registre des espaces de noms que tient
l'IANA. Si vous voulez juste faire des exemples,
il existe un NID example
specifié dans le RFC 6963 (oui, j'aurais dû l'utiliser au lieu de foo
).
Comme expliqué dans la section 5, ce mécanisme d'espaces de noms suppose que, dans chaque espace, il existe une autorité d'enregistrement qui accepte (ou refuse) les enregistrements et que, d'autre part, il existe une autorité qui enregistre les espaces de noms (en l'occurrence l'IANA). La section 6 de notre RFC est consacrée aux procédures de cette dernière autorité et aux mécanismes pour enregistrer un identificateur d'espace de noms (NID pour namespace identifier). (La résolution des URN en autres identificateurs n'est par contre pas couverte, mais on peut toujours regarder le RFC 2483.) Des exemples d'autorité d'enregistrement dans un espace de noms donné sont le gouvernement néo-zélandais (RFC 4350) ou l'OGC (RFC 5165).
Notez que certains URN sont créés en partant de rien, alors que
d'autres sont juste une transcription en syntaxe URN d'identificateurs
déjà enregistrés par ailleurs. C'est le cas des
ISBN (RFC 8254) ou des
RFC eux-mêmes (RFC 2648, avec le NID
ietf
, ce RFC est donc urn:ietf:rfc:8141
).
Tiens, et où peut-on mettre des URN ? Syntaxiquement, un
URN est un URI, donc on peut en mettre partout où on peut mettre des
URI (section 4 du RFC). Par exemple, comme nom d'espace de
noms XML. Mais ce n'est pas une bonne idée de mettre des
URN partout. Par exemple, en argument d'un <a
href="…
, c'est même certainement une mauvaise idée (car le
navigateur Web typique ne sait pas résoudre des URN).
Autre recommandation pratique de notre RFC, si un logiciel affiche
un URN, bien penser à l'afficher en complet (contrairement à ce que
font certains navigateurs Web qui, stupidement, tronquent l'URI, par
exemple en omettant le plan http://
). En effet,
une abréviation peut ne pas avoir le résultat attendu. En effet, il
existe un NID urn:xmpp
(RFC 4854) et un
plan d'URI xmpp:
(RFC 5122). Si on n'affiche pas le plan
urn:
, il y a un gros risque de confusion.
La section 5 du RFC détaille ce qu'est un espace de noms (un ensemble d'identificateurs uniques géré, c'est-à-dire que tous les noms syntaxiquements corrects n'en font pas partie, uniquement ceux qui ont été enregistrés). Par exemple, les ISBN forment un tel espace (dont l'utilisation dans des URN a fait l'objet du RFC 8254). À l'intérieur d'un espace de noms, les règles d'enregistrement et le travail quotidien du registre ne sont pas gérés par l'IETF ou l'IANA mais par l'autorité d'enregistrement de cet espace.
La section 5 introduit les différents types d'espaces de noms. Il y a les espaces informels (section 5.2),
dont le NID commence par urn-
et est composé de
chiffres et les espaces formels (section 5.1)
dont le NID est composé de lettres et qui, contrairement aux
informels, sont censés fournir un bénéfice aux utilisateurs de
l'Internet (les espaces informels ont le droit
d'être réservés à une communauté déconnectée). Contrairement encore
aux informels, l'enregistrement des espaces formels fait l'objet d'un
examen par un expert (cf. RFC 5226)
et il est
recommandé que cet enregistrement fasse
l'objet d'une spécification écrite, par exemple un
RFC. L'organisation
qui gère un NID formel doit également démontrer sa stabilité et son
sérieux sur le long terme.
Un des principes des URN est la durabilité : un URN devrait être stable dans le temps (et, logiquement, jamais réaffecté si jamais il est supprimé). Mais cette stabilité dépend essentiellement de facteurs non-techniques, comme la permanence dans le temps du registre (une organisation privée et fermée comme l'IDF est, par exemple, typiquement un mauvais choix pour assurer la permanence). Toutefois, si on ne peut pas garantir la stabilité d'un espace de noms, on connait en revanche des facteurs qui diminuent la probabilité de permanence et l'IETF peut donc analyser les spécifications à la recherche de tels facteurs (c'est une variante du problème très riche mais bien connu de la stabilité des identificateurs). Au passage, la plupart des grandes questions liées aux URN (permanence, compromis entre facilité d'enregistrement et désir de ne pas permettre « n'importe quoi ») sont des questions bien plus anciennes que les URN, et même plus anciennes que l'Internet, et ne feront probablement pas l'objet d'un consensus de si tôt (cf. section 1.2).
Enfin, le processus d'enregistrement lui-même. Il faut en effet un
peu de bureaucratie pour s'assurer que le NID est bien enregistré et
que le registre des
NID soit lui-même stable. Les procédures sont différentes selon
le type d'espace de noms. Les informels, qui commencent par la chaîne
de caractères urn-
suivie d'un nombre, ont leur
propre
registre, avec un processus d'enregistrement léger, mais très
peu utilisé.
Le gros morceau est constitué des espaces de noms formels. Cette
fois, le processus d'enregistrement est plus complexe, mais on obtient
un « vrai » NID comme mpeg
(RFC 3614),
oasis
(RFC 3121) ou
3gpp
(RFC 5279).
Le formulaire d'enregistrement complet est disponible dans l'annexe
A du RFC. Bon courage aux futurs enregistreurs. N'oubliez pas de lire
tout le RFC. Notez par exemple qu'il faudra décrire les mécanismes
par lesquels vous allouerez des noms (si vous gérez
urn:example
, et que je demande
urn:example:boycott-de-mcdo
, que
répondrez-vous ?), et qu'il faudra une analyse de sécurité et aussi
(c'est une nouveauté par rapport au RFC 2141) de
vie privée.
Ce nouveau RFC remplace à la fois le RFC 2141
et le RFC 3406, et le processus fut long (six
ans) et difficile. L'annexe B indique les
principaux changements depuis le RFC 2141 :
alignement de la syntaxe sur le RFC 3986 (en
pratique, la nouvelle syntaxe est plus acceptante), introduction des
composants (pour pouvoir faire des choses comme les fragments, qui
marchaient avec tous les autres URI)… L'annexe C, elle, présente les
changements par rapport au RFC 3406 :
politique d'enregistrement d'un NID plus libérale (examen par un expert
au lieu d'examen par l'IETF), suppression des NID expérimentaux (nom
commençant par x-
) en raison du RFC 6648…
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : C. Morgan (AMS)
Pour information
Première rédaction de cet article le 11 mars 2017
Un petit RFC purement bureaucratique publié hier, il décrit les procédures par lesquelles l'IETF nomme un représentant dans un des innombrables comités de l'ICANN, le RZERC (Root Zone Evolution Review Committee), qui travaille sur la gestion de la zone racine du DNS.
Ce nouveau comité RZERC est chargé des mécanismes de publication de la zone racine, une zone évidemment cruciale puisque la nature arborescente du DNS fait que, si elle a des problèmes, plus rien ne marche. Notez que le RZERC ne s'occupe que de la création et de la publication de la zone racine, pas de servir cette zone. Cette tâche incombe en effet aux serveurs racines du DNS, qui sont indépendants de l'ICANN (contrairement à ce qu'on lit souvent dans des médias mal informés). L'actuelle charte du RZERC est en ligne et elle prévoit que le comité comprend entre autres « The Chair or delegate of the Internet Engineering Task Force ».
C'est l'IAB qui désigne le représentant IETF, le premier étant Jim Reid. Les qualités nécessaires sont citées en section 2 de notre RFC. Sans surprise, il faut être techniquement très compétent, et il faut pouvoir traduire des recommandations en des termes compréhensibles par la bureaucratie ICANN (« be able to articulate those technology issues such that the ICANN Board can be provided with sound technical perspectives »). Le RFC précise également qu'il faut comprendre l'articulation de la gouvernance Internet et les rôles des différents organismes, une tâche complexe, c'est sûr !
Suivant les procédures décrites en section 3 du RFC, un appel à volontaires avait été lancé le 25 mai 2016, il y avait quatre candidats (Marc Blanchet, Warren Kumari, Kaveh Ranjbar et Jim Reid), et Jim Reid a été nommé le 11 août 2016. Depuis, si on veut savoir ce que fait ce comité, il faut regarder sa page Web officielle. Son rôle n'est pas encore bien défini et fait l'objet de la plupart des discussions. En gros, il devrait intervenir uniquement lorsqu'une proposition de changement important est faite, pas pour la gestion quotidienne.
Date de publication du RFC : Juin 2017
Auteur(s) du RFC : M. Cotton (ICANN), B. Leiba (Huawei
Technologies), T. Narten (IBM
Corporation)
Première rédaction de cet article le 21 juin 2017
Un aspect peu connu du travail de normalisation est la nécessité de tenir des registres de certains paramètres, lorsque la liste de ces derniers n'est pas fixée dans un RFC. Par exemple, les algorithmes publiés dans le DNS pour IPsec ne sont pas définis de manière limitative dans le RFC 4025 mais sont enregistrés dans un registre public. Même chose pour les types de média du RFC 6838, qui ont leur propre registre. Pour les RFC, ces registres sont tenus par l'IANA. Celle-ci ne décide pas quels registres elle doit tenir ni à quelle condition un nouveau paramètre peut y rentrer, elle applique les décisions contenues dans la section IANA Considerations d'un RFC. C'est cette section qui est décrite ici. Ce RFC remplace l'ancien RFC 5226.
Prenons l'exemple du RFC 3315 (DHCP). Sa
section 24 contient le texte « This document defines several
new name spaces associated with DHCPv6 and DHCPv6 options: Message
types, Status codes, DUID and Option codes. IANA has established a
registry of values for each of these name spaces, which are described
in the remainder of this section. These name spaces will be managed
by the IANA and all will be managed separately from the name spaces
defined for DHCPv4. ». En application de ce texte,
l'IANA a créé le registre DHCPv6 and
DHCPv6 options qu'on peut consulter en ligne à https://www.iana.org/assignments/dhcpv6-parameters
. Et comment
ajoute-t-on des entrées dans ce registre ? En suivant les règles
données dans ce même RFC : « New DHCP option codes are
tentatively assigned after the specification for the associated
option, published as an Internet Draft, has received expert review by
a designated expert [11]. The final assignment of DHCP option codes
is through Standards Action, as defined in RFC 2434.
».
L'intérêt d'avoir une section obligatoire IANA Considerations est de concentrer en un seul endroit les informations nécessaires à l'IANA pour faire son travail. Pour aider les auteurs de RFC à écrire correctement cette section IANA Considerations, notre RFC 8126, qui succède au RFC 5226, pose quelques règles.
La section 1 du RFC décrit le problème général de la gestion d'un espace de nommage (namespace). Tous ces espaces n'ont pas les mêmes caractéristiques. Certains sont très petits (le champ protocole, qui n'a que huit bits soit 256 valeurs possibles, cf. RFC 5237) et doivent donc être gérés avec prudence, certains sont hiérarchiques comme le DNS ou comme les OID et peuvent donc être délégués, certains sont immenses, et peuvent être gérés avec moins de précautions (mais nécessitent quand même des règles, comme expliqué dans la section 1).
Cette même section 1 résume les points essentiels que doit connaitre l'auteur d'un RFC, notamment d'avoir une section dédiée IANA Considerations, et de n'y mettre que ce qui est strictement nécessaire à l'IANA (pas de digressions, pas de détails techniques).
La section 2 est consacrée à la création d'un nouveau registre. Il y a bien des décisions à prendre à ce stade. Par exemple, notre RFC recommande de voir si on ne peut pas faire un registre arborescent, où l'action de l'IANA se limiterait à la racine de ce registre, diminuant ainsi sa charge de travail. (C'est le cas des URN du RFC 8141.)
Si un RFC doit demander une telle création, il doit préciser quelle politique d'enregistrement devra suivre l'IANA. C'est une des parties les plus intéressantes du RFC, notamment sa section 4 qui explique les politiques possibles :
iso.org.dod.internet.private.enterprise
sont un
bon exemple (https://www.iana.org/assignments/enterprise-numbers
mais
attention, le registre est lourd à charger). C'est aussi le cas des
plans d'URI provisoires (RFC 7595) ou des états de traitement du courrier (RFC 6729). C'est sans doute la plus
« libérale » des politiques d'enregistrement. (Il n'y a pas de
mécanisme explicite contre les vilains qui enregistreraient plein de
valeurs inutiles, avait noté l'examen
de sécurité.)Le choix d'une politique n'est pas évident : elle ne doit pas être trop stricte (ce qui ferait souffrir les auteurs de protocoles, confrontés à une bureaucratie pénible) mais pas non plus trop laxiste (ce qui risquerait de remplir les registres de valeurs inutiles, les rendant difficilement utilisables). En tout cas, c'est une des décisions importantes qu'il faut prendre lors de l'écriture d'une spécification.
Notre RFC conseille (sans l'imposer) d'utiliser une de ces politiques (« well-known policies »). Elles ont été testés en pratique et fonctionnent, concevoir une nouvelle politique fait courir le risque qu'elle soit incohérente ou insuffisamment spécifiée, et, de toute façon, déroutera les lecteurs et l'IANA, qui devront apprendre une nouvelle règle.
Parmi les autres points que doit spécifier le RFC qui crée un nouveau registre, le format de celui-ci (section 2.2 ; la plupart des registres sont maintenus en XML, mais même dans ce cas, des détails de syntaxe, comme les valeurs acceptables, peuvent devoir être précisés). Notez que le format n'est pas forcément automatiquement vérifié par l'IANA. Notre RFC recommande également de bien préciser si le registre accepte des caractères non-ASCII (cf. RFC 8264, section 10).
Autre choix à faire dans un registre, le pouvoir de changer les
règles (change control). Pour des normes IETF (RFC
sur le chemin des normes), c'est en général l'IETF qui a ce pouvoir,
mais des registres IANA peuvent être créés pour des protocoles qui ne sont
pas gérés par l'IETF et, dans ce cas, le pouvoir peut appartenir à une
autre organisation. C'est ainsi que les types de données
XML (RFC 7303), comme le
application/calendar+xml
(RFC 6321) sont contrôlés par le W3C.
La section 3 couvre l'enregistrement de nouveaux paramètres dans un registre existant. C'est elle qui précise, entre autres, que l'IANA ne laisse normalement pas le choix de la valeur du paramètre au demandeur (mais, en pratique, l'IANA est sympa et a accepté beaucoup de demandes humoristiques comme le port TCP n° 1984 pour le logiciel Big Brother...)
La section 6 donne des noms aux différents états d'enregistrement d'une valeur. Le registre note l'état de chaque valeur, parmi ces choix :
Par exemple, si on prend les types de messages
BGP, on voit dans le
registre que 0 est réservé, les valeurs à partir de 6 sont
libres, les autres sont affectées (1 = OPEN
, etc).
La section 5 décrit le rôle des experts sur
lesquels doit parfois s'appuyer l'IANA. Certains registres nécessitent
en effet un choix technique avec l'enregistrement d'un nouveau
paramètre et l'IANA n'a pas forcément les compétences nécessaires pour
cette évaluation. Elle délègue alors cette tâche à un expert
(designated expert, leur nom est noté à côté de celui du registre). Par exemple, pour le registre des langues, défini par le RFC 5646, l'expert actuel est Michael Everson. Ce registre utilise également une autre
possibilité décrite dans cette section, une liste de discussion qui sert à un examen collectif des requêtes
(pour le registre des langues, cette liste est
ietf-languages@iana.org
). La section 5.1 discute
des autres choix qui auraient pu être faits (par exemple un examen par le groupe de travail qui a
créé le RFC, ce qui n'est pas possible, les groupes de travail IETF
ayant une durée de vie limitée). Elle explique ensuite les devoirs de
l'expert (comme la nécessité de répondre relativement rapidement,
section 5.3, chose qui est loin d'être toujours faite).
Enfin, diverses questions sont traitées dans la section 9, comme la récupération de valeurs qui avaient été affectées mais qui ne le sont plus (le RFC 3942 l'avait fait mais c'est évidemment impossible dès que les paramètres en question ont une... valeur, ce qui est le cas entre autres des adresses IP).
Bien que la plupart des allocations effectuées par l'IANA ne sont guère polémiques (à l'exception des noms de domaine et des adresses IP, qui sont des sujets très chauds), notre RFC 8126 prévoit une procédure d'appel, décrite en section 10. Cela n'a pas suffit à régler quelques cas pénibles comme l'enregistrement de CARP.
Ce RFC 8126 remplace le RFC 5226. Les principaux changements sont détaillés dans la section 14.1 :
Notez que notre RFC est également complété en ligne par des informations plus récentes.
Les relations entre l'IETF et l'IANA sont fixées par le MOU contenu dans le RFC 2860. À noter que tout n'est pas couvert dans ce RFC, notamment des limites aux demandes de l'IETF. Que se passerait-il par exemple si l'IETF demandait à l'IANA, qui ne facture pas ses prestations, de créer un registre de milliards d'entrées, très dynamique et donc très coûteux à maintenir ? Pour l'instant, l'IANA ne peut pas, en théorie, le refuser et la question s'est parfois posée à l'IETF de savoir si tel ou tel registre n'était pas trop demander.
Puisque l'IANA est un acteur important de l'Internet, rappelons aussi que, bien que la fonction de l'IANA soit actuellement assurée par l'ICANN, la tâche de gestion des protocoles et des registres n'a rien à voir avec les activités, essentiellement politiciennes, de l'ICANN. La « fonction IANA » (création et gestion de ces registres) est formellement nommée IFO (IANA Functions Operator) ou IPPSO (IANA Protocol Parameter Services Operator) mais tout le monde dit simplement « IANA ».
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : M. Hardy, L. Masinter, D. Markovic (Adobe Systems), D. Johnson (PDF Association), M. Bailey (Global Graphics)
Pour information
Première rédaction de cet article le 18 mars 2017
Le format PDF, largement utilisé sur l'Internet, n'a sans doute pas besoin
d'être présenté ici. De toute façon, ce nouveau
RFC ne prétend pas décrire PDF, juste le
type de contenu
application/pdf
. Ce RFC remplace l'ancien
RFC 3778, notamment pour tenir compte du fait
qu'officiellement, PDF n'est plus une spécification
Adobe mais une norme
ISO, 32000-1:2008.
Donc, si vous envoyez des documents PDF
via l'Internet, que ce soit par courrier ou par le
Web, vous êtes censé les étiqueter avec le
type MIME
application/pdf
(le type de premier niveau
applicaton/
indiquant que c'est un format
binaire, non utilisable en dehors des applications
spécialisées). Ce type a été enregistré à l'IANA
(section 8 du RFC).
PDF avait été conçu pour le monde du papier (les commerciaux d'Adobe répétaient dans les années 90 que PDF permettait d'avoir « le même rendu partout » ce qui n'a pas de sens sur écran, où tous les écrans sont différents), ce qui se retrouve dans de nombreux concepts archaïques de PDF comme le découpage en pages. Un document PDF est un « rendu final », typiquement non modifiable, avec du texte utilisant différentes polices, des images… PDF permet également de représenter des liens hypertexte, une table des matières… On peut même inclure du JavaScript pour avoir des documents interactifs. PDF permet également le chiffrement et la signature, et a un mécanisme (en fait, plusieurs) pour placer des métadonnées, XMP. Bref, PDF est un format très complexe, ce qui explique les nombreuses failles de sécurité rencontrées par les programmes qui lisent du PDF.
La norme PDF est désormais déposée à l'ISO (ISO 32000-1) mais l'archaïque ISO ne distribue toujours pas librement ces documents. Si on veut apprendre PDF, il faut donc le télécharger sur le site d'Adobe.
Pour les protocoles où il y a une notion d'identificateur de
fragment (comme les URI, où cet
identificateur figure après le croisillon),
PDF permet d'indiquer une partie d'un document. Cela fera partie
de la future norme ISO, mais c'était déjà dans l'ancien RFC 3778. Cet identificateur prend la forme d'un ou plusieurs
couples clé=valeur, où la clé est, par exemple,
page=N
(pour aller à la page n° N),
comment=ID
(aller à l'endroit marqué par
l'annotation ID), zoom=S
(agrandir d'un
facteur S), search=MOT
(aller à la première
occurrence de MOT)… (Je n'ai pas réussi à faire fonctionner ces
identificateurs de fragments avec le lecteur PDF inclus dans
Chrome. Quelqu'un connait un logiciel où ça marche ?)
PDF a également des sous-ensembles. La norme est riche, bien
trop riche, et il est donc utile de la restreindre. Il y a eu
plusieurs de ces sous-ensembles de PDF normalisés (voir sections 2
et 4 du RFC). Ainsi, PDF/A, sous-ensemble
de PDF pour l'archivage à long terme (ISO 19005-3:2012), limite
les possibilités de PDF, pour augmenter la probabilité que le
document soit toujours lisible dans 50 ou 100 ans. Par exemple,
JavaScript y est interdit. PDF/X (ISO 15930-8:2008), lui, vise le
cas où on envoie un fichier à un imprimeur. Il restreint également
les possibilités de PDF, pour accroitre les chances que
l'impression donne exactement le résultat attendu. Enfin,
PDF/UA (ISO 14289-1:2014) vise
l'accessibilité, en insistant sur une
structuration sémantique (et non pas fondée sur l'apparence
visuelle) du document. Tous ces sous-ensembles s'étiquettent avec
le même type application/pdf
. Ils ne sont pas
mutuellement exclusifs : un document PDF peut être à la fois PDF/A
et PDF/UA, par exemple.
Il existe d'innombrables mises en œuvre de PDF, sur toutes les plate-formes possible. Celle que j'utilise le plus sur Unix est Evince.
Un mot sur la sécurité (section 7 du RFC). On l'a dit, PDF est un format (trop) complexe, ce qui a des conséquences pour la sécurité. Comme l'impose la section 4.6 du RFC 6838, notre RFC inclut donc une analyse des risques. (Celle du RFC 3778 était trop limitée.) Notamment, PDF présente les risques suivants :
Et c'est sans compter sur les risques plus génériques, comme la complexité de l'analyseur. Il y a eu de nombreuses failles de sécurité dans les lecteurs PDF (au hasard, par exemple CVE-2011-3332 ou bien CVE-2013-3553). La revue de sécurité à l'IETF avait d'ailleurs indiqué que les premières versions du futur RFC étaient encore trop légères sur ce point, et demandait un mécanisme pour mieux étiqueter les contenus « dangereux ».
Vous avez peut-être noté (lien « Version PDF de cette page » en bas) que tous les articles de ce blog ont une version PDF, produite via LaTeX (mais elle n'est pas toujours complète, notamment pour les caractères Unicode). Une autre solution pour obtenir des PDF de mes articles est d'imprimer dans un fichier, depuis le navigateur.
La section 2 du RFC rappelle l'histoire de PDF. La première
version date de 1993. PDF a été un très
grand succès et est largement utilisé aujourd'hui. Si on
google
filetype:pdf
, on trouve « Environ 2 500 000
000 résultats » (valeur évidemment très approximative, le chiffre
rond indiquant que Google n'a peut-être pas tout compté) . Si PDF a été créé et reste largement
contrôlé par Adobe, il en existe une
version ISO, la norme 32000-1, qui date
de 2008 (pas de mise à jour depuis, bien
qu'une révision soit attendue en 2017). ISO
32000-1:2008 est identique à la version PDF 1.7 d'Adobe.
Normalement, les anciens lecteurs PDF doivent pouvoir lire les versions plus récentes, évidemment sans tenir compte des nouveautés (section 5 du RFC).
Quels sont les changements depuis l'ancienne version, celle du RFC 3778 ? La principale est que le change controller, l'organisation qui gère la norme et peut donc demander des modifications au registre IANA est désormais l'ISO et non plus Adobe. Les autres changements sont :
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : C. Huitema (Private Octopus
Inc.), D. Thaler
(Microsoft), R. Winter (University of Applied
Sciences Augsburg)
Pour information
Réalisé dans le cadre du groupe de travail IETF intarea
Première rédaction de cet article le 12 mars 2017
« Je suis l'iPhone de Jean-Luc ! » Traditionnellement, les ordinateurs connectés à l'Internet ont un nom, et ce nom est souvent annoncé à l'extérieur par divers protocoles. Cette pratique très répandue, dont l'origine remonte à l'époque où on n'avait que quelques gros serveurs partagés, et fixes, est dangereuse pour la vie privée, dans un monde de mobilité et de machines individuelles. Comme le note ce nouveau RFC, « c'est comme si on se promenait dans la rue avec une étiquette bien visible portant son nom ». Ce RFC dresse l'état des lieux, fait la liste des protocoles problématiques, et suggère, lorsqu'on ne peut pas changer le protocole, d'utiliser des noms aléatoires, ne révélant rien sur la machine.
Pour illustrer le problème, voici un exemple du trafic WiFi pendant une réunion, en n'écoutant qu'un seul protocole, mDNS (RFC 6762). Et d'autres protocoles sont tout aussi bavards. Notez que cette écoute n'a nécessité aucun privilège particulier sur le réseau (cf. RFC 8386), ni aucune compétence. N'importe quel participant à la réunion, ou n'importe quelle personne située à proximité pouvait en faire autant avec tcpdump (j'ai changé les noms des personnes) :
% sudo tcpdump -n -vvv port 5353 tcpdump: listening on wlp2s0, link-type EN10MB (Ethernet), capture size 262144 bytes 15:03:16.909436 IP6 fe80::86a:ed2c:1bcc:6540.5353 > ff02::fb.5353: 0*- [0q] 2/0/3 0.4.5.6.C.C.B.1.C.2.D.E.A.6.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.E.F.ip6.arpa. (Cache flush) [2m] PTR John-Smiths-iPhone-7.local., [...] 15:03:17.319992 IP 172.25.1.84.5353 > 224.0.0.251.5353: 0*- [0q] 2/0/3 C.4.1.6.F.8.D.E.0.3.6.3.4.1.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.8.E.F.ip6.arpa. (Cache flush) [2m] PTR Jane-iPhone.local., [...] 15:03:20.699557 IP6 fe80::e2ac:cbff:fe95:da80.5353 > ff02::fb.5353: 0 [5q] [4n] [1au] PTR (QU)? _googlecast._tcp.local. ANY (QU)? info-mac-66._smb._tcp.local. [...]
On y voit que les noms des machines présentes sont annoncés à
tous (ff02::fb
et
224.0.0.251
sont des adresses
multicast). Certains
noms sont très révélateurs (nom, prénom et type de la machine),
d'autres un peu moins (prénom et type), d'autres sont presques
opaques (juste un type de machine, très général). Un indiscret qui
regarde le trafic sur des réseaux publiquement accessibles peut
ainsi se faire une bonne idée de quelles machines sont présentes,
voire de qui est présent. Les deux exemples des noms
info-mac-66
et
John-Smiths-iPhone-7
illustrent les deux
risques. Dans le premier cas, si le nom est stable, il permet de
suivre à la trace une machine qui se déplacerait. Le second cas
est encore pire puisqu'on a directement le nom du propriétaire.
Le fait que les ordinateurs aient des noms est une tradition très ancienne (voir la définition de host name dans le RFC 8499). Un nom court (sans point à l'intérieur) combiné avec un suffixe forme un FQDN (Fully Qualified Domain Name, cf. RFC 1983). On utilise ces noms courts et ces FQDN à plein d'endroits. IP lui-même n'utilise pas de noms du tout mais plein de protocoles de la famille TCP/IP le font, comme mDNS montré plus haut.
Un nom court doit être unique dans un contexte donné mais n'est pas forcément unique mondialement. Le FQDN, lui, est unique au niveau mondial.
Je vous recommande l'excellent travail de M. Faath, F. Weisshaar et R. Winter, « How Broadcast Data Reveals Your Identity and Social Graph » à l'atelier TRAC 2016 (supports de leur exposé), montrant toutes les fuites d'information liées à cette utilisation des noms, et ce qu'un méchant peut en faire. (C'est ce groupe qui avait écouté le trafic WiFi lors d'une réunion IETF à Prague, déclenchant une grande discussion sur les attentes en matière de vie privée quand le trafic est diffusé.)
Pourquoi nomme-t-on les ordinateurs, au fait, à part la tradition ? Sur un réseau, bien des systèmes d'exploitation, à commencer par Unix et Windows tiennent pour acquis que les ordinateurs ont un nom, et ce nom peut être utilisé dans des tas de cas. Il existe plusieurs schémas de nommage (section 2 du RFC), du plus bucolique (noms de fleurs) au plus français (noms de vins) en passant par les schémas bien geeks comme les noms des personnages du Seigneur des Anneaux. Mais, parfois, c'est le système d'exploitation lui-même qui nomme l'ordinateur, en combinant le nom de l'utilisateur et les caractéristiques de l'ordinateur, comme on le voit avec les iPhones dans l'exemple tcpdump ci-dessus. (Sur les schémas de nommage, voir le RFC 1178, et, sur un ton plus léger, le RFC 2100. Il existe une excellente page Web pleine d'idées de noms. L'ISC fait des statistiques sur les noms vus sur Internet. Entre 1995 et 2017, vous pouvez constater la décroissance des noms sympas en faveur des noms utilitaires.)
Dans les environnements corporate, on ne
laisse pas l'utilisateur choisir et il y a un schéma officiel. Par
exemple, sur le réseau interne de
Microsoft, le nom est dérivé du nom de
login de l'utilisateur et un des auteurs du RFC
a donc une machine huitema-test-2
.
Est-il nécessaire de donner des noms aux « objets », ces
machines à laver ou brosses à dents connectés, qui sont des
ordinateurs, mais ne sont en général pas perçus comme tels (ce qui
a des graves conséquences en terme de sécurité) ? Comme ces engins
n'offrent en général pas de services, ils ont moins besoin d'un
nom facile à retenir, et, lorsque les protocoles réseaux employés
forcent à utiliser un nom, c'est également un nom fabriqué à
partir du nom du fabricant, du modèle de l'appareil et de son
numéro de série (donc, un nom du genre
BrandX-edgeplus-4511-2539
). On voit même
parfois la langue parlée par l'utilisateur utilisée dans ce nom,
qui est donc très « parlant ».
Même un identificateur partiel peut être révélateur (section 3
du RFC). Si on ordinateur se nomme
dthaler-laptop
, on ne peut pas être sûr qu'il
appartienne vraiment au co-auteur du RFC Dave Thaler. Il y a
peut-être d'autres D. Thaler dans le monde. Mais si on
observe cet ordinateur faire une connexion au réseau interne de
Microsoft (pas besoin de casser le
chiffrement, les
métadonnées suffisent), on est alors
raisonnablement sûr qu'on a identifié le propriétaire.
Beaucoup de gens croient à tort qu'un identificateur personnel
doit forcément inclure le nom d'état civil de l'utilisateur. Mais
ce n'est pas vrai : il suffit que l'identificateur soit stable, et
puisse être relié, d'une façon ou d'une autre, au nom de
l'utilisateur. Par exemple, si un ordinateur
portable a le nom stable a3dafaaf70950
(nom
peu parlant) et que l'observateur ait pu voir une fois cette
machine faire une connexion à un compte
IMAP jean_dupont
, on
peut donc associer cet ordinateur à Jean Dupont, et le suivre
ensuite à la trace.
Ce risque est encore plus important si l'attaquant maintient une base de données des identifications réussies (ce qui est automatisable), et des machines associées. Une ou deux fuites d'information faites il y a des mois, voire des années, et toutes les apparitions ultérieures de cette machine mèneront à une identification personnelle.
Donc, n'écoutez pas les gens qui vous parleront d'« anonymat »
parce que les noms de machine ne sont pas parlants (comme le
a3dafaaf70950
plus haut). Si quelqu'un fait
cela, cela prouve simplement qu'il ne comprend rien à la sécurité
informatique. Un nom stable, pouvant être observé (et on a vu que
bien des protocoles étaient très indiscrets), permet
l'observation, et donc la surveillance.
Justement, quels sont les protocoles qui laissent ainsi fuiter des noms de machine, que l'observateur pourra noter et enregistrer (section 4 du RFC) ? Il y a d'abord DHCP, où le message de sollicitation initial (diffusé à tous…) contient le nom de la machine en clair. Le problème de vie privée dans DHCP est analysé plus en détail dans les RFC 7819 et RFC 7824. Les solutions pour limiter les dégâts sont dans le RFC 7844.
Le DNS est également une cause de fuite,
par exemple parce qu'il permet d'obtenir le nom d'une machine à
partir de son adresse IP, avec les requêtes PTR dans
in-addr.arpa
ou ip6.arpa
, nom qui peut
réveler des détails. C'est le cas avec tout protocole conçu
justement pour distribuer des informations, comme celui du RFC 4620 (qui ne semble pas très déployé dans la nature).
Plus sérieux est le problème de mDNS (RFC 6762), illustré par le tcpdump montré plus haut. Les requêtes sont diffusées à tous sur le réseau local, et contiennent, directement ou indirectement, les noms des machines. Même chose avec le DNS Service Discovery du RFC 6763 et le LLMNR du RFC 4795 (beaucoup moins fréquent que mDNS).
Enfin, NetBIOS (quelqu'un l'utilise encore ?) est également une grande source d'indiscrétions.
Assez décrit le problème, comment le résoudre (section 5) ? Bien sûr, il faudra des protocoles moins bavards, qui ne clament pas le nom de la machine à tout le monde. Mais changer d'un coup des protocoles aussi répandus et aussi fermement installés que, par exemple, DHCP, ne va pas être facile. De même, demander aux utilisateurs de ne pas faire de requêtes DHCP lorsqu'ils visitent un réseau « non sûr » est difficile (déjà, comment l'utilisateur va-t-il correctement juger si le réseau est sûr ?), d'autant plus qu'ils risquent fort de ne pas avoir de connectivité du tout, dans ce cas. Certes, couper les protocoles non nécessaires est un bon principe de sécurité en général. Mais cet angle d'action semble quand même bien trop drastique. (Il faut aussi noter qu'il existe des protocoles privés, non-IETF, qui peuvent faire fuire des noms sans qu'on le sache. Le client Dropbox diffuse à la cantonade l'ID du client, et celui des shares où il se connecte. Il est facile de faire un graphe des utilisateurs en mettant ensemble ceux qui se connectent au même share.)
La suggestion de notre RFC est donc d'attaquer le problème d'une autre façon, en changeant le nom de la machine, pour lui substituer une valeur imprévisible (comme le fait le RFC 7844 pour les adresses MAC). Pour chaque nouveau réseau où est connectée la machine, on génère aléatoirement un nouveau nom, et c'est celui qu'on utilisera dans les requêtes DHCP ou mDNS. Ces protocoles fonctionneront toujours mais la surveillance des machines mobiles deviendra bien plus difficile. Bien sûr, pour empêcher toute corrélation, le changement de nom doit être coordonné avec les changements des autres identificateurs, comme l'adresse IP ou l'adresse MAC.
Windows a même un concept de « nom de machine par réseau », ce qui permet aux machines ayant deux connexions de présenter deux identités différentes (malheureusement, Unix n'a pas ce concept, le nom est forcément global).
Bien sûr, on n'a rien sans rien (section 6). Si on change les noms des machines, on rendra l'administration système plus difficile. Par exemple, l'investigation sur un incident de sécurité sera plus complexe. Mais la défense de la vie privée est à ce prix.
Pour l'instant, à ma connaissance, il n'y a pas encore de mise en œuvre de cette idée de noms imprévisibles et changeants. (Une proposition a été faite pour Tails. Notez qu'il existe d'autres possibilités comme d'avoir un nom unique partout.)
Date de publication du RFC : Mai 2017
Auteur(s) du RFC : D. Farinacci (lispers.net), A. Jain
(Juniper
Networks), I. Kouvelas, D. Lewis
(cisco Systems)
Expérimental
Première rédaction de cet article le 27 mai 2017
Ce RFC concerne les utilisateurs de LISP (le protocole réseau, pas le langage de programmation). Il décrit un nouvel outil, rig, le Referral Internet Groper, qui permet d'interroger les tables de correspondance identificateur->localisateur.
Un point important de LISP (RFC 9300) est en effet cette séparation de l'EID (l'identificateur d'une machine) et du RLOC (le localisateur de cette machine, qui indique où envoyer les paquets). Tout système ayant cette séparation doit maintenir une correspondance (mapping) entre les deux : lorsque je veux écrire à telle machine dont je connais l'EID, il faut que je trouve le localisateur. LISP permet plusieurs mécanismes pour cette correspondance. L'outil rig, présenté dans ce RFC, est conçu pour le mécanisme DDT (RFC 8111), une base de données arborescente et répartie. rig est donc un client DDT de débogage, lig (RFC 6835) étant un autre outil, plus général (il peut interroger d'autres bases que DDT).
Un client DDT comme rig (ou comme un routeur LISP lors de son
fonctionnement normal) va donc
envoyer des Map-Request
(RFC 9300, section 4.2, et aussi RFC 9301) aux serveurs DDT.
La section 4 de notre RFC résume le fonctionnement de rig. Il
envoie le Map-Request
et affiche le
Map-Referral
de réponse. Il peut ensuite suivre
cette référence jusqu'à arriver au Map Server
qui gère ce préfixe. (Notez que c'est le RLOC du Map
Server qu'on obtient, sinon, on aurait un intéressant
problème d'œuf et de poule si on avait
besoin de DDT pour utiliser DDT...)
rig a donc besoin d'au moins deux paramètres, l'EID (l'identificateur) qu'on cherche à résoudre, et le serveur DDT par lequel on va commencer la recherche. (Pour l'EID, rig accepte également un nom de domaine, qu'il va traduire en EID dans le DNS.) La syntaxe typique est donc :
rig <eid> to <ddt-node>
La section 5 décrit les mises en œuvres existantes, sur les routeurs Cisco. La syntaxe est un peu différente de ce que je trouve dans la doc' de Cisco mais, bon, tout ceci est expérimental et en pleine évolution. Voici un exemple tiré de la documentation officielle de Cisco (LISP DDT Configuration Commands) :
Device# lisp-rig 172.16.17.17 to 10.1.1.1 rig LISP-DDT hierarchy for EID [0] 172.16.17.17 Send Map-Request to DDT-node 10.1.1.1 ... replied, rtt: 0.007072 secs EID-prefix [0] 172.16.17.16/28, ttl: 1, action: ms-not-registered, referrals: 10.1.1.1, priority/weight: 0/0 10.2.1.1, priority/weight: 0/0 10.3.1.1, priority/weight: 0/0
Et voilà, on sait que l'EID
172.16.17.17
, il faut aller demander aux
serveurs 10.1.1.1
,
10.2.1.1
et
10.3.1.1
. Dans le RFC, on trouve un exemple
où rig suit ces références :
Router# rig 12.0.1.1 to 1.1.1.1 Send Map-Request to DDT-node 1.1.1.1 ... node referral, rtt: 4 ms EID-prefix: [0] 12.0.0.0/16, ttl: 1440 referrals: 2.2.2.2 Send Map-Request to DDT-node 2.2.2.2 ... node referral, rtt: 0 ms EID-prefix: [0] 12.0.1.0/24, ttl: 1440 referrals: 4.4.4.4, 5.5.5.5 Send Map-Request to DDT-node 4.4.4.4 ... map-server acknowledgement, rtt: 0 ms EID-prefix: [0] 12.0.1.0/28, ttl: 1440 referrals: 4.4.4.4, 5.5.5.5
Si vous voulez en savoir plus sur DDT et rig, vous pouvez aussi regarder l'exposé de Cisco ou celui de Paul Vinciguerra à NANOG, ou bien la page officielle de la racine DDT (qui semble peu maintenue).
Date de publication du RFC : Mai 2017
Auteur(s) du RFC : V. Fuller, D. Lewis, V. Ermagan
(Cisco), A. Jain (Juniper
Networks), A. Smirnov (Cisco)
Expérimental
Réalisé dans le cadre du groupe de travail IETF lisp
Première rédaction de cet article le 27 mai 2017
Le protocole LISP (dont on ne rappelera jamais assez qu'il ne faut pas le confondre avec le langage de programmation du même nom) sépare les deux rôles de l'adresse IP, identificateur et localisateur. C'est très joli de séparer, cela permet plein de choses intéressantes, comme de lutter contre la croissance illimitée de la DFZ mais cela présente un défi : comment obtenir un localisateur quand on a un identificateur ? Dit autrement, « où est cette fichue machine que j'essaie de joindre ? » Ajouter une indirection, en informatique, oblige toujours à créer un système de correspondance permettant de passer par dessus le fossé qu'on vient juste de créer. LISP a plusieurs systèmes de correspondance possibles, tous expérimentaux, et ce nouveau DDT (Delegated Database Tree) vient les rejoindre. C'est le système qui est le plus proche du DNS dans ses concepts. Comme je connais un peu le DNS, j'utiliserai souvent dans cet article des comparaisons avec le DNS.
Pour résumer DDT en un paragraphe : dans LISP (RFC 9300), l'identificateur se nomme EID (Endpoint Identifier) et le localisateur RLOC (Routing Locator). Les EID ont une structure arborescente (leur forme syntaxique est celle d'adresses IP). Cet arbre est réparti sur plusieurs serveurs, les nœuds DDT. Un nœud DDT fait autorité pour un certain nombre de préfixes d'EID. Il délègue ensuite les sous-préfixes à d'autres nœuds DDT, ou bien à des Map Servers LISP (RFC 9301) quand on arrive en bas de l'arbre. (Une des différences avec le DNS est donc que les serveurs qui délèguent sont d'une nature distincte de ceux qui stockent les feuilles de l'arbre.)
LISP a une interface standard avec les serveurs qui font la
résolution d'EID en RLOC, décrite dans le RFC 9301. En gros, le client envoie un message
Map-Request
et obtient une réponse
Map-Reply
, ou bien une délégation
(Map-Referral
) qu'il va devoir suivre en
envoyant le Map-Request
suivant au RLOC
indiqué dans la délégation. Derrière cette interface, LISP ne spécifie pas
comment les serveurs obtiennent l'information. Plusieurs
propositions ont déjà été faites (comme ALT, dans le RFC 6836, ou NERD, dans le
RFC 6837…), auxquelles s'ajoute celle de
notre RFC. Un bon résumé est dans cette
image (mais qui ne montre qu'un seul niveau de délégation, il
peut y en avoir davantage.)
DDT vise avant tout le passage à l'échelle, d'où la structuration hiérarchique de l'information. La notion de délégation (d'un préfixe général à un sous-préfixe plus spécifique) est centrale dans DDT. Un client (le routeur LISP qui a un paquet destiné à un EID donné et qui cherche à quel RLOC le transmettre, ou bien un résolveur, un serveur spécialisé agissant pour le compte de ce routeur) va donc devoir suivre cette délégation, descendant l'arbre jusqu'à l'information souhaitée.
La délégation est composée, pour un préfixe délégué, d'un ensemble de RLOC (pas d'EID pour éviter des problèmes d'œuf et de poule) désignant les nœuds qui ont une information sur le préfixe délégué. (Ce sont donc l'équivalent des enregistrements NS du DNS, mais avec une indirection en moins, comme si la partie droite d'un enregistrement NS stockait directement une adresse IP.)
J'ai écrit jusque là que la clé d'accès à l'information (rôle tenu par le nom de domaine dans le DNS) était l'EID mais c'est en fait un peu plus compliqué : la clé est le XEID (eXtended EID), qui est composé de plusieurs valeurs, dont l'EID (section 4.1 de notre RFC).
Pour indiquer au résolveur qu'il doit transmettre sa requête à
une autre machine, ce RFC crée un nouveau type de message LISP,
Map-Referral
, type 6 (cf. le registre IANA)
détaillé en section 6,
envoyé en réponse à un Map-Request
, quand le
nœud DDT ne connait pas la réponse. (Comme indiqué plus haut,
c'est l'équivalent d'une réponse
DNS avec uniquement une section Autorité contenant des
enregistrements NS.)
Continuons un peu la terminologie (section 3 du RFC) :
Map-Request
, cf. RFC 9301) et suit les
Map-Referral
jusqu'au résultat. C'est en
général un résolveur (Map Resolver, RFC 9301) mais cela peut être aussi un routeur
LISP (ITR, Ingress Tunnel Router).Map-Request
, et client DDT de
l'autre, il envoie des requêtes DDT. Il gère un cache (une
mémoire des réponses récentes). Le résolveur maintient également
une liste des requêtes en cours, pas encore satisfaites.La base des données des serveurs DDT est décrite en section
4. Elle est indexée par XEID. Un XEID est un EID (identificateur
LISP) plus un AFI (Address Family Identifier,
1 pour IPv4, 2 pour IPv6, etc), un identificateur d'instance (voir RFC 9300, section 3, et RFC 8060, section 4.1) qui sert à avoir plusieurs espaces d'adressage, et quelques autres paramètres, pas
encore utilisés. Configurer un serveur DDT, c'est lui indiquer la
liste de XEID qu'il doit connaitre, avec les RLOC des serveurs qui
pourront répondre. Désolé, je n'ai pas de serveur DDT sous la main
mais on peut trouver un exemple, dans la
documentation de Cisco, où on délègue au Map
Server de RLOC 10.1.1.1
:
router lisp ddt authoritative 2001:db8:eeee::/48 delegate 10.1.1.1 eid-prefix 172.16.0.0/16 delegate 10.1.1.1 eid-prefix 2001:db8:eeee::/48
Un autre exemple de délégation est l'actuelle liste des données dans la racine DDT.
Le DNS n'a qu'un type de serveurs faisant autorité, qu'ils soient essentiellement serveurs de délégation (ceux des TLD, par exemple) ou qu'ils soient serveurs « finaux » contenant les enregistrements autres que NS. Au contraire, LISP+DDT a deux types de serveurs, les nœuds DDT présentés dans ce RFC, qui ne font que de la délégation, et les traditionnels Map Servers, qui stockent les correspondances entre EID et RLOC (entre identificateurs et localisateurs). Dit autrement, DDT ne sert pas à trouver la réponse à la question « quel est le RLOC pour cet EID », il sert uniquement à trouver le serveur qui pourra répondre à cette question.
Comme pour le DNS, il existe une racine, le nœud qui peut
répondre (enfin, trouver une délégation) pour tout XEID. (Sur le
Cisco cité plus haut, la directive ddt root
permettra d'indiquer le RLOC des serveurs de la racine, voir aussi
la section 7.3.1 de notre RFC.) Une
racine expérimentale existe, vous trouverez ses RLOC en
http://ddt-root.org/
.
La section 5 de notre RFC décrit en détail la modification au
message Map-Request
que nécessite DDT. Ce
message était normalisé par le RFC 9300
(section 6.1.2) et un seul ajout est fait : un bit qui était
laissé vide sert désormais à indiquer que la requête ne vient pas
directement d'un routeur LISP mais est passée par des nœuds DDT.
La section 6, elle, décrit un type de message nouveau,
Map-Referral
, qui contient les RLOC du nœud
DDT qui pourra mieux répondre à la question. Cette réponse contient un code
qui indique le résultat d'un Map-Request
. Ce
résultat peut être « positif » :
NODE-REFERRAL
, renvoi à un autre nœud DDT,MS-REFERRAL
, renvoi à un Map
Server (rappelez-vous que, contrairement au DNS, il y
a une nette distinction entre nœud intermédiaire et Map
Server final),MS-ACK
, réponse positive d'un
Map Server.Mais aussi des résultats « négatifs » :
MS-NOT-REGISTERED
, le Map
Server ne connait pas cet EID,DELEGATION-HOLE
, l'EID demandé
tombe dans un trou (préfixe non-LISP dans un préfixe LISP),NOT-AUTHORITATIVE
, le nœud DDT n'a
pas été configuré pour ce préfixe.Le fonctionnement global est décrit en détail dans la section 7
du RFC. À lire si on veut savoir exactement ce que doivent faire
le Map Resolver, le Map
Server, et le nouveau venu, le nœud DDT. La même
description figure sous forme de
pseudo-code dans la section 8. Par exemple,
voici ce que doit faire un nœud DDT lorsqu'il reçoit un
Map-Request
(demande de résolution d'un EID
en RLOC) :
if ( I am not authoritative ) { send map-referral NOT_AUTHORITATIVE with incomplete bit set and ttl 0 } else if ( delegation exists ) { if ( delegated map-servers ) { send map-referral MS_REFERRAL with ttl 'Default_DdtNode_Ttl' } else { send map-referral NODE_REFERRAL with ttl 'Default_DdtNode_Ttl' } } else { if ( eid in site) { if ( site registered ) { forward map-request to etr if ( map-server peers configured ) { send map-referral MS_ACK with ttl 'Default_Registered_Ttl' } else { send map-referral MS_ACK with ttl 'Default_Registered_Ttl' and incomplete bit set } } else { if ( map-server peers configured ) { send map-referral MS_NOT_REGISTERED with ttl 'Default_Configured_Not_Registered_Ttl' } else { send map-referral MS_NOT_REGISTERED with ttl 'Default_Configured_Not_Registered_Ttl' and incomplete bit set } } } else { send map-referral DELEGATION_HOLE with ttl 'Default_Negative_Referral_Ttl' } }
Un exemple complet et détaillé figure dans la section 9, avec description de tous les messages envoyés.
Question sécurité, je vous renvoie à la section 10 du RFC. DDT dispose d'un mécanisme de signature des messages (l'équivalent de ce qu'est DNSSEC pour le DNS). La délégation inclut les clés publiques des nœuds à qui on délègue.
Il existe au moins deux mises en œuvre de DDT, une chez Cisco et l'autre chez OpenLisp. (Le RFC ne sort que maintenant mais le protocole est déployé depuis des années.)
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : P. Koch
(DENIC), M. Larson, P. Hoffman
(ICANN)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 16 mars 2017
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 (initialisation ?), opération que décrit ce RFC.
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 ...
Cette configuration initiale du résolveur est décrite dans la section 2.3 du RFC 1034, mais ce dernier ne décrit pas réellement le priming (quoi que dise notre nouveau RFC), priming que tous les résolveurs actuels mettent en œuvre. 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, là ou encore 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 !
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. C'est ainsi que fonctionnent les racines alternatives comme Yeti. Si on veut utiliser cette racine expérimentale et pas la racine « officielle », on édite la configuration de son résolveur :
server: root-hints: "yeti-hints"
Et le fichier, téléchargé chez Yeti, contient :
. 3600000 IN NS bii.dns-lab.net bii.dns-lab.net 3600000 IN AAAA 240c:f:1:22::6 . 3600000 IN NS yeti-ns.tisf.net yeti-ns.tisf.net 3600000 IN AAAA 2001:559:8000::6 . 3600000 IN NS yeti-ns.wide.ad.jp yeti-ns.wide.ad.jp 3600000 IN AAAA 2001:200:1d9::35 . 3600000 IN NS yeti-ns.as59715.net ...
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 @k.root-servers.net NS . ; <<>> DiG 9.10.3-P4-Debian <<>> +norecurse +nodnssec @k.root-servers.net NS . ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42123 ;; flags: qr aa; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 27 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;. IN NS ;; ANSWER SECTION: . 518400 IN NS a.root-servers.net. . 518400 IN NS b.root-servers.net. . 518400 IN NS c.root-servers.net. . 518400 IN NS d.root-servers.net. . 518400 IN NS e.root-servers.net. . 518400 IN NS f.root-servers.net. . 518400 IN NS g.root-servers.net. . 518400 IN NS h.root-servers.net. . 518400 IN NS i.root-servers.net. . 518400 IN NS j.root-servers.net. . 518400 IN NS k.root-servers.net. . 518400 IN NS l.root-servers.net. . 518400 IN NS m.root-servers.net. ;; ADDITIONAL SECTION: a.root-servers.net. 518400 IN A 198.41.0.4 a.root-servers.net. 518400 IN AAAA 2001:503:ba3e::2:30 b.root-servers.net. 518400 IN A 192.228.79.201 b.root-servers.net. 518400 IN AAAA 2001:500:84::b c.root-servers.net. 518400 IN A 192.33.4.12 c.root-servers.net. 518400 IN AAAA 2001:500:2::c d.root-servers.net. 518400 IN A 199.7.91.13 d.root-servers.net. 518400 IN AAAA 2001:500:2d::d e.root-servers.net. 518400 IN A 192.203.230.10 e.root-servers.net. 518400 IN AAAA 2001:500:a8::e f.root-servers.net. 518400 IN A 192.5.5.241 f.root-servers.net. 518400 IN AAAA 2001:500:2f::f g.root-servers.net. 518400 IN A 192.112.36.4 g.root-servers.net. 518400 IN AAAA 2001:500:12::d0d h.root-servers.net. 518400 IN A 198.97.190.53 h.root-servers.net. 518400 IN AAAA 2001:500:1::53 i.root-servers.net. 518400 IN A 192.36.148.17 i.root-servers.net. 518400 IN AAAA 2001:7fe::53 j.root-servers.net. 518400 IN A 192.58.128.30 j.root-servers.net. 518400 IN AAAA 2001:503:c27::2:30 k.root-servers.net. 518400 IN A 193.0.14.129 k.root-servers.net. 518400 IN AAAA 2001:7fd::1 l.root-servers.net. 518400 IN A 199.7.83.42 l.root-servers.net. 518400 IN AAAA 2001:500:9f::42 m.root-servers.net. 518400 IN A 202.12.27.33 m.root-servers.net. 518400 IN AAAA 2001:dc3::35 ;; Query time: 3 msec ;; SERVER: 2001:7fd::1#53(2001:7fd::1) ;; WHEN: Fri Mar 03 17:29:05 CET 2017 ;; MSG SIZE rcvd: 811
(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 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 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 :
% dig @k.root-servers.net A g.root-servers.net ; <<>> DiG 9.10.3-P4-Debian <<>> @k.root-servers.net A g.root-servers.net ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49091 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 13, ADDITIONAL: 26 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;g.root-servers.net. IN A ;; ANSWER SECTION: g.root-servers.net. 3600000 IN A 192.112.36.4 ...
Vous pouvez voir ici les requêtes et réponses de priming d'un Unbound utilisant Yeti. D'abord, décodées par tcpdump :
20:31:36.226325 IP6 2001:4b98:dc2:43:216:3eff:fea9:41a.7300 > 2a02:cdc5:9715:0:185:5:203:53.53: 50959% [1au] NS? . (28) 20:31:36.264584 IP6 2a02:cdc5:9715:0:185:5:203:53.53 > 2001:4b98:dc2:43:216:3eff:fea9:41a.7300: 50959*- 26/0/7 NS bii.dns-lab.net., NS yeti.bofh.priv.at., NS yeti.ipv6.ernet.in., NS yeti.aquaray.com., NS yeti.mind-dns.nl., NS dahu1.yeti.eu.org., NS dahu2.yeti.eu.org., NS yeti1.ipv6.ernet.in., NS ns-yeti.bondis.org., NS yeti-ns.ix.ru., NS yeti-ns.lab.nic.cl., NS yeti-ns.tisf.net., NS yeti-ns.wide.ad.jp., NS yeti-ns.conit.co., NS yeti-ns.datev.net., NS yeti-ns.switch.ch., NS yeti-ns.as59715.net., NS yeti-ns1.dns-lab.net., NS yeti-ns2.dns-lab.net., NS yeti-ns3.dns-lab.net., NS xn--r2bi1c.xn--h2bv6c0a.xn--h2brj9c., NS yeti-dns01.dnsworkshop.org., NS yeti-dns02.dnsworkshop.org., NS 3f79bb7b435b05321651daefd374cd.yeti-dns.net., NS ca978112ca1bbdcafac231b39a23dc.yeti-dns.net., RRSIG (1225)
Et ici par tshark :
1 0.000000 2001:4b98:dc2:43:216:3eff:fea9:41a → 2a02:cdc5:9715:0:185:5:203:53 DNS 90 Standard query 0xc70f NS <Root> OPT 2 0.038259 2a02:cdc5:9715:0:185:5:203:53 → 2001:4b98:dc2:43:216:3eff:fea9:41a DNS 1287 Standard query response 0xc70f NS <Root> NS bii.dns-lab.net NS yeti.bofh.priv.at NS yeti.ipv6.ernet.in NS yeti.aquaray.com NS yeti.mind-dns.nl NS dahu1.yeti.eu.org NS dahu2.yeti.eu.org NS yeti1.ipv6.ernet.in NS ns-yeti.bondis.org NS yeti-ns.ix.ru NS yeti-ns.lab.nic.cl NS yeti-ns.tisf.net NS yeti-ns.wide.ad.jp NS yeti-ns.conit.co NS yeti-ns.datev.net NS yeti-ns.switch.ch NS yeti-ns.as59715.net NS yeti-ns1.dns-lab.net NS yeti-ns2.dns-lab.net NS yeti-ns3.dns-lab.net NS xn--r2bi1c.xn--h2bv6c0a.xn--h2brj9c NS yeti-dns01.dnsworkshop.org NS yeti-dns02.dnsworkshop.org NS 3f79bb7b435b05321651daefd374cd.yeti-dns.net NS ca978112ca1bbdcafac231b39a23dc.yeti-dns.net RRSIG AAAA 240c:f:1:22::6 AAAA 2a01:4f8:161:6106:1::10 AAAA 2001:e30:1c1e:1:
Et un décodage plus détaillé de tshark dans ce fichier.
Enfin, la section 5 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, 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. 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.
Ce RFC a connu une très longue gestation puisque le premier brouillon date de février 2007 (vous pouvez admirer la chronologie).
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : J. Jeong (Sungkyunkwan
University), S. Park (Samsung
Electronics), L. Beloeil (France Telecom
R&D), S. Madanapalli (iRam
Technologies)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 18 mars 2017
Il existe deux méthodes pour configurer une machine IPv6 automatiquement, DHCP (RFC 8415) et RA (Router Advertisement, RFC 4862). Toutes les deux peuvent indiquer d'autres informations que l'adresse IP, comme par exemple les adresses des résolveurs DNS. Notre RFC normalise cette possibilité pour les RA. Il remplace le RFC 6106, avec peu de changements.
Si on gère un gros réseau, avec de nombreuses machines dont
certaines, portables, vont et viennent, s'assurer que toutes ces
machines ont les adresses IP
des serveurs de noms à utiliser n'est pas trivial (section 1 du RFC). On ne peut évidemment pas utiliser le DNS, cela serait tenter de voler en tirant
sur les lacets de ses chaussures. Et configurer à la main les adresses
sur chaque machine (par exemple, sur Unix, en
les écrivant dans le fichier /etc/resolv.conf
)
est bien trop difficile à maintenir. Se passer du DNS est hors de
question. Pour les machines bi-protocoles (IPv4
et IPv6), une solution possible était
d'utiliser un serveur de noms en v4. Mais pour une solution purement v6 ?
La solution la plus populaire était DHCP (RFC 8415 et RFC 3646). Son principal inconvénient est qu'elle est à état : le serveur DHCP doit se souvenir des baux qu'il a attribué. Sur un gros réseau local, le nombre de requêtes à traiter, chacune nécessitant une écriture dans une base de données, peut devenir très lourd.
Une autre solution est sans état et repose sur
une nouveauté d'IPv6, les RA (Router
Advertisements, cette méthode est aussi appelée ND, pour
Neighbor Discovery, les RA en étant un cas particulier), décrits dans le RFC 4862. Ce sont des messages envoyés à intervalles
réguliers par les routeurs et
qui informent les machines non-routeuses des caractéristiques
essentielles du réseau, comme le préfixe utilisé (par exemple
2001:db8:beef:42::/64
). Le routeur diffuse ses
messages et n'a pas besoin d'écrire quoi que ce soit sur son disque,
ni de faire des traitements compliqués lors d'une sollicitation, il
répond toujours par le même message RA.
Ces RA peuvent diffuser diverses informations, par le biais d'un système d'options. Le principe de notre RFC est donc d'utiliser ces RA pour transporter l'information sur les serveurs de noms récursifs utilisables sur le réseau local, via des options notamment celle nommée RDNSS (le numéro 25 lui a été affecté par l'IANA).
La section 1.1 du RFC rappelle qu'il existe plusieurs choix, notre RFC 8106 n'étant qu'une possibilité parmi d'autres. Le RFC 4339 contient une discussion plus détaillée de ce problème du choix d'une méthode de configuration des serveurs de noms (notons qu'il existe d'autres méthodes comme l'anycast avec une adresse « bien connue »). La section 1.2 décrit ce qui se passe lorsque plusieurs méthodes (par exemple DHCP et RA) sont utilisées en même temps.
La méthode RA décrite dans notre RFC repose sur deux options, RDNSS, déjà citée, et DNSSL (section 4). La première permet de publier les adresses des serveurs de noms, la seconde une liste de domaine à utiliser pour compléter les noms courts (formés d'un seul composant). Les valeurs pour ces deux options doivent être configurées dans le routeur qui va lancer les RA. (Le routeur Turris Omnia le fait automatiquement. Si on veut changer les paramètres, voici comment faire. En général, pour OpenWrt, il faut lire cette documentation, l'ancien logiciel radvd n'étant plus utilisé.)
La première option, RDNSS, de numéro 25, est décrite en section 5.1. Elle indique une liste d'adresse IPv6 que le client RA mettra dans sa liste locale de serveurs de noms interrogeables.
La seconde option, DNSSL, de numéro 31, est en section 5.2 (les
deux options sont enregistrées dans le registre
IANA, cf. section 8). Elle
publie une liste de domaines, typiquement ceux qui, sur une machine
Unix, se retrouveront dans l'option
search
de /etc/resolv.conf
.
Sur Linux, le démon rdnssd permet de recevoir ces RA et de modifier la configuration DNS. Pour FreeBSD, on peut consulter une discussion sur leur liste. Les CPE de Free, les Freebox, émettent de telles options dans leurs RA (apparemment, la dernière fois que j'ai regardé, uniquement des RDNSS). Voici ce qu'affiche Wireshark :
... Ethernet II, Src: FreeboxS_c3:83:23 (00:07:cb:c3:83:23), Dst: IPv6mcast_00:00:00:01 (33:33:00:00:00:01) ... Internet Control Message Protocol v6 Type: 134 (Router advertisement) ... ICMPv6 Option (Recursive DNS Server) Type: Recursive DNS Server (25) Length: 40 Reserved Lifetime: 600 Recursive DNS Servers: 2a01:e00::2 (2a01:e00::2) Recursive DNS Servers: 2a01:e00::1 (2a01:e00::1)
et les serveurs DNS annoncés répondent correctement. (Vous pouvez récupérer le paquet entier sur pcapr.net.)
Autre mise en œuvre de ces options, dans radvd (ainsi que pour les logiciels auxiliaires). Wireshark, on l'a vu, sait décoder ces options.
La section 6 de notre RFC donne des conseils aux programmeurs qui voudraient mettre en œuvre ce document. Par exemple, sur un système d'exploitation où le client RA tourne dans le noyau (pour configurer les adresses IP) et où la configuration DNS est dans l'espace utilisateur, il faut prévoir un mécanisme de communication, par exemple un démon qui interroge le noyau régulièrement pour savoir s'il doit mettre à jour la configuration DNS.
RA pose divers problèmes de sécurité, tout comme DHCP, d'ailleurs. Le problème de ces techniques est qu'elles sont conçues pour faciliter la vue de l'utilisateur et de l'administrateur réseau et que « faciliter la vie » implique en général de ne pas avoir de fonctions de sécurité difficiles à configurer. La section 7 traite de ce problème, par exemple du risque de se retrouver avec l'adresse d'un serveur DNS méchant qui vous redirigerait Dieu sait où (les RA ne sont pas authentifiés). Ce risque n'a rien de spécifique aux options DNS, toute la technique RA est vulnérable (par exemple, avec un faux Neighbor Advertisement). Donc, notre RFC n'apporte pas de risque nouveau (cf. RFC 6104). Si on considère cette faiblesse de sécurité comme insupportable, la section 7.2 recommande d'utiliser le RA guard du RFC 6105, ou bien SEND (RFC 3971, mais il est nettement moins mis en avant que dans le précédent RFC).
Ce problème d'une auto-configuration simple des machines connectées à IPv6 est évidemment particulièrement important pour les objets connectés et c'est sans doute pour cela que le RFC contient la mention « This document was supported by Institute for Information & communications Technology Promotion (IITP) grant funded by the Korea government (MSIP) [10041244, Smart TV 2.0 Software Platform] ».
Les changements faits depuis le précédent RFC, le RFC 6106, figurent dans l'annexe A. On y trouve notamment :
MaxRtrAdvInterval
à trois fois sa valeur, soit
1 800 secondes avec la valeur par défaut de cette variable), pour diminuer le nombre de cas où
l'information expire parce que le réseau perdait trop de
paquets,fe80::/10
), comme
adresses de résolveurs DNS,À noter que ce RFC n'intègre pas encore les résolveurs sécurisés du RFC 7858, car il se contente de réviser un RFC existant. Il n'y a donc pas de moyen de spécifier un résolveur sécurisé, pas de port 853.
Et pour finir, voici le RA émis par défaut par le routeur Turris, décodé par Wireshark :
Internet Protocol Version 6, Src: fe80::da58:d7ff:fe00:4c9e, Dst: ff02::1 0110 .... = Version: 6 .... 0000 0000 .... .... .... .... .... = Traffic class: 0x00 (DSCP: CS0, ECN: Not-ECT) .... 0000 00.. .... .... .... .... .... = Differentiated Services Codepoint: Default (0) .... .... ..00 .... .... .... .... .... = Explicit Congestion Notification: Not ECN-Capable Transport (0) .... .... .... 0101 1110 1011 0100 0001 = Flow label: 0x5eb41 Payload length: 152 Next header: ICMPv6 (58) Hop limit: 255 Source: fe80::da58:d7ff:fe00:4c9e [Source SA MAC: CzNicZSP_00:4c:9e (d8:58:d7:00:4c:9e)] Destination: ff02::1 [Source GeoIP: Unknown] [Destination GeoIP: Unknown] Internet Control Message Protocol v6 Type: Router Advertisement (134) Code: 0 Checksum: 0x35ed [correct] [Checksum Status: Good] Cur hop limit: 64 Flags: 0x80 1... .... = Managed address configuration: Set .0.. .... = Other configuration: Not set ..0. .... = Home Agent: Not set ...0 0... = Prf (Default Router Preference): Medium (0) .... .0.. = Proxy: Not set .... ..0. = Reserved: 0 Router lifetime (s): 1800 Reachable time (ms): 0 Retrans timer (ms): 0 ICMPv6 Option (Source link-layer address : d8:58:d7:00:4c:9e) Type: Source link-layer address (1) Length: 1 (8 bytes) Link-layer address: CzNicZSP_00:4c:9e (d8:58:d7:00:4c:9e) ICMPv6 Option (MTU : 1480) Type: MTU (5) Length: 1 (8 bytes) Reserved MTU: 1480 ICMPv6 Option (Prefix information : fde8:9fa9:1aba::/64) Type: Prefix information (3) Length: 4 (32 bytes) Prefix Length: 64 Flag: 0xc0 1... .... = On-link flag(L): Set .1.. .... = Autonomous address-configuration flag(A): Set ..0. .... = Router address flag(R): Not set ...0 0000 = Reserved: 0 Valid Lifetime: 7200 Preferred Lifetime: 1800 Reserved Prefix: fde8:9fa9:1aba:: ICMPv6 Option (Prefix information : 2a01:e35:8bd9:8bb0::/64) Type: Prefix information (3) Length: 4 (32 bytes) Prefix Length: 64 Flag: 0xc0 1... .... = On-link flag(L): Set .1.. .... = Autonomous address-configuration flag(A): Set ..0. .... = Router address flag(R): Not set ...0 0000 = Reserved: 0 Valid Lifetime: 7200 Preferred Lifetime: 1800 Reserved Prefix: 2a01:e35:8bd9:8bb0:: ICMPv6 Option (Route Information : Medium fde8:9fa9:1aba::/48) Type: Route Information (24) Length: 3 (24 bytes) Prefix Length: 48 Flag: 0x00 ...0 0... = Route Preference: Medium (0) 000. .000 = Reserved: 0 Route Lifetime: 7200 Prefix: fde8:9fa9:1aba:: ICMPv6 Option (Recursive DNS Server fde8:9fa9:1aba::1) Type: Recursive DNS Server (25) Length: 3 (24 bytes) Reserved Lifetime: 1800 Recursive DNS Servers: fde8:9fa9:1aba::1 ICMPv6 Option (Advertisement Interval : 600000) Type: Advertisement Interval (7) Length: 1 (8 bytes) Reserved Advertisement Interval: 600000
On y voit l'option RDNSS (l'avant-dernière) mais pas de DNSSL.
Merci à Alexis La Goutte pour ses informations.
Date de publication du RFC : Mai 2017
Auteur(s) du RFC : P. Mariager, J. Petersen (RTX A/S), Z. Shelby (ARM), M. Van de Logt (Gigaset Communications GmbH), D. Barthel (Orange Labs)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6lo
Première rédaction de cet article le 3 mai 2017
Tout le monde connait DECT, la technique utilisée, depuis vingt ans, entre votre téléphone sans fil à la maison, et sa base. DECT ne sert pas que pour la voix, il peut aussi être utilisé pour transmettre des données, par exemple de capteurs situés dans une « maison intelligente ». DECT a une variante « basse consommation », DECT ULE (pour Ultra Low Energy) qui est spécialement conçue pour des objets connectés ayant peu de réserves d'énergie. Elle vise donc la domotique, s'appuyant sur la vaste distribution de DECT, la disponibilité de composants bon marché et très diffusés, de fréquences dédiées, etc. Il ne restait donc plus qu'à faire passer IPv6 sur DECT ULE. C'est ce que normalise ce RFC, qui réutilise les techniques 6LoWPAN (RFC 4919).
DECT est normalisé par l'ETSI sous le numéro 300.175 (et la norme est en ligne). ULE s'appuie sur DECT pour le cas particulier des engins sans guère de réserves énergétiques. Il est normalisé dans les documents ETSI 102.939-1 et 102.939-2. Dans la terminologie DECT, le FP (Fixed Part) est la base, le PP (Portable Part) le téléphone sans fil (ou bien, dans le cas d'ULE, le capteur ou l'actionneur ). Le FP peut être connecté à l'Internet (et c'est évidemment là qu'IPv6 est important).
Un réseau DECT ULE typique serait une maison où des capteurs (les PP) mesureraient des informations et les transmettraient au FP qui, étant doté de capacités de calcul et de réserves d'énergie suffisantes, les traiterait, en ferait des jolis graphiques, aurait une interface Web, etc. Le RFC cite un autre exemple où une personne âgée serait munie d'un pendentif qui enverrait des signaux de temps en temps (consommant peu d'énergie) mais permettrait d'établir une liaison vocale avec le FP (et, de là, avec les services médicaux) en cas d'urgence.
Et IPv6 ? Il permettrait d'avoir une communication avec tous les équipements IP. IPv6 fonctionne déjà sur une autre technologie similaire, IEEE 802.15.4, avec le système 6LoWPAN (RFC 4944, RFC 6282 et RFC 6775). Comme DECT ULE ressemble à 802.15.4 (mais est un protocole différent, attention), ce RFC décrit comment faire passer de l'IPv6, en s'inspirant de ce qui marchait déjà pour 802.15.4.
La section 2 du RFC rappelle ce qu'il faut savoir de DECT et d'ULE. ULE permet le transport de la voix et des données mais ce RFC ne se préoccupe que des données. Le protocole utilise les bandes de fréquence entre 1880 et 1920 MHz, à 1,152 Mbauds. La topologie théorique est celle d'un réseau cellulaire mais, en pratique, DECT est la plupart du temps organisé en étoile, un FP (la base) au « centre » et des PP (téléphones et capteurs) qui lui sont rattachés. Toute session peut commencer à l'initiative du FP ou d'un PP mais attention : avec ULE, bien des PP seront des engins aux batteries limitées, qui dormiront pendant l'essentiel du temps. Au minimum, il y aura une sérieuse latence s'il faut les réveiller.
Comme, dans le cas typique, le FP est bien moins contraint que le PP (connecté au courant électrique, processeur plus puissant), ce sera le FP qui jouera le rôle de 6LBR (6LoWPAN Border Router, voir RFC 6775), et le PP celui de 6LN (6LoWPAN Node, même RFC). Contrairement à 802.15.4, DECT ULE ne permet que des liens directs, pour aller au delà, il faut un routeur (le FP). Tous les PP connectés à un FP forment donc un seul lien, leurs adresses seront dans le même préfixe IPv6.
Comment attribuer cette adresse ? Alors, là, faites attention, c'est un point délicat et important. Chaque PP a un IPEI (International Portable Equipment Identity) de 40 bits, qui est l'identifiant DECT. Les FP ont un RFPI (Radio Fixed Part Identity, également 40 bits). Les messages envoyés entre PP et FP ne portent pas l'IPEI mais le TPUI (Temporary Portable User Identity, 20 bits). Pas mal de mises en œuvre de DECT attribuent répétitivement le même TPUI à une machine, même si ce n'est pas obligatoire. Il peut donc être un identifiant stable, en pratique, comme le sont IPEI et RFPI.
L'adresse IPv6 est composée du préfixe du réseau et d'un identifiant d'interface, qu'on peut construire à partir de l'adresse MAC (les équipements DECT peuvent aussi avoir une adresse MAC, en sus des identificateurs déjà cités). Adresse MAC, IPEI, RFPI ou TPUI, tout ce qui est stable pose des problèmes de protection de la vie privée (RFC 8065), et n'est pas recommandé comme identifiant d'interface par défaut.
Un petit mot aussi sur la MTU : les paquets DECT ne sont que 38 octets, bien trop petit pour IP. Certes, DECT fournit un mécanisme de fragmentation et de réassemblage, qui fournit une MTU « virtuelle » qui est, par défaut, de 500 octets. La MTU minimum exigée par IPv6 étant de 1 280 octets (RFC 2460, section 5), il faudra donc reconfigurer le lien DECT pour passer à une MTU de 1 280. Ainsi, les paquets n'auront jamais besoin d'être fragmentés par IP. Évidemment, plus le paquet est gros, plus le coût énergétique de transmission est élevé, au détriment de la durée de vie de la batterie.
Place maintenant à la spécification elle-même, en section 3 du RFC. La base (alias FP, alias 6LBR) et l'objet (alias PP, alias 6LN) vont devoir se trouver et établir une session DECT classique. On aura alors une couche 2 fonctionnelle. Ensuite, on lancera IPv6, qui fournira la couche 3. La consommation de ressources, notamment d'énergie, étant absolument cruciale ici, il faudra s'appuyer sur les technologies IPv6 permettant de faire des économies, notamment RFC 4944 (IPv6 sur un autre type de réseau contraint, IEEE 802.15.4), RFC 6775 (optimisation des mécanismes de neighbor discovery pour les 6LoWPAN) et RFC 6282 (compression des paquets et notamment des en-têtes).
Comme indiqué plus haut, les PP ne peuvent parler qu'au FP, pas directement de l'un à l'autre. Si tous les PP d'un même FP (d'une même base) forment un sous-réseau IPv6 (choix le plus simple), le modèle sera celui d'un NBMA. Lorsqu'un PP écrira à un autre PP, cela sera forcément relayé par le FP.
Les adresses IPv6 des PP seront formées à partir du préfixe (commun à tous les PP d'un même FP) et d'un identifiant d'interface. Pour les adresses locales au lien, cet identifiant d'interface dérivera des identifiants DECT, les IPEI et RFPI, complétés avec des zéros pour atteindre la taille requise. Le bit « unique mondialement » sera à zéro puisque ces identifiants ne seront pas uniques dans le monde (ils ont juste besoin d'être uniques localement, ce fut un des points les plus chauds lors de l'écriture de ce RFC).
Pour les adresses globales des PP, pas question d'utiliser des identificateurs trop révélateurs (RFC 8065), il faut utiliser une technique qui préserve la vie privée comme les CGA (RFC 3972), les adresses temporaires du RFC 8981 ou les adresses stables mais opaques du RFC 7217.
Le FP, la base, a une connexion avec l'Internet, ou en tout cas avec d'autres réseaux IP, et routera donc les paquets, s'ils viennent d'un PP et sont destinés à une adresse extérieure au sous-réseau (idem avec les paquets venus de l'extérieur et destinés au sous-réseau.) Au fait, comment est-ce que la base, qui est un routeur IPv6, obtient, elle, le préfixe qu'elle va annoncer ? Il n'y a pas de méthode obligatoire mais cela peut être, par exemple, la délégation de préfixe du RFC 8415, ou bien le RFC 4193.
Question mises en œuvre, il semble que RTX et Gigaset en aient déjà, et que peut-être l'alliance ULE va produire une version en logiciel libre.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : T. Hansen (AT&T
Laboratories), A. Melnikov (Isode)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 1 mars 2017
Une demande fréquente des utilisateurs du courrier électronique est d'avoir un mécanisme permettant de savoir si et quand le message a été lu par le destinataire. Comme toutes les demandes des utilisateurs, il ne faut pas forcément la satisfaire sans réfléchir (elle pose des gros problèmes de vie privée et, en outre, elle ne garantit pas que le message a été traité, juste que le logiciel l'a affiché). Ce n'est pas par hasard que cette fonction « accusé de réception » était souvent présente (et mise en avant par les vendeurs) pour les systèmes de messagerie conçus pour des environnements très bureaucratiques (le RFC cite l'antédiluvien X.400). Mais, bon, si les gens y tiennent, cette possibilité existe dans la norme : ce nouveau RFC spécifie un mécanisme permettant de signaler qu'on souhaite un tel accusé de réception, ainsi qu'un format structuré (lisible par un programme comme le MUA) pour les accusés de réception qui seront (peut-être) envoyés. Ces accusés de réception sont appelés MDN pour Message Disposition Notification. Ce RFC remplace son prédécesseur, le RFC 3798.
Donc, résumé général du fonctionnement de ce système :
l'émetteur d'un message qui veut un accusé de réception met un
en-tête Disposition-Notification-To:
dans son
message. Le récepteur, s'il le désire,
répondra à cette demande lors de la lecture du message, en
envoyant un message de type MIME
message/disposition-notification
(a priori
situé à l'intérieur d'un rapport plus général, de type
multipart/report
, cf. RFC 6522). Tout ceci
est sous un format structuré, donc peut être traité par un
programme, typiquement le MUA. Voilà, vous
connaissez l'essentiel de ce RFC. Place aux
détails.
À quoi servent les MDN (Message Disposition Notification, un concept plus large que celui d'accusé de réception) ? Voici le cahier des charges proposé par notre RFC :
Première partie de la norme, la demande
d'un MDN (section 2). L'émetteur le fait en ajoutant dans son message un
en-tête Disposition-Notification-To:
indiquant les adresses auxquelles envoyer le MDN. Par exemple :
Disposition-Notification-To: stephane+mdn@bortzmeyer.org
Le risque d'utilisation de ce truc pour bombarder de message un
tiers innocent est évident. C'est pour cela que le RFC recommande
d'ignorer cet en-tête si l'adresse indiquée ne coïncide pas avec
celle stockée dans l'en-tête
Return-Path:
(voir section 6.4). Dans tous les cas,
rappelez-vous bien que le logiciel à la réception est libre de
faire ce qu'il veut. Il peut estimer que ces MDN ne servent à rien
et ignorer les Disposition-Notification-To:
,
il peut demander une autorisation à l'utilisateur
il peut envoyer le MDN de
manière totalement automatique (après les vérifications de
vraisemblance comme celle du Return-Path:
), etc.
Deuxième partie de la norme, le format du MDN lui-même (section
3 du RFC). La réponse est dans un message de type
multipart/report
(type défini dans le RFC 6522), avec le type de rapport (paramètre report-type
)
disposition-notification
. Le MDN lui-même a
deux ou trois parties : une première partie est du texte libre,
lisible par un humain, une deuxième est structurée, et de type
MIME message/disposition-notification
, la
troisième partie est optionnelle et est le message auquel on
« répond ».
La deuxième partie du MDN est la plus intéressante. Son corps est
composé de plusieurs champs nom: valeur
, dont
deux sont obligatoires, Final-Recipient:
et
Disposition:
qui indique ce qui est arrivé au
message. Parmi les autres champs, notez le
Reporting-UA:
, indiquant le logiciel qui a
répondu, et dont le RFC recommande qu'il ne soit pas trop
détaillé, car il donne des informations qui peuvent être utiles à
un éventuel attaquant. Comme Reporting-UA:
,
le champ Original-Message-ID:
n'est pas
obligatoire mais il est très utile : c'est lui qui permet à
l'émetteur du message original de faire
la jointure entre ce qu'il a envoyé et le MDN reçu. (Il n'est pas
obligatoire car le message original n'a pas forcément un
Message-ID:
. Mais, s'il en a un, il faut
inclure Original-Message-ID:
dans le
MDN.)
Le champ le plus important est sans doute
Disposition:
. Il indique ce qui est arrivé au
message original (disposition type) : a-t-il
été affiché à un utilisateur (displayed
, ce
qui ne garantit pas du tout qu'il soit arrivé au cerveau de l'utilisateur), traité sans être montré à un
utilisateur (processed
), effacé
(deleted
) ? Ce champ
Disposition:
indique aussi
(disposition mode) si le sort du
message a été décidé par un être humain ou bien automatiquement
(par exemple par Sieve),
et si le MDN a été généré suite à une autorisation explicite ou
bien automatiquement. Notez bien (et c'est la principale raison
pour laquelle les accusés de réception sont une fausse bonne idée)
que la seule façon d'être sûr que le message aura été traité par
son destinataire, est de recevoir une réponse explicite et
manuelle de sa part.
Enfin, le champ Error:
sert à transporter
des messages... d'erreur.
Voici un exemple complet de MDN, tiré de la section 9 :
Date: Wed, 20 Sep 1995 00:19:00 (EDT) -0400 From: Joe Recipient <Joe_Recipient@example.com> Message-Id: <199509200019.12345@example.com> Subject: Re: First draft of report To: Jane Sender <Jane_Sender@example.org> MIME-Version: 1.0 Content-Type: multipart/report; report-type=disposition-notification; boundary="RAA14128.773615765/example.com" --RAA14128.773615765/example.com Content-type: text/plain The message sent on 1995 Sep 19 at 13:30:00 (EDT) -0400 to Joe Recipient <Joe_Recipient@example.com> with subject "First draft of report" has been displayed. This is no guarantee that the message has been read or understood. --RAA14128.773615765/example.com Content-type: message/disposition-notification Reporting-UA: joes-pc.cs.example.com; Foomail 97.1 Original-Recipient: rfc822;Joe_Recipient@example.com Final-Recipient: rfc822;Joe_Recipient@example.com Original-Message-ID: <199509192301.23456@example.org> Disposition: manual-action/MDN-sent-manually; displayed --RAA14128.773615765/example.com Content-type: message/rfc822 [original message optionally goes here] --RAA14128.773615765/example.com--
Notez la première partie, en langue naturelle (ici en
anglais), la seconde, avec les informations structurées (ici, le
destinataire a affiché le message - manual-action
... displayed
- puis autorisé/déclenché manuellement
l'envoi du MDN - MDN-sent-manually
), et la
présence de la troisième partie, qui est optionnelle.
Un peu de sécurité pour finir le RFC. D'abord, évidemment, il
ne faut pas accorder trop d'importance aux MDN. Ils peuvent être
fabriqués de toutes pièces, comme n'importe quel message sur
l'Internet. Ensuite, il faut faire attention à la vie
privée des utilisateurs. Le destinataire n'a pas
forcément envie qu'on sache si et quand il a lu un message ! Le
destinataire, ou son logiciel, ont donc parfaitement le droit de
refuser d'envoyer un MDN (ce qui diminue encore l'intérêt de cette
technique, qui était déjà très faible). Même des informations
inoffensives à première vue, comme le contenu du champ
Disposition:
peuvent être considérées comme
sensibles. Si on configure Sieve pour
rejeter (RFC 5429) automatiquement tous les
messages d'une certaine personne, on n'a pas forcément envie
qu'elle le sache. Le RFC précise donc qu'on peut envoyer
manual-action/MDN-sent-manually
dans ce cas,
pour cacher le fait que c'était automatique.
Quels sont les changements depuis le précédent RFC, le RFC 3798 ? Ils sont résumés dans l'annexe
A. Tout ce qui touche à la vie privée a été sérieusement renforcé
(les MDN sont très indiscrets). Les
champs commençant par un X-
ont été supprimés
de la spécification, suivant le RFC 6648. La
grammaire a été corrigée (plusieurs bogues
et ambiguïtés).
En pratique, les MDN ne semblent guère utilisés dans l'Internet et ont peu de chance de marcher. Je note par exemple qu'aussi bien le MUA Unix mutt que le service Gmail semblent les ignorer complètement. Mais d'autres logiciels ont cette fonction.
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : G. Fairhurst (University of Aberdeen), B. Trammell, M. Kuehlewind (ETH Zurich)
Pour information
Réalisé dans le cadre du groupe de travail IETF taps
Première rédaction de cet article le 10 mars 2017
Les protocoles de transport (couche 4 dans le modèle en couches traditionnel), comme le fameux TCP, fournissent certains services aux applications situées au-dessus d'eux. Mais quels services exactement ? Qu'attend-on de la couche de transport ? Le but de ce RFC de synthèse est de lister tous les services possibles de la couche 4, et d'analyser ensuite tous les protocoles existants pour voir lesquels de ces services sont offerts. Ce document ne normalise donc pas un nouveau protocole, il classe et organise les protocoles existants. (L'idée est de pouvoir ensuite développer une interface abstraite permettant aux applications d'indiquer quels services elles attendent de la couche transport au lieu de devoir, comme c'est le cas actuellement, choisir un protocole donné. Une telle interface abstraite permettrait au système d'exploitation de choisir le protocole le plus adapté à chaque environnement.)
C'est d'autant plus important qu'il n'y a pas que TCP mais aussi des protocoles comme SCTP, UDP, DCCP, les moins connus FLUTE ou NORM, et même HTTP, qui est devenu une couche de transport de fait. Toute évolution ultérieure de l'architecture de l'Internet, des middleboxes, des API offertes par le système d'exploitation, implique une compréhension détaillée de ce que fait exactement la couche transport.
Pour TCP, tout le monde connait (ou croit connaitre) : il fournit un service de transport de données fiable (les données qui n'arrivent pas sont retransmises automatiquement, l'application n'a pas à s'en soucier, la non-modification est - insuffisamment - contrôlée via une somme de contrôle), et ordonné (les octets arrivent dans l'ordre d'envoi même si, dans le réseau sous-jacent, un datagramme en a doublé un autre). TCP ne fournit pas par contre de service de confidentialité, ce qui facilite le travail de la NSA ou de la DGSI. Tout le monde sait également qu'UDP ne fournit aucun des deux services de fiabilité et d'ordre : si l'application en a besoin, elle doit le faire elle-même (et il est donc logique que la plupart des applications utilisent TCP).
Parfois, le service de transport offert aux applications est lui-même bâti sur un autre service de transport. C'est la raison pour laquelle ce RFC présente des protocoles qui ne sont pas « officiellement » dans la couche 4 (mais, de toute façon, le modèle en couches n'a toujours été qu'une vague indication ; en faire une classification rigide n'a aucun intérêt, et a été une des raisons de l'échec du projet l'OSI). Un exemple est TLS. Une application qui s'en sert ne voit pas directement le TCP sous-jacent, elle confie ses données à TLS qui, à son tour, fait appel à TCP. Le service de transport vu par l'application offre ainsi les fonctions de TCP (remise fiable et ordonnée des données) plus celles de TLS (confidentialité, authentification et intégrité). Il faudrait être particulièrement pédant pour s'obstiner à classer TLS dans les applications comme on le voit parfois.
Le même phénomène se produit pour UDP : comme ce protocole n'offre quasiment aucun service par lui-même, on le complète souvent avec des services comme TFRC (RFC 5348) ou LEDBAT (RFC 6817) qui créent ainsi un nouveau protocole de transport au-dessus d'UDP.
La section 1 de notre RFC liste les services possibles d'une couche de transport :
La section 3 du RFC est le gros morceau. Elle liste tous les protocoles de transport possibles (au moins ceux normalisés par l'IETF), en donnant à chaque fois une description générale du protocole, l'interface avec les applications, et enfin les services effectivement offerts par ce protocole.
À tout seigneur, tout honneur, commençons par l'archétype des protocoles de transport, TCP. Normalisé dans le RFC 793, très largement répandu (il est difficile d'imaginer une mise en œuvre d'IP qui ne soit pas accompagnée de TCP), utilisé quotidiennement par des milliards d'utilisateurs. Le RFC originel a connu pas mal de mises à jour et, aujourd'hui, apprendre TCP nécessite de lire beaucoup de RFC (le RFC 7414 en donne la liste). Ainsi, la notion de données urgentes, qui était dans le RFC originel, a été supprimée par le RFC 6093.
TCP multiplexe les connexions en utilisant les numéros de port, comme beaucoup de protocoles de transport. Une connexion est identifiée par un tuple {adresse IP source, port source, adresse IP destination, port destination}. Le port de destination identifie souvent le service utilisé (c'est moins vrai aujourd'hui, où la prolifération de middleboxes stupides oblige à tout faire passer sur les ports 80 et 443). TCP fournit un service de données non-structurées, un flot d'octets, mais, en interne, il découpe ces octets en segments, dont la taille est négociée au début (en général, TCP essaie de faire que cette taille soit la MTU du chemin, en utilisant les RFC 1191, RFC 1981 et de plus en plus le RFC 4821). Chaque octet envoyé a un numéro, le numéro de séquence, et c'est ainsi que TCP met en œuvre la fiabilité et l'ordre. (Contrairement à ce que croient certaines personnes, c'est bien l'octet qui a un numéro, pas le segment.) Autrefois, si deux segments non contigus étaient perdus, il fallait attendre la réémission du premier pour demander celle du second, mais les accusés de réception sélectifs du RFC 2018 ont changé cela.
Quant au contrôle de congestion de TCP, il est décrit en détail dans le RFC 5681. TCP réagit à la perte de paquets (ou bien à leur marquage avec l'ECN du RFC 3168) en réduisant la quantité de données envoyées.
Les données envoyées par l'application ne sont pas forcément transmises immédiatement au réseau. TCP peut attendre un peu pour remplir davantage ses segments (RFC 896). Comme certaines applications (par exemple celles qui sont fortement interactives comme SSH) n'aiment pas les délais que cela entraine, ce mécanisme est typiquement débrayable.
Enfin, pour préserver l'intégrité des données envoyées, TCP utilise une somme de contrôle (RFC 793, section 3.1, et RFC 1071). Elle ne protège pas contre toutes les modifications possibles et il est recommandé aux applications d'ajouter leur propre contrôle d'intégrité (par exemple, si on transfère un fichier, via un condensat du fichier).
Et l'interface avec les applications, cruciale, puisque le rôle
de la couche transport est justement d'offrir des services aux
applications ? Celle de TCP est décrite de manière relativement
abstraite dans le RFC 793 (six commandes,
Open, Close,
Send, Receive, etc). Des
points comme les options TCP n'y sont pas spécifiés. Le RFC 1122 est un peu plus détaillé, mentionnant
par exemple l'accès aux messages ICMP qui
peuvent indiquer une erreur TCP. Enfin, une interface concrète est
celle des prises,
normalisées par POSIX (pas de RFC à ce sujet). Vous créez une prise
avec l'option SOCK_STREAM
et hop, vous
utilisez TCP et tous ses services.
Quels services, justement ? TCP fournit :
Par contre, TCP ne fournit pas de confidentialité, et l'authentification se limite à une protection de l'adresse IP contre les attaquants situés hors du chemin (RFC 5961).
Après TCP, regardons le deuxième protocole de transport étudié, MPTCP (Multipath TCP, RFC 6824). C'est une extension de TCP qui permet d'exploiter le multi-homing. Pour échapper aux middleboxes intrusives, MPTCP fonctionne en créant plusieurs connexions TCP ordinaires depuis/vers toutes les adresses IP utilisées, et en multiplexant les données sur ces connexions (cela peut augmenter le débit, et cela augmente la résistance aux pannes, mais cela peut aussi poser des problèmes si les différents chemins ont des caractéristiques très différentes). La signalisation se fait par des options TCP.
L'interface de base est la même que celle de TCP, mais il existe des extensions (RFC 6897) pour tirer profit des particularités de MPTCP.
Les services sont les mêmes que ceux de TCP avec, en prime le multi-homing (il peut même y avoir des adresses IPv4 et IPv6 dans la même session MPTCP), et ses avantages notamment de résilience.
Après TCP, UDP est certainement le protocole de transport le plus connu. Il est notamment très utilisé par le DNS. Le RFC 8085 explique comment les applications peuvent l'utiliser au mieux. La section 3.3 de notre RFC lui est consacrée, pour décrire son interface et ses services.
Contrairement à TCP, UDP n'a pas la notion de connexion (on envoie directement les données, sans négociation préalable), UDP découpe les données en messages (voilà pourquoi les messages DNS en UDP ne sont pas précédés d'une longueur : UDP lui-même fait le découpage), n'a pas de contrôle de congestion, et ne garantit pas le bon acheminement. UDP dispose d'un contrôle d'intégrité, mais il est facultatif (quoique très recommandé) en IPv4, où on peut se contenter du contrôle d'intégrité d'IP. IPv6 n'ayant pas ce contrôle, UDP sur IPv6 doit activer son propre contrôle, sauf dans certains cas très précis (RFC 6936).
En l'absence de contrôle de congestion, l'application doit être prudente, veiller à ne pas surcharger le réseau, et ne pas s'étonner si l'émetteur envoie plus que ce que le récepteur peut traiter. D'une façon générale, il faut penser à lire le RFC 8085, qui explique en détail tout ce qu'une application doit faire si elle tourne sur UDP.
Il est d'ailleurs recommandé de bien se poser la question de l'utilité d'UDP, dans beaucoup de cas. Un certain nombre de développeurs se disent au début d'un projet « j'ai besoin de vitesse [sans qu'ils fassent bien la différence entre latence et capacité], je vais utiliser UDP ». Puis ils découvrent qu'ils ont besoin de contrôle de flux, d'ordre des données, de bonne réception des données, ils ajoutent à chaque fois des mécanismes ad hoc, spécifiques à leur application et, au bout du compte, ils ont souvent réinventé un truc aussi lourd que TCP, mais bien plus bogué. Attention donc à ne pas réinventer la roue pour rien.
L'interface d'UDP, maintenant. Le RFC 768 donne quelques indications de base, que le RFC 8085 complète. Bien qu'UDP n'ait
pas le concept de connexion, il est fréquent que les
API aient une opération
connect()
ou analogue. Mais il ne faut pas
la confondre avec l'opération du même nom sur TCP : ce
connect()
UDP est purement local, associant
la structure de données locale à une machine distante (c'est
ainsi que cela se passe avec les prises
Berkeley).
Et les services d'UDP ? La liste est évidemment bien plus courte que pour TCP. Elle comprend :
Nettement moins connu qu'UDP est UDP-Lite, normalisé dans le RFC 3828. C'est une version très légèrement modifiée d'UDP, où la seule différence est que les données corrompues (détectées par la somme de contrôle) sont quand même données à l'application réceptrice, au lieu d'être jetées comme avec UDP. Cela peut être utile pour certains applications, notamment dans les domaines audio et vidéo.
Avec UDP-Lite, le champ Longueur de l'en-tête UDP change de sémantique : il n'indique plus la longueur totale des données mais la longueur de la partie qui est effectivement couverte par la somme de contrôle. Typiquement, on ne couvre que l'en-tête applicatif. Le reste est... laissé à la bienveillance des dieux (ou des démons). Pour tout le reste, voyez la section sur UDP.
Notez qu'il n'existe pas d'API spécifique pour UDP-Lite. Si quelqu'un parmi mes lecteurs a des exemples de code bien clairs...
Bien plus original est SCTP (RFC 4960). C'est un protocole à connexion et garantie d'acheminement et d'ordre des données, comme TCP. Mais il s'en distingue par sa gestion du multi-homing. Avec SCTP, une connexion peut utiliser plusieurs adresses IP source et destination, et passer de l'une à l'autre pendant la session, assurant ainsi une bonne résistance aux pannes. Plus drôle, cet ensemble d'adresses peut mêler des adresses IPv4 et IPv6.
Notez aussi qu'une connexion SCTP (on dit une association) comporte plusieurs flux de données, afin de minimiser le problème connu sous le nom de head of line blocking (un paquet perdu empêche la délivrance de toutes les données qui suivent tant qu'il n'a pas été réémis).
SCTP avait surtout été conçu pour la signalisation dans les réseaux téléphoniques. Mais on le trouve dans d'autres cas, comme ForCES (cf. RFC 5811) ou comme la signalisation WebRTC (RFC 8825).
Contrairement à TCP, SCTP utilise une quadruple poignée de mains pour établir la connexion, ce qui permet de ne négocier les options qu'une fois certain de l'identité du partenaire (les techniques anti-DoS de TCP sont incompatible avec l'utilisation des options, cf. RFC 4987, section 3.6). La somme de contrôle fait 32 bits (au lieu des 16 bits de TCP et UDP) et est donc normalement plus robuste.
SCTP est très extensible et plusieurs extensions ont déjà été définies comme l'ajout ou le retrait d'adresses IP pendant l'association (RFC 5061), ou bien la possibilité de n'accepter qu'une fiabilité partielle (RFC 3758). Pour la sécurité, on peut faire tourner TLS sur SCTP (RFC 3436) au prix de la perte de quelques fonctions, ou bien utiliser DTLS (RFC 6083), qui préserve quasiment toutes les fonctions de SCTP.
Victime fréquente des middleboxes stupides qui ne connaissent qu'UDP et TCP, SCTP peut tourner sur UDP (RFC 6951), au lieu de directement reposer sur IP, afin de réussir à passer ces middleboxes.
Contrairement à des protocoles de transport plus anciens, SCTP a une interface bien spécifiée. Le RFC 4960 définit l'interface abstraite, et une extension aux prises Berkeley, spécifiée dans le RFC 6458, lui donne une forme concrète. Cette API prévoit également certaines extensions, comme celle des reconfigurations dynamiques d'adresses du RFC 5061.
Les services fournis par SCTP sont très proches de ceux fournis par TCP, avec deux ajouts (la gestion du multi-homing et le multi-flux), et un changement (données structurées en messages, au lieu d'être un flot d'octets continu comme TCP).
Un autre protocole de transport peu connu, et ne fournissant pas, lui, de fiabilité de l'envoi des données, est DCCP (RFC 4340). DCCP est une sorte d'UDP amélioré, qui peut fournir des services supplémentaires à ceux d'UDP, tout en restant plus léger que TCP (la description du besoin figure dans le RFC 4336). DCCP est bien adapté aux applications multimédia ou aux jeux en ligne, où une faible latence est cruciale, mais où peut aimer avoir des services en plus. Sans DCCP, chaque application qui veut de l'« UDP amélioré » devrait tout réinventer (et ferait sans doute des erreurs).
DCCP a des connexions, comme TCP, qu'on établit avant de communiquer et qu'on ferme à la fin. Il offre une grande souplesse dans le choix des services fournis, choix qui peuvent être unilatéraux (seulement l'envoyeur, ou bien seulement le récepteur) ou négociés lors de l'ouverture de la connexion. Le paquet d'ouverture de connexion indique l'application souhaitée (RFC 5595), ce qui peut être une information utile aux équipements intermédiaires. S'il faut faire passer DCCP à travers des middleboxes ignorantes, qui n'acceptent qu'UDP et TCP, on peut, comme avec SCTP, encapsuler dans UDP (RFC 6773).
L'interface avec DCCP permet d'ouvrir, de fermer et de gérer une connexion. Il n'y a pas d'API standard. Les services fournis sont :
Autre exemple de protocole de transport, même s'ils ne sont en général pas décrits comme tels, TLS (RFC 5246) et son copain DTLS (RFC 6347). Si on est un fanatique du modèle en couches, on ne met pas ces protocoles de sécurité en couche 4 mais, selon l'humeur, en couche 5 ou en couche 6. Mais si on est moins fanatique, on reconnait que, du point de vue de l'application, ce sont bien des protocoles de transport : c'est à eux que l'application confie ses données, comptant sur les services qu'ils promettent.
TLS tourne sur TCP et DTLS sur UDP. Du point de vue de l'application, TLS fournit les services de base de TCP (transport fiable d'un flot d'octets) et DTLS ceux d'UDP (envoi de messages qui arriveront peut-être). Mais ils ajoutent à ces services de base leurs services de sécurité :
Le RFC rappelle qu'il est important de se souvenir que TLS ne spécifie pas un mécanisme d'authentification unique, ni même qu'il doit y avoir authentification. On peut n'authentifier que le serveur (c'est actuellement l'usage le plus courant), le client et le serveur, ou bien aucun des deux. La méthode la plus courante pour authentifier est le certificat PKIX (X.509), appelé parfois par une double erreur « certificat SSL ».
DTLS ajoute également au service de base quelques trucs qui n'existent pas dans UDP, comme une aide pour la recherche de PMTU ou un mécanisme de cookie contre certaines attaques.
Il n'y a pas d'API standard de TLS. Si on a écrit une application avec l'API d'OpenSSL, il faudra refaire les appels TLS si on passe à WolfSSL ou GnuTLS. C'est d'autant plus embêtant que les programmeurs d'application ne sont pas forcément des experts en cryptographie et qu'une API mal conçue peut les entrainer dans des erreurs qui auront des conséquences pour la sécurité (l'article « The most dangerous code in the world: validating SSL certificates in non-browser software » en donne plusieurs exemples).
Passons maintenant à RTP (RFC 3550). Ce protocole est surtout utilisé pour les applications multimédia, où on accepte certaines pertes de paquet, et où le format permet de récupérer après cette perte. Comme TLS, RTP fonctionne au-dessus du « vrai » protocole de transport, et peut exploiter ses services (comme la protection de l'intégrité d'une partie du contenu, que fournissent DCCP et UDP-Lite).
RTP comprend en fait deux protocoles, RTP lui-même pour les données et RTCP pour le contrôle. Par exemple, c'est via RTCP qu'un émetteur apprend que le récepteur ne reçoit pas vite et donc qu'il faudrait, par exemple, diminuer la qualité de la vidéo.
RTP n'a pas d'interface standardisée offerte aux programmeurs. Il faut dire que RTP est souvent mis en œuvre, non pas dans un noyau mais directement dans l'application (comme avec libortp sur Unix). Ces mises en œuvre sont donc en général optimisées pour une utilisation particulière, au lieu d'être généralistes comme c'est le cas avec les implémentations de TCP ou UDP.
Autre cas d'un protocole de transport qui fonctionne au-dessus d'un autre protocole de transport, HTTP (RFC 7230 et suivants). Il n'était normalement pas conçu pour cela mais, dans l'Internet d'aujourd'hui, où il est rare d'avoir un accès neutre, où les ports autres que 80 et 443 sont souvent bloqués, et où hôtels, aéroports et écoles prétendent fournir un « accès Internet » qui n'est en fait qu'un accès HTTP, bien des applications qui n'ont rien à voir avec le Web en viennent à utiliser HTTP comme protocole de transport. (Même si le RFC 3205 n'encourage pas vraiment cette pratique puisque HTTP peut ne pas être adapté à tout. Mais, souvent, on n'a pas le choix.)
Outre cette nécessité de contourner blocages et limitations, l'utilisation de HTTP comme transport a quelques avantages : protocole bien connu, disposant d'un grand nombre de mises en œuvre, que ce soit pour les clients ou pour les serveurs, et des mécanismes de sécurité existants (RFC 2617, RFC 2817…). L'un des grands succès de HTTP est le style REST : de nombreuses applications sont conçues selon ce style.
Les applications qui utilisent HTTP peuvent se servir des
méthodes existantes (GET
,
PUT
, etc) ou bien en créer de nouvelles
(qui risquent de moins bien passer partout).
Je ne vais pas refaire ici la description de HTTP que contient le RFC (suivant le même plan que pour les autres protocoles de transport), je suppose que vous connaissez déjà HTTP. Notez quand même quelques points parfois oubliés : HTTP a un mécanisme de négociation du contenu, qui permet, par exemple, de choisir le format lorsque la ressource existe en plusieurs formats, HTTP a des connexions persistentes donc on n'est pas obligé de se taper un établissement de connexion TCP par requête, et HTTP a des mécanismes de sécurité bien établis, à commencer par HTTPS.
Il y a plein de bibliothèques qui permettent de faire de l'HTTP facilement (libcurl et neon en C, Requests en Python, etc). Chacune a une API différente. Le W3C a normalisé une API nommée XMLHttpRequest, très utilisée par les programmeurs JavaScript.
Les services que fournit HTTP quand on l'utilise comme protocole de transport sont :
Beaucoup moins connus que les protocoles précédents sont deux des derniers de notre liste, FLUTE et NORM.
FLUTE (File Delivery over Unidirectional Transport/ Asynchronous Layered Coding Reliable Multicast) est normalisé dans le RFC 6726. Il est conçu pour un usage très spécifique, la distribution de fichiers à des groupes multicast de grande taille, où on ne peut pas demander à chaque récepteur d'accuser réception. Il est surtout utilisé dans le monde de la téléphonie mobile (par exemple dans la spécification 3GPP TS 26.346).
FLUTE fonctionne sur UDP, et le protocole ALC du RFC 5775. Il est souvent utilisé sur des réseaux avec une capacité garantie, et où on peut donc relativiser les problèmes de congestion. Il n'y a pas d'interface de programmation spécifiée.
Les services de FLUTE sont donc :
Et NORM (NACK-Oriented Reliable Multicast ? Normalisé dans le RFC 5740, il rend à peu près les mêmes services que FLUTE (distribution massive de fichiers). À noter qu'il en existe une mise en œuvre en logiciel libre.
Reste un cas amusant, ICMP. Bien sûr, ICMP n'est pas du tout conçu pour être un protocole de transport, c'est le protocole de signalisation d'IP (RFC 792 pour ICMP sur IPv4 et RFC 4443 pour ICMP sur IPv6). Mais, bon, comme il est situé au-dessus de la couche 3, on peut le voir comme un protocole de transport.
Donc, ICMP est sans connexion, sans fiabilité, et unidirectionnel. Évidemment pas de contrôle de congestion. Pas vraiment d'interface standard, les messages ICMP ne sont signalés qu'indirectement aux applications (dans certains cas, une application peut demander à recevoir les messages ICMP). On ne peut pas tellement s'en servir comme protocole de transport, bien que des programmes comme ptunnel s'en servent presque ainsi.
Après cette longue section 3 qui faisait le tour de tous les protocoles de transport ou assimilés, la section 4 de notre RFC revient sur la question cruciale de la congestion. Sans contrôle de congestion, si chacun émettait comme ça lui chante, l'Internet s'écroulerait vite sous la charge. C'est donc une des tâches essentielles d'un protocole de transport que de fournir ce contrôle de congestion. Pour ceux qui ne le font pas, l'application doit le faire (et c'est très difficile à faire correctement).
À noter que la plupart des protocoles de transport tendent à ce que chaque flot de données utilise autant de capacité disponible que les autres flots. Au contraire, il existe des protocoles « décroissants » comme LEDBAT (RFC 6817) qui cèdent la place aux autres et n'utilise la capacité que lorsque personne n'est en concurrence avec eux.
La section 5 de notre RFC revient sur la notion de fonctions fournies par le protocole de transport, et classe sur un autre axe que la section 3. La section 3 était organisée par protocole et, pour chaque protocole, indiquait quelles étaient ses fonctions. La section 5, au contraire, est organisée par fonction et indique, pour chaque fonction, les valeurs qu'elle peut prendre, et les protocoles qui correspondent. Première catégorie de fonctions, celle du contrôle. Ainsi, une des fonctions de base d'un protocole de transport est l'adressage, celui-ci peut être unicast (TCP, UDP, SCTP, TLS, HTTP), multicast (UDP encore, FLUTE, NORM), broadcast (UDP toujours), anycast (UDP, quoique TCP puisse l'utiliser si on accepte le risque de connexions coupées lorsque le routage change).
Autre fonction, la façon dont se fait l'association entre les deux machines, et elle peut être avec connexion (TCP, SCTP, TLS) ou sans connexion (UDP). La gestion du multi-homing peut être présente (MPTCP, SCTP) ou pas. La signalisation peut être faite avec ICMP ou bien dans le protocole d'application (RTP).
Seconde catégorie de fonctions, la délivrance de données. Première fonction dans cette catégorie, la fiabilité, qui peut être complète (TCP, SCTP, TLS), partielle (RTP, FLUTE, NORM) ou inexistante (UDP, DCCP). Deuxième fonction, la détection d'erreurs, par une somme de contrôle qui couvre toutes les données (TCP, UDP, SCTP, TLS), une partie (UDP-Lite), et qui peut même être optionnelle (UDP en IPv4). Troisième fonction de délivrance, l'ordre des données, qui peut être maintenu (TCP, SCTP, TLS, HTTP, RTP) ou pas (UDP, DCCP, DTLS). Quatrième fonction, le découpage des données : flot sans découpage (TCP, TLS) ou découpage en messages (UDP, DTLS).
Troisième catégorie de fonctions, celles liées au contrôle de la transmission et notamment de la lutte contre la congestion.
Enfin, quatrième et dernière catégorie de fonctions, celles liées à la sécurité : authentification (TLS, DTLS) et confidentialité (les mêmes) notamment.
Voilà, armé de ce RFC, si vous êtes développeurs d'un nouveau protocole applicatif sur Internet, vous pouvez choisir votre protocole de transport sans vous tromper.
Une lecture recommandée est « De-Ossifying the Internet Transport Layer: A Survey and Future Perspectives », de Giorgos Papastergiou, Gorry Fairhurst, David Ros et Anna Brunström.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : T. Reddy
(Cisco), D. Wing, P. Patil
(Cisco)
Expérimental
Réalisé dans le cadre du groupe de travail IETF dprive
Première rédaction de cet article le 2 mars 2017
Dernière mise à jour le 22 mai 2022
Le DNS fonctionne traditionnellement surtout sur UDP, notamment pour minimiser la latence : quand on veut une réponse DNS, on la veut rapidement. Dans le cadre du projet « DNS et vie privée », le choix avait été fait de chiffrer le trafic DNS avec TLS (RFC 7858), imposant ainsi l'usage de TCP. Certains pensaient quand même qu'UDP était bien adapté au DNS et, puisqu'il existe une version de TLS adaptée à UDP, DTLS, ce serait une bonne idée de l'utiliser pour chiffrer le DNS. C'est ce que décrit ce nouveau RFC (qui n'a pas eu un avenir brillant, peu de gens ont été intéressés, regardez plutôt le RFC 9250).
De toute façon, il est très possible que le DNS utilise de plus en plus TCP, et le RFC 7766 allait dans ce sens, demandant davantage de la part des mises en œuvre de DNS sur TCP. Mais, bon, il est toujours bon d'essayer des alternatives, d'où ce RFC, dans l'état « Expérimental ». Outre les RFC déjà cités, il est recommandé, avant de le lire, de prendre connaissance du RFC 7626, qui décrit les problèmes de vie privée que pose le DNS, et le RFC 9147, qui normalise DTLS (bien moins connu que son copain TLS, et peu utilisé jusqu'à présent, à part pour WebRTC).
Les motivations pour explorer une alternative au DNS-sur-TLS du RFC 7858 sont :
De même qu'un serveur et un client DNS ne peuvent pas se contenter d'UDP (pour pouvoir envoyer des données de grande taille, il faudra de toute façon passer à TCP), DNS-sur-DTLS ne peut pas suffire seul, et il faudra donc que les clients et serveurs aient également DNS-sur-TLS.
La spécification de DNS-sur-DTLS est dans la section 3 de notre
RFC. DNS-sur-DTLS va tourner, comme DNS-sur-TLS, sur le
port 853 (sauf accord préalable entre
client et serveur, s'ils sont adultes et consentants). Un client
peut déterminer si le serveur gère DNS-sur-DTLS en envoyant un
message DTLS ClientHello
vers le port 853. En
l'absence de réponse, le client réessaie, puis laisse
tomber DTLS. Selon sa configuration (plus ou moins paranoïaque), le
client va alors tenter le DNS habituel en clair, ou bien
complètement renoncer. En tout cas, interdiction d'utiliser le
port 853 pour transmettre des messages DNS en clair. L'utilisation
de ce port sur UDP implique DTLS.
Si, par contre, le serveur répond et qu'une session DTLS est établie, le client DNS-sur-DTLS authentifie le serveur avec les mêmes méthodes que pour TLS, en suivant les bonnes pratiques de sécurité de TLS (RFC 7525) et les profils d'authentification de DNS-sur-TLS décrits dans le RFC 8310. Une fois que tout cela est fait, les requêtes et réponses DNS sont protégées et les surveillants sont bien embêtés, ce qui était le but.
DTLS tourne sur UDP et reprend sa sémantique. Notamment, il est parfaitement normal qu'une réponse arrive avant une autre, même partie plus tôt. Le client DNS-sur-DTLS ne doit donc pas s'étonner et, pour faire correspondre les requêtes et les réponses, il doit, comme avec le DNS classique sur UDP, utiliser le Query ID ainsi que la question posée (qui est répétée dans les réponses, dans la section Question).
Pour ne pas écrouler le serveur sous la charge, le client ne devrait créer qu'une seule session DTLS vers chaque serveur auquel il parle, et y faire passer tous les paquets. S'il y a peu de requêtes, et que le client se demande si le serveur est toujours là, il peut utiliser l'extension TLS du « battement de cœur » (RFC 6520), qui peut également servir à rafraichir l'état d'un routeur NAT éventuel. Le RFC recommande aux serveurs DNS-sur-DTLS un délai d'au moins une seconde en cas d'inutilisation de la session, avant de raccrocher. Le problème est délicat : si ce délai est trop long, le serveur va garder des ressources inutiles, s'il est trop court, il obligera à refaire le travail d'établissement de session trop souvent. En tout cas, le client doit être prêt à ce que le serveur ait détruit la session unilatéralement, et doit la réétablir s'il reçoit l'alerte DTLS qui lui indique que sa session n'existe plus.
Un petit mot sur les performances, maintenant, puisque rappelons-nous que le DNS doit aller vite (section 4). L'établissement d'une session DTLS peut nécessiter d'envoyer des certificats, qui sont assez gros et peuvent nécessiter plusieurs paquets. Il peut donc être utile d'utiliser les clés brutes (pas de certificat) du RFC 7250, ou bien l'extension TLS Cached Information Extension (RFC 7924).
Dans le cas d'un lien stub resolver vers résolveur, le serveur DNS parle à beaucoup de clients, chaque client ne parle qu'à très peu de serveurs. L'état décrivant les sessions DTLS doit donc plutôt être gardé chez le client (RFC 5077). Cela permettra de réétablir les sessions DTLS rapidement, sans pour autant garder d'état sur le serveur.
Le DNS est la principale application qui se tape les problèmes de PMTU (Path MTU, la MTU du chemin complet). Les réponses DNS peuvent dépasser les 1 500 octets magiques (la MTU d'Ethernet et, de facto, la PMTU de l'Internet). DTLS ajoute au moins 13 octets à chaque paquet, sans compter l'effet du chiffrement. Il est donc impératif (section 5) que clients et serveurs DNS-sur-DTLS gèrent EDNS (RFC 6891) pour ne pas être limité par l'ancien maximum DNS de 512 octets, et que les serveurs limitent les paquets DTLS à la PMTU (RFC 9147).
Contrairement au DNS classique, où chaque requête est indépendante, toute solution de cryptographie va nécessiter un état, l'ensemble des paramètres cryptographiques de la session. L'anycast, qui est répandu pour le DNS, ne pose donc pas de problème au DNS classique : si le routage change d'avis entre deux requêtes, et que la seconde requête est envoyée à un autre serveur, aucun problème. Avec DTLS, ce n'est plus le cas (section 6 du RFC) : le deuxième serveur n'a pas en mémoire la session cryptographique utilisée. Le serveur qui la reçoit va répondre avec une alerte TLS fatale (la méthode recommandée) ou, pire, ne pas répondre. Dans les deux cas, le client doit détecter le problème et réétablir une session cryptographique. (À noter que l'alerte TLS n'est pas authentifiée et ne peut donc pas être utilisée comme seule indication du problème. C'est d'ailleurs pareil pour d'éventuels messages d'erreur ICMP.) Le cas est donc proche de celui où le serveur ferme la session unilatéralement, et la solution est la même : le client doit toujours être prêt à recommencer l'ouverture de session DTLS.
Un point de sécurité, pour finir (section 9). Le RFC recommande l'utilisation de l'extension TLS « agrafage OCSP » (RFC 6066, section 8), notamment pour éviter la grosse fuite d'information que représente OCSP.
Il n'existe aucune mise en œuvre de DNS-sur-DTLS, et aucune n'est prévue. Cette expérimentation semble clairement un échec.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : J. Snijders (NTT)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 17 février 2017
Ce très court RFC ne fait pas grand'chose : il marque juste comme « à ne pas utiliser » (deprecated) un certain nombre d'attributs BGP.
BGP est le protocole de routage de l'Internet. En permanence, les routeurs s'envoient des annonces de routes, annonces portant certains attributs (RFC 4271, section 5) qui précisent des caractéristiques de la route. La liste de ces attributs figure dans un registre IANA. Les attributs cités dans ce RFC sont marqués comme officiellement abandonnés. Ce n'est pas qu'ils ne servaient pas : au contraire, ils étaient « squattés » en étant annoncés bien qu'ils n'aient jamais fait l'objet d'un enregistrement formel. Mieux valait donc les marquer dans le registre.
Mais pourquoi est-ce que des gens peuvent utiliser des attributs non enregistrés ? Parce qu'il n'y a pas de police de l'Internet (en dépit de raccourcis franchements abusifs, par exemple de certains journalistes qui écrivent que « l'ICANN est le régulateur de l'Internet »). Personne ne peut donner des ordres à tous les routeurs, et les faire appliquer.
Bref, il y a des mises en œuvre de BGP qui fabriquent des
annonces avec des attributs non enregistrés. C'est la vie. Mais
c'est ennuyeux car cela peut entrainer des collisions avec de
nouveaux attributs qui, eux, suivent les règles. C'est ainsi que
l'attribut LARGE_COMMUNITY
du RFC 8092 avait d'abord reçu la valeur numérique 30 avant
qu'on s'aperçoive que cette valeur était squattée par un autre
attribut (merci, Huawei)... Résultat, les routeurs squatteurs, quand ils
recevaient des annonces avec un attribut
LARGE_COMMUNITY
ne lui trouvaient pas la
syntaxe attendue et retiraient donc la route de leur table de
routage (conformément au RFC 7606). LARGE_COMMUNITY
a donc dû
aller chercher un autre numéro (32), et 30 a été ajouté au
registre, pour indiquer « territoire dangereux, squatteurs ici
». Le même traitement a été appliqué aux attributs 31, 129, 241,
242 et 243, qui étaient également squattés.
Le groupe de travail à l'IETF s'est demandé s'il n'aurait pas mieux valu « punir » les squatteurs en allouant délibérement le numéro officiel pour un autre attribut que le leur mais cela aurait davantage gêné les utilisateurs de l'attribut légitime que les squatteurs, qui avaient déjà une base installée.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : J. Heitz (Cisco), J. Snijders
(NTT), K. Patel (Arrcus), I. Bagdonas
(Equinix), N. Hilliard (INEX)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 19 février 2017
Ce RFC normalise un nouvel attribut des annonces BGP, « Large Communities ». Les « communautés » BGP sont des courtes données collées aux annonces BGP et qui permettent d'indiquer certaines caractéristiques des routes. Les décisions des routeurs peuvent utiliser ces caractéristiques. Mais les communautés originales étaient trop courtes (seulement quatre octets) : le nouvel attribut permet des communautés de douze octets.
Les « communautés » sont définies dans le RFC 1997. On les apprend via les documentations des
opérateurs ou des points d'échange. Par
exemple, celle du point
d'échange irlandais (section « Community based prefix filtering »). Un attribut COMMUNITY
dans une annonce BGP peut comporter plusieurs
communautés. Traditionnellement, les quatre octets des
communautés initiales sont utilisées pour représenter le numéro
d'AS dans les deux premiers octets (ainsi,
une communauté est mondialement unique, par ce système
d'allocation à deux niveaux), et des données spécifiques à l'AS
dans les deux suivants. Évidemment, avec les numéros d'AS de
quatre octets du RFC 6793, ça ne marchait
plus. D'où cet attribut LARGE_COMMUNITY
,
désormais stocké dans le registre
IANA sous le numéro (type code) 32. (Il
y a bien eu une autre tentative d'augmenter la taille des
communautés, dans le RFC 4360, mais pas
suffisamment pour que les AS à quatre octets puissent être
utilisés partout.) Comme pour les « petites » communautés, ces grandes
communautés dans une annonce forment un ensemble (donc, non
ordonné) : plusieurs routeurs auront pu ajouter une communauté à
cet ensemble.
Les communautés sont importantes car elles sont utilisées dans
la politique de routage. BGP ne cherche pas
à trouver le meilleur chemin : il fait du routage politique, où
les décisions sont prises en fonction de choix faits par les
opérateurs (privilégier tel ou tel lien pour le trafic entrant,
par exemple). Les informations contenues dans une annonce BGP
(section 4.3 du RFC 4271) habituelle ne sont pas toujours
suffisantes, et c'est pour cela que les communautés ont été
introduites par le RFC 1997, pour ajouter
des informations utiles, comme l'endroit où telle route a été
apprise.
L'attribut
COMMUNITY
(numéro 8) est transitif (section
5 du RFC 4271), ce qui
veut dire qu'après réception d'une annonce, il est transmis aux autres routeurs (d'où
l'importance de marquer la communauté avec un numéro d'AS, pour
que les communautés soient uniques au niveau mondial, sans qu'il
existe un registre central des communautés).
Le nouvel attribut LARGE_COMMUNITY
(numéro
32) est également optionnel et transitif (section 2 de notre RFC). Il se compose d'un
ensemble de grandes communautés, chacune étant stockée sur douze
octets. L'idée est qu'on utilise les quatre premiers octets pour
identifier l'AS (ce qui va bien avec les grands AS du du RFC 6793), ce qui va garantir l'unicité des
communautés. Le nombre de communautés dans un attribut
LARGE_COMMUNITY
est donné par le champ
Longueur de l'attribut, les attributs BGP étant encodés en
TLV (cf. RFC 4271,
section 4.3).
En cas d'agrégation de routes (section 3 du RFC), il est recommandé d'utiliser comme communautés l'union des ensembles de communautés des différentes annonces.
Et comment on va représenter ces grandes communautés sous forme
texte ? (Sur le câble, entre les deux routeurs, c'est du binaire,
en gros boutien, cf. RFC 4271, section 4.) On note trois groupes de quatre
octets, séparés par un deux-points, par
exemple 2914:65400:38016
(section 4 de notre RFC), où le premier champ
est presque toujours l'AS. (On trouve plein d'exemples dans le RFC 8195.)
Comme toutes les grandes communautés font exactement douze octets, si le champ Longueur de l'attribut n'est pas un multiple de douze, l'attribut est invalide, et le routeur qui reçoit cette annonce doit la gérer comme étant un retrait de la route (RFC 7606).
Un point de sécurité important en section 6 du RFC ; en gros, les grandes communautés ont quasiment les mêmes propriétés de sécurité que les anciennes petites communautés. Notamment, elles ne sont pas protégées contre une manipulation en transit : tout AS dans le chemin peut ajouter des communautés (même « mensongères », c'est-à-dire indiquant un autre AS que le sien) ou retirer des communautés existantes. La section 11 du RFC 7454 donne quelques détails à ce sujet. Ce problème n'est pas spécifique aux communautés, c'est un problème général de BGP. L'Internet n'a pas de chef et il est donc difficile de concevoir un mécanisme permettant de garantir l'authenticité des annonces.
Il existe déjà de nombreuses mises en œuvre de BGP qui gèrent
ces grandes communautés. Par exemple IOS
XR, ExaBGP,
BIRD, OpenBGPD, GoBGP,
Quagga, bgpdump depuis la
version 1.5,
pmacct... Une liste plus complète figure
sur le
Wiki. Mais il y a aussi le site Web du projet,
où vous trouverez plein de choses. Si vous avez accès à un routeur
BGP, ou à un looking
glass qui affiche les grandes communautés
(c'est le cas de celui du
Ring de NLnog),
les deux préfixes 2001:67c:208c::/48
et
192.147.168.0/24
ont une grande communauté
(15562:1:1
).
Si vous essayez sur un routeur qui a un vieux logiciel, ne
comprenant pas ces grandes communautés, vous verrez sans doute
quelque chose du genre « unknown attribute ». Ici
sur IOS à Route Views :
% telnet route-views.oregon-ix.net ... Username: rviews route-views> show ip bgp 192.147.168.0 BGP routing table entry for 192.147.168.0/24, version 37389686 Paths: (41 available, best #21, table default) Not advertised to any peer Refresh Epoch 1 3333 1273 2914 15562 193.0.0.56 from 193.0.0.56 (193.0.0.56) Origin IGP, localpref 100, valid, external Community: 1273:22000 2914:410 2914:1206 2914:2203 2914:3200 unknown transitive attribute: flag 0xE0 type 0x20 length 0xC value 0000 3CCA 0000 0001 0000 0001 ...
Ici sur un vieux IOS-XR (le test a été fait à l'époque où l'attribut avait le numéro 30 et pas 32, d'où le 0x1e) :
RP/0/RSP0/CPU0:Router# show bgp ipv6 unicast 2001:67c:208c::/48 unknown-attributes BGP routing table entry for 2001:67c:208c::/48 Community: 2914:370 2914:1206 2914:2203 2914:3200 Unknown attributes have size 15 Raw value: e0 1e 0c 00 00 3c ca 00 00 00 01 00 00 00 01
Et ici sur JunOS :
user at JunOS-re6> show route 2001:67c:208c::/48 detail 2001:67c:208c::/48 (1 entry, 1 announced) AS path: 15562 I Unrecognized Attributes: 15 bytes Attr flags e0 code 1e: 00 00 3c ca 00 00 00 01 00 00 00 01
Notez que certaines configurations (parfois activées par défaut) du routeur peuvent supprimer l'attribut « grandes communautés ». Pour empêcher cela, il faut, sur JunOS :
[edit protocols bgp] user at junos# delete drop-path-attributes 32
Et sur IOS-XR :
configure router bgp YourASN attribute-filter group ReallyBadIdea ! avoid creating bogons no attribute 32 ! !
Trois lectures pour finir :
Date de publication du RFC : Février 2017
Auteur(s) du RFC : M. Kerwin (QUT)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 20 février 2017
Vous connaissez le plan d'URI
file:
, qui indique que la ressource se
trouve sur le système de fichiers local ? (Par exemple, ce fichier
que je suis en train d'éditer est
file:///home/stephane/Blog/RFC/8089.xml
.) Ce plan avait été défini
très brièvement dans le RFC 1738 (section 3.10). Tellement
brièvement qu'il y manquait pas mal de choses. Ce nouveau
RFC remplace cette partie du RFC 1738 et fournit cette fois une description
complète du plan file:
. Ce n'était pas une
tâche facile car les différents systèmes de fichiers ont des
syntaxes et des comportements très différents. Le RFC lui-même est
très court, mais il comporte plusieurs annexes plus longues,
discutant de points spécifiques parfois assez tordus.
Donc, d'abord, la définition (section 1 de notre RFC) : un fichier est un objet qui se trouve rangé dans un environnement structuré, qui fournit notamment un système de nommage, environnement qu'on nomme le système de fichiers. (Et le fichier contient des données mais ce point n'est pas crucial pour les URI et n'est donc pas mentionné.) Ensuite, les URI : ce sont les identificateurs standard du Web. Leur syntaxe générique est définie dans le RFC 3986 et ce nouveau RFC ne fait donc que spécialiser le RFC 3986. Normalement, ce RFC est parfaitement compatible avec l'ancienne syntaxe, celle du RFC 1738 mais, en pratique, comme l'ancienne définition était vraiment trop vague, il y aura forcément quelques différences. (L'annexe E donne quelques exemples de pratiques utilisées dans la nature et qui ne sont pas strictement alignées sur les nouvelles règles. Elle cite par exemple l'ajout d'un nom d'utilisateur dans l'URI. Un exemple des problèmes que ces différences posent aux navigateurs est bien expliqué dans cet article de Microsoft.)
Les URI file:
ne supposent pas
l'utilisation d'un protocole particulier, ni d'un type
de média particulier.
Ce plan d'URI désigne des « fichiers locaux ». Un fichier local est accessible juste à partir de son nom, sans autre information (par exemple sans utiliser une adresse réseau explicite). Mais, en pratique (section 1.1), il peut être physiquement sur une autre machine, grâce à des techniques comme NFS ou SMB.
La syntaxe de ces URI figure en section 2 de notre RFC,
formalisée en ABNF (RFC 5234). S'appuyant sur la syntaxe générique du RFC 3986, elle diffère légèrement de celle du
RFC 1738 (l'annexe A liste les
différences). Le plan file:
est référencé
dans le registre des plans d'URI. Je vous laisse découvrir sa
grammaire dans le RFC, je donne juste des
exemples qui illustrent certains points de la syntaxe :
file:///tmp/toto.txt
. Il désigne le fichier local
/tmp/toto.txt
de l'ordinateur sur lequel on
travaille. La syntaxe du nom de fichier est celle
d'Unix, même si ledit ordinateur
n'utilise pas Unix. Ainsi, le fichier
c:\machin\truc
sur une machine
Windows sera quand même
file:///c:/machin/truc
(il existe plein de
variantes non-standard, voir l'annexe E, et l'article
cité plus haut, sur les problèmes que cela pose). Sur
VMS,
DISK1:[CS.JANE]PAPER.PS
deviendra
file:///disk1/cs/jane/paper.ps
(cf. annexe D).file:
, on peut mettre dans ce champ
localhost
, voire n'importe quel nom qui
désigne la machine locale (je ne suis pas sûr de l'intérêt que
cela présente, mais c'est la norme qui, il est vrai, déconseille
cet usage). Donc, l'URI cité
au début aurait pu (mais ce n'est pas recommandé) être
file://localhost/tmp/toto.txt
. (Voir aussi
la section 3 du RFC.)file:/tmp/toto.txt
est donc légal.file:///c:/machin/truc
et
file:///c:/Machin/TRUC
sont deux URI
différents même si on sait bien que, sur une machine
Windows, ils désigneront le même fichier.Que peut-on faire avec un fichier ? Plein de choses (l'ouvrir,
lire les données, le détruire… La norme
POSIX peut donner des idées à ce sujet.) Le plan d'URI
file:
ne limite pas les opérations
possibles.
Évidemment, l'encodage des caractères
utilisé va faire des histoires, puisqu'il varie d'une machine à
l'autre. C'est parfois UTF-8, parfois un
autre encodage et, parfois, le système de fichiers ne définit
rien, le nom est juste une suite d'octets, qui devra être
interprétée par les applications utilisées (c'est le cas
d'Unix). Notre RFC (section 4) recommande bien sûr
d'utiliser UTF-8, avant l'optionelle transformation
pour cent (RFC 3986,
section 2.5). Ainsi, le fichier
/home/stéphane/café.txt
aura l'URI
file:/home/st%C3%A9phane/caf%C3%A9.txt
, quel
qu'ait été son encodage sur la machine. Au passage, j'ai essayé
avec curl et
file:///tmp/café.txt
,
file:/tmp/café.txt
,
file:/tmp/caf%C3%A9.txt
,
file://localhost/tmp/caf%C3%A9.txt
et même
file://mon.adresse.ip.publique/tmp/caf%C3%A9.txt
marchent tous.
Et la sécurité ? Toucher aux fichiers peut évidemment avoir des
tas de conséquences néfastes. Par exemple, si l'utilisateur charge
le fichier file:///home/michu/foobar.html
,
aura-t-il la même origine (au sens de la sécurité du Web) que
file:///tmp/youpi.html
? Après tout, ils
viennent du même domaine (le domaine vide, donc la machine
locale). Le RFC note qu'au contraire l'option la plus sûre est de
considérer que chaque fichier est sa propre origine (RFC 6454).
Autre question de sécurité rigolote, les systèmes de fichiers
ont en général des caractères spéciaux (comme la barre
oblique ou un couple de points pour Unix). Accéder bêtement à un
fichier en passant juste le nom au système de fichiers peut
soulever des problèmes de sécurité (c'est évidemment encore pire
si on passe ces noms à des interpréteurs comme le
shell, qui rajoutent leur propre liste de
caractères spéciaux). Le RFC ne spécifie pas de liste de
caractères « dangereux » car tout nouveau système de fichiers peut
l'agrandir. C'est aux programmeurs qui font les logiciels de faire
attention, pour le système d'exploitation
pour lequel ils travaillent. (Un problème du même ordre existe
pour les noms de fichiers spéciaux, comme
/dev/zero
sur Unix ou
aux
et
lpt
sur Windows.)
Une mauvaise gestion de la sensibilité à la casse ou de l'encodage des caractères peut aussi poser des problèmes de sécurité (voir par exemple le rapport technique UAX #15 d'Unicode.)
Notons qu'il existe d'autres définitions possibles d'un URI
file:
(annexe C de notre RFC). Par exemple,
le WhatWG maintient une liste des plans d'URI, non synchronisée avec celle
« officielle », et dont l'existence a
fait pas mal de remous à l'IETF, certains
se demandant s'il fallait quand même publier ce RFC, au risque
d'avoir des définitions contradictoires (cela a sérieusement
retardé la sortie du RFC). En gros,
l'IETF se concentre plutôt sur la syntaxe,
et le WhatWG sur le comportement des navigateurs (rappelez-vous que les URI ne sont
pas utilisés que par des navigateurs…). Il y a aussi les
définitions Microsoft comme UNC
ou leurs
règles sur les noms de fichier.
Et, pour finir, je vous recommande cet autre article de Microsoft sur l'évolution du traitement des URI dans IE.
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : L. Yong (Huawei
Technologies), E. Crabbe
(Oracle), X. Xu (Huawei
Technologies), T. Herbert (Facebook)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tsvwg
Première rédaction de cet article le 9 mars 2017
Le protocole de tunnel GRE, normalisé dans les RFC 2784 et RFC 7676, tourne normalement directement sur IP (numéro de protocole 47, TCP étant 6 et UDP 17). Cela présente quelques inconvénients, notamment la difficulté à passer certaines middleboxes, et ce nouveau RFC propose donc une encapsulation de GRE dans UDP, et non plus directement dans IP. Un des autres avantages de cette encapsulation est que le port source UDP peut être utilisé comme une source d'entropie supplémentaire : sa vérification permet d'améliorer la (faible) sécurité de GRE. GRE sur UDP permet aussi l'utilisation de DTLS si on veut chiffrer (ce que ne fait pas GRE classiquement).
Un autre avantage est que l'encapsulation dans UDP peut améliorer les performances, dans le cas où il y a des répartiteurs de charge ECMP : ils pourront alors faire passer tous les paquets d'un même tunnel GRE par le même chemin, puisqu'ils prennent leurs décisions sur la base du tuple {protocole, adresse IP source, adresse IP destination, port source, port destination}.
Vu du réseau, un tunnel GRE sur UDP sera juste du trafic UDP normal. Attention, toutefois, le trafic UDP sur l'Internet public doit normalement obéir à certaines règles, notamment de contrôle de la congestion (ces règles figurent dans le RFC 8085). Avec TCP, c'est le protocole de transport qui s'en charge, avec UDP, c'est à l'application de le faire. Si on transporte du trafic quelconque, pas spécialement raisonnable, dans un tunnel GRE sur UDP, on viole les règles du RFC 8085. Il faut donc s'assurer que le trafic dans le tunnel a des mécanismes de contrôle de la congestion, ou bien réserver GRE sur UDP à des réseaux fermés, où on prend les risques qu'on veut. (Voir aussi la section 8 de notre RFC.)
Donc, on peut se servir de GRE sur UDP au-dessus d'IPv4 ou d'IPv6 (section 2 du RFC). La somme de contrôle UDP est très recommandée (elle est obligatoire en IPv6). On doit vérifier que le trafic transporté fera attention au contrôle de congestion. Le port source UDP doit être dans la plage des ports éphémères (de 49 152 à 65 535, voir aussi la section 3.2.1). Utiliser un port par flot encapsulé facilite la tâche des équipements comme les répartiteurs de trafic. Mais on peut aussi n'utiliser qu'un seul port pour tout ce qui passe dans le tunnel et, dans ce cas, il faut le choisir de manière imprévisible, pour des raisons de sécurité (RFC 6056). Et en IPv6, merci de penser à utiliser le flow label (RFC 6438).
Ce protocole GRE sur UDP a eu une histoire longue et compliquée, pris dans des efforts pour fournir des mécanismes génériques d'encapsulation dans UDP (projet GUE), efforts qui n'ont guère débouché (cf. le RFC 7510 pour un autre exemple que GRE).
Voilà, après ces grands principes, le format exact (section 3). Au-dessus de l'en-tête IP (v4 ou v6), on met un en-tête UDP (RFC 768) et un en-tête GRE (RFC 2784).
La section 5 du RFC couvre le cas de DTLS (RFC 6347), qui a l'avantage de donner à GRE les moyens de chiffrer le trafic, sans modifier GRE lui-même.
Évidemment, dans l'Internet réellement existant, le problème, ce sont les middleboxes (section 7 du RFC). C'est d'ailleurs parfois uniquement à cause d'elles qu'il faut utiliser GRE sur UDP et pas GRE tout court, car certaines se permettent de bloquer les protocoles qu'elles ne connaissent pas (typiquement, tout sauf UDP et TCP).
Même en mettant GRE dans UDP, tous les problèmes ne sont pas résolus. Le trafic GRE est unidirectionnel (il y a en fait deux tunnels différents, chacun à sens unique). Il n'y est pas censé avoir des réponses au port source du trafic. Mais certaines middleboxes vont insister pour que ce soit le cas. Une solution possible, pour ces middleboxes pénibles, est de n'utiliser qu'un seul port source.
Il existe des mises en œuvre de ce RFC pour
Linux et BSD. Les
tests suivants ont été faits sur des machines Linux,
noyaux 4.4 et 4.8. ip
tunnel
ne fournit pas de choix pour « GRE sur
UDP ». Il faut passer par le système FOU
(Foo-over-UDP, cf. cet article de LWN),
qui a l'avantage d'être plus générique :
# modprobe fou # lsmod|grep fou fou 20480 0 ip_tunnel 28672 1 fou ip6_udp_tunnel 16384 1 fou udp_tunnel 16384 1 fou
La machine qui va recevoir les paquets doit configurer FOU pour indiquer que les paquets à destination de tel port UDP sont en fait du GRE :
# ip fou add port 4754 ipproto 47
(47 = GRE) La machine émettrice, elle, doit créer une interface GRE encapsulée grâce à FOU :
# ip link add name tun1 type gre \ remote $REMOTE local $LOCAL ttl 225 \ encap fou encap-sport auto encap-dport 4754 # ip link set tun1 up
Et il faut évidemment configurer une route passant par cette
interface tun1
, ici pour le préfixe
172.16.0.0/24
:
# ip route add 172.16.0.0/24 dev tun1
Avec cette configuration, lorsque la machine émettrice
pingue
172.16.0.1
, les paquets arrivent bien sur la
machine réceptrice :
12:10:40.138768 IP (tos 0x0, ttl 215, id 10633, offset 0, flags [DF], proto UDP (17), length 116) 172.19.7.106.46517 > 10.17.124.42.4754: [no cksum] UDP, length 88
On peut les examiner plus en détail avec Wireshark :
User Datagram Protocol, Src Port: 1121 (1121), Dst Port: 4754 (4754) Source Port: 1121 Destination Port: 4754 Length: 96 Checksum: 0x0000 (none) [Good Checksum: False] [Bad Checksum: False] [Stream index: 0] Data (88 bytes) 0000 00 00 08 00 45 00 00 54 3e 99 40 00 40 01 ef 6f ....E..T>.@.@..o ...
Wireshark ne connait apparemment pas le GRE sur UDP. Mais, dans les données, on reconnait bien l'en-tête GRE (les quatre premiers octets où presque tous les bits sont à zéro, le bit C étant nul, les quatre octets suivants optionnels ne sont pas inclus, le 0x800 désigne IPv4, cf. RFC 2784), et on voit un paquet IPv4 ensuite. Pour que ce paquet soit correctement traité par la machine réceptrice, il faut le transmettre à GRE. Comme ce dernier n'a pas de mécanisme permettant de mettre plusieurs tunnels sur une même machine (l'en-tête GRE n'inclut pas d'identificateurs), il faut activer l'unique interface GRE :
# ip link set gre0 up
On voit bien alors notre ping qui arrive :
# tcpdump -vv -n -i gre0 tcpdump: listening on gre0, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes 14:02:06.922367 IP (tos 0x0, ttl 64, id 47453, offset 0, flags [DF], proto ICMP (1), length 84) 10.10.86.133 > 172.16.0.1: ICMP echo request, id 13947, seq 17, length 64
Voilà, je vous laisse faire la configuration en sens inverse.
Si vous voulez en savoir plus sur la mise en œuvre de FOU, voyez cet excellent exposé d'un des auteurs, Tom Herbert, cet article du même, et enfin sa vidéo.
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : L. Eggert (NetApp), G. Fairhurst
(University of Aberdeen), G. Shepherd (Cisco
Systems)
Réalisé dans le cadre du groupe de travail IETF tsvwg
Première rédaction de cet article le 9 mars 2017
La grande majorité des applications Internet tourne sur le protocole de transport TCP. Mais son concurrent UDP, normalisé dans le RFC 768, prend de l'importance avec le multimédia et les jeux en ligne pour lesquels il est souvent bien adapté. Contrairement à TCP, UDP ne fournit aucun mécanisme de contrôle de la congestion. C'est donc aux applications de fournir ce contrôle, suivant les règles expliquées par ce RFC. (Qui parle surtout de congestion mais aussi d'autres sujets importants pour ceux qui utilisent UDP, comme la taille des messages ou comme les sommes de contrôle.) Il remplace le RFC 5405.
UDP est apprécié pour certaines applications car il est simple et léger et le fait qu'il ne garantisse pas l'acheminement de la totalité des paquets n'est pas forcément un problème dans les applications multimédia : si on perd quelques secondes d'une communication téléphonique RTP, il vaut mieux passer à la suite que de perdre du temps à la retransmettre comme le ferait TCP. Mais UDP ne fournit pas non plus de contrôle de la congestion. Une application UDP enthousiaste peut envoyer des paquets au débit maximum permis, saturant tous les liens situés en aval. (Il ne faut pas juste tenir compte de la capacité du lien auquel on est connecté, mais de celle du chemin complet. L'exemple du RFC, avec un chemin à seulement 56 kb/s, que cinq paquets UDP de 1 500 octets par seconde saturent, n'est pas invraisemblable.) Protéger le réseau de la congestion est pourtant nécessaire (RFC 2914 et RFC 7567), à la fois pour assurer que le réseau continue à être utilisable et également pour assurer une certaine équité entre les différents flux de données, pour éviter qu'une seule application gourmande ne monopolise le réseau pour elle. (Ceci concerne l'Internet public. Si on est dans un environnement fermé, utilisant TCP/IP mais où la capacité réseau, et son usage, sont contrôlés, le problème est évidemment différent. Voir notamment la section 3.6.)
UDP ne faisant pas ce contrôle de congestion, il faut bien que l'application le fasse et, pour cela, qu'elle mette en œuvre les conseils de ce RFC. (Notre RFC contient également des conseils pour d'autres aspects de l'utilisation d'UDP que le contrôle de congestion : mais c'est le plus important.)
Le gros du RFC est dans la section 3 qui détaille ces conseils (la
section 7 contient un excellent résumé sous forme d'un tableau des
conseils à suivre). Le
premier est qu'il vaut peut-être mieux ne pas utiliser UDP. Beaucoup
de développeurs d'applications pensent à UDP en premier parce qu'il
est simple et facile à comprendre et qu'il est « plus rapide que
TCP ». Mais, rapidement, ces développeurs se rendent compte qu'ils ont
besoin de telle fonction de TCP, puis de telle autre, ils les mettent
en œuvre dans leur application et arrivent à une sorte de TCP en
moins bien, davantage bogué et pas plus rapide. Notre RFC conseille
donc d'abord de penser aux autres protocoles de transport comme
TCP (RFC 793),
DCCP (RFC 4340) ou
SCTP (RFC 9260). Ces protocoles sont
d'autant plus intéressants qu'ils ont souvent fait l'objet de réglages
soigneux depuis de nombreuses années et qu'il est donc difficile à un
nouveau programme de faire mieux. D'autant plus qu'il existe souvent
des réglages spécifiques pour les adapter à un usage donné. Par
exemple, on peut dire à TCP de donner la priorité à la
latence (paramètre
TCP_NODELAY
de setsockopt) ou bien au débit.
Si on ne suit pas ces sages conseils, et qu'on tient à se servir d'UDP, que doit-on faire pour l'utiliser intelligemment ? La section 3.1 couvre le gros morceau, le contrôle de congestion. Celui-ci doit être pris en compte dès la conception de l'application. Si cette dernière fait de gros transferts de données (section 3.1.2, c'est le cas de RTP, RFC 3550), elle doit mettre en œuvre TFRC, tel que spécifié dans le RFC 5348, donc faire à peu près le même travail que TCP. Et ce mécanisme doit être activé par défaut.
Si l'application transmet peu de données (section 3.1.3), elle doit quand même faire attention et le RFC demande pas plus d'un datagramme par RTT, où le RTT est un cycle aller-retour avec la machine distante (si le calcul n'est pas possible, le RFC demande une durée de trois secondes). L'application doit également détecter les pertes de paquet pour ralentir son rythme si ces pertes - signe de congestion - sont trop fréquentes.
Si l'application est bi-directionnelle (le cas de loin le plus fréquent), le contrôle de la congestion doit se faire indépendamment dans les deux directions.
Notez que se retenir d'envoyer des paquets n'est pas le seul moyen pour une application d'éviter la congestion. Elle peut aussi (si l'API utilisée le permet) se servir d'ECN (RFC 3168) pour transmettre l'information qui permettra de réguler le trafic.
Enfin, le RFC demande (section 3.1.10) un mécanisme de « disjoncteur » (circuit breaker, cf. RFC 8084 ou bien RFC 8083 pour l'exemple spécifique de RTP). C'est un mécanisme de dernier recours pour couper la communication en cas de risque d'effondrement du réseau.
Le cas où l'application est un tunnel au-dessus d'UDP est également couvert (section 3.1.11). C'est par exemple le cas du protocole GRE quand il tourne sur UDP (RFC 8086).
En suivant toutes ces règles, l'application gère proprement la congestion. Et le reste ? La section 3.2 fournit des pistes sur la gestion de la taille des paquets. La charge utile d'un paquet UDP peut théoriquement faire 65 507 octets en IPv4 et 65 527 en IPv6. Mais c'est théorique. En pratique, la fragmentation marche mal sur l'Internet, et notre RFC conseille de rester en dessous de la MTU, et d'utiliser la découverte de la MTU du chemin spécifiée dans des RFC comme les RFC 4821 et RFC 8899. (Aujourd'hui, la principale application qui envoie des paquets UDP plus gros que la MTU, et doit donc se battre avec la fragmentation, est le DNS ; voir par exemple l'étude de Geoff Huston sur les comportements très variés des serveurs de la racine.)
La section 3.3 explique la question de la fiabilité : par défaut, UDP ne retransmet pas les paquets perdus. Si c'est nécessaire, c'est l'application qui doit le faire. Elle doit aussi gérer l'eventuelle duplication des paquets (qu'UDP n'empêche pas). Le RFC note que les retards des paquets peuvent être très importants (jusqu'à deux minutes, normalise le RFC, ce qui me semble très exagéré pour l'Internet) et que l'application doit donc gérer le cas où un paquet arrive alors qu'elle croyait la session finie depuis longtemps.
La section 3.4 précise l'utilisation des sommes de contrôle (facultatives pour UDP sur IPv4 mais qui devraient être utilisées systématiquement). Si une somme de contrôle pour tout le paquet semble excessive, et qu'on veut protéger uniquement les en-têtes de l'application, une bonne alternative est UDP-Lite (RFC 3828), décrit dans la section 3.4.2. (Il y a aussi des exceptions à la règle « somme de contrôle obligatoire en IPv6 » dans le cas de tunnels.)
Beaucoup de parcours sur l'Internet sont encombrés de
« middleboxes », ces engins intermédiaires qui
assurent diverses fonctions (NAT,
coupe-feu, etc) et qui sont souvent de médiocre
qualité logicielle, bricolages programmés par un inconnu et jamais
testés. La section 3.5 spécifie les règles que devraient suivre les applications UDP pour passer au travers sans
trop de heurts. Notamment, beaucoup de ces « middleboxes »
doivent maintenir un état par flux qui les
traverse. En TCP, il est relativement facile de détecter le début et
la fin d'un flux en observant les paquets d'établissement (SYN
) et de
destruction (FIN
) de la connexion. En UDP, ces paquets n'ont pas
d'équivalent et la détection d'un flux repose en général sur des
heuristiques. L'engin peut donc se tromper et mettre fin à un flux qui
n'était en fait pas terminé. Si le DNS s'en
tire en général (c'est un simple protocole requête-réponse, avec la
lupart du temps moins d'une seconde entre l'une et l'autre), d'autres
protocoles basés sur UDP pourraient avoir de mauvaises
surprises. Ces protocoles doivent donc se préparer à de soudaines interruptions
de la communication, si le timeout d'un engin
intermédiaire a expiré alors qu'il y avait encore des paquets à
envoyer. (La solution des
keepalives est déconseillée
par le RFC car elle consomme de la capacité du réseau et ne dispense
pas de gérer les coupures, qui se produiront de toute façon.)
La section 5 fera le bonheur des
programmeurs qui y trouveront des conseils pour
mettre en œuvre les principes de ce RFC, via
l'API des prises
(sockets, RFC 3493). Elle est largement documentée mais en général plutôt
pour TCP que pour UDP, d'où l'intérêt du résumé qu'offre ce RFC, qui
ne dispense évidemment pas de lire le Stevens. Par exemple, en
l'absence de mécanisme de TIME_WAIT
(la prise
reste à attendre d'éventuels paquets retardés, même après sa fermeture
par l'application), une application UDP peut ouvrir une prise... et
recevoir immédiatement des paquets qu'elle n'avait pas prévus, qui
viennent d'une exécution précédente.
Le RFC détaille également la bonne stratégie à utiliser pour les ports. Il existe un registre des noms et numéros de ports (RFC 6335), et le RFC 7605 explique comment utiliser les ports. Notre RFC conseille notamment de vérifier les ports des paquets reçus, entre autre pour se protéger de certaines attaques, où l'attaquant, qui ne peut pas observer le trafic et doit injecter des paquets aveuglément, ne connait pas les ports utilisés (en tout cas pas les deux). L'application devrait utiliser un port imprévisible, comme le fait TCP (RFC 6056). Pour avoir suffisamment d'entropie pour les répartiteurs de charge, le RFC rappelle qu'en IPv6, on peut utiliser le champ flow label (RFC 6437 et RFC 6438).
Le protocole ICMP fournit une aide utile, que les applications UDP peuvent utiliser (section 5.2). Mais attention, certains messages ICMP peuvent refléter des erreurs temporaires (absence de route, par exemple) et ne devraient pas entraîner de mesures trop drastiques. Autre piège, il est trivial d'envoyer des faux paquets ICMP. Une application doit donc essayer de déterminer, en examinant le contenu du message ICMP, s'il est authentique. Cela nécessite de garder un état des communications en cours, ce que TCP fait automatiquement mais qui, pour UDP, doit être géré par l'application. Enfin, il faut se rappeler que pas mal de middleboxes filtrent stupidement l'ICMP et l'application doit donc être prête à se débrouiller sans ces messages.
Après tous ces conseils, la section 6 est dédiée aux questions de sécurité. Comme TCP ou SCTP, UDP ne fournit en soi aucun mécanisme d'intégrité des données ou de confidentialité. Pire, il ne fournit même pas d'authentification de l'adresse IP source (authentification fournie, avec TCP, par le fait que, pour établir la connexion, il faut recevoir les réponses de l'autre). Cela permet, par exemple, les injections de faux trafic (contre lesquelles il est recommandé d'utiliser des ports source imprévisibles, comme le fait le DNS), ou bien les attaques par amplification.
L'application doit-elle mettre en œvre la sécurité seule ? Le RFC conseille plutôt de s'appuyer sur des protocoles existants comme IPsec (RFC 4301, dont notre RFC note qu'il est très peu déployé) ou DTLS (RFC 9147). En effet, encore plus que les protocoles de gestion de la congestion, ceux en charge de la sécurité sont très complexes et il est facile de se tromper. Il vaut donc mieux s'appuyer sur un système existant plutôt que d'avoir l'hubris et de croire qu'on peut faire mieux que ces protocoles ciselés depuis des années.
Pour authentifier, il existe non seulement IPsec et DTLS mais également d'autres mécanismes dans des cas particuliers. Par exemple, si les deux machines doivent être sur le même lien (un cas assez courant), on peut utiliser GTSM (RFC 3682) pour s'en assurer.
Enfin, notre RFC se termine (section 7) par un tableau qui synthétise les recommandations, indiquant à chaque fois la section du RFC où cette recommandation est développée. Développeu·r·se d'applications utilisant UDP, si tu ne lis qu'une seule section du RFC, cela doit être celle-ci !
Quels changements depuis le RFC précédent, le RFC 5405 ? Le fond des recommandations reste le même, la principale addition est celle de nombreuses recommandations spécifiques au multicast (dont je n'ai pas parlé ici) mais aussi à l'anycast, aux disjoncteurs, et aux tunnels. Il y a également l'introduction d'une différence entre l'Internet public (où il se faut se comporter en bon citoyen) et des réseaux privés et fermés utilisant les mêmes protocoles, mais où on a droit à des pratiques qui seraient jugées anti-sociales sur l'Internet public (comme d'envoyer des paquets sans tenir compte de la congestion). Ce RFC est donc bien plus long que son prédécesseur.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : C. Lilley (W3C)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF justfont
Première rédaction de cet article le 1 mars 2017
Les types de contenu, servant
à indiquer le type des données envoyées (par le
Web, par le
courrier, etc) sont composés de deux
parties, un type de premier niveau (top level type, ou
type proprement dit, c'est la
catégorie des données) et un
sous-type (il indique le format des données). Type et sous-type
sont séparés par une barre
oblique. Par exemple, image/png
est un type MIME identifiant une image au format
PNG. Des nouveaux sous-types sont
enregistrés très souvent, c'est un événement banal. Mais des
nouveaux types de premier niveau sont bien plus rares. Ce
RFC en décrit un, le type
font/
, qui sert à identifier les formats pour des
polices de caractères. Ainsi, on pourra
envoyer un fichier de polices au format TTF en
l'étiquetant font/ttf
. (Notre RFC procède
également à l'enregistrement de plusieurs sous-types pour des
formats de polices particuliers.)
Une police de caractères, c'est une
description de comment représenter visuellement un ensemble de
caractères, description qu'un programme peut lire et comprendre.
Il existe bien des façons de faire cette représentation. Les
premiers formats de polices numériques étaient
matriciels mais on est depuis passés à des
formats vectoriels, qui permettent des
changements de taille à volonté. Ces descriptions de caractères
peuvent être distribuées via l'Internet et
la question se pose alors du type de média
à utiliser. En pratique, cela a souvent été
application
, un type un peu fourre-tout. On
trouve ainsi enregistré, par exemple, un application/font-woff
. Le
RFC 6838, sur l'enregistrement des types et
sous-types de contenus, permet (dans sa section 4.2.7, qui ajoute « Such cases are expected to be quite
rare »)
l'enregistrement d'un nouveau type de premier niveau. C'est ainsi
que ce RFC 8081 crée font/
.
Le besoin provient entre autres de l'usage de plus en plus
important des Web
fonts. C'est ansi qu'HTTP
Archive a vu passer le pourcentage de sites Web utilisant cette
technique de 1 % en 2010 à 50 % en 2015. L'analyse de Kuetell montrait une
certaine confusion chez les utilisateurs quant au type MIME à
utiliser pour ces polices. Certains utilisaient le type de premier
niveau font/
avant même son enregistrement
officiel et on voyait donc déjà des
font/truetype
pour le format
TrueType. D'autres se servaient
d'application/
pour des
application/octet-stream
(fichier binaire
quelconque) ou des application/x-font-ttf
(utilisant le préfixe x-
, pourtant abandonné
par le RFC 6648). On voit même des
text/plain
pour des ressources pourtant
clairement binaires... Les rares types officiellement enregistrés,
comme application/font-woff
, enregistré par un
groupe du W3C, sont peu utilisés.
Au fait, pourquoi est-ce qu'application/
est une mauvaise idée ? Une des principales raisons est qu'il est
regardé avec suspicion par les logiciels de filtrage, qui se
méfient de la capacité de ces fichiers à transporter du
logiciel malveillant. (Certains formats de police incluent un
langage de Turing, et peuvent donc offrir
des possibilités insoupçonnées…) Ensuite, en l'absence d'un type
de premier niveau, il n'était pas possible de définir un jeu
commun de paramètres précisant le type. Enfin, les polices de
caractères ne sont pas des logiciels et posent des problèmes
spécifiques, notamment de licence. Bref, il
fallait un type pour les formats de polices.
Ah, et puisque j'ai parlé de sécurité, la section 3 du RFC fait le point sur les problèmes que peuvent poser les polices de ce côté. Un fichier décrivant une police contient des données, mais aussi des programmes (hinting instructions) pour les opérations de rendu les plus sophistiquées. Par exemple, quand on agrandit un caractère, il ne faut pas agrandir uniformément selon toutes les dimensions ; ou bien certaines caractéristiques d'un caractère dépendent des caractères qui l'entourent. Bref, le rendu est une chose trop compliquée pour être spécifié sans un langage de programmation. C'est par exemple ce qu'on trouve dans les polices TrueType (cf. l'article de Wikipédia). Bien sûr l'exécution de ces « programmes » se fait en général dans un bac à sable, et ils n'ont pas accès à l'extérieur, mais certaines attaques restent possibles, par exemple des attaques par déni de service visant à bloquer le moteur de rendu. Les langages utilisés sont en général trop riches pour que des protections simples suffisent.
Et même si on se limite aux données, la plupart des formats (comme SFNT) sont extensibles et permettent l'ajout de nouvelles structures de données. Cette extensibilité est une bonne chose mais elle présente également des risques (par exemple, elle facilite la dissimulation de données dans les fichiers de polices).
Bon, je vous ai assez fait peur avec les risques de sécurité,
place à l'enregistrement de font/
à
l'IANA (section 4 du
RFC). font/
n'indique pas un format
particulier, mais une catégorie de contenu. Le format sera indiqué
dans le sous-type et c'est là seulement que le logiciel qui reçoit
ce contenu saura s'il peut en faire quelque chose d'utile. (Le RFC
suggère que les sous-types inconnus devraient être traités comme
du binaire quelconque, comme s'ils étaient
application/octet-stream
.) Six
sous-types sont enregistrés par notre RFC.
On peut utiliser un identificateur de fragment (RFC 3986, section 3.5, cet identificateur est le truc après
le croisillon dans un URI), pour désigner
une police particulière au sein d'une collection présente dans les
données envoyées. L'identificateur est le nom
PostScript. Attention, certains caractères
peuvent être utilisés dans un nom PostScript mais interdits pour
un identificateur de fragment, et doivent donc être
échappés
avec la notation pour-cent. Par exemple, l'identificateur de
la police Caret^stick
sera
#Caret%5Estick
.
Le RFC enregistre plusieurs sous-types. Si on veut en ajouter au registre des polices, il faut suivre les procédures du RFC 6838. Il est recommandé que la spécification du format soit librement accessible (ce qui n'est pas évident dans ce milieu).
Le RFC se termine avec les six sous-types de
font/
officiellement enregistrés. D'abord,
sfnt
pour le format générique
SFNT. Il prend des paramètres optionnels,
outlines
(qui prend comme valeur
TTF, CFF ou
SVG) et layout
(valeurs OTL, AAT et
SIL). On pourra donc écrire, par exemple,
font/sfnt; layout=SIL
. Ce
font/sfnt
remplace l'ancien type enregistré,
application/font-sfnt
. Notez que la
spécification de ce format est la norme ISO
ISO/IEC 14496-22, dite
« Open Font Format ».
SFNT est un format générique, qui sera sans doute rarement
utilisé tel quel. On verra plutôt ttf
ou otf
.
Un exemple d'un format spécifique est en effet
TrueType. Ce sera le sous-type
ttf
. Il aura également un paramètre optionnel
layout
(mêmes valeurs possibles). On pourra
donc voir dans une réponse HTTP
Content-Type: font/ttf
.
Troisième sous-type enregistré, otf
pour
OpenType.
On trouve aussi un sous-type collection
pour mettre plusieurs polices ensemble.
Viennent enfin WOFF versions 1
(woff
) et 2 (woff2
). Il
s'agit cette fois d'une norme W3C. Ce
nouveau type font/woff
remplace l'ancien application/font-woff
.
Voilà, c'est tout, le nouveau type de premier niveau
font
est désormais inclus dans le registre
IANA des types de premier niveau, et les polices
enregistrées sont dans cet
autre registre IANA.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : O. Sury (CZ.NIC), R. Edmonds
(Fastly)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF curdle
Première rédaction de cet article le 15 février 2017
Ce RFC (premier RFC du groupe CURDLE) spécifie comment utiliser les algorithmes de cryptographie à courbe elliptique Ed25519 et Ed448 dans DNSSEC.
Contrairement à ce qu'on a pu parfois lire sous la plume de trolls ignorants, DNSSEC, mécanisme d'authentification des enregistrements DNS, n'est en rien lié à RSA. Au contraire, comme tous les protocoles cryptographiques de l'IETF, il dispose d'une propriété nommée agilité cryptographique. Ce nom désigne un système utilisant la cryptographie qui n'est pas lié à un algorithme cryptographique particulier. Il peut donc en changer, notamment pour suivre les progrès de la cryptanalyse, qui rend l'abandon de certains algorithmes nécessaire. Aujourd'hui, par exemple, RSA semble bien démodé, et les courbes elliptiques ont le vent en poupe. Aucun problème pour DNSSEC : aussi bien les clés que les signatures disposent d'un champ numérique qui indique l'algorithme cryptographique utilisé. Les valeurs possibles de ce champ figurent dans un registre IANA, registre auquel viennent d'être ajoutés (cf. sections 5 et 7) 15 pour Ed25519 et 16 pour Ed448.
Notez que ces algorithmes ne sont pas les premiers algorithmes à courbes elliptiques normalisés pour DNSSEC : le premier avait été GOST R 34.10-2001 (RFC 5933), il y a six ans, et le second ECDSA (RFC 6605).
Les algorithmes cryptographiques Ed25519 et Ed448, instances de EdDSA, sont spécifiés dans le RFC 8032. Ils peuvent d'ailleurs servir à d'autres systèmes que DNSSEC (par exemple SSH, cf. RFC 7479).
Les détails pratiques pour DNSSEC, maintenant (section 3 du RFC). Notre nouveau RFC est court car l'essentiel était déjà fait dans le RFC 8032, il ne restait plus qu'à décrire les spécificités DNSSEC. Une clé publique Ed25519 fait 32 octets (section 5.1.5 du RFC 8032) et est encodée sous forme d'une simple chaîne de bits. Une clé publique Ed448 fait, elle, 57 octets (section 5.2.5 du RFC 8032).
Les signatures (cf. section 4 de notre RFC) font 64 octets pour Ed25519 et 114 octets pour Ed448. La façon de les générer et de les vérifier est également dans le RFC 8032, section 5.
Voici un exemple de clé publique Ed25519, et des signatures faites avec cette clé, extrait de la section 6 du RFC (attention, il y a deux erreurs, les RFC ne sont pas parfaits) :
example.com. 3600 IN DNSKEY 257 3 15 ( l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4= ) example.com. 3600 IN DS 3613 15 2 ( 3aa5ab37efce57f737fc1627013fee07bdf241bd10f3b1964ab55c78e79 a304b ) example.com. 3600 IN MX 10 mail.example.com. example.com. 3600 IN RRSIG MX 3 3600 ( 1440021600 1438207200 3613 example.com. ( Edk+IB9KNNWg0HAjm7FazXyrd5m3Rk8zNZbvNpAcM+eysqcUOMIjWoevFkj H5GaMWeG96GUVZu6ECKOQmemHDg== )
Et, pour une vraie clé dans un vrai domaine, cette fois sans erreur :
% dig DNSKEY ed25519.monshouwer.eu ; <<>> DiG 9.9.5-9+deb8u9-Debian <<>> DNSKEY ed25519.monshouwer.eu ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46166 ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;ed25519.monshouwer.eu. IN DNSKEY ;; ANSWER SECTION: ed25519.monshouwer.eu. 3537 IN DNSKEY 257 3 15 ( 2wUHg68jW7/o4CkbYue3fYvGxdrd83Ikhaw38bI9dRI= ) ; KSK; alg = 15; key id = 42116 ed25519.monshouwer.eu. 3537 IN RRSIG DNSKEY 15 3 3600 ( 20170223000000 20170202000000 42116 ed25519.monshouwer.eu. Gq9WUlr01WvoXEihtwQ6r7t9AfkQfyETKTfm84WtcZkd M04KEe+4xu9jqhnG9THDAmV3FKASyWQ1LtCaOFr5Dw== ) ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Wed Feb 15 13:13:27 CET 2017 ;; MSG SIZE rcvd: 215
Quelques questions de sécurité pour conclure ce RFC (section 8). Les clés Ed25519 font l'équivalent de 128 bits de sécurité (et 224 pour Ed448). Du fait de l'existence d'algorithmes efficaces pour les casser sur un ordinateur quantique, cela ne sera pas suffisant le jour où on disposera d'un tel ordinateur. Ce jour est probablement très lointain (bien que la NSA, organisme de confiance, dise le contraire).
Enfin, la même section 8 rappelle l'existence des attaques trans-protocoles : il ne faut pas utiliser une clé dans DNSSEC et dans un autre protocole.
À noter que ce RFC est un pas de plus vers une cryptographie 100 % Bernstein, avec cette adaptation des algorithmes utilisant Curve25519 à DNSSEC. Bientôt, l'ancien monopole de RSA aura été remplacé par un monopole de Curve25519.
Apparemment, le RFC est un peu en avance sur le logiciel, les systèmes DNSSEC existants ne gèrent pas encore Ed25519 ou Ed448 (à part, semble-t-il, PowerDNS et, bientôt, DNS Go. BIND a eu EdDSA à partir de la version 9.12.)
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : O. Gudmundsson
(CloudFlare), P. Wouters (Red Hat)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 11 mars 2017
Un problème fréquent avec DNSSEC est de transmettre à sa zone parente les clés publiques de signature de sa zone, pour que le parent puisse signer un lien qui va vers ces clés (l'enregistrement de type DS). Le RFC 7344 apportait une solution partielle, avec ses enregistrements CDS et CDNSKEY. Il y manquait deux choses : la création du premier DS (activation initiale de DNSSEC), et le retrait de tout les DS (on arrête de faire du DNSSEC). Ce nouveau RFC 8078 comble ces deux manques (et, au passage, change l'état du RFC 7344, qui passe sur le Chemin des Normes).
Avant le RFC 7344, tout changement des clés KSK (Key Signing Key) d'une zone nécessitait une interaction avec la zone parente, par le biais d'un mécanisme non-DNS (« out-of-band », par exemple un formulaire Web). La solution du RFC 7344, elle, n'utilise que le DNS (« in-band »). Ce nouveau RFC complète le RFC 7344 pour les configurations initiales et finales. (Le problème est complexe car il peut y avoir beaucoup d'acteurs en jeu. Par exemple, le BE n'est pas forcément l'hébergeur DNS. Ces difficultés ont certainement nui au déploiement de DNSSEC.)
Lorsqu'on change d'hébergeur DNS, la solution la plus propre est de faire un remplacement des clés, depuis celle de l'ancien hébergeur jusqu'à celle du nouveau. Cette solution préserve en permanence la sécurité qu'offre DNSSEC. Mais une des procédures mentionnées par notre RFC passe au contraire par un état non sécurisé, où la zone n'est pas signée. C'est dommage mais cela est parfois nécessaire si :
Une zone non signée vaut certainement mieux qu'une signature invalide. Mais le RFC oublie de dire que cela va casser certaines applications de sécurité qui exigent DNSSEC comme DANE (RFC 6698) ou SSHFP (RFC 4255).
Avant de lire la suite de ce RFC, deux conseils :
Les enregistrements CDS (Client-side Delegation Signer) servent à trois choses (section 2 du RFC) :
Avec le RFC 7344, seule la deuxième était possible (c'est la moins dangereuse, qui ne nécessite aucun changement dans les relations de confiance,notamment entre parente et fille). Notre RFC 8078 permet désormais les deux autres, plus délicates, car posant davantage de problèmes de sécurité.
La sémantique des enregistrements CDS (ou CDNSKEY) est donc désormais « la publication d'un ou plusieurs CDS indique un souhait de synchronisation avec la zone parente ; celle-ci est supposée avoir une politique en place pour accepter/refuser/vérifier ce ou ces CDS, pour chacune des trois utilisations notées ci-dessus ». Quand des CDS différents des DS existants apparaissent dans la zone fille, le responsable parental doit agir.
D'abord, l'installation initiale d'un DS alors qu'il n'y en avait pas avant (section 3 du RFC). La seule apparition du CDS ou du CDNSKEY ne peut pas suffire car comment le vérifier, n'ayant pas encore de chaîne DNSSEC complète ? Le responsable parental peut utiliser les techniques suivantes :
La deuxième utilisation des CDS, remplacer une clé est, on l'a vu, déjà couverte par le RFC 7344.
Et pour la troisième utilisation, la suppression de tous les DS chez le parent ? Elle fait l'objet de la section 4 du RFC. Pour demander cette suppression, on publie un CDS (ou un CDNSKEY) avec un champ « algorithme » à zéro. Cette valeur n'est pas affectée à un vrai algorithme dans le registre officiel, elle est réservée (cf. section 6 du RFC) pour dire « efface ». (Le RFC 4398 utilisait déjà le même truc.)
Pour éviter tout accident, le RFC est plus exigeant que cela et exige cette valeur spécifique pour ces enregistrements :
DOMAINNAME IN CDS 0 0 0 0
ou bien :
DOMAINNNAME IN CDNSKEY 0 3 0 0
(Le 3 étant l'actuel numéro de version de DNSSEC, voir le RFC 4034, section 2.1.2.)
Une fois le CDS (ou CDNSKEY) « zéro » détecté, et validé par DNSSEC, le parent retire le DS. Une fois le TTL passé, le fils peut « dé-signer » la zone.
À noter que ce RFC a été retardé par la question du déplacement du RFC 7344, de son état « pour information », au Chemin des Normes. La demande était discrète, et avait été raté par certains relecteurs, qui ont protesté ensuite contre ce « cavalier ». L'« élévation » du RFC 7344 est désormais explicite.
Cette possibilité est opérationnelle en .ch ou .cz. Jan-Piet Mens a fait un excellent article sur son utilisation dans .ch.
Date de publication du RFC : Mars 2017
Auteur(s) du RFC : K. Moriarty (Dell EMC
Corporation), M. Ford (Internet
Society)
Pour information
Première rédaction de cet article le 29 mars 2017
Ce nouveau RFC fait le bilan de l'atelier CARIS (Coordinating Attack Response at Internet Scale) qui s'est tenu à Berlin le 18 juin 2015. Cet atelier avait pour but d'explorer les problèmes de coordination entre les victimes et témoins d'une attaque portant sur l'Internet. Ce RFC est un compte-rendu, les positions exprimées ne sont pas forcément celles de l'IAB ou de l'Internet Society (organisateurs de l'atelier). D'autant plus que les participants ont mis les pieds dans le plat, expliquant très bien pourquoi il n'y a toujours pas de coordination globale des acteurs de l'Internet face aux attaques. (Un deuxième atelier sur ce thème a eu lieu en 2019, et son compte-rendu figure dans le RFC 8953.)
L'atelier avait été organisé pour que les participants à la réunion FIRST de Berlin puissent venir. Il rassemblait cinquante acteurs (c'était un atelier fermé, sur invitation seulement) de l'Internet, représentant une grande diversité d'organisations. La liste des participants figure dans l'annexe A du RFC. Chaque participant avait rempli un article de deux pages expliquant son point de vue et/ou les problèmes qu'il·elle souhaitait aborder. Tous ces documents sont disponibles en ligne (je vous encourage à les lire). Dans le reste du RFC, n'attendez pas d'attribution de tel discours à tel participant, l'atelier était tenu sous la règle de Chatham House, pour que les discussions restent libre.
L'atelier a vu cinq sessions (cf. section 2 du RFC) autour de ce thème des attaques, et de la coordination pour y faire face :
Parmi les organisations qui ont participé à la première session, on notait, entre autres :
Les principaux points mis en avant pendant cette session ont été :
Deuxième session, sur les mesures de lutte contre les dDoS et les botnets, notamment sur la question du passage à l'échelle de ces efforts. Les points essentiels abordé furent :
Ensuite, troisième session, consacrée aux organisations de l'infrastructure DNS (par exemple les registres) et aux RIR. Les points étudiés :
Quatrième session, les problèmes de confiance. Tout le monde est d'accord pour dire que c'est cool de partager les données, et que c'est indispensable pour lutter efficacement contre les attaques. Alors, pourquoi si peu d'organisations le font ? Il n'y a clairement pas une raison unique. C'est un mélange de crainte pour la vie privée, de contraintes juridiques, de problèmes techniques, de différences culturelles et de simples malentendus. Sans compter le pur égoïsme (« partager mes données avec nos concurrents ??? ») Enfin, il faut rappeler qu'il est impossible de s'assurer du devenir de données numériques : si on passe nos données à quelqu'un, pour aider pendant une attaque, que deviendront-elles après ? (Envoyer des données aux USA, c'est la certitude qu'elles seront vendues et revendues.) Le RFC note que tous les participants à l'atelier ont estimé que ces raisons étaient mauvaises ou, plus exactement, qu'on pouvait trouver des solutions. Les points précis discutés :
La section 4 du RFC concerne le « et maintenant ? ». Il y a eu un consensus sur la nécessité de la formation (au sens large). Par exemple, on trouve toujours des professionnels de l'Internet qui ne connaissent pas BCP 38. Du travail pour les pédagogues (et les auteurs de blogs...)
Plus technique, la question des mécanismes techniques d'échange d'information a suscité des débats animés. Le RFC 6545 date de plus de dix ans. Il ne semble pas être universellement adopté, loin de là. Le groupe de travail DOTS fera-t-il mieux ? D'autres techniques ont été discutées comme TAXII ou XMPP-Grid. Ce dernier, fondé sur XMPP (RFC 6120) semble prometteur et est déjà largement mis en œuvre. Le groupe de travail nommé MILE a aussi un protocole nommé ROLIE (pas encore de RFC).
Date de publication du RFC : Janvier 2017
Auteur(s) du RFC : B. Leiba (Huawei)
Première rédaction de cet article le 29 janvier 2017
Un très (vraiment très court) RFC purement bureaucratique, pour un très léger (vraiment très léger) changement des règles concernant les références d'un RFC à un autre RFC.
Le problème était simple : un RFC situé sur le Chemin des Normes est dans une étape donnée. Au nombre de trois au début (RFC 2026), ces étapes sont désormais deux (RFC 6410) : Proposition de norme et Norme. D'autre part, un RFC a des références à d'autres RFC, dans sa bibliographie, et ces références peuvent être normatives (il faut avoir lu et compris les RFC cités) ou informatives (elles sont juste là pour compléter et éclairer). Une règle de l'IETF est qu'un RFC ne peut pas avoir de référence normative à un RFC situé à une étape inférieure. Le but était d'éviter qu'une norme ne dépende d'un texte de maturité et d'adoption inférieurs.
Le RFC 3967 introduisait une exception à cette règle, mais en imposant un processus jugé désormais trop rigide. On pouvait donc, quand c'était nécessaire, déroger à la règle « pas de références vers le bas [du chemin des normes, downward reference en anglais] » mais il fallait le documenter dans le Last Call (dernier appel avant adoption). Si quelque chose changeait dans les références d'un RFC, il pouvait donc y avoir à refaire le Last Call.
C'était d'autant plus gênant que la question se pose plus souvent maintenant. En effet, les groupes de travail de l'IETF qui bossent sur un sujet compliqué font souvent un document « de base », définissant les concepts, la terminologie, etc, et ces documents ne sont pas sur le chemin des normes (ils sont juste « pour information »). Impossible donc de mettre une référence « vers le bas ».
La nouvelle règle figure en section 2 du RFC : le RFC 3967 est légèrement mis à jour. Désormais, il n'est plus nécessaire de mentionner l'existence d'une référence « vers le bas » au moment du dernier appel. En cas de changement des références, il ne sera donc plus obligatoire de répéter le dernier appel. C'est donc entièrement à l'IESG de déterminer si une référence à un RFC « inférieur » est acceptable ou non.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : D. Thaler (Microsoft)
Pour information
Réalisé dans le cadre du groupe de travail IETF 6lo
Première rédaction de cet article le 24 février 2017
Entre la couche 3 (du modèle en couches) et la couche 2 (par exemple Ethernet) se trouve une adaptation, qui définit comment on va mettre les paquets IPv6 sur la couche sous-jacente. Certaines de ces adaptations posent des problèmes de protection de la vie privée. Ce RFC résume les problèmes existants. Chaque adaptation devra ensuite travailler à s'améliorer sur ce plan (le RFC donne des idées). L'idée est d'améliorer les normes actuelles et futures, pour mieux prendre en compte ce problème de vie privée.
Ce problème de la vie privée pour IPv6 a déjà été beaucoup discuté, notamment en raison d'un choix initial de privilégier une adaptation à Ethernet qui gardait une partie de l'adresse IPv6 constante, même quand la machine changeait de réseau. Ce problème est résolu depuis longtemps (RFC 8981) mais d'autres peuvent demeurer, surtout si la couche 2 a des contraintes qui empêchent de déployer certaines protections de la vie privée.
Les documents de référence à lire d'abord sont le RFC général sur la vie privée, RFC 6973 (sa section 5.2 est particulièrement utile ici), et, plus spécifique à IPv6, le RFC 7721. Le risque qui concerne l'adaptation est lié au mécanisme de génération des IID (identificateurs d'interface, cf. RFC 4291), puisque cet IID fait partie de l'adresse IPv6 (typiquement les 64 derniers bits) et va donc être potentiellement visible publiquement. Si l'IID est trop prévisible ou trop stable, il permettra notamment :
Un concept important est celui d'entropie, c'est-à-dire du nombre de bits dans l'IID qui sont réellement imprévisibles. Idéalement, l'entropie devrait être de 64 bits (le préfixe IPv6 ayant typiquement une longueur de 64 bits pour un réseau, cf. RFC 7421).
Voilà pourquoi le RFC 8064 déconseille de créer un IID à partir d'une adresse « couche 2 » fixe, comme l'est souvent l'adresse MAC. Il recommande au contraire la technique du RFC 7217 si on veut des adresses stables tant qu'on ne se déplace pas, et celle du RFC 8981 si on veut être vraiment difficile à tracer (au prix d'une administration réseaux plus difficile). Le RFC sur la sélection des adresses source, RFC 6724 privilégie déjà par défaut les adresses temporaires du RFC 8981.
Revenons maintenant à cette histoire d'entropie (section 2 du
RFC). Combien de bits sont-ils nécessaires ? Prenons le cas le plus
difficile, celui d'un balayage du réseau local, avec des paquets
ICMP Echo Request ou bien avec des
TCP SYN
. L'entropie minimum
est celle qui minimise les chances d'un attaquant de trouver une
adresse qui réponde. Quel temps faudra-t-il pour avoir une chance
sur deux de trouver une adresse ? (Notez que la capacité de
l'attaquant à trouver des machines dépend aussi du fait qu'elles
répondent ou pas. Si une machine ne répond pas aux ICMP
Echo Request, et n'envoie pas de
RST
aux paquets TCP SYN
,
la tâche de l'attaquant sera plus compliquée. Cf. RFC 7288, notamment sa section 5. Même si la machine répond,
un limiteur de trafic peut rendre la tâche de l'attaquant
pénible. Avec la valeur par défaut d'IOS de
deux réponses ICMP par seconde, il faut une année pour balayer un
espace de seulement 26 bits.)
Les formules mathématiques détaillées sont dans cette section 2 du RFC. L'entropie nécessaire dépend de la taille de l'espace d'adressage mais aussi de la durée de vie du réseau. Avec 2^16 machines sur le réseau (c'est un grand réseau !) et un réseau qui fonctionne pendant 8 ans, il faudra 46 bits d'entropie pour que l'attaquant n'ait qu'une chance sur deux de trouver une machine qui réponde (avec la même limite de 2 requêtes par seconde ; sinon, il faudra davantage d'entropie).
Et combien de bits d'entropie a-t-on avec les techniques actuelles ? La section 3 donne quelques exemples : seulement 48 bits en Bluetooth (RFC 7668), 8 (oui, uniquement 256 adresses possibles, mais c'est nécessaire pour permettre la compression des en-têtes) en G.9959 (RFC 7428) et le pire, 5 bits pour NFC (RFC pas encore paru).
Ces adaptations d'IPv6 à diverses couches 2 utilisent comme identificants d'interface des adresses IEEE (comme les adresses MAC) ou bien des « adresses courtes ». Commençons par les adresses reposant sur des adresses IEEE. Dans certains cas, la carte ou la puce qui gère le réseau dispose d'une adresse EUI-48 ou EUI-64 (comme l'adresse MAC des cartes Ethernet). On peut facilement construire une adresse IPv6 avec ces adresses, en concaténant le préfixe avec cette adresse IEEE utilisée comme identificateur d'interface (IID). L'entropie dépend du caractère imprévisible de l'adresse IEEE. L'IEEE a d'ailleurs des mécanismes (pas forcément déployés dans le vrai monde) pour rendre ces adresses imprévisibles. Même dans ce cas, la corrélation temporelle reste possible, sauf si on change les adresses de temps en temps (par exemple avec macchanger).
Un argument souvent donné en faveur des adresses MAC est leur unicité, qui garantit que les adresses IPv6 seront « automatiquement » distinctes, rendant ainsi inutile la détection d'adresses dupliquées (DAD, RFC 4862, section 5.4, et RFC 4429, annexe A). Sauf que ce n'est pas vrai, les adresses MAC ne sont pas forcément uniques, en pratique et les identificateurs d'interface aléatoires sont donc préférables, pour éviter les collisions d'adresses.
En dehors des adresses allouées par un mécanismes de l'IEEE, il y a les « adresses courtes » (16 bits, utilisées par IEEE 802.15.4, cf. RFC 4944), allouées localement, et uniques seulement à l'intérieur du réseau local. Vu leur taille, elles n'offrent évidemment pas assez d'entropie. Il faut donc les étendre avant de s'en servir comme identificateur d'interface. Le RFC cite par exemple un condensat de la concaténation de l'adresse courte avec un secret partagé par toutes les machines du réseau.
On peut aussi utiliser dans le condensat le numéro de version spécifié dans la section 4.3 du RFC 6775. Ainsi, un changement de numéro de version permettra une rénumérotation automatique.
Bien, après cette analyse, les recommandations (section 4) :
Date de publication du RFC : Février 2017
Auteur(s) du RFC : F. Gont (SI6 Networks /
UTN-FRH), A. Cooper (Cisco), D. Thaler
(Microsoft), W. Liu (Huawei
Technologies)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF 6man
Première rédaction de cet article le 24 février 2017
Ce RFC parle de vie privée mais il est très court, car il se contente de changer une règle, la nouvelle étant déjà largement acceptée. Désormais, si une machine IPv6 configure son adresse par le système SLAAC, et que cette adresse doit être stable dans le temps, désormais, donc, la méthode recommandée est celle du RFC 7217 et non plus celle, mauvaise pour la vie privée, d'utiliser l'adresse MAC. (Si l'adresse n'a pas besoin d'être stable, aucun changement, la méthode recommandée reste celle du RFC 8981, les adresses temporaires.)
Que veut dire SLAAC, au fait ? Ce mécanisme de configuration d'une adresse IPv6 est normalisé dans le RFC 4862. L'idée est que la machine écoute sur le réseau les annonces faites par les routeurs, apprenant ainsi le·s préfixe·s IP du réseau. Elle ajoute ensuite à ce préfixe un terme, l'identificateur d'interface (IID, cf. RFC 4291), formant ainsi une adresse IPv6 mondiale, et unique (si l'IID est bien choisi). La méthode originelle était de dériver l'IID de l'adresse MAC. Celle-ci est en effet unique et, en prime, son utilisation présente certains avantages (compression des en-têtes du RFC 6775, par exemple). Mais s'en servir soulève plein de problèmes de sécurité et notamment de vie privée : traçabilité des utilisateurs dans le temps, et dans l'espace (si la machine se déplace, elle change de préfixe mais garde le même identificateur d'interface), facilitation du balayage des adresses dans le réseau, etc (cf. RFC 7721). D'une manière générale, réutiliser des identificateurs d'un autre « monde » est une fausse bonne idée, souvent dangereuse en matière de vie privée. Voilà pourquoi ce RFC dit clairement que, désormais, il est fortement déconseillé d'utiliser les adresses MAC. (Plusieurs mises en œuvre d'IPv6, comme celles de Microsoft, avaient déjà cessé, avant même que ce RFC ne soit publié.)
Et ce RFC 7217 qu'il faut désormais suivre, il dit quoi ? Il propose de fabriquer l'identificateur d'interface en condensat une concaténation du préfixe et de diverses valeurs stables. Si on change de réseau, on a une nouvelle adresse (on ne peut donc pas suivre à la trace une machine mobile). Mais, si on reste sur le même réseau, l'adresse est stable.
La section 1 de notre RFC rappelle aussi la différence entre les adresses stables et les autres. Toutes les adresses IP n'ont pas besoin d'être stables. La solution la meilleure pour la vie privée est certainement celle du RFC 8981, des adresses temporaires, non stables (pour de telles adresses, on peut aussi utiliser le système des adresses MAC si elles changent souvent par exemple avec macchanger). Toutefois, dans certains cas, les adresses stables sont souhaitables : l'administration réseaux est plus simple, les journaux sont plus faciles à lire, on peut mettre des ACL, on peut avoir des connexions TCP de longue durée, etc. Et, bien sûr, si la machine est un serveur, ses adresses doivent être stables. Il y a donc une place pour une solution différente de celle du RFC 8981, afin de fournir des adresses stables. C'est seulement pour ces adresses stables que notre RFC recommande désormais la solution du RFC 7217.
La nouvelle règle figure donc en section 3 de notre RFC : lorsqu'une machine veut utiliser SLAAC et avoir des adresses stables, qui ne changent pas dans le temps, tant que la machine reste sur le même réseau, alors, dans ce cas et seulement dans ce cas, la méthode à utiliser est celle du RFC 7217. L'ancienne méthode (qu'on trouve par exemple dans le RFC 2464) d'ajouter le préfixe à l'adresse MAC ne doit plus être utilisée.
Notez donc bien que ce RFC ne s'adresse pas à toutes les
machines IPv6. Ainsi, si vous configurez vos serveurs (qui ont
clairement besoin d'une adresse stable) à la main, avec des
adresses en leet comme
2001:db8::bad:dcaf
, ce RFC 8064 ne
vous concerne pas (puisqu'il n'y a pas de
SLAAC).
Les RFC comme le RFC 4944, RFC 6282, RFC 6775 ou RFC 7428 devront donc être remplacés par des documents tenant compte de la nouvelle règles. (Cf. RFC 8065.)
Aujourd'hui, il semble que les dernières versions de Windows, MacOS, iOS et Android mettent déjà en œuvre la nouvelle règle.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : H.W. Ribbers, M.W. Groeneweg (SIDN), R. Gieben, A.L.J Verschuren
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF eppext
Première rédaction de cet article le 15 février 2017
Un des problèmes pratiques que pose DNSSEC est le changement d'hébergeur DNS. Si on sous-traite sa gestion des clés cryptographiques, et ses signatures à un tiers, que faire lors du changement de prestataire, pour ne pas casser la chaîne de confiance DNSSEC ? On ne peut évidemment pas demander à l'ancien hébergeur de transmettre la clé privée au nouveau ! Même pour les données non confidentielles, comme la clé publique, la transmission est difficile car les deux hébergeurs n'ont pas de canal de transmission sécurisé commun. Ce nouveau RFC propose d'utiliser comme canal sécurisé le registre de la zone parente et, plus concrètement, de définir une extension au protocole EPP, qui permet un mécanisme de « messagerie » électronique sécurisée, afin de l'utiliser entre deux clients du même registre.
Pour bien comprendre le problème et sa solution, il faut faire un peu de terminologie :
Dans le cas le plus fréquent, l'hébergeur DNS assure la gestion des clés (création, suppression, etc) et connait donc la clé privée, qu'il utilise pour signer les enregistrements. Si le titulaire du domaine veut changer d'hébergeur, pas question bien sûr de transmettre la clé privée. Le nouvel hébergeur (le « gagnant ») va donc créer des nouvelles clés et les utiliser pour signer. Le problème est qu'un résolveur DNS peut avoir des signatures de l'ancien hébergeur (le « perdant ») et des clés du nouveau (ou bien le contraire). Dans ces deux cas, la validation échouera, le domaine sera vu comme invalide.
Une solution à ce problème serait que l'ancien hébergeur mette à l'avance (rappelez-vous, le temps est crucial dès qu'on fait du DNS...) dans les clés qu'il publie la nouvelle clé du nouvel hébergeur. Mais cela suppose qu'il connaisse cette clé. Le titulaire du nom peut servir de relais mais il n'est pas forcément compétent pour cela (« M. Michu, votre nouvel hébergeur a dû vous remettre une clé cryptographique. C'est une série de lettres et de chiffres incompréhensiblles. Pouvez-nous nous la transmettre sans la moindre altération ? »). L'ancien hébergeur ne peut pas non plus utiliser le DNS puisque les serveurs du nouveau ne sont pas encore configurés et, même s'ils le sont, l'ancien hébergeur ne peut pas valider leur contenu avec DNSSEC. L'idée de notre RFC est donc d'utiliser le registre comme intermédiaire de confiance. Si les deux hébergeurs sont également BE, ils ont déjà un canal sécurisé avec le registre (la session EPP). Sinon, on peut espérer que le BE acceptera de servir de relais entre l'hébergeur et le registre.
Avec la solution de ce RFC, le nouvel hébergeur (on va suppose qu'il est également BE, ce sera plus simple) va créer ses clés, les transmettre (la clé publique seulement, bien sûr) au registre via l'extension EPP de notre nouveau RFC, l'ancien hébergeur va les lire (le registre ne sert que de boîte aux lettres sécurisée), les mettre dans la zone DNS. Au bout d'un temps déterminé par le TTL des enregistrements, tous les résolveurs auront l'ancienne et la nouvelle clé publique dans leur mémoire, et pourront valider aussi bien les anciennes que les nouvelles signatures.
Une autre façon de résoudre ce problème serait évidemment que chacun gère sa zone DNS lui-même, et donc ne change jamais d'« hébergeur » mais ce n'est évidemment pas souhaitable pour la plupart des titulaires.
Ce RFC ne spécifie qu'un mécanisme de messagerie, pas une
politique, ni une procédure détaillée. La politique est du ressort
du registre et de ses BE (via le contrat qui les lie, qui spécifie
typiquement des obligations comme « le BE perdant doit coopérer au
transfert du domaine, et mettre les nouvelles clés dans la zone
qu'il gère encore »). La procédure n'est pas décrite dans un
RFC. (Il y a eu une tentative via le
document draft-koch-dnsop-dnssec-operator-change
,
mais qui n'a pas abouti. La lecture de ce document est quand même
très recommandée.) Le mécanisme de messagerie décrit dans notre RFC
est donc « neutre » : il ne suppose pas une politique
particulière. Une fois la clé transmise, sa bonne utilisation va
dépendre des règles en plus et de si elles sont obéies ou
pas. Comme le dit le RFC, « The
registry SHOULD have certain policies in place that require the
losing DNS operator to cooperate with this transaction, however this
is beyond this document. »
Les détails EPP figurent en section 2. Les clés publiques sont
dans un élément XML
<keyRelayData>
. Il contient deux
éléments, <keyData>
, avec la clé
elle-même (encodée en suivant la section 4 du RFC 5910), et <expiry>
(optionnel) qui
indique combien de temps (en absolu ou bien en relatif) garder cette clé dans la zone. La syntaxe formelle complète figure en section 4,
en XML Schema.
Les commandes EPP liées à cette extension figurent en section
3. Certaines commandes EPP ne sont pas modifiées par cette
extension, comme check
, info
,
etc. La commande create
, elle, est étendue
pour permettre d'indiquer la nouvelle clé (un exemple figure dans
notre RFC, section 3.2.1). Si le serveur EPP accepte cette
demande, il met la clé dans la file d'attente de messages du
client EPP qui gère le nom de domaine en question (typiquement le
BE « perdant »). Sinon, il répond pas le code de retour 2308.
La nouvelle clé apparaitra dans le système de « messagerie » d'EPP (poll queue), RFC 5730, section 2.9.2.3. Un exemple de réponse figure dans notre RFC, section 3.1.2.
Quelques points de sécurité pour finir (section 6). Un client
EPP méchant pourrait envoyer des clés à plein de gens juste pour
faire une attaque par déni de
service. C'est au serveur EPP de détecter ces abus et
d'y mettre fin. Le serveur EPP peut exiger un
authinfo
correct dans le message de création,
pour vérifier que l'action a bien été autorisée par le
titulaire. Enfin, cette technique d'envoi des clés ne garantit
pas, à elle seule, que tout aura bien été fait de bout en
bout. Par exemple, le nouvel hébergeur a tout intérêt à vérifier,
par une requête DNS epxlicite, que l'ancien
a bien mis la nouvelle clé dans la zone.
Ce RFC a eu une histoire longue et compliquée, malgré une forte demande des utilisateurs. Il y a notamment eu un gros problème avec un brevet futile (comme la plupart des brevets logiciels) de Verisign, qui a fait perdre beaucoup de temps (la déclaration de Verisign est la n° 2393, le brevet lui-même est le US.201113078643.A, la décision de l'IETF de continuer malgré ce brevet, et malgré l'absence de promesses de licence, est enregistrée ici).
Question mise en œuvre, la bibliothèque Net::DRI gère déjà ce RFC. Combien de registres et de BE déploieront ce RFC ? Le coût pour le registre est probablement assez faible, puisqu'il a juste à relayer la demande, utilisant un mécanisme de « messagerie » qui existe déjà dans EPP. Mais, pour les BE, il y a certainement un problème de motivation. Ce RFC aidera le BE « gagnant », et le titulaire du domaine, mais pas le BE « perdant ». Il n'a donc pas de raison de faire des efforts, sauf contrainte explicite imposée par le registre (et l'expérience indique que ce genre de contraintes n'est pas toujours strictement respecté).
Comme bonne explication de ce RFC, vous pouvez aussi lire l'excellent explication de SIDN (avec une jolie image). En parlant de SIDN, vous pouvez noter que leur première mention d'un déploiement d'une version préliminaire de cette solution a eu lieu en 2013 (cf. leur rapport d'activité). Le même SIDN vient de publier un article de premier bilan sur ce projet.
Merci à Patrick Mevzek pour les explications, le code et les opinions.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : D. Farinacci (lispers.net), D. Meyer
(Brocade), J. Snijders (NTT)
Expérimental
Réalisé dans le cadre du groupe de travail IETF lisp
Première rédaction de cet article le 3 février 2017
Le protocole LISP permet d'utiliser comme identificateur ou comme localisateur aussi bien des adresses IPv4 qu'IPv6. Pour rendre les mécanismes de résolution d'identificateur en localisateur aussi génériques que possibles, ce nouveau RFC décrit un format d'adresses qui permet de gérer les deux familles d'adresses (et davantage).
Il existait des méthodes partielles pour représenter ces deux
familles. Par exemple, on peut décider de tout mettre en
IPv6 et de représenter les adresses
IPv4 sous la forme
« IPv4-mapped » (RFC 4291, section 2.5.5.2, par exemple
::ffff:192.0.2.151
). Ou on pourrait, comme
c'est le cas dans les URL, représenter les
adresses sous forme texte en utilisant les
crochets pour distinguer IPv4 et IPv6 (RFC 3986, section 3.2.2, par exemple
https://192.0.2.151/
vs. https://[2001:db8:3f:ae51::78b:ff1]/
). Mais
le groupe de travail à l'IETF a choisi une
solution qui traite les deux familles sur un pied d'égalité, et
est parfaitement générique (elle intégre d'autres
familles que simplement IPv4 et IPv6). La solution finalement documentée dans ce RFC est très
souple et peut servir à bien d'autres que LISP, dès qu'on veut
représenter des requêtes ou réponses d'un système d'annuaire.
À propos de familles, un terme important à retenir est celui d'AFI (Address Family Identifier). C'est un nombre identifiant la famille d'adresses utilisée. Il avait été introduit dans le RFC 2453 puis précisé dans le RFC 4760, et peut prendre plusieurs valeurs, stockées dans un registre à l'IANA (1 pour IPv4, 2 pour IPv6, etc). 0 indique une famille non spécifiée.
Le format de ces adresses LCA (LISP Canonical Address) est décrit dans la section 3 de notre RFC. L'adresse LCAF commence par l'AFI de LISP (16387) suivi de divers champs notamment la longueur totale de l'adresse et son type. Une LCA peut en effet contenir plus que des adresses IP (type 1). Elle peut aussi servir à transporter des numéros d'AS (type 3), des coordonnées géographiques (type 5), etc. La liste des types possibles est enregistrée à l'IANA. La section 4 explique les différents types et l'encodage du contenu associé.
Lorsqu'une LCA indique des adresses IP, elle utilise le type 1 : son contenu est une série de couples {AFI, adresse}. Des adresses IPv4 (AFI 1) et IPv6 (AFI 2) peuvent donc apparaitre dans cette liste (mais aussi des adresses MAC, AFI 6, lorsqu'on crée des VPN de couche 2, ou bien des noms de domaine, AFI 16). Ce seront ces LCA qui seront sans doute les plus utilisées dans les systèmes de correspondance LISP comme DDT (RFC 8111) ou ALT (RFC 6836).
Pour les numéros d'AS (type 3), la LCA contient un numéro d'AS, puis un préfixe (IPv4 ou IPv6) affecté à cet AS. Quant aux coordonnées géographiques (type 5), elles sont indiquées sous forme de latitude, longitude et altitude dans le système WGS-84. Cela permet, dans une réponse du système de correspondance LISP, d'indiquer la position physique du réseau du préfixe encodé dans la LCA. (Attention, le RFC note bien que cela a des conséquences pour la vie privée.) On peut aussi stocker des clés cryptographiques (type 11) dans une LCA (voir RFC 8111 et RFC 8061).
Les mises en œuvre existantes de LISP utilisent déjà les LCA (mais ne gèrent pas forcément tous les types officiels).
Date de publication du RFC : Janvier 2017
Auteur(s) du RFC : J. Levine (Taughannock
Networks), T. Herkula (optivo)
Chemin des normes
Première rédaction de cet article le 31 janvier 2017
Le RFC 2369 décrit des en-têtes du
courrier électronique qui indiquent, entre
autres, comment se désabonner d'une liste de
diffusion (en-tête
List-Unsubscribe:
, qui indique des
URI à activer pour se désabonner). Cela peut permettre des
désabonnements sans interaction explicite avec l'utilisateur : le
MUA voit cette en-tête, propose un bouton
« Désabonnement » à l'utilisateur, et le MUA effectue la requête
(par exemple HTTP) tout
seul. L'inconvénient est que certains logiciels (par exemple des
anti-spam) visitent automatiquement tous
les liens hypertexte présents dans les
messages, et risquent alors de désabonner l'abonné
accidentellement. Pour éviter cela, la page pointée par l'URI
présent dans List-Unsubscribe:
n'est en
général pas « one-click » : il faut une action
explicite une fois cette page affichée. Ce nouveau
RFC propose des ajouts à
List-Unsubscribe:
pour indiquer qu'un
désabonnement one-click est possible.
Voici un exemple de cet en-tête traditionnel
List-Unsubscribe:
dans une liste de
diffusion IETF :
List-Unsubscribe: <https://www.ietf.org/mailman/options/ietf>, <mailto:ietf-request@ietf.org?subject=unsubscribe>
La page indiquée dans l'URI HTTPS est une landing page : elle ne désabonne pas directement, il faut indiquer son adresse et sélectionner le bouton Unsubscribe. C'est acceptable quand l'utilisateur est un humain, mais pas dans les cas où il n'y a pas d'interaction possible. Imaginons un logiciel qui traite automatiquement les messages après le départ ou le décès d'un utilisateur, et qui veut donc le désabonner proprement de toutes les listes où il était abonné avec cette adresse désormais invalide. Ce logiciel doit pouvoir fonctionner sans intervention humaine, donc sans analyser une page HTML compliquée.
À noter que l'en-tête List-Unsubscribe:
ci-dessus, comme dans la plupart des cas, propose également un URI
de plan mailto:
, déclenchant l'envoi d'un
message par courrier électronique. La
plupart des services qui envoient de grosses quantités de message
(les hébergeurs de listes de diffusion à grand volume) ont du mal
à gérer le flot de messages de désinscription (ils sont optimisés
pour envoyer beaucoup, pas pour recevoir beaucoup), et ce
RFC ne prend donc en compte que les URI de
plan https:
.
La section 1 résume le cahier des charges de la solution présentée ici :
La section 3 du RFC spécifie la solution retenue. Elle consiste
dans l'ajout d'un nouvel en-tête,
List-Unsubscribe-Post:
, dont le contenu est un
couple clé=valeur,
List-Unsubscribe=One-Click
. (Ce nouvel
en-tête a été ajouté dans le
registre IANA.) Par exemple,
l'en-tête montré plus haut deviendrait :
List-Unsubscribe: <https://www.ietf.org/mailman/options/ietf?id=66fd1aF64>, <mailto:ietf-request@ietf.org?subject=unsubscribe> List-Unsubscribe-Post: List-Unsubscribe=One-Click
Cela indiquerait clairement que le désabonnement en un clic est
possible. Le MUA, s'il désire effectuer le désabonnement, va alors
faire une requête HTTPS de méthode POST
, en
mettant dans le corps de la requête le contenu de l'en-tête
List-Unsubscribe-Post:
. Le serveur HTTPS,
s'il voit ce List-Unsubscribe=One-Click
dans
la requête, doit
exécuter la requête sans poser de questions supplémentaires (le
but étant de faire un désabonnement en un seul clic).
Notez qu'il faut indiquer quelque part l'adresse à désabonner,
le serveur HTTP ne peut pas la deviner autrement. Pour rendre plus
difficile la création de fausses instructions de désabonnement,
cela se fait indirectement, via une donnée opaque, comprise
seulement du serveur (le id
dans l'exemple
hypothétique ci-dessus).
Les données contenues dans le List-Unsubscribe-Post:
doivent idéalement être envoyées avec le type MIME
multipart/form-data
(RFC 7578), et, sinon, en
application/x-www-form-urlencoded
, comme le
ferait un navigateur Web. Bien sûr, le MUA
doit avoir la permission de l'utilisateur pour effectuer ce
désabonnement (on est en « un seul clic », pas en « zéro clic »).
Au fait, pourquoi la méthode POST
?
GET
ne peut pas modifier l'état du serveur et
PUT
ou DELETE
sont
rarement accessibles.
Le courrier électronique n'étant pas vraiment sécurisé, il y a
un risque de recevoir des messages avec un
List-Unsubscribe-Post:
mensonger. C'est
pourquoi le RFC demande (section 4) qu'il y ait un minimum
d'authentification du message. La seule
méthode d'authentification décrite est DKIM
(RFC 6376), avec une étiquette
d=
identifiant le domaine. La signature DKIM
doit évidemment inclure dans les en-têtes signés les
List-Unsubscribe:
et
List-Unsubscribe-Post:
dans l'étiquette DKIM h=
.
Avec l'exemple plus haut, la requête HTTP POST
ressemblerait à :
POST /mailman/options/ietf?id=66fd1aF64 HTTP/1.1 Host: www.ietf.org Content-Type: multipart/form-data; boundary=---FormBoundaryjWmhtjORrn Content-Length: 124 ---FormBoundaryjWmhtjORrn Content-Disposition: form-data; name="List-Unsubscribe" One-Click ---FormBoundaryjWmhtjORrn--
La section 6 de notre RFC décrit les éventuels problèmes de
sécurité. En gros, il en existe plusieurs, mais tous sont en fait
des problèmes génériques du courrier électronique, et ne sont pas
spécifiques à cette nouvelle solution. Par exemple, un spammeur pourrait envoyer plein de
messages ayant l'en-tête
List-Unsubscribe-Post:
, pour faire générer
plein de requêtes POST
vers le pauvre
serveur. Mais c'est déjà possible aujourd'hui en mettant des URI
dans un message, avec les logiciels qui vont faire des
GET
automatiquement.
Je n'ai pas encore vu cet en-tête
List-Unsubscribe-Post:
apparaitre dans de
vrais messages.
Un des auteurs du RFC a écrit un bon résumé de son utilité, article qui explique bien comment fonctionne le courrier aujourd'hui.
Date de publication du RFC : Janvier 2017
Auteur(s) du RFC : J. Gould (Verisign)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 18 janvier 2017
Deux protocoles utilisés dans l'industrie des noms de domaine, EPP et RDAP, ont la notion d'état d'un nom de domaine, indiquant, par exemple, que ce nom est verrouillé et ne doit pas être modifié. Mais les états de EPP et de RDAP sont différents, et pas toujours évidents à rapprocher. Ce nouveau RFC précise la correspondance entre les états EPP et les états RDAP, établissant la liste comparée.
EPP (protocole d'avitaillement d'objets dans un registre, par exemple un registre de noms de domaine) est normalisé dans divers RFC (STD 69), ceux qui décrivent les états sont les RFC 5731 (section 2.3), RFC 5732 (section 2.3), RFC 5733 (section 2.2) et RFC 3915 (section 3.1). Les états RDAP (protocole d'information sur les objets d'un registre, qui vise à remplacer whois) sont normalisés dans le RFC 9083 (section 10.2) qui crée un registre IANA des états possibles. Pourquoi des états différents dans ces deux protocoles ? Disons qu'ils ont été conçus pour des buts différents, et que la nécessité de faire correspondre les états de l'un avec ceux de l'autre n'est devenue évidente qu'après. Le but de ce nouveau RFC est justement d'établir une correspondance univoque entre les états d'EPP et de RDAP.
La section 2 de notre RFC commence par une liste des états
EPP, avec leur équivalent RDAP (quand il existe). Par exemple, il
est assez évident que le pendingDelete
d'EPP
(RFC 5731) correspond au pending
delete
de RDAP. De même, le ok
d'EPP est clairement l'équivalent du active
de RDAP. Mais les addPeriod
(RFC 3915, durée après l'enregistrement d'un nom de domaine
pendant laquelle on peut annuler l'enregistrement gratuitement) ou
clientHold
(RFC 5731,
le client a demandé que ce nom de domaine ne soit pas publié dans
le DNS)
d'EPP n'ont pas d'équivalent RDAP. L'inverse existe aussi, le
delete prohibited
de RDAP n'a pas un
équivalent simple en EPP, qui a deux états pour cela, selon que
l'interdiction a été posée par le client EPP ou par le
serveur.
La section 2 du RFC se continue donc avec ce qui est désormais
la liste officielle des correspondances, en tenant compte des
nouveaux états ajoutés, par exemple dans le registre
RDAP. C'est ainsi qu'un add period
et
un client hold
ont été ajoutés (section 3 du RFC), ainsi qu'un
client delete prohibited
et un
server delete prohibited
, pour préciser le
delete prohibited
.
Date de publication du RFC : Janvier 2017
Auteur(s) du RFC : K. Murchison (Carnegie Mellon
University), J. Élie
Chemin des normes
Première rédaction de cet article le 26 janvier 2017
Ce nouveau RFC définit un mécanisme standard de compression des news échangées en NNTP, sur Usenet.
NNTP, normalisé dans le RFC 3977 est un protocole gourmand en débit. Comprimer les données transmises est donc très souhaitable. C'est aussi un protocole très ancien, ce qui se voit dans certaines références du RFC, comme l'allusion à la compression PPP du RFC 1962 ou bien à la compression par modem comme V42bis :-)
Mais, malgré ce besoin de compression, il n'y avait pas encore
de solution standard en NNTP. Un certain nombre de mécanismes
non-standards avaient été déployés avec des noms comme XZVER
,
XZHDR
, XFEATURE COMPRESS
,
ou MODE COMPRESS
. Outre l'absence de
normalisation, ils souffraient de ne comprimer que les réponses
du serveur de news.
Compte-tenu du déploiement de plus en plus fréquent de TLS, pour assurer la confidentialité des échanges, il avait été envisagé à une époque de compter sur le mécanisme de compression de TLS (RFC 4642, remis en cause par le RFC 8143). Celui-ci présente malheureusement des dangers, qui fait que son usage est déconseillé dans beaucoup de cas (section 3.3 du RFC 7525, section 2.6 du RFC 7457, et RFC 8143). En outre, la solution de ce RFC bénéficie de davantage de souplesse : elle peut par exemple n'être activée qu'une fois l'authentification faite, pour éviter les attaques comme CRIME (voir aussi les sections 2.2.2 et 7 de notre RFC, pour tous les détails de sécurité).
Pour assurer l'interopérabilité maximale, un seul algorithme de compression est défini, et il est, logiquement, obligatoire. Cela garantit qu'un client et un serveur NNTP auront toujours cet algorithme en commun. Il s'agit de Deflate, normalisé dans le RFC 1951.
(Un petit point qui n'a rien à voir avec NNTP et la compression : comme le demandait l'Internet-Draft qui a donné naissance à notre RFC, j'ai mis un accent à la première lettre du nom d'un des auteurs, ce qui n'est pas possible dans le RFC original, cela ne le sera que lorsque le RFC 7997 sera mis en œuvre.)
Maintenant, les détails techniques (section 2 du RFC). Le
serveur doit annoncer sa capacité à comprimer en réponse à la
commande CAPABILITIES
. Par exemple (C =
client, S = serveur) :
[C] CAPABILITIES [S] 101 Capability list: [S] VERSION 2 [S] READER [S] IHAVE [S] COMPRESS DEFLATE SHRINK [S] LIST ACTIVE NEWSGROUPS [S] .
L'annonce de la capacité est suivie de la liste des algorithmes
gérés. On trouve évidemment l'algorithme obligatoire
DEFLATE
mais aussi un
algorithme non-standard (imaginaire, ce n'est qu'un exemple)
SHRINK
.
Le client peut alors utiliser la commande
COMPRESS
, suivie du nom d'un algorithme
(cette commande a été ajoutée
au registre IANA des commandes NNTP). Voici un exemple où
le serveur accepte la compression :
[C] COMPRESS DEFLATE [S] 206 Compression active (À partir de là, le trafic est comprimé)
Attention à ne pas confondre la réponse du serveur à une demande
de ses capacités, et la commande envoyée par le client (dans les
deux cas, ce sera une ligne COMPRESS DEFLATE
).
Et voici un exemple où le serveur refuse, par exemple parce que la compression a déjà été activée :
[C] COMPRESS DEFLATE [S] 502 Command unavailable
Si on utilise TLS, ce qui est évidemment recommandé pour des raisons de confidentialité et d'authentification, l'envoyeur doit d'abord comprimer, puis (si SASL est activé) appliquer SASL (RFC 4422), puis seulement à la fin chiffrer avec TLS. À la réception, c'est bien sûr le contraire, on déchiffre le TLS, on analyse SASL, puis on décomprime.
Voici un exemple d'un dialogue plus détaillé, avec TLS et compression :
[C] CAPABILITIES [S] 101 Capability list: [S] VERSION 2 [S] READER [S] STARTTLS [S] AUTHINFO [S] COMPRESS DEFLATE [S] LIST ACTIVE NEWSGROUPS [S] . [C] STARTTLS [S] 382 Continue with TLS negotiation (Négociation TLS) (Désormais, tout est chiffré) [C] CAPABILITIES [S] 101 Capability list: [S] VERSION 2 [S] READER [S] AUTHINFO USER [S] COMPRESS DEFLATE [S] LIST ACTIVE NEWSGROUPS [S] . [C] AUTHINFO USER michu [S] 381 Enter passphrase [C] AUTHINFO PASS monsieur [S] 281 Authentication accepted [C] CAPABILITIES [S] 101 Capability list: [S] VERSION 2 [S] READER [S] POST [S] COMPRESS DEFLATE [S] LIST ACTIVE NEWSGROUPS [S] . [C] COMPRESS DEFLATE [S] 206 Compression active (Désormais, toutes les données envoyées sont comprimées, puis chiffrées) [C] CAPABILITIES [S] 101 Capability list: [S] VERSION 2 [S] READER [S] POST [S] LIST ACTIVE NEWSGROUPS [S] .
Et voici deux exemples où le serveur refuse la compression. D'abord parce qu'il ne peut pas (manque de mémoire, par exemple) :
[C] COMPRESS DEFLATE [S] 403 Unable to activate compression
Et ici parce que le client essaie d'utiliser un algorithme que le serveur ne connait pas :
[C] COMPRESS SHRINK [S] 503 Compression algorithm not supported
La liste des algorithmes standards (pour l'instant réduite à un seul) est dans un registre IANA.
NNTP est un protocole dont les spécificités posent des
problèmes amusants lorsqu'on veut comprimer son trafic (section 3
du RFC). Les messages sont très divers, ce qui peut être contrariant pour une
compression fondée sur un dictionnaire. Les réponses à certaines
commandes (DATE
, GROUP
,
NEXT
, et le CHECK
du
RFC 4644) sont peu comprimables. Par contre, les réponses
à LIST
, LISTGROUP
ou
NEWNEWS
sont facilement réduites à 25 à 40 %
de la taille originale
avec zlib.
En outre, les news envoyées sont dans des formats différents. Un article sera parfois du texte seul, relativement court (et souvent uniquement en ASCII) et se comprimera bien. Les textes plus longs sont souvent envoyés sous un format déjà comprimé et, là, le compresseur NNTP va s'essouffler pour rien. Mais il y a aussi souvent des données binaires (images, par exemple), encodées en Base64 ou uuencode. On peut souvent les réduire à 75 % de l'original. (Deflate marche bien sur des données en 8 bits mais l'encodage lui dissimule la nature 8-bitesque de ces données.) Si les données sont encodées en yEnc, elles seront moins compressibles.
Il y a apparemment au moins un logiciel serveur (INN) et un client (flnews) qui gèrent cette compression.
Merci à Julien Élie pour sa relecture attentive (et pour avoir trouvé au moins une grosse faute.)
Date de publication du RFC : Janvier 2017
Auteur(s) du RFC : A. Bierman (YumaWorks), M. Bjorklund
(Tail-f Systems), K. Watsen
(Juniper)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF netconf
Première rédaction de cet article le 1 février 2017
Pour configurer à distance un équipement réseau (par exemple un routeur ou bien un commutateur), il existait déjà le protocole NETCONF (RFC 6241). Fondé sur un échange de données en XML, il n'a pas convaincu tout le monde, qui aurait préféré un protocole REST. C'est désormais fait avec RESTCONF, décrit dans ce RFC.
RESTCONF est donc bâti sur HTTP (RFC 7230) et les opérations CRUD. Comme NETCONF, RESTCONF utilise des modèles de données décrits en YANG (RFC 7950). Il devrait donc permettre de configurer plus simplement les équipements réseau. (Les historiens noteront que l'ancêtre SNMP avait été prévu, non seulement pour lire des informations, mais également pour écrire, par exemple pour modifier la configuration d'un routeur. Cette possibilité n'a eu aucun succès. Cet échec est une des raisons pour lesquelles NETCONF a été développé.)
Les opérations CRUD sont donc faites
avec les méthodes classiques de HTTP (RFC 7231). Lire l'état de l'engin se fait
évidemment avec la méthode GET
. Le modifier
se fait avec une des méthodes parmi
PUT
, DELETE
,
POST
ou PATCH
(cf. section 4 du RFC). Les
données envoyées, ou bien lues, sont encodées, au choix, en
XML ou en JSON. À
noter que RESTCONF, conçu pour les cas relativement simples, est plus limité que NETCONF (il n'a pas de
fonction de verrou, par exemple). Il ne peut donc
pas complètement remplacer NETCONF (ainsi, si un client NETCONF a
mis un verrou sur une ressource, le client RESTCONF ne peut
pas y accéder, voir la section 1.4 du RFC). Mais le RFC fait quand
même plus de 130 pages, avec plein d'options.
La syntaxe des URL utilisés comme
paramètres de ces méthodes est spécifiée dans ce RFC en utilisant
le langage de gabarit du RFC 6570. Ainsi, dans ce langage, les URL de
Wikipédia sont décrits par
http://fr.wikipedia.org/wiki/{topic}
ce qui
signifie « le préfixe
http://fr.wikipedia.org/wiki/
suivi d'une
variable (qui est le sujet de la page) ».
En connaissant le modèle de données YANG du serveur, le client peut ainsi générer les requêtes REST nécessaires.
Les exemples du RFC utilisent presque tous la configuration d'un... juke-box (le module YANG est décrit dans l'annexe A). Cet engin a une fonction « jouer un morceau » et voici un exemple de requête et réponse REST encodée en JSON où le client demande au serveur ce qu'il sait faire :
GET /top/restconf/operations HTTP/1.1 Host: example.com Accept: application/yang-data+json HTTP/1.1 200 OK Date: Mon, 23 Apr 2016 17:01:00 GMT Server: example-server Cache-Control: no-cache Last-Modified: Sun, 22 Apr 2016 01:00:14 GMT Content-Type: application/yang-data+json { "operations" : { "example-jukebox:play" : [null] } }
L'opération play
, elle, est décrite dans
l'annexe A, le modèle de données du juke-box :
rpc play { description "Control function for the jukebox player"; input { leaf playlist { type string; mandatory true; description "playlist name"; } leaf song-number { type uint32; mandatory true; ...
Autre exemple, où on essaie de redémarrer le juke-box (et,
cette fois, on encode en XML pour montrer la différence avec JSON
- notez les différents types
MIME, comme
application/yang-data+xml
ou
application/yang-data+json
, et le fait que la
racine est /restconf
et plus /top/restconf
) :
POST /restconf/operations/example-ops:reboot HTTP/1.1 Host: example.com Content-Type: application/yang-data+xml <input xmlns="https://example.com/ns/example-ops"> <delay>600</delay> <message>Going down for system maintenance</message> <language>en-US</language> </input> HTTP/1.1 204 No Content Date: Mon, 25 Apr 2016 11:01:00 GMT Server: example-server
(Le serveur a accepté - le code de retour commence par 2 - mais n'a rien à dire, d'où le corps de la réponse vide, et le code 204 au lieu de 200.)
Et voici un exemple de récupération d'informations avec
GET
:
GET /restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light HTTP/1.1 Host: example.com Accept: application/yang-data+xml HTTP/1.1 200 OK Date: Mon, 23 Apr 2016 17:02:40 GMT Server: example-server Content-Type: application/yang-data+xml Cache-Control: no-cache ETag: "a74eefc993a2b" Last-Modified: Mon, 23 Apr 2016 11:02:14 GMT <album xmlns="http://example.com/ns/example-jukebox" xmlns:jbox="http://example.com/ns/example-jukebox"> <name>Wasting Light</name> <genre>jbox:alternative</genre> <year>2011</year> </album>
Pour éviter les collisions d'édition (Alice lit une variable,
tente de l'incrémenter, Bob tente de la multiplier par deux, qui
va gagner ?), RESTCONF utilise les mécanismes classiques de HTTP,
If-Unmodified-Since:
,
If-Match:
, etc.
Allez, encore un exemple, je trouve qu'on comprend mieux avec des exemples, celui-ci est pour modifier une variable :
PUT /restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light HTTP/1.1 Host: example.com Content-Type: application/yang-data+json { "example-jukebox:album" : [ { "name" : "Wasting Light", "genre" : "example-jukebox:alternative", "year" : 2011 } ] } HTTP/1.1 204 No Content Date: Mon, 23 Apr 2016 17:04:00 GMT Server: example-server Last-Modified: Mon, 23 Apr 2016 17:04:00 GMT ETag: "b27480aeda4c"
Et voici un exemple utilisant la méthode
PATCH
, qui avait été introduite dans le RFC 5789, pour changer l'année de l'album :
PATCH /restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light HTTP/1.1 Host: example.com If-Match: "b8389233a4c" Content-Type: application/yang-data+xml <album xmlns="http://example.com/ns/example-jukebox"> <year>2011</year> </album> HTTP/1.1 204 No Content Date: Mon, 23 Apr 2016 17:49:30 GMT Server: example-server Last-Modified: Mon, 23 Apr 2016 17:49:30 GMT ETag: "b2788923da4c"
Et on peut naturellement détruire une ressource :
DELETE /restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light HTTP/1.1 Host: example.com HTTP/1.1 204 No Content Date: Mon, 23 Apr 2016 17:49:40 GMT Server: example-server
L'URL utilisé dans la requête comprend les différentes parties habituelles d'un URL (RFC 3986) : le chemin, la requête, l'identificateur, etc. (Dans les exemples précédents, je n'ai pas mis de requête ou d'identificateur.)
Notre RFC impose HTTPS, puisque modifier la configuration d'un équipement réseau est évidemment une opération sensible. Pour la même raison, le serveur RESTCONF doit évidemment authentifier le client. La méthode recommandée est le certificat client (section 7.4.6 du RFC 5246) mais on peut aussi utiliser une autre méthode d'authentification HTTP.
Date de publication du RFC : Février 2017
Auteur(s) du RFC : R. Pan, P. Natarajan, F. Baker (Cisco Systems), G. White (CableLabs)
Expérimental
Réalisé dans le cadre du groupe de travail IETF aqm
Première rédaction de cet article le 1 mars 2017
Mais c'est quoi, ce bufferbloat (obésité du tampon ?) et pourquoi est-ce mauvais ? Le bufferbloat désigne la tendance à mettre dans les routeurs des tampons d'entrée/sortie de plus en plus gros. Cette tendance vient de la baisse du prix des mémoires, et d'un désir de pouvoir encaisser les brusques pics de trafic (bursts) qui sont fréquents sur l'Internet. Mais le bufferbloat a une conséquence négative : la latence augmente, puisque le paquet risque de devoir attendre longtemps dans un tampon qui, une fois rempli, aura du mal à se vider. Ce RFC propose donc un mécanisme de gestion des files d'attente, PIE (Proportional Integral controller Enhanced) où le routeur surveille la latence des paquets dans les tampons d'entrée/sortie, et jette des paquets, même si le tampon n'est pas plein, pour limiter la latence. Le paquet perdu dira en effet aux émetteurs de ralentir.
La latence est particulièrement à surveiller dans le cas d'applications fortement interactives comme les jeux en ligne ou la vidéoconférence. On cherche donc à diminuer la latence, pour fournir une meilleure qualité de service aux utilisateurs. PIE a fait l'objet d'analyses théoriques, de simulations, puis de mise en œuvre dans le noyau Linux, et semble aujourd'hui une solution intéressante. PIE est une solution purement locale au routeur, et ne pose donc pas de problèmes d'interopérablité : les autres routeurs avec lesquels on parle n'ont pas besoin de participer.
L'un des problèmes centraux de l'Internet a toujours été la congestion. Les paquets arrivent quand ils veulent, et peuvent dépasser la capacité du réseau. Deux solutions pour un routeur, jeter les paquets (IP est prévu pour cela, il travaille en mode datagramme), et attendre que les couches supérieures comme TCP s'en aperçoivent et ralentissent, ou bien deuxième solution, mettre les paquets dans un tampon, en attendant de pouvoir les envoyer. Ce tampon va permettre de lisser un trafic Internet qui est très irrégulier. En pratique, les deux solutions doivent être déployées : le tampon a une taille finie et, s'il est plein, il faut bien se résigner à jeter des paquets.
Comme la perte de paquets entraine un ralentissement du transfert de données (TCP va automatiquement diminuer le débit), il existe une forte demande pour limiter cette perte. La baisse des prix des mémoires permet de satisfaire cette demande, en augmentant la taille des tampons. (Voir le site Web consacré au « bufferbloat », qui contient notamment une bonne introduction au problème.)
L'effet pervers de cette augmentation de taille est que les protocoles comme TCP, ne voyant pas de perte de paquets, vont continuer à augmenter leur débit, et envoyer plein de paquets jusqu'à ce que, le tampon étant plein, le routeur commence à jeter des paquets, calmant TCP. Mais, à ce moment, il est trop tard, le tampon est plein et risque de rester plein longtemps, l'émetteur continuant à envoyer des paquets, même si c'est à un rythme réduit. Les paquets vont donc patienter dans le tampon, augmentant la latence. Et plus le tampon est grand, plus on aggrave la latence. On est donc passé de Charybde en Scylla : pour éviter les pertes de paquets, qui diminuent le débit, on a augmenté la latence. (On voit d'ailleurs que la notion de performance, dans les réseaux, est une notion compliquée. C'est pour cela que des termes flous et passe-partout comme « vitesse » ne devraient pas être employés.)
Un système de gestion de la file d'attente (AQM) va permettre de mieux contrôler le problème, en essayant de faire en sorte que les pics soudains d'activité puissent passer, tout en limitant la latence pour les transferts de longue durée. Un exemple de mécanisme d'AQM est RED, initialement proposé dans le RFC 2309 il y a dix-huit ans. RED a deux limites, il nécessite un réglage manuel de ses paramètres, et il agit sur la longueur de la file d'attente, pas sur la latence. C'est entre autre pour cela que le RFC 7567 avait demandé à ce que de nouveaux mécanismes d'AQM soient développés.
L'algorithme de ce RFC, PIE, se veut, comme RED, simple et facile à mettre en œuvre. Comme RED, son principal moyen d'action est de jeter, de manière partiellement aléatoire, des paquets avant qu'ils ne soient mis dans la file d'attente. Contrairement à RED, il agit sur la latence, pas sur la longueur de la file d'attente.
Les objectifs de PIE sont décrits dans la section 3 du RFC :
La section 4 du RFC décrit PIE, et c'est la section à lire si vous voulez mettre en œuvre PIE dans un routeur, ou simplement le comprendre complètement. L'algorithme effectue trois tâches :
La description complète originale figure dans l'article de Pan, R., Natarajan, P. Piglione, C., Prabhu, M.S., Subramanian, V., Baker, F. Steeg et B. V., « PIE: A Lightweight Control Scheme to Address the Bufferbloat Problem » en 2013. Cet algorithme suit les principes de stabilité de théorie du contrôle.
Dans cette section 4, notre RFC présente l'algorithme PIE sous
forme de texte et de pseudo-code. La
première tâche (section 4.1), jeter les paquets entrants selon une certaine
probabilité (PIE->drop_prob_
) va s'exprimer :
//Safeguard PIE to be work conserving if ( (PIE->qdelay_old_ < QDELAY_REF/2 && PIE->drop_prob_ < 0.2) || (queue_.byte_length() <= 2 * MEAN_PKTSIZE) ) { return ENQUE; else randomly drop the packet with a probability PIE->drop_prob_.
La première branche du if
est là pour éviter
du travail inutile : si la probabilité de jeter un paquet est
faible, ou bien si la file d'attente est loin d'être pleine (moins
de deux paquets en attente), ou bien si la latence est bien plus
faible que la latence visée,
dans ces cas, on le jette rien. C'est le fonctionnement idéal du
routeur, lorsque la congestion n'est qu'une menace lointaine.
La deuxième tâche, calculer automatiquement la probabilité de
jeter un paquet, est plus délicate (section 4.2). Il faut
connaitre la latence mais aussi la tendance (est-ce que la latence
tend à diminuer ou bien à augmenter). C'est ce qu'on nomme le
contrôleur Proportional Integral qui a donné
son nom à l'algorithme PIE. La formule de base (voir le
pseudo-code complet dans le RFC, notamment dans l'annexe A) est que la probabilité est la
latence (current_qdelay
) multipliée par un coefficient (alpha
),
augmentée de la différence entre la latence actuelle et la latence
précédente (et, donc, si la latence diminue, la probabilité sera diminuée) :
p = alpha*(current_qdelay-QDELAY_REF) + beta*(current_qdelay-PIE->qdelay_old_);
Et la troisième tâche, le calcul de la latence, est fait en suivant la loi de Little (section 4.3) :
current_qdelay = queue_.byte_length()/dequeue_rate;
Cette formule est une estimation de la latence. On peut aussi la mesurer directement (mais cela fait plus de travail pour le routeur), par exemple en ajoutant une estampille temporelle aux paquets entrants et en la lisant à la sortie.
Ce pseudo-code n'est encore qu'une approximation du vrai algorithme. L'un des gros problèmes de tout système de gestion de la file d'attente est que le trafic Internet est sujet à de brusques pics où un grand nombre de paquets arrive en peu de temps. Cela va remplir la file et augmenter la latence, mais cela ne veut pas dire qu'il faille subitement augmenter la probabilité d'abandon de paquets (section 4.4). Donc, la première tâche, jeter certains paquets, devient :
if PIE->burst_allowance_ > 0 enqueue packet; else randomly drop a packet with a probability PIE->drop_prob_. if (PIE->drop_prob_ == 0 and current_qdelay < QDELAY_REF/2 and PIE->qdelay_old_ < QDELAY_REF/2) PIE->burst_allowance_ = MAX_BURST;
Et dans la seconde, le calcul de la probabilité d'abandon de paquets, on ajoute :
PIE->burst_allowance_ = max(0,PIE->burst_allowance_ - T_UPDATE);
Cette fois, on a un PIE complet. Mais on peut, optionnellement, y ajouter certains éléments (section 5 du RFC). Le plus évident est, au lieu de jeter le paquet, ce qui fait qu'il aura été émis et transmis par les routeurs amont pour rien, de marquer les paquets avec ECN (RFC 3168). La première tâche regarde donc si le flot de données gère ECN et utilise cette possibilité dans ce cas, au lieu de jeter aveuglément :
if PIE->drop_prob_ < mark_ecnth && ecn_capable_packet: mark packet; else: drop packet;
Le trafic réseau varie beaucoup dans le temps. La plupart du temps, si le réseau est bien dimensionné, il n'y a pas de problème et il serait dommage que PIE jette au hasard des paquets quand on n'est dans cette phase heureuse. Un autre ajout utile à PIE est donc une désctivation automatique quand la file d'attente est peu remplie. Un des avantages de couper complètement PIE (par rapport à simplement décider de ne pas jeter les paquets) est de gagner du temps dans le traitement des paquets.
Pour réactiver PIE quand la congestion commence, c'est un peu plus compliqué. Si PIE est coupé, il n'y a plus de calcul de la latence, et on ne peut donc pas utiliser une augmentation de la latence pour décider de remettre PIE en marche. Le RFC suggère de remettre PIE en route dès qu'on passe au-dessus d'un tiers d'occupation de la file d'attente.
Autre question délicate, les problèmes que crée le hasard. Par défaut, PIE prend ses décisions en jetant les dés. Si la latence est importante, indiquant qu'on approche de la congestion, PIE jette des paquets au hasard. Mais le hasard n'est pas prévisible (évidemment). Et il ne mène pas à une répartition uniforme des pertes de paquets. Il se peut qu'aucun paquet ne soit jeté pendant longtemps, ce qui fait que le routeur ne réagira pas à l'augmentation de la latence. Mais il se peut aussi qu'un massacre de paquets se produise à certains moments. L'utilisation du hasard mène forcément à des « séries noires » (ou à des « séries blanches »). Notre RFC propose donc un mécanisme (optionnel) de « dé-hasardisation », où un nouveau paramètre augmente avec la probabilité d'abandon de paquet, et est remis à zéro lorsqu'on jette un paquet. La décision de laisser tomber un paquet n'est prise que lorsque ce paramètre est entre deux valeurs pré-définies.
La section 6 du RFC se penche sur les problèmes concrets de mise en œuvre (programmeurs, on pense à vous). PIE peut être mis en œuvre en logiciel ou bien en matériel (sur beaucoup de routeurs, la mise en file d'attente est typiquement « plus logicielle » que le retrait de la file). PIE est simple, et peut être programmé de manière très économique (ou plus coûteuse si on met une estampille temporelle à chaque paquet, ce qui permet de mieux mesurer la latence, mais nécessite davantage de mémoire).
La deuxième tâche de PIE, recalculer la probabilité d'abandon, se fait typiquement en parallèle avec le traitement de la file d'attente. Vu le rythme d'entrée et de sortie des paquets dans un routeur moderne, ce sont des milliers de paquets qui sont passés entre deux recalculs. Le routeur ne pourra donc pas réagir instantanément.
Comme tous les bons algorithmes, PIE est évidemment plombé par un brevet, en l'occurrence deux brevets de Cisco. Cette entreprise a promis une licence gratuite et sans obligations (mais avec la classique clause de représailles, annulant cette licence si quelqu'un essaie d'utiliser ses brevets contre Cisco).
Aujourd'hui, Linux, FreeBSD (voir la page Web du projet) et d'autres mettent en œuvre PIE.
Date de publication du RFC : Janvier 2017
Auteur(s) du RFC : S. Josefsson (SJD AB), I. Liusvaara (Independent)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF cfrg
Première rédaction de cet article le 25 janvier 2017
Ce RFC est la description IETF de l'algorithme de signature cryptographique EdDSA. EdDSA est en fait une famille, il prend un certain nombre de paramètres, comme la courbe elliptique Edwards utilisée et ce RFC décrit son utilisation avec les courbes Edwards25519 et Edwards448.
EdDSA n'avait apparemment été décrit auparavant que dans des publications scientifiques, ce RFC sert à la fois à avoir une référence IETF, et également à décrire EdDSA dans des termes plus familiers aux programmeurs. Pourquoi faire de l'EdDSA, d'ailleurs ? Parce que cet algorithme (ou plutôt cette famille d'algorithmes) a plusieurs avantages, notamment :
Un site Web sur EdDSA a été créé par ses auteurs. La référence officielle d'EdDSA est l'article « High-speed high-security signatures » de D. Bernstein, N. Duif, T. Lange, P. Schwabe, P., et B. Yang. Une extension à d'autres courbes est décrite dans « EdDSA for more curves ». Sur les courbes elles-mêmes, on peut consulter le RFC 7748.
La section 3 du RFC décrit l'algorithme générique d'EdDSA. Comme il laisse ouvert pas moins de onze paramètres, on voit qu'on peut créer une vaste famille d'algorithmes se réclamant d'EdDSA. Mais, évidemment, toutes les combinaisons possibles pour ces onze paramètres ne sont pas sérieuses du point de vue de la sécurité cryptographique, et notre RFC ne décrira que cinq algorithmes spécifiques, dont ed25519 et ed448. L'algorithme générique est surtout utile pour la culture générale.
C'est parce que EdDSA est un algorithme générique (au contraire
de ECDSA) que les programmes qui
l'utilisent ne donnent pas son nom mais celui de l'algorithme
spécifique. Ainsi, OpenSSH vous permet de
générer des clés Ed25519 (ssh-keygen -t
ed25519
) mais pas de clés EdDSA (ce qui ne voudrait
rien dire).
La section 4 du RFC décrit en détail un des paramètres importants de EdDSA : le choix de la fonction prehash. Celle-ci peut être l'identité (on parle alors de Pure EdDSA) ou bien une fonction de condensation cryptographique (on parle alors de Hash EdDSA).
La section 5, elle, spécifie EdDSA avec les autres paramètres, décrivant notamment Ed25519 et Ed448. Ainsi, Ed25519 est EdDSA avec la courbe Edwards25519, et une fonction prehash qui est l'identité. (Pour les autres paramètres, voir le RFC.) L'algorithme Ed25519ph est presque identique sauf que sa fonction prehash est SHA-512.
Comme tout algorithme de cryptographie, il faut évidemment beaucoup de soin quand on le programme. La section 8 du RFC contient de nombreux avertissements indispensables pour le programmeur. Un exemple typique est la qualité du générateur aléatoire. EdDSA n'utilise pas un nombre aléatoire par signature (la plaie de DSA), et est déterministe. La sécurité de la signature ne dépend donc pas d'un bon ou d'un mauvais générateur aléatoire. (Rappelons qu'il est très difficile de faire un bon générateur aléatoire, et que beaucoup de programmes de cryptographie ont eu des failles de sécurité sérieuses à cause d'un mauvais générateur.) Par contre, la génération des clés, elle, dépend de la qualité du générateur aléatoire (RFC 4086).
Il existe désormais pas mal de mises en œuvre d'EdDSA, par
exemple dans OpenSSH cité plus haut. Sinon,
les annexes A et B du RFC contiennent une mise en œuvre en
Python d'EdDSA. Attention, elle est conçue
pour illustrer l'algorithme, pas forcément pour être utilisée en
production. Par exemple, elle n'offre aucune protection contre les
attaques exploitant la différence de temps de calcul selon les
valeurs de la clé privée (cf. la section 8.1). J'ai extrait ces deux
fichiers, la bibliothèque eddsalib.py
et le
programme de test eddsa-test.py
(ils nécessitent
Python 3). Le programme de test prend comme entrée un fichier
composé de plusieurs vecteurs de test,
chacun comprenant quatre champs, séparés par des
deux-points, clé secrète, clé publique, message et
signature. La section 7 du RFC contient des vecteurs de
test pour de nombreux cas. Par exemple, le test 2 de la section 7.1 du RFC s'écrit
4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb:3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c:72:92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00
et l'exécution du programme de test affiche juste le numéro de
ligne quand tout va bien :
% python3 eddsa-test.py < vector2.txt 1
On peut aussi utiliser des tests plus détaillés comme ce fichier de vecteurs de test :
% python3 eddsa-test.py < sign.input 1 2 3 ... 1024
Si on change le message, la signature ne correspond évidemment plus et le programme de test indique une assertion erronée :
% python3 eddsa-test.py < vector2-modified.txt 1 Traceback (most recent call last): File "eddsa-test.py", line 30, in <module> assert signature == Ed25519.sign(privkey, pubkey, msg) AssertionError
Date de publication du RFC : Novembre 2016
Auteur(s) du RFC : W. Hardaker
(USC/ISI), O. Gudmundsson
(CloudFlare), S. Krishnaswamy
(Parsons)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 4 décembre 2016
Normalement, en 2016, tous les résolveurs DNS sérieux devraient valider avec DNSSEC. Mais ce n'est pas le cas. Il y a plusieurs raisons à cela, mais ce nouveau RFC se focalise sur un problème précis : le cas d'un résolveur connecté via un réseau pourri, non-neutre, et qui se permet d'interférer avec le transport des paquets IP, menant le résolveur à de sérieuses difficultés. Comment détecter ces réseaux pourris ? Et que faire pour valider quand même ?
Si le résolveur est une grosse machine dans un centre de données, connectée directement à des opérateurs neutres, il n'y a pas trop de problème. C'est le cas des résolveurs des FAI, par exemple. Mais la situation est bien moins favorable à M. Michu. Si celui-ci veut, à juste titre, avoir son propre résolveur DNS sur sa machine, il dépend des réseaux où on laisse M. Michu se connecter, et ceux-ci sont rarement neutres. (Le RFC couvre le cas où ce résolveur local fait suivre - forwarde - les requêtes à un autre résolveur, et celui où il parle directement aux serveurs faisant autorité.)
La section 1.2 du RFC décrit les cas où la validation DNSSEC va être difficile ou impossible :
Bien des outils ont été développés pour contourner ces problèmes, comme dnssec-trigger.
Pour faire des tests des résolveurs et de tous les équipements
intermédiaires qui peuvent poser des problèmes de validation
DNSSEC dans certains cas, le RFC parle d'une zone de test nommée
test.example.com
. Elle n'existe pas en vrai
mais, aujourd'hui, la zone
test.dnssec-tools.org
fait la même chose
(elle est par exemple utilisée pour les travaux pratiques lors de
la formation DNSSEC chez HSC). Cette zone
est délibérement peuplée avec des noms mal signés. Ainsi, le nom
badsign-aaaa.test.dnssec-tools.org
a un
enregistrement AAAA dont la signature a été modifiée, la rendant
invalide. Testons (pour tous les tests, comme le but était de voir
le comportement DNSSEC, j'ai utilisé un fichier de configuration
~/.digrc
contenant +dnssec
+multiline
, merci à Landry Minoza de l'avoir remarqué) :
% dig AAAA badsign-aaaa.test.dnssec-tools.org ; <<>> DiG 9.9.5-9+deb8u8-Debian <<>> AAAA badsign-aaaa.test.dnssec-tools.org ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 60910 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;badsign-aaaa.test.dnssec-tools.org. IN AAAA ;; Query time: 3759 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Sun Dec 04 17:12:28 CET 2016 ;; MSG SIZE rcvd: 63 % dig +cd AAAA badsign-aaaa.test.dnssec-tools.org ; <<>> DiG 9.9.5-9+deb8u8-Debian <<>> +cd AAAA badsign-aaaa.test.dnssec-tools.org ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29404 ;; flags: qr rd ra cd; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 5 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;badsign-aaaa.test.dnssec-tools.org. IN AAAA ;; ANSWER SECTION: badsign-aaaa.test.dnssec-tools.org. 86400 IN AAAA 2001:470:1f00:ffff::1 badsign-aaaa.test.dnssec-tools.org. 86400 IN RRSIG AAAA 5 4 86400 ( 20170101064820 20161202054820 19442 test.dnssec-tools.org. nZ8bPLBleW/sW6x135+Iz4IhO6Lr04V8C9fC1bMVfCVY 3rKqbOoBk1i+wnnGDCTWQ5iCicWTKLIpbDmCSW9C33pj P2j7C/ensspbdwpD/7Ia8zN+XUSN+ThLU6lgYGKFuoVL QmIG/vr1lOn6xdjXY2E4mStAjaGuertvKKDYy/I= ) ;; AUTHORITY SECTION: test.dnssec-tools.org. 280 IN NS dns1.test.dnssec-tools.org. test.dnssec-tools.org. 280 IN NS dns2.test.dnssec-tools.org. test.dnssec-tools.org. 280 IN RRSIG NS 5 3 86400 ( 20170101064820 20161202054820 19442 test.dnssec-tools.org. AK95JOAuvfZ1ZwEsrKiR8DP1zluoBvBkXHRXa78rrK5U UuZdLnZwnYlnNplrZZOrQNuUaPyb4zI0TGfw/+aa/ZTU qyx8uQODSHuBTPQTlcmCFAfTIyd1Q+tSTEs2TuGUhjKe H9Hk+w6yOjI/o52c2OcTMTJ4Jmt2GlIssrrDlxY= ) ;; ADDITIONAL SECTION: dns1.test.dnssec-tools.org. 280 IN A 168.150.236.43 dns2.test.dnssec-tools.org. 280 IN A 75.101.48.145 dns1.test.dnssec-tools.org. 86400 IN RRSIG A 5 4 86400 ( 20170101064820 20161202054820 19442 test.dnssec-tools.org. zoa0V/Hwa4QM0spG6RlhGM6hK3rQVALpDve1rtF6NvUS Sb6/HBzQOP6YXTFQMzPEFUza8/tchYp5eQaPBf2AqsBl i4TqSjkIEklHohUmdhK7xcfFjHILUMcT/5AXkEStJg7I 6AqZE1ibcOh7Mfmt/2f0vj2opIkz6uK740W7qjg= ) dns2.test.dnssec-tools.org. 86400 IN RRSIG A 5 4 86400 ( 20170101064820 20161202054820 19442 test.dnssec-tools.org. hGq7iAtbHrtjCYJGMPQ3fxijhu4Izk8Ly+xZOa0Ag24R lqpFgdd2amDstFVLTRs3x15UqQIO+hmFdlbSOterDkbg /o2/FhtZOJr7c75Pu3EWi/DDbT9pULk4Uwjlie1QBopv LLZ94SlqKO7eQ02NRyy5EL4gD2G5rSffsUqEkj8= ) ;; Query time: 206 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Sun Dec 04 17:12:44 CET 2016 ;; MSG SIZE rcvd: 885
Le second test, celui fait avec le bit CD (Checking Disabled), montre que le problème vient bien de DNSSEC. Autre test, avec une signature expirée :
% dig A pastdate-a.test.dnssec-tools.org ... ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 46319 ... % dig +cd A pastdate-a.test.dnssec-tools.org ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49547 ;; flags: qr rd ra cd; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 5 ... ;; ANSWER SECTION: pastdate-a.test.dnssec-tools.org. 86400 IN A 64.90.35.104 pastdate-a.test.dnssec-tools.org. 86400 IN RRSIG A 5 4 86400 ( 20161201224321 20161101234821 19442 test.dnssec-tools.org. lIL0zcEZpG/4uG5hImvpivH1C/D3PFI3RNYHlPbZ [...]
La liste de tous les noms à tester est en ligne.
Le but de ce RFC est de lister tous les tests que peut et devrait faire un validateur local, pour arriver à valider malgré des résolveurs amont, ou bien un réseau, hostile. Ces stratégies sont mises en œuvre, par exemple, dans dnssec-trigger.
En détectant la non-conformité (compliance, un terme à la mode dans les organisations), le validateur situé sur la machine terminale, ou bien dans le réseau local, peut alors adopter la meilleure stratégie de contournement (ou, dans le pire des cas, prévenir loyalement l'utilisateur qu'on ne pourra pas faire de validation DNSSEC). Les tests doivent être faits au début d'une nouvelle connexion réseau, ou bien lorsque celle-ci change.
La section 3 du RFC est consacrée à ces tests de
non-conformité. Je ne vais pas décrire la totalité de ces tests,
un sous-ensemble suffira. Ainsi, le premier test, le plus trivial,
est que la machine puisse parler en UDP à son résolveur attitré (celui
typiquement reçu en DHCP). On lui demande
good-a.test.dnssec-tools.org
et on doit avoir
une réponse sous forme d'une adresse IP (comme son nom l'indique, ce nom est correctement
signé). Si un test aussi trivial ne marche pas, ce n'est sans
doute pas la peine d'aller plus loin. Un peu plus subtil, on teste
le même résolveur en TCP.
Après, on passe à EDNS (RFC 6891), qui est indispensable pour DNSSEC. Une requête pour ce même nom, mais avec une option EDNS, doit passer. Si EDNS ne marche pas, on peut arrêter, DNSSEC ne marchera pas non plus. Mais s'il marche ? On teste alors avec le bit DO (DNSSEC OK) qui indique au serveur qu'il doit envoyer les données DNSSEC, notamment les signatures. La réponse doit inclure ce même bit DO. (C'est plus tard qu'on teste qu'on a bien reçu des signatures. Rappelez-vous que la plupart des middleboxes sont horriblement boguées. Certaines acceptent le bit DO et le renvoient, sans pour autant transmettre les signatures.)
On teste alors des zones qu'on sait signées et on regarde si le
résolveur met le bit AD (Authentic Data), au
moins pour les algoritmes RSA
+ SHA-1 et RSA +
SHA-256. Si cela ne marche pas, ce n'est
pas forcément une erreur fatale, puisque, de toute façon, on
voulait faire la validation nous-même. Il faut aussi penser à
faire le test inverse : un résolveur validant doit mettre le bit
AD pour une réponse signée correctement, et doit répondre avec le
code de retour SERVFAIL (Server Failure) si la
réponse devrait être signée mais ne l'est pas, ou bien l'est
mal. Cela se fait en testant badsign-a.test.dnssec-tools.org
.
Dans les enregistrements DNSSEC, il n'y a pas que les
signatures (RRSIG), il y a aussi les enregistrements servant à
prouver la non-existence, NSEC et NSEC3. Il faut donc également
tester qu'ils sont reçus car, en pratique, on voit en effet des
middleboxes qui laissent passer les RRSIG mais
bloquent stupidement les NSEC et les NSEC3. On demande donc
non-existent.test.dnsssec-tools.org
, et on
doit récupérer non seulement une réponse avec le code NXDOMAIN
(No Such Domain) mais également les NSEC ou
NSEC3 permettant de valider cette réponse :
% dig AAAA non-existent.test.dnsssec-tools.org [...] ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 40218 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 8, ADDITIONAL: 1 [...] ;; AUTHORITY SECTION: h9p7u7tr2u91d0v0ljs9l1gidnp90u3h.org. 900 IN NSEC3 1 1 1 D399EAAB ( H9PARR669T6U8O1GSG9E1LMITK4DEM0T NS SOA RRSIG DNSKEY NSEC3PARAM ) iruevfos0vs8jssfj22me5p458p0qj1e.org. 900 IN RRSIG NSEC3 7 2 86400 ( 20161222153046 20161201143046 3947 org. kgCZC/gE4ySP7eZUb1+2ORYRhTrvL5YBIHLCBK5F8pqK MXGJXJ/hX+8LLrg4jHJaER2AelUgUGywRn4uY80ajYpg eTuSGzRX1aVCKAR8UB80bX/YLUPUPKWOdfgxTekD4nZk eoi/9JNmIMZRc0cmMGp8LSVMqX98F2bVJnZro8U= ) iruevfos0vs8jssfj22me5p458p0qj1e.org. 900 IN NSEC3 1 1 1 D399EAAB ( IRVVBMC65HCBCFQNQS8NQFTAB943LCFU NS DS RRSIG ) vaittv1g2ies9s3920soaumh73klnhs5.org. 900 IN RRSIG NSEC3 7 2 86400 ( 20161222153046 20161201143046 3947 org. Nj/zvU0GB8vQ7bFfpSSWW+inE7RiOFjOpNc1K/TMnQqG QsKTLD9gBM8vgh3K1WdPXOCzthf/isDJAy2xLA/oRFFq KZ+Coo+33FManVmuyndGJ5bdgQqnpa0xGP7yOgjTfUsh Ff9HkX0mkzqYtWYzw0J7WnMPcOjmrlg26WsfwlU= ) vaittv1g2ies9s3920soaumh73klnhs5.org. 900 IN NSEC3 1 1 1 D399EAAB ( VAJB898DELVT5UJ4I9D1BRD2FRTBSCM1 NS DS RRSIG )
Certains serveurs DNS (ou, plus exactement, certains ensembles serveur+réseau+middlebox) n'acceptent que certains types d'enregistrement DNS (les plus connus, comme A, AAAA, MX, parfois SRV, etc). Il faut donc tester que le serveur accepte bien tous les types d'enregistrement,
Jusqu'à présent, on n'a testé que le résolveur « normal ». Même s'il ne valide pas, tant qu'il transmet fidèlement toutes les données DNS, on pourra au moins l'utiliser comme relais et cache. Par contre, dans certains cas, si on veut valider avec DNSSEC, il faudra complètement le court-circuiter. Ainsi, s'il ne transmet pas les signatures, on n'a pas d'autre choix que d'aller les demander à un autre résolveur, ou bien directement aux serveurs faisant autorité. Il faut donc tester qu'on puisse interroger ces serveurs, avec UDP et avec TCP. (Ce n'est pas toujours possible, certains réseaux violent tellement la neutralité de l'Internet qu'ils bloquent le port 53, celui du DNS.)
Avec DNSSEC, les réponses sont souvent de grande taille, et parfois fragmentées. Il faut donc tester que les fragments passent (ils sont souvent bloqués par des administrateurs réseau incompétents).
Une fois ces tests faits, il reste à synthétiser les résultats (section 4). L'idée est de pouvoir dire si le résolveur « normal » est :
En pratique, tous les résolveurs (ou plutôt l'ensemble du résolveur et du réseau situé devant, avec ses middleboxes qui cassent tout) ne rentrent pas parfaitement dans une de ces trois catégories. Ainsi, certains vont bloquer les fragments mais accepter TCP (ce qui permettra de quand même faire passer les données de grande taille), tandis que d'autres n'auront pas TCP mais qu'UDP fonctionnera bien, même en cas de fragmentation.
Une fois ces données collectées, et le résolveur correctement classé, on pourra alors déterminer comment contourner les éventuels problèmes (section 5 du RFC). Par exemple :
tcp80:
185.49.140.67
ou ssl443: 185.49.140.67
...
).La section 6 du RFC sert de voiture-balai, en mentionnant les
cas spéciaux qui peuvent être embêtants. Par exemple, DNSSEC
dépend de l'horloge, puisqu'il faut vérifier que les signatures
n'ont pas expiré. Mais la synchronisation de l'horloge dépend de
NTP donc parfois du
DNS si on a mis des noms de domaine dans son
ntp.conf
. Si la machine a une horloge assez
stable pour garder l'heure entre un arrêt et un démarrage, ce
n'est pas trop grave. Mais si la machine est un engin bon marché
avec une horloge qui dévie beaucoup (genre le Raspberry
Pi), que faire ?
Autre problème, les affreux portails captifs. Tant qu'on n'a pas cliqué sur « j'accepte cinquante pages de conditions d'utilisation que je n'ai pas lues, je veux recevoir du spam, et je promets de ne pas partager de la culture », on n'a pas un vrai accès Internet et le port 53 est sans doute bloqué. Il faudrait donc refaire les tests après le passage par le portail captif.
Face à ce genre de problèmes, une première solution est de ne pas tenter de faire du DNSSEC tant qu'on n'a pas synchronisé l'horloge, passé le portail captif (c'est ce que fait dnssec-trigger), au détriment de la sécurité. Au moins, on peut prévenir l'utilisateur et lui proposer de réessayer plus tard.
Date de publication du RFC : Novembre 2016
Auteur(s) du RFC : Matthew Thomas, Allison Mankin, Lixia Zhang (UCLA)
Pour information
Première rédaction de cet article le 27 novembre 2016
Ce nouveau RFC est le compte-rendu d'un atelier qui s'était tenu du 8 au 10 mars 2014 à Londres sur le thème des « collisions ». Ce terme exagéré et sensationnaliste désigne le phénomène qui peut se produire quand un acteur de l'Internet a bêtement choisi de se créer un TLD à lui dans le DNS, et que ce TLD est ensuite créé par l'ICANN.
Supposons que l'entreprise Bidon décide de nommer ses
ressources internes (site Web réservé aux employés, etc) sous le
TLD inexistant
.bidon
. C'est une
mauvaise idée mais elle est fréquente. L'entreprise Bidon
compte sur le fait que ses employés utiliseront les résolveurs DNS
internes, qui ont été configurés pour reconnaître
.bidon
. Par exemple, avec
Unbound, et un serveur faisant autorité en 2001:db8:666::1:541f
, les résolveurs ont été configurés
ainsi :
stub-zone: name: "bidon" stub-addr: 2001:db8:666::1:541f
Si un employé
tente accidentellement d'accéder à une ressource en
.bidon
, alors qu'il n'utilise pas les
résolveurs de la boîte, la requête filera vers la racine du
DNS, qui répondra
NXDOMAIN
(No Such
Domain). C'est ainsi que la racine voit souvent des
requêtes pour des noms se terminant en
.local
, .home
ou
.belkin
. Si, quelques années après,
l'ICANN délègue effectivement ce TLD à une
autre organisation, ces requêtes à la racine donneront désormais
un vrai résultat. Au lieu d'un message d'erreur, le malheureux
employé sera peut-être redirigé vers un autre site Web que celui
attendu. C'est ce phénomène que Verisign
avait baptisé « collision » (name collision),
terme conçu pour faire peur.
C'est dans ce contexte qu'il y a plus de deux ans s'était tenu le « Workshop on Root Causes and Mitigation of Name Collisions », dont ce RFC est le compte-rendu tardif. Le premier rapport de l'ICANN qui mentionnait ce phénomène était le SAC 045 en 2010. Il pointait le risque que la délégation effective d'un nouveau TLD change la réponse obtenue, pour les clients mal configurés (qui interrogeaient à tort un résolveur extérieur, et donc la racine, au lieu de leurs résolveurs internes).
L'ICANN a même créé une page Web
dédiée à cette question, dont la source réelle est le
recouvrement de deux espaces de noms, l'interne et
l'externe. La bonne pratique idéale serait de ne pas utiliser de
noms qui n'existent pas ou, pire, qui existent avec une autre
signification dans l'arbre public des noms de domaine (et, là, relire le RFC 2826 peut aider). Pour reprendre l'exemple de
l'entreprise Bidon, si elle est titulaire de
bidon.fr
, elle devrait nommer ses ressources
internes avec des noms se terminant en
privé.bidon.fr
ou
interne.bidon.fr
. Si on ne veut pas faire les
choses proprement, et qu'on utilise quand même le TLD inexistant
.bidon
, alors il faut veiller très
soigneusement à séparer les deux espaces de nommage et à éviter
qu'ils ne se rencontrent un jour (ce qui est difficile à l'ère des
mobiles, avec des appareils qui rentrent et qui sortent du réseau
de l'entreprise). Sinon, on verra ces fameuses collisions.
En pratique, pas mal d'administrateurs système surestiment leurs compétences et croient qu'ils vont réussir à empêcher toute fuite vers le DNS public. C'est ce qui explique une partie des requêtes pour des noms inexistants que reçoit la racine (ces noms inexistants forment la majorité du trafic des serveurs racine du DNS). Un des problèmes de fond de l'Internet est en effet que l'administrateur de base ne se tient pas au courant et n'est pas informé des problèmes du monde extérieur. « Après moi, le déluge »
Autrefois, le problème était surtout théorique. Le nombre de
TLD n'avait pas bougé depuis de très
nombreuses années, et personne ne pensait que des TLD comme
.pizza
ou .green
verraient le jour. Mais, en 2012, l'ICANN a
lancé officiellement son programme d'ajout d'un grand nombre de
TLD, et le risque est soudain devenu une question pratique. D'où
l'atelier de 2014.
La section 2 du RFC revient en détail sur l'arrière-plan de ce
problème de collision. Outre le rapport SAC 045 cité plus haut, il
y avait eu une
déclaration de l'IAB, puis un autre rapport du
SSAC (Security and Stability Advisory
Committee, un comité de l'ICANN), le SAC 046,
une déclaration
du RSSAC et plein d'autres textes sur les conséquences d'un
agrandissement important de la zone racine. Par exemple, le rapport SAC 057 faisait remarquer que les
AC attribuaient souvent des certificats
pour des noms de domaine dans des TLD
purement locaux. Cela montrait le déploiement de ces TLD privés et
cela inquiétait. Si la société Bidon exploite
.bidon
et obtient d'une AC un certificat pour
www.compta.bidon
, après la délégation de ce
même TLD dans la racine publique, ce certificat pourrait être
utilisé pour usurper l'identité d'un autre serveur.
J'ai parlé plus haut des fuites vers le DNS public. Quelle est
leur ampleur exacte ? Ce n'est pas si évident que cela de le
savoir. Contrairement à un raccourci journalistique fréquent,
l'ICANN ne gère pas la racine. Chaque opérateur d'un
serveur DNS racine se débrouille
indépendamment, supervise son serveur mais ne rend pas forcément
compte à d'autres acteurs ou au public. En pratique, les
opérateurs des serveurs racine ont un niveau d'ouverture très
variable. (Cf. l'analyse
de l'ICANN à ce sujet.) Un des moments où plusieurs
opérateurs de serveurs racine collectent en même temps de
l'information est le Day in the Life of the
Internet et c'est sur la base de ces données qu'a
été fait le rapport d'Interisle « Name
Collision in the DNS ». Entre autres, ce rapport
classait les futurs TLD selon qu'ils présentaient un risque de
collision élevé ou faible (.home
,
.corp
et .site
se
retrouvaient en tête du classement). L'ICANN a alors publié un plan pour gérer ce risque de
collisions, notant que .home
et
.corp
étaient de loin les plus « risqués »,
car ils sont fréquemment utilisés comme TLD locaux. Bien d'autres
documents ont été publiés par l'ICANN, qui a une productivité
extraordinaire lorsqu'il s'agit de faire de la paperasse. Le
dernier
mettait en place le système dit de « controlled
interruption » qui, en gros, impose à tous les nouveaux
TLD de résoudre, pendant les premiers temps de leur délégation,
tous les noms de domaine vers l'adresse IP
127.0.53.53
. Voici l'exemple de
.box
en novembre 2016 (ce cas avait fait
l'objet d'un
article de Heise en allemand, car le routeur
Fritz!Box, très populaire en Allemagne,
utilisait ce TLD) :
% dig ANY box. ; <<>> DiG 9.10.3-P4-Debian <<>> ANY box. ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14573 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 24, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;box. IN ANY ;; ANSWER SECTION: box. 3600 IN A 127.0.53.53 box. 3600 IN SRV 10 10 0 your-dns-needs-immediate-attention.box. box. 3600 IN TXT "Your DNS configuration needs immediate attention see https://icann.org/namecollision" box. 3600 IN MX 10 your-dns-needs-immediate-attention.box. box. 900 IN SOA a.nic.box. support.ariservices.com. ( 1478481375 ; serial 1800 ; refresh (30 minutes) 300 ; retry (5 minutes) 1814400 ; expire (3 weeks) 1800 ; minimum (30 minutes) ) box. 172800 IN NS b.nic.box. box. 172800 IN NS d.nic.box. box. 172800 IN NS c.nic.box. box. 172800 IN NS a.nic.box. [...] ;; Query time: 89 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Mon Nov 21 17:23:17 CET 2016 ;; MSG SIZE rcvd: 2938
Ces enregistrements ont pour but d'attirer l'attention des
utilisateurs sur le risque de collision. Le TLD étant récent et
pas encore peuplé, il ne devrait pas y avoir de requêtes DNS. S'il
y en a quand même, c'est peut-être le résultat d'une
collision avec un usage local. L'adresse IP
127.0.53.53
est une adresse locale à la
machine. Si M. Michu tente de se connecter à
http://quelquechose.box/
aujourd'hui, il sera
redirigé vers la machine locale. Il verra une erreur (pas de
serveur HTTP qui écoute) ou bien la page
Web par défaut de sa machine (avec un message peu informatif comme
« It works ») s'il y a un serveur HTTP. Si
l'utilisateur regarde les enregistrements SRV, MX ou TXT, ou bien
si un administrateur système regarde les
requêtes DNS qui passent, ou bien les
journaux du serveur de messagerie,
peut-être comprendra-t-il qu'il doit agir. (Je trouve
personnellement que la probabilité que cela arrive est assez faible.)
L'atelier lui-même, financé par Verisign (l'entreprise qui avait le plus crié « au loup » sur les name collisions), s'est donc tenu du 8 au 10 mars à Londres. Un site Web avait été mis en place pour cet atelier, et il contient les supports et les vidéos.
Je ne vais pas décrire tous les exposés de l'atelier, la liste complète figure dans l'annexe C du RFC, et les supports sont en ligne. Le RFC note qu'il y a eu plusieurs interventions sur la qualité des données du DITL (Day in the Life of the Internet) : il est trivial de les polluer (le DITL est annoncé publiquement, et à l'avance) par des requêtes artificielles. Aucune preuve n'a été trouvée d'une manipulation délibérée. De toute façon, les données montrent surtout qu'il y a beaucoup de n'importe quoi dans le trafic que reçoivent les serveurs racine (par exemple des requêtes avec le bit RD - Recursion Desired - alors que les serveurs de la racine ne sont pas récursifs). Cela peut être le résultat de bogues dans les résolveurs, de tests ou bien d'attaques délibérées.
La question de l'éducation des utilisateurs est revenue plusieurs fois. Faut-il s'inspirer du téléphone ou du système postal, qui ont tous les deux connu des changements qui nécessitaient une adaptation de l'utilisateur, qui s'est faite par le biais d'importantes campagnes de sensibilisation et d'éducation ?
Le comité de programme avait conclu que le sujet était loin d'être épuisé. Manque de données, manque de théories explicatives, manque d'intérêt pour la question, en tout cas, celle-ci restait largement ouverte après l'atelier (et je ne suis personnellement pas sûr que cela soit mieux aujourd'hui, plus de deux ans après l'atelier de Londres).
Date de publication du RFC : Janvier 2017
Auteur(s) du RFC : F. Gont (SI6 Networks /
UTN-FRH), W. Liu (Huawei
Technologies), T. Anderson (Redpill
Linpro)
Pour information
Première rédaction de cet article le 7 janvier 2017
C'est quoi, un « fragment atomique » ? Décrits dans le RFC 6946, ces charmants objets sont des datagrammes IPv6 qui sont des fragments... sans en être. Ils ont un en-tête de fragmentation sans être fragmentés du tout. Ce RFC estime qu'ils ne servent à rien, et sont dangereux, et devraient donc ne plus être générés.
Le mécanisme de fragmentation d'IPv6 (assez différent de celui d'IPv4) est décrit dans le RFC 2460, sections 4.5 et 5. Que se passe-t-il si un routeur génère un message ICMP Packet Too Big (RFC 4443, section 3.2) en indiquant une MTU inférieure à 1 280 octets, qui est normalement la MTU minimale d'IPv6 ? (Le plus beau, c'est que ce routeur n'est pas forcément en tort, cf. RFC 6145, qui décrivait leur utilisation pour être sûr d'avoir un identificateur de datagramme.) Eh bien, dans ce cas, l'émetteur du datagramme trop gros doit mettre un en-tête « Fragmentation » dans les datagrammes suivants, même s'il ne réduit pas sa MTU en dessous de 1 280 octets. Ce sont ces datagrammes portant un en-tête « Fragmentation » mais pas réellement fragmentés (leur bit M est à 0), qui sont les fragments atomiques du RFC 6946.
Malheureusement, ces fragments atomiques permettent des attaques contre les machines IPv6 (section 2 du RFC). Il existe des attaques liées à la fragmentation (RFC 6274 et RFC 7739). Certaines nécessitent que les datagrammes soient réellement fragmentés mais ce n'est pas le cas de toutes : il y en a qui marchent aussi bien avec des fragments atomiques. Un exemple d'une telle attaque exploite une énorme erreur de certaines middleboxes, jeter les datagrammes IPv6 ayant un en-tête d'extension, quel qu'il soit (y compris, donc, l'en-tête Fragmentation). Ce comportement est stupide mais hélas répandu (cf. RFC 7872). Un attaquant peut exploiter cette violation de la neutralité du réseau pour faire une attaque par déni de service : il émet des faux messages ICMP Packet Too Big avec une MTU inférieur à 1 280 octets, la source se met à générer des fragments atomiques, et ceux-ci sont jetés par ces imbéciles de middleboxes.
Le RFC décrit aussi une variante de cette attaque, où deux pairs BGP jettent les fragments reçus (méthode qui évite certaines attaques contre le plan de contrôle du routeur) mais reçoivent les ICMP Packet Too Big et fabriquent alors des fragments atomiques. Il serait alors facile de couper la session entre ces deux pairs. (Personnellement, le cas me parait assez tiré par les cheveux...)
Ces attaques sont plus faciles à faire qu'on ne pourrait le croire car :
Outre ces problèmes de sécurité, le RFC note (section 3) que les fragments atomiques ne sont de toute façon pas quelque chose sur lequel on puisse compter. Il faut que la machine émettrice les génère (elle devrait, mais la section 6 du RFC 6145 note que beaucoup ne le font pas), et, malheureusement, aussi bien les messages ICMP Packet Too Big que les fragments sont souvent jetés par des machines intermédiaires.
D'ailleurs, il n'est même pas certain que la méthode du RFC 6145 (faire générer des fragments atomiques afin d'obtenir un identificateur par datagramme) marche vraiment, l'API ne donnant pas toujours accès à cet identificateur de fragment. (Au passage, pour avoir une idée de la complexité de la mise en œuvre des fragments IP, voir cet excellent article sur le noyau Linux.)
En conclusion (section 4), notre RFC demande qu'on abandonne les fragments atomiques :
Date de publication du RFC : Novembre 2016
Auteur(s) du RFC : S. Bortzmeyer (AFNIC), S. Huque (Verisign Labs)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 9 novembre 2016
Tout le monde apprend à l'école que les noms de domaine sont organisés en un
arbre. (Et j'invite tout le monde à lire la
section 3.1 du RFC 1034, pour dire moins de
bêtises sur les noms de domaine.) Il en découle logiquement que, si un
nœud de l'arbre n'existe pas, les nœuds situés en dessous
n'existent pas non plus. C'est évident ? Hélas, non. En pratique,
bien des résolveurs DNS sont prudents et, lorsqu'ils reçoivent une
réponse négative pour un nom, mettons
foo.example
, ils n'enregistrent pas pour
autant le fait que les sous-domaines comme
bar.foo.example
n'existent pas non plus, et,
si un client leur demande des informations sur ce sous-domaine,
ils vont relayer la question aux serveurs faisant autorité, alors
qu'ils auraient parfaitement pu répondre à partir de leur
cache. Ce nouveau RFC remet les choses en
place : les noms de domaine sont organisés en arbre, ce
comportement traditionnel est donc bel et bien erroné, et un
résolveur devrait, lorsqu'il reçoit une réponse négative,
mémoriser le fait qu'il n'y a pas non plus de sous-domaines de ce
nom. Cela améliorera les performances du DNS et, dans certains
cas, sa résistance à des attaques par déni de
service.
Voyons d'abord ce que fait un résolveur actuel. J'ai choisi
Unbound. Il vient de démarrer, on lui
demande foobar56711.se
, qui n'existe pas :
% dig MX foobar56711.se. ... ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 56324
On a logiquement un NXDOMAIN
(No
Such Domain, ce nom n'existe pas ; cette erreur se
nommait autrefois Name Error, et a le code 3). Où le résolveur
a-t-il trouvé cette information ? Il a demandé aux serveurs
faisant autorité, comme nous le montre
tcpdump :
14:57:14.488196 IP (tos 0x0, ttl 57, id 52537, offset 0, flags [none], proto UDP (17), length 1063) 130.239.5.114.53 > 10.10.86.133.44861: [udp sum ok] 64329 NXDomain*- q: MX? foobar56711.se. 0/6/1 ...
Le serveur d'IIS (le registre de .se
), 130.239.5.114
lui a bien dit NXDOMAIN
.
Maintenant, demandons au même résolveur
xyz.foobar56711.se
, sous-domaine du
précédent :
% dig MX xyz.foobar56711.se. ... ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 64776
Et, si on regarde le trafic avec tcpdump, on voit que le résolveur a demandé encore au serveur faisant autorité, alors que c'était inutile !
15:00:32.929219 IP (tos 0x0, ttl 64, id 42641, offset 0, flags [none], proto UDP (17), length 75) 10.10.86.133.40616 > 130.239.5.114.53: [bad udp cksum 0x8d98 -> 0xd7df!] 10643% [1au] MX? xyz.foobar56711.se. ar: . OPT UDPsize=4096 OK (47) 15:00:32.939437 IP (tos 0x0, ttl 57, id 14256, offset 0, flags [none], proto UDP (17), length 1067) 130.239.5.114.53 > 10.10.86.133.40616: [udp sum ok] 10643 NXDomain*- q: MX? xyz.foobar56711.se. 0/6/1 ...
Pourquoi le résolveur est-il si prudent, et pose-t-il au
serveur faisant autorité une question dont il aurait déjà dû
connaitre la réponse ? Il y a plusieurs raisons mais la principale
est que le RFC originel sur le
DNS, le RFC 1034, est
ambigu. Il ne décrivait pas de manière parfaitement claire ce
qu'il faut faire lorsqu'un nom de domaine est un ENT, un
Empty Non-Terminal, c'est-à-dire un nom de domaine qui n'a
pas d'enregistrements mais qui a des sous-domaines. Certains ont
pensé que cela autorisait à répondre NXDOMAIN
lorsque le nom demandé est un ENT. Ce comportement a été
clairement noté comme incorrect dans les RFC ultérieurs (section
7.16 du RFC 2136 et sections 2.2.2
et 2.2.3 du RFC 4592) mais tout le monde
n'en avait pas forcément tiré les conséquences. Autre RFC qui
contribuait au comportement erroné, le RFC 2308 (dans sa section 5) faisait l'erreur de dire qu'un résolveur ne pouvait
renvoyer un NXDOMAIN
que si la question
portait sur exactement le même nom que celui qui avait été mis en
cache. Notre nouveau RFC 8020 corrige cette erreur : un
résolveur doit également renvoyer NXDOMAIN
si
la question est un sous-domaine d'un domaine inexistant.
La règle qui forme le cœur de ce nouveau RFC tient en une phrase
(section 2) : « si un résolveur reçoit un
NXDOMAIN
, il peut et il devrait mémoriser le
fait que ce nom et tous ceux en dessous
n'existent pas ». Logiquement, les questions ultérieures portant
sur un sous-domaine de ce nom devraient recevoir immédiatement un
NXDOMAIN
, sans déranger les serveurs faisant
autorité. C'est d'ailleurs ce que fait
Unbound, si on active l'option
harden-below-nxdomain
ainsi :
server: harden-below-nxdomain: yes
On voit alors qu'Unbound, face aux deux requêtes successives pour
foobar56711.se
et
xyz.foobar56711.se
, n'écrit qu'une seule fois
aux serveurs de .se
.
(Si cela ne marche pas pour vous, c'est peut-être que votre Unbound
ne valide pas, vérifiez sa configuration DNSSEC, ou que le domaine
est signé avec l'option opt-out.) Unbound suit
donc le bon comportement mais, malheureusement, pas par
défaut. (C'est déjà mieux que BIND, qui a
toujours le mauvais comportement de demander systématiquement aux
serveurs faisant autorité, ce qui annule partiellement l'intérêt
d'avoir un cache.)
Voilà, vous savez maintenant l'essentiel sur le principe du
NXDOMAIN cut. Voyons quelques détails, toujours
en section 2 du RFC. D'abord, il faut noter que la règle n'est pas
absolue : un résolveur, s'il y tient, peut continuer à renvoyer des
réponses positives à une question sur un sous-domaine, même si le
domaine parent n'existe pas, si le cache (la mémoire) du résolveur
contenait des réponses pour ces sous-domaines. En terme
d'implémentation, cela veut dire que le mode préféré est de
supprimer tout le sous-arbre du cache lorsqu'on reçoit un
NXDOMAIN
, mais que ce n'est pas
obligatoire.
D'autre part, rien n'est éternel dans le monde du DNS. Les informations reçues par le résolveur ne sont valables que pendant une période donnée, le TTL. Ainsi, une information positive (ce domaine existe) n'est vraie que jusqu'à expiration du TTL (après, il faut revalider auprès des serveurs faisant autorité). Même chose pour une information négative : la non-existence d'un domaine (et de tout le sous-arbre qui part de ce domaine) est établie pour un TTL donné (qui est celui du champ Minimum du SOA, cf. RFC 2308).
Dernier petit piège, s'il y a une chaîne d'alias menant au nom
de domaine canonique, le NXDOMAIN
s'applique
au dernier nom de la chaîne (RFC 6604), et pas au nom explicitement demandé.
La section 4 de notre RFC détaille les bénéfices attendus du NXDOMAIN cut. Le principal est la diminution de la latence des réponses, et celle de la charge des serveurs faisant autorité. On aura moins de requêtes, donc un bénéfice pour tout l'écosystème. Cela sera encore plus efficace avec la QNAME minimisation du RFC 9156, puisque le résolveur pourra arrêter son traitement dès qu'il y aura un domaine absent.
Cela sera aussi utile dans le cas de certaines attaques par déni de service, notamment les attaques random QNAMEs avec un suffixe un peu long (comme dans le cas de l'attaque dafa888).
Mais tout n'est pas idéal dans cette vallée de larmes. Le
NXDOMAIN cut peut aussi poser des problèmes, ce
qu'examine la section 5. Le principal risque est celui que pose des
serveurs faisant autorité bogués, comme ceux
d'Akamai. Regardons le domaine de
l'Université de Pennsylvanie,
www.upenn.edu
:
% dig A www.upenn.edu www.upenn.edu. 300 IN CNAME www.upenn.edu-dscg.edgesuite.net.
C'est un alias pour un nom Edgesuite
(une marque d'Akamai). Mais les serveurs de
edgesuite.net
sont bogués, ils répondent
NXDOMAIN
pour un ENT (Empty
Non-Terminal, comme
edu-dscg.edgesuite.net
:
% dig @ns1-2.akam.net A edu-dscg.edgesuite.net ... ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 659 ...
La réponse correcte aurait dû être
NODATA
(c'est-à-dire le code
NOERROR
et une section
Answer de taille nulle). Tant qu'Akamai n'aura
pas réparé ses serveurs, des problèmes subsisteront.
Au fait, pourquoi ne pas se servir de
l'enregistrement SOA, qui est renvoyé en cas
de réponse négative, pour trouver un NXDOMAIN
cut situé encore plus haut, et qui sera donc plus
efficace pour limiter les requêtes ultérieures ? L'annexe A du RFC
explique pourquoi c'est une fausse bonne idée. Prenons l'exemple
d'un nom non existant,
anything.which.does.not.exist.gouv.fr
:
% dig AAAA anything.which.does.not.exist.gouv.fr ... ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 35377 ... ;; AUTHORITY SECTION: fr. 5400 IN SOA nsmaster.nic.fr. hostmaster.nic.fr. ( 2224131472 ; serial ...
Le SOA renvoyé indique fr
. Il ne faut pas en
déduire que les noms plus spécifiques n'existent pas
(gouv.fr
existe, mais ce n'est pas une zone
séparée, voilà pourquoi le SOA indiquait son parent fr
).
La section 6 du RFC contient quelques conseils pour les
implémenteurs. Rappelez-vous que les exigences de ce RFC concernent
le comportement extérieur du résolveur, pas la façon dont il est
mis en œuvre. Cette réalisation concrète va donc dépendre de
comment sont représentés les domaines dans la mémoire du
résolveur. La représentation la plus évidente est d'utiliser un
arbre puisque c'est le modèle des noms de
domaine. Mais ce n'est pas obligatoire. Un autre choix pourrait
être celui d'un dictionnaire, plus rapide
(pour un nom de domaine très profond, il y aura moins de lectures
dans la structure de données) mais qui rend certaines opérations
plus difficiles (toutes celles définies par rapport au modèle
d'arbre, et elles sont nombreuses dans le DNS). Et il existe des
implémentations intermédiaires, par exemple avec un arbre augmenté
d'un index. Bref, le programmeur a le
choix. S'il a opté pour un arbre, la façon la plus simple de
respecter les exigences du RFC et, en recevant un
NXDOMAIN
, de supprimer tout sous-arbre qui
partirait du nom ainsi nié.
Un petit mot de sécurité, maintenant qu'on approche de la
fin. Si un résolveur accepte un NXDOMAIN
mensonger (attaque par empoisonnement), les
conséquences risquent d'être sérieuses puisque c'est un sous-arbre
entier qui serait « détruit ». C'est pour cela que le RFC autorise
un résolveur prudent à ne pratiquer le NXDOMAIN
cut que si le NXDOMAIN
a été validé
avec DNSSEC. C'est ce que fait
Unbound, cité plus haut.
Notez que, si on a DNSSEC, une technique encore plus puissante
consiste à synthétiser des réponses NXDOMAIN
en utilisant les enregistrements NSEC. Elle
est décrite dans un
Internet-Draft
actuellement en cours de discussion.
Quels sont les résolveurs qui gèrent aujourd'hui le NXDOMAIN cut ? Outre Unbound, déjà cité, il y a PowerDNS Recursor, mais qui est, curieusement, limité au cas particulier de la racine.
Un peu d'histoire pour finir : la prise de conscience du problème que pose le manque d'agressivité des caches des résolveurs est ancienne. Voici par exemple une partie d'un rapport de Paul Mockapetris à la réunion IETF n° 4 en 1986 :
Mais le projet concret qui a mené à ce RFC est bien plus récent. Il a été lancé (cf. les supports) à la réunion IETF de Yokohama, à peine plus d'un an avant la publication du RFC (on peut voir ici l'histoire des différents brouillons). On voit que l'IETF peut agir vite.
Date de publication du RFC : Octobre 2016
Auteur(s) du RFC : J. Laganier (Luminate Wireless)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF hip
Première rédaction de cet article le 26 novembre 2016
Le protocole HIP n'avait pas à l'origine de mécanisme pour trouver l'identificateur d'une machine distante. Cela avait été fait dans le RFC 5205, qui permettait de trouver l'identificateur dans le DNS. Ce nouveau RFC remplace le RFC 5205.
HIP fait partie de la famille des protocoles qui visent à séparer l'identificateur du localisateur. Les identificateurs HIP se nomment les HI (Host Identifier) et, autrefois, le seul moyen de trouver l'HI d'une autre machine était d'attendre qu'elle vous contacte, ou bien de le configurer manuellement. Avec ce RFC, on peut trouver l'HI, comme une adresse IP, dans le DNS.
Notre RFC crée donc un nouveau type d'enregistrement DNS, nommé logiquement HIP (numéro 55), qui stocke, en échange d'un nom de domaine, le HI, son condensat (résumé) cryptographique - le HIT (Host Identifier Tag) - et les éventuels serveurs de rendez-vous, serveurs qui, dans le protocole HIP, servent d'intermédiaires facultatifs lorsqu'on veut contacter une machine distante (cf. RFC 8004).
Notre RFC permet de trouver l'identificateur à partir du nom mais pas le localisateur ; les serveurs de rendez-vous sont une solution possible pour cela ; une autre est d'utiliser les traditionnels enregistrements A et AAAA du DNS, le localisateur HIP étant une adresse IP.
Les localisateurs peuvent changer fréquemment alors que le DNS n'est pas temps-réel et ne change pas instantanément. Si un hôte HIP veut pouvoir être contacté malgré des changements d'adresse IP rapides, il vaut peut-être mieux qu'il utilise le système de rendez-vous du RFC 8004.
Curieusement (pour moi), le HIT est donc stocké dans les données DNS, alors que celles-ci n'offrent aucune sécurité au point que le RFC exige en section 4.1 de recalculer le HIT qui vient d'être obtenu dans le DNS.
Le tout ressemble donc aux enregistrements IPSECKEY du RFC 4025, ce qui est normal, le HI étant une clé cryptographique publique.
Voici un exemple d'enregistrement HIP tel qu'il apparaitrait dans un fichier de zone (sections 5 et 6 de notre RFC). On y trouve l'algorithme cryptographique utilisé (2 = RSA), le HIT (en hexadécimal), le HI (encodé en Base64) et les éventuels serveurs de rendez-vous (ici, deux, indiqués à la fin) :
www.example.com. IN HIP ( 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p 9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQ b1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2.example.com. )
Par contre, je n'ai pas réussi à trouver encore ce genre d'enregistrement dans la nature.
L'ensemble du RFC est assez court, ce mécanisme d'annuaire qu'est le DNS étant simple et bien connu.
Quels sont les changements depuis le premier RFC, le RFC 5205 ? Évidement le passage sur le chemin des normes, faisant de HIP une norme complète. Mais aussi l'ajout de l'algorithme de cryptographie asymétrique ECDSA, et plusieurs clarifications du RFC original sur le format des enregistrements DNS, aussi bien leur format sur le réseau que dans le fichier de zone.
Date de publication du RFC : Octobre 2016
Auteur(s) du RFC : J. Laganier (Luminate Wireless,), L. Eggert (NetApp)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF hip
Première rédaction de cet article le 26 novembre 2016
HIP, par défaut, nécessite que l'initiateur d'une association connaisse le localisateur, l'adresse IP du répondeur. Si celui-ci bouge souvent, et qu'il n'est donc pas pratique de mettre cette adresse dans le DNS, une solution est d'utiliser le mécanisme de rendez-vous, décrit par ce RFC, où l'initiateur contacte un serveur de rendez-vous qui relaie vers le répondeur.
Le schéma est clairement expliqué dans la section 3 du RFC. En fonctionnement habituel de HIP, l'initiateur trouve l'identificateur et le localisateur du répondeur (typiquement, dans le DNS, cf. RFC 8005), puis le contacte directement. Si le localisateur n'est pas connu (ce qui est fréquent si le répondeur est un engin mobile, changeant souvent d'adresse IP), l'initiateur envoie le premier paquet (I1) au serveur de rendez-vous, qui le relaie au répondeur. Les autres paquets (R1, I2 et R2) seront transmis directement entre les deux machines HIP. Le mécanisme est détaillé dans la section 3.3 (il faut notamment procéder avec soin à la réécriture des adresses IP, en raison entre autre du RFC 2827).
Et comment l'initiateur trouve-t-il le serveur de rendez-vous ? En général dans le DNS, via les enregistrements de type HIP. Et comment le répondeur avait-il fait connaitre au serveur de rendez-vous son adresse IP ? Via le protocole d'enregistrement du RFC 8003, comme l'explique la section 4.
Comme toute indirection, le système de rendez-vous ouvre des problèmes de sécurité amusants. Si l'initiateur connaissait déjà l'identificateur du répondeur (donc sa clé publiqué), pas de problème, le passage par le serveur de rendez-vous ne diminue pas la sécurité. Si ce n'est pas le cas, alors, il n'y a rien à faire, l'initiateur n'a aucun moyen de vérifier l'identité du répondeur (section 5 du RFC).
Aucun changement depuis la première spécification, le RFC 5204, juste l'alignement avec la nouvelle version de HIP, celle du RFC 7401, désormais norme complète (et pas juste « expérimentale »).
Date de publication du RFC : Octobre 2016
Auteur(s) du RFC : J. Laganier (Luminate Wireless), L. Eggert (NetApp)
Chemin des normes
Première rédaction de cet article le 26 novembre 2016
Le protocole HIP, décrit dans le RFC 7401 est très bien adapté au cas où l'adresse IP (le localisateur) change après l'établissement d'une association. Mais cela laisse ouvert le grand problème de la connexion initiale. Comment trouver une machine HIP ? Par le mécanisme de rendez-vous du RFC 8004 ? C'est certainement une bonne solution mais, alors, comment les machines HIP sont-elles connues du serveur de rendez-vous ? C'est là que notre RFC rentre en jeu pour normaliser un mécanisme d'enregistrement auprès d'un service. C'est un mécanisme générique, qui peut servir à d'autres choses que le rendez-vous, d'ailleurs. (Il était à l'origine spécifié dans le RFC 5203, que notre RFC remplace.)
Le mécanisme est très simple et le RFC court. On réutilise
simplement les établissements d'associations de HIP, avec de nouveaux
types de paramètres, notamment REG_INFO
(pour
l'hôte qui accepte d'être registrar, c'est-à-dire
d'enregistrer) et REG_REQUEST
(pour celui qui
demande un enregistrement). Le mécanisme exact est détaillé dans la
section 3 et les nouveaux
paramètres dans la section 4.
HIP authentifiant les deux parties bien plus solidement que IP seul, le registrar (terme d'ailleurs mal choisi, on risque de confondre avec les bureaux d'enregistrement de noms de domaine) peut alors décider s'il accepte l'enregistrement ou pas (sections 3.3 et 6).
Le rendez-vous, normalisé dans le RFC 8004 est donc une simple application de notre RFC mais d'autres pourront apparaître à l'avenir (comme celle du RFC 5770).
Quels sont les changements depuis le premier RFC, le RFC 5203 ? La principale est qu'HIP, qui avait le statut « Expérimental » est désormais sur le chemin des Normes et que les références de notre RFC ont donc changé (nouveau protocole HIP en RFC 7401). Mais ce nouveau RFC ajoute aussi la possibilité d'authentifier le registrar par certificat (RFC 8002), ainsi qu'un nouveau type d'erreur, le numéro 2, « ressources insuffisantes chez le registrar ».
Question mise en œuvre, je n'ai pas vérifié mais, normalement, HIP for Linux et OpenHIP devraient s'adapter aux nouveaux RFC HIP.
RFC des différentes séries : 0 1000 2000 3000 4000 5000 6000 7000 8000 9000