Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Mon livre « Cyberstructure »

Ève

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.


RFC 9567: DNS Error Reporting

Date de publication du RFC : Avril 2024
Auteur(s) du RFC : R. Arends (ICANN), M. Larson (ICANN)
Chemin des normes
Première rédaction de cet article le 27 avril 2024


Lorsqu'un résolveur DNS détecte un problème avec une zone, l'empêchant de résoudre les noms dans cette zone, il n'avait pas de moyen simple et automatique de prévenir les gérants des serveurs faisant autorité pour cette zone. Leur envoyer un message en utilisant l'information dans l'enregistrement SOA ou les adresses classiques du RFC 2142 ? Mais, justement, si la zone ne marche pas, le courrier ne partira pas forcément. Ce nouveau RFC propose un nouveau système. Les serveurs faisant autorité annoncent un domaine (qui marche, espérons-le), qui acceptera des requêtes DNS spéciales signalant le problème.

Cela dépend évidemment du problème pratique qui se pose. Si la zone n'a aucun serveur faisant autorité qui marche, il n'y a évidemment rien à faire. Mais s'ils marchent, tout en servant des données problématiques (par exemple des signatures DNSSEC expirées), alors, le résolveur pourra agir. Les serveurs faisant autorité mettent dans leurs réponses une option EDNS qui indique le domaine qui recevra les rapports (cela doit être un autre domaine, qui n'a pas de problème), le résolveur fera alors une requête DNS se terminant par le nom du domaine de signalement, et encodant le problème. L'agent, le domaine de signalement, pourra alors récolter ces requêtes et savoir qu'il y a un problème. Cela ne traite pas tous les cas (il faudra toujours utiliser RDAP ou whois pour récolter des informations sur les contacts du domaine erroné, puis leur écrire depuis un autre réseau) mais c'est simple, léger et automatisable. Les gérants de domaine sérieux, qui prennent au sérieux les signalements de problèmes techniques (soit 0,00001 % des domaines) pourront alors agir. (Note si vous gérez un résolveur et que vous constatez un problème avec un domaine et que les contacts ne répondent pas : un message méchant sur Twitter est souvent plus efficace.)

Donc, les détails techniques : le domaine qui veut recevoir les éventuels signalements va devoir configurer ses serveurs faisant autorité pour renvoyer une option EDNS, de numéro 18 (section 5 du RFC), indiquant l'agent, c'est-à-dire le domaine qui va recevoir les signalements (il faut évidemment veiller à ce qu'il n'ait pas de point de défaillance commun avec le domaine surveillé). Notez que cette option est systématiquement envoyée, le client (le résolveur) n'a pas à dire quoi que ce soit (la question avait fait l'objet d'un sérieux débat à l'IETF).

En cas de problème, notamment DNSSEC, le résolveur qui a noté le problème va alors construire un nom de domaine formé, successivement (section 6.1.1) par :

  • Le composant _er,
  • Le type de données qui posait problème (adresse IP, enregistrement de service, etc),
  • Le nom de domaine qui était initialement demandé par le résolveur,
  • L'erreur étendue (EDE, RFC 8914),
  • Le composant _er (oui, encore),
  • Le nom de domaine de l'agent.

Par exemple, si le domaine dyn.bortzmeyer.fr annonce comme agent report.dyn.sources.org, et qu'un résolveur découvre des signatures DNSSEC expirées (EDE 7) en cherchant à résoudre hello.dyn.bortzmeyer.fr / TXT (TXT a la valeur 16), la requête de signalement du résolveur sera _er.16.hello.dyn.bortzmeyer.fr.7._er.report.dyn.sources.org (ouf). Le type demandé est TXT. Lorsque cette requête arrivera au serveur faisant autorité pour report.dyn.sources.org, il pourra enregistrer qu'il y a eu un problème, et mettre cette information à la disposition de son administrateur système.

Ce serveur faisant autorité est censé répondre au signalement avec une réponse de type TXT comme ici :


% dig _er.16.hello.dyn.bortzmeyer.fr.7._er.report.dyn.sources.org TXT
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12032
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
…
;; ANSWER SECTION:
_er.16.hello.dyn.bortzmeyer.fr.7._er.report.dyn.sources.org. 30	IN TXT "Thanks for the report of error 7 on hello.dyn.bortzmeyer.fr"
…

  

L'agent peut ensuite être interrogé, par des méthodes propres à la mise en œuvre utilisée :

% echo report | socat - UNIX-CONNECT:/home/drink/drink.sock
REPORT state:
hello.dyn.bortzmeyer.fr, 7
ip.dyn.bortzmeyer.fr, 7
  

Ici, on voit que deux domaines ont été signalés comme ayant des signatures expirées (rassurez-vous, c'était juste des tests). Le nombre de signalements n'est pas indiqué, ni la source des signalements (travail futur).

Quelques petits points de sécurité à garder en tête (section 9 du RFC) :

  • Le fait de signaler va, par définition, donner au serveur faisant autorité des informations sur le résolveur (par exemple, un résolveur menteur qui signalerait les blocages informerait sur sa politique, ce que les censeurs ne font en général pas).
  • Il en donnera aussi aux serveurs des zones parentes du domaine agent, et il est donc très recommandé de minimiser le nom (RFC 9156).
  • Il n'y a pas spécialement d'authentification donc tous ces rapports doivent être traités avec prudence. Un méchant peut facilement fabriquer de faux rapports, de toute façon. Ils doivent donc toujours être vérifiés.

Cette technique a été mise en œuvre dans Drink lors d'un hackathon IETF. Drink peut à la fois signaler un domaine agent, et être serveur pour un domaine agent.

Un exemple de signalisation EDNS de cette option, vu la version de développement de Wireshark (merci à Alexis La Goutte) : wireshark-domain-report.jpeg


Téléchargez le RFC 9567


L'article seul

RFC 9562: Universally Unique IDentifiers (UUIDs)

Date de publication du RFC : Mai 2024
Auteur(s) du RFC : K. Davis (Cisco Systems), B. Peabody (Uncloud), P. Leach (University of Washington)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF uuidrev
Première rédaction de cet article le 12 mai 2024


Ce RFC normalise les UUID, une famille d'identificateurs uniques, obtenus sans registre central. Il remplace l'ancienne norme, le RFC 4122, avec pas mal de nouveautés et un RFC complètement refait.

Les UUID, également connus autrefois sous le nom de GUID (Globally Unique IDentifiers), sont issus à l'origine du système des Apollo, adopté ensuite dans la plate-forme DCE. Les UUID ont une taille fixe, 128 bits, et sont obtenus localement, par exemple à partir d'un autre identificateur unique comme un nom de domaine, ou bien en tirant au hasard, la grande taille de leur espace de nommage faisant que les collisions sont très improbables (section 6.7 du RFC). Ce RFC reprend la spécification (bien oubliée aujourd'hui) de DCE de l'Open Group (ex-OSF) et ajoute la définition d'un espace de noms pour des URN (sections 4 et 7). (Il existe aussi une norme ITU sur les UUID et un registre des UUID, pour ceux qui y tiennent.)

Les UUID peuvent donc convenir pour identifier une entité sur le réseau, par exemple une machine mais aussi, vu leur nombre, comme identificateur unique pour des transactions (ce qui était un de leurs usages dans DCE). En revanche, ils ne sont pas résolvables, contrairement aux noms de domaine. Mais ils sont présents dans beaucoup de logiciels (Windows, par exemple, les utilise intensivement). On les utilise comme clé primaire dans les bases de données, comme identificateur de transaction, comme nom de machine, etc.

Les UUID peuvent se représenter sous forme d'un nombre binaire de 128 bits (la section 5 du RFC décrit les différents champs qui peuvent apparaitre) ou bien sous forme texte. Sur Unix, on peut fabriquer un UUID avec la commande uuidgen, qui affiche la représentation texte standard que normalise notre RFC (section 4) :

% uuidgen 
317e8ed3-1428-4ef1-9dce-505ffbcba11a

% uuidgen
ec8638fd-c93d-4c6f-9826-f3c71436443a

Sur Linux, vous pouvez aussi simplement faire cat /proc/sys/kernel/random/uuid. Sur une machine FreeBSD, un UUID de la machine est automatiquement généré (par le script /etc/rc.d/hostid) et stocké dans le fichier /etc/hostid.

Pour l'affichage sous forme d'URN (RFC 8141), on ajoute juste l'espace uuid par exemple urn:uuid:ec8638fd-c93d-4c6f-9826-f3c71436443a. Il a été ajouté au registre IANA des espaces de noms des URN.

La section 4 du RFC détaille le format de l'UUID. En dépit des apparences, l'UUID n'est pas plat, il a une structure, mais il est très déconseillé aux applications de l'interpréter (section 6.12). Un des champs les plus importants est le champ Version (qui devrait plutôt s'appeler Type) car il existe plusieurs types d'UUID :

  • UUID version 1, fondé sur le temps (notez tout de suite que les versions 6 et 7 sont recommandées à sa place).
  • UUID version 2, réservé, non utilisé.
  • UUID version 3, fondé sur un espace de noms (par exemple le DNS) et un nom qui est condensé en MD5 (la version 5 est préférable).
  • UUID version 4, aléatoire.
  • UUID version 5, fondé sur un espace de noms et un nom qui est condensé en SHA-1.
  • UUID version 6, fondé sur le temps, avec les mêmes champs que la version 1, mais avec un format différent.
  • UUID version 7, fondé sur le temps, avec une autre définition, et des champs différents.
  • UUID version 8, pour les usages expérimentaux, si vous voulez jouer localement avec un nouveau mécanisme de génération.

Ces différents types / versions figurent dans un registre IANA. Ce registre ne peut être modifié que par une action de normalisation (cf. RFC 8126).

uuidgen, vu plus haut, peut générer des UUID de version 1 option -t, de version 3 (-m), de version 4 (c'est son comportement par défaut, mais on peut utiliser l'option -r si on veut être explicite) ou de version 5 (-s). Ici, on voit les UUID fondés sur une estampille temporelle (version 1) augmenter petit à petit :

% uuidgen -t
42ff1626-0fc7-11ef-8162-49e9505fb2f3

% uuidgen -t
4361fae8-0fc7-11ef-8162-49e9505fb2f3

% uuidgen -t
45381d02-0fc7-11ef-8162-49e9505fb2f3
  

Ici, dans le cas d'un UUID fondé sur un nom (version 3), l'UUID est stable (essayez chez vous, vous devriez obtenir le même résultat que moi), une propriété importante des UUID de version 3 et 5 :

 
% uuidgen -m -n @dns -N foobar.example
8796bf1a-793c-3c44-9ec5-a572635cd3d4

% uuidgen -m -n @dns -N foobar.example
8796bf1a-793c-3c44-9ec5-a572635cd3d4
  

Les espaces de noms sont enregistrés dans un registre IANA, d'autres peuvent être ajoutés si on écrit une spécification (cf. RFC 8126). Notez que chaque espace a son UUID (6ba7b810-9dad-11d1-80b4-00c04fd430c8 pour l'espace DNS).

Les UUID de version 6 et 7, nouveautés de ce RFC 9562, ne sont pas mis en œuvre par uuidgen, ni d'ailleurs par beaucoup d'autres programmes.

Les sections 6.1 et 6.2, elles, décrivent le processus de génération d'un UUID à base temporelle. Idéalement, il faut utiliser une graine enregistrée sur le disque (pour éviter de générer des UUID identiques) ainsi que l'instant de la génération. Mais lire sur le disque prend du temps (alors qu'on peut vouloir générer des UUID rapidement, par exemple pour identifier des transactions) et l'horloge de la machine n'a pas toujours une résolution suffisante pour éviter de lire deux fois de suite le même instant. Ces sections contiennent donc également des avis sur la génération fiable d'UUID, par exemple en gardant en mémoire le nombre d'UUID générés, pour les ajouter à l'heure.

La section 8, consacrée à la sécurité, rappelle qu'un UUID ne doit pas être utilisé comme capacité (car il est trop facile à deviner) et qu'il ne faut pas demander à un humain de comparer deux UUID (ils se ressemblent trop pour un œil humain).

Il est évidemment recommandé d'utiliser les UUID de version 5 plutôt que de version 3 (RFC 6151) mais SHA-1 a aussi ses problèmes (RFC 6194) et, de toute façon, pour la plupart des utilisations des UUID, les faiblesses cryptographiques de MD5 et de SHA-1 ne sont pas gênantes.

La section 2.1 du RFC détaille les motivations pour la mise à jour du RFC 4122 et quels sont les changements effectués. Certaines utilisations des UUID ont remis en cause des suppositions originales. Ainsi, les UUID sont souvent utilisés dans un contexte réparti, où leur capacité à être uniques sans registre central est très utile. Mais quelques points manquaient au RFC 4122 :

  • UUID version 4 (aléatoire) avait une mauvaise localité : deux UUID créés l'un après l'autre en un temps très court n'ont aucun rapport, ce qui est gênant pour certains usages (par exemple comme étiquette dans un B-tree).
  • UUID version 1 (fondé entre autre sur le temps écoulé depuis l'epoch) utilise un incrément peu pratique (cent nanosecondes).
  • Certaines méthodes fabrication des UUID posaient des gros problèmes de vie privée, par exemple l'utilisation des adresses MAC dans UUID version 1, désormais déconseillée.
  • Le RFC 4122 descendait trop dans les détails de mise en œuvre.
  • Le RFC 4122 ne séparait pas les exigences pour la génération d'UUID et celles pour leur stockage.

Seize mises en œuvre des UUID ont été étudiées pour préparer le nouveau RFC (vous avez la liste dans la section 2.1), menant aux constations suivantes :

  • Beaucoup d'errata dans le RFC 4122.
  • Spécification du format qui mélangeait trop les diverses versions d'UUID.
  • Absence de vecteurs de test (ils figurent désormais dans l'annexe A).

En Python, il existe un module UUID qui offre des fonctions de génération d'UUID de différentes versions (mais pas les plus récentes) :

import uuid

myuuid = uuid.uuid1() # Version 1, Time-based UUID
heruuid = uuid.uuid3(uuid.NAMESPACE_DNS, "foo.bar.example") # Version
            # 3, Name-based ("hash-based") UUID, a name hashed by MD5
otheruuid = uuid.uuid4() # Version 4, Random-based UUID
yetanotheruuid = uuid.uuid5(uuid.NAMESPACE_DNS,
                            "www.example.org")
                      # Version 5, a name hashed by SHA1
if (myuuid == otheruuid or \
    myuuid == heruuid or \
    myuuid == yetanotheruuid or \
    otheruuid == yetanotheruuid):
    raise Exception("They are equal, PANIC!")

print(myuuid)
print(heruuid) # Will always be the same
print(otheruuid)
print(yetanotheruuid) # Will always be the same

Et comme le dit la documentation, Note that uuid1() may compromise privacy since it creates a UUID containing the computer’s network address. (méthode de génération des UUID version 1 qui est désormais déconseillée).

Le SGBD PostgreSQL inclut un type UUID. Cela évite de stocker les UUID sous leur forme texte, ce qui est techniquement absurde et consomme 288 bits au lieu de 128 (section 6.13 du RFC).

essais=> CREATE TABLE Transactions (id uuid, value INT);
CREATE TABLE
essais=> INSERT INTO Transactions VALUES 
           ('74738ff5-5367-5958-9aee-98fffdcd1876', 42);
INSERT 0 1
essais=> INSERT INTO Transactions VALUES 
            ('88e6441b-5f5c-436b-8066-80dca8222abf', 6);
INSERT 0 1
essais=> INSERT INTO Transactions VALUES ('Pas correct', 3);
ERROR:  invalid input syntax for type uuid: "Pas correct"
LINE 1: INSERT INTO Transactions VALUES ('Pas correct', 3);
                                         ^
-- PostgreSQL peut seulement générer la version 4, les aléatoires
essais=> INSERT INTO Transactions VALUES (gen_random_uuid () , 0);
INSERT 0 1
essais=> SELECT * FROM Transactions;
                  id                  | value 
--------------------------------------+-------
 74738ff5-5367-5958-9aee-98fffdcd1876 |    42
 88e6441b-5f5c-436b-8066-80dca8222abf |     6
 41648aef-b123-496e-8a4c-52e573d17b6a |     0
(3 rows)

Attention, le RFC (section 6.13) déconseille l'utilisation des UUID fondés sur un nom pour servir de clé primaire dans la base de données, sauf si on est absolument certain (mais c'est rare) que les noms ne changeront pas.

Il existe plusieurs exemples d'utilisation des UUID. Par exemple, Linux s'en sert pour identifier les disques attachés à la machine, de préférence à l'ancien système où l'ajout d'un nouveau disque pouvait changer l'ordre des numéros sur le bus et empêcher le système de trouver un disque. Un /etc/fstab typique sur Linux contient donc des :

UUID=da8285a0-3a70-413d-baed-a1f48d7bf7b2       /home   ext3 defaults ...

plutôt que les anciens :

/dev/sda3       /home           ext3    defaults  

car sda3 n'est pas un identificateur stable. L'UUID, lui, est dans le système de fichiers et ne changera pas avec les changements sur le bus. On peut l'afficher avec dumpe2fs :


# dumpe2fs -h /dev/sda3
...
Filesystem UUID:          da8285a0-3a70-413d-baed-a1f48d7bf7b2
...

Un exemple d'utilisation de la nouvelle version 7 est décrit dans l'excellent article « Goodbye integers. Hello UUIDv7! ». Une mise en œuvre de cette version 7 apparait dans « UUIDv7 in 20 languages ».

La section 6 décrit les bonnes pratiques de génération d'un UUID. Ainsi :

  • Pour tous les UUID fondés sur le temps, si vous avez besoin d'unicité des UUID, attention aux cas où l'horloge peut changer, en raison d'une modification manuelle, d'une seconde intercalaire ou d'un ajustement fait par NTP.
  • Si vous générez des UUID de manière répartie, sur plusieurs machines, ce qui est un des points forts des UUID, mais que vous voulez quand même de l'unicité, vous pouvez préfixer vos UUID avec un identificateur de la machine, ou bien compter sur un registre central (mais, en général, c'est justement ce qu'on veut éviter avec des UUID).
  • UUID rend les collisions (génération de deux UUID identiques) peu probables mais pas impossibles. Lorsque vous concevez une application, demandez-vous si une collision est juste un petit inconvénient ou bien si elle est inacceptable (le RFC cite l'exemple fictif d'un système de contrôle aérien où chaque avion serait identifié par un UUID ; l'attribution du même UUID à deux avions différents serait clairement catastrophique). Dans ce cas, vous devez trouver un moyen de ne pas avoir de collision.
  • Si vous voulez des UUID qui ne soient pas prévisibles par un observateur extérieur, utilisez la version 4 et assurez vous que votre générateur de nombres aléatoires suit bien les prescriptions des RFC 4086 et RFC 8937, ainsi que « Random Number Generator Recommendations for Applications ».
  • Les versions 6 et 7 (nouveautés de ce RFC 9562) sont prévues pour que les UUID soient triables chronologiquement, sans avoir besoin d'analyse, uniquement en triant la version binaire. Un des avantages est que des UUID générés à des moments proches ont des valeurs proches, ce qui peut être utile dans certaines applications.

Téléchargez le RFC 9562


L'article seul

RFC 9557: Date and Time on the Internet: Timestamps with Additional Information

Date de publication du RFC : Avril 2024
Auteur(s) du RFC : U. Sharma (Igalia, S.L.), C. Bormann (Universität Bremen TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sedate
Première rédaction de cet article le 29 avril 2024


Ce RFC modifie légèrement le format des estampilles temporelles du RFC 3339, changeant à la marge une définition et, surtout, permettant d'y attacher des informations supplémentaires.

Le RFC 3339 décrit un des formats d'estampilles temporelles possibles sur l'Internet (car, malheureusement, toutes les normes Internet ne l'utilisent pas). Un exemple, sur Unix :

% date --rfc-3339=seconds 
2024-04-29 06:22:33+00:00
  

Le format du RFC 3339 est du type « YYYY-MM-DD HH:MM:SS » avec, à la fin, ajout d'une information sur le décalage avec le temps de référence (on verra que notre nouveau RFC 9557 change un peu cette dernière information). Un format gros boutien, donc, qui permet notamment de trier les dates-et-heures uniquement en suivant l'ordre lexicographique des caractères.

Mais certaines applications voudraient en savoir plus, et ajouter à ce format des détails. D'ailleurs, c'est déjà fait dans des solutions non normalisées, comme le format de Java, très populaire, qui permet des estampilles comme 2011-12-03T10:15:30+01:00[Europe/Paris] (fuseau horaire, ajouté après la date au format du RFC 3339 ; lisez le RFC 6557 pour en savoir davantage sur les fuseaux horaires).

Ce nouveau RFC prévoit donc une extension optionnelle (les dates-et-heures qui suivaient le format de l'ancien RFC restent parfaitement valdies), compatible avec celle de Java, et généraliste (on pourra indiquer autre chose que le fuseau horaire). Ce format étendu est baptisé IXDTF pour Internet Extended Date/Time Format. En revanche, le nouveau format ne gère pas des cas compliqués (la gestion du temps en informatique est toujours compliquée) comme les dates futures lorque la définition du fuseau horaire changera, par exemple en supprimant l'heure d'été, ou des échelles de temps différentes, comme TAI (le format IXDTF ne marche que pour l'échelle d'UTC).

Donc, concrètement, notre RFC commence par changer un peu la définition du décalage à la fin des estampilles. Dans le RFC 3339, il y avait trois cas subtilement différents pour une estampille indiquant l'absence de décalage :

  • Une lettre Z indiquait qu'on utilisait UTC comme référence,
  • Un +00:00 indiquait qu'on utilisait UTC comme référence (identique au cas précédent),
  • Un -00:00 indiquait qu'on n'avait aucune idée sur la référence (typiquement parce qu'on voudrait connaitre l'heure locale mais qu'on ne la connait pas).

(Au passage, si vous ne connaissiez pas ces trois cas et leurs différences, ne vous inquiétez pas, vous n'étiez pas seul·e.) Notre RFC change cela (section 2) en décidant que Z est désormais synonyme de -00:00 plutôt que de +00:00, un changement qui aura sans doute peu d'importance en pratique.

L'autre nouveauté de ce RFC 9557 est plus marquante, c'est le format étendu IXDTF (section 3). Il consiste à ajouter à la fin de l'estampille une série (facultative) de couples {clé, valeur}, entre crochets. Si la clé est absente, c'est que la valeur est un fuseau horaire, suivant la syntaxe de la base TZ. Voici un exemple :

2022-07-08T12:14:37+02:00[Europe/Paris][u-ca=hebrew]
  

Cet exemple indique que le fuseau horaire était celui de Paris (cf. le format de ces noms de fuseaux) et que le calendrier préféré (clé u-ca), pour afficher cette date, serait le calendrier hébreu (ce qui indiquera 9 Tammouz 5782, sauf erreur).

Un point d'exclamation avant la clé indiquerait que la clé doit être comprise par le lecteur, sinon, il faut qu'il ignore l'estampille temporelle (la plupart du temps, l'application peut ignorer les clés et leurs valeurs). Un trait bas indique une clé expérimentale, non officiellement enregistrée.

Car notre RFC crée aussi un registre des clés. Pour l'instant, il ne compte qu'une seule clé, u-ca, pour indiquer le calendrier préféré. Pour enregistrer une nouvelle clé, il faut une spécification écrite (cf. RFC 8126), mais il y a aussi des enregistrements temporaires, plus légers.

Et on termine par un petit mot sur la sécurité (section 7). Le RFC rappelle que plus on donne d'informations, plus on risque d'en donner trop. Ainsi, l'indication du calendrier préféré peut indiquer une origine ethnique ou religieuse, qui peut être considérée comme privée, surtout s'il y a des risques d'attaques racistes.


Téléchargez le RFC 9557


L'article seul

RFC 9547: Report from the IAB Workshop on Environmental Impact of Internet Applications and Systems, 2022

Date de publication du RFC : Février 2024
Auteur(s) du RFC : J. Arkko, C. S. Perkins, S. Krishnan
Pour information
Première rédaction de cet article le 24 avril 2024


La question de l'empreinte environnementale du numérique suscite beaucoup de débats. L'IAB avait organisé en décembre 2022 un atelier sur le cas de l'empreinte environnementale de l'Internet dont ce RFC est le compte-rendu (tardif, oui, je sais, mais mon propre article de résumé du RFC est aussi en retard).

Le sujet est très complexe, relativement nouveau, et surtout noyé sous beaucoup d'approximations, voire de franches bêtises (l'ADEME s'en est fait une spécialité, avec des chiffres tirés du chapeau et à la méthodologie inconnue). Il y a donc du travail sur la planche. L'IAB commence par estimer que l'Internet a certes une empreinte environnementale mais peut aussi servir à diminuer l'empreinte environnementale globale, ce qui n'est franchement pas étayé (le RFC cite l'exemple de réunions physiques remplacées par des réunions en ligne, sans citer de calcul détaillé qui permettrait de voir s'il y a vraiment un gain, et en oubliant que de toute façon une réunion en ligne ne rend pas les mêmes services). Mais l'IAB note aussi que l'Internet a des effets indirects, et pas forcément positifs : il cite l'exemple de l'augmentation de la consommation de biens matériels que produit le commerce en ligne.

Clairement, l'Internet n'est pas virtuel, contrairement à ce que prétend le marketing qui abuse de termes comme cloud pour faire croire que le numérique est immatériel. A contrario, l'Internet dépend de machines, de l'électricité (et des humains qui font fonctionner ces machines). Que peut-on faire pour diminuer l'empreinte environnementale de l'Internet ? (Sans pour autant suivre les conseils débiles de l'ADEME, comme de supprimer ses messages.)

Comme tous les ateliers de l'IAB, celui-ci a fonctionné en demandant aux participants des position papers expliquant leur point de vue. Ne participent à l'atelier que des gens ayant écrit un de ces articles, ce qui garantit que tout le monde a dû travailler le sujet. Ces articles sont disponibles en ligne, plus exactement à cet endroit. (Tous les documents liés à cet atelier sont également disponibles.) Parmi les papiers acceptés :

La section 2 du RFC détaille les sujets qui étaient dans le programme de l'atelier :

  • Les impacts environnementaux directs de l'Internet (consommation électrique des machines, des routeurs aux serveurs en passant par les machines terminales, refroidissement, fabrication des machines, et leur traitement final, souvent oublié dans les études),
  • Les impacts indirects de l'Internet, provenant des effets qu'il a sur la société,
  • Les métriques (souvent très maltraitées dans les artcles parlant de l'empreinte environnementale du numérique, par exemple en confondant énergie et puissance), un sujet crucial puisqu'on ne peut pas améliorer ce qu'on ne peut pas mesurer,
  • Les questions non techniques, comme les enjeux financiers ou comme la régulation,
  • Les questions techniques où l'IETF pourrait travailler pour améliorer les choses (par exemple, mais non cité par le RFC, annoncer dans les réponses HTTP le coût environnemental de la requête),
  • Mais aussi les questions liées aux usages et aux utilisateurices, qui sont souvent sollicités, mais en général de manière culpabilisatrice (« regardez les vidéos en basse définition et pas de porno »).

Ah et, si vous vous le demandez, l'atelier a été entièrement en ligne (section 2.1 du RFC).

La première des quatre sessions de l'atelier essayait d'aborder le problème de manière générale. Le problème du réchauffement climatique est évidemment bien plus vaste que l'Internet seul et n'a pas de solution simple et unique. Et les solutions ne sont pas toutes techniques, donc il y a des limites à ce que l'IETF peut faire (ce qui ne veut pas dire qu'il ne faut rien faire !). Même la publicité est mentionnée dans cette section du RFC, avec un très prudent « davantage d'études sont nécessaires » (opinion personnelle : son attitude au sujet de la publicité est un bon moyen de savoir si votre interlocuteur veut sérieusement lutter contre le réchauffement climatique, ou bien s'il veut juste faire des discours).

Ensuite, deuxième session sur les mesures et la récolte des faits. Par exemple, où sont les gros postes de consommation électrique dans le numérique ? Les serveurs ? Les routeurs ? Les terminaux ? C'est d'autant plus important que le RFC note la quantité de fausses informations qui circulent (citant par exemple un article qui confondait MB/s et Mb/s, soit un facteur 8 de différence). De même, contrairement à ce qui est encore souvent dit, la session a mis en évidence le fait que la consommation électrique n'est pas du tout proportionnelle au trafic. Des phrases comme « envoyer un courrier dégage autant de dioxyde de carbone qu'un vol Paris-Quelquepart » n'ont donc aucun sens. (Un des papiers acceptés, « Towards a power-proportional Internet » expliquait pourquoi il fallait changer cela et comment le faire.) Par contre, les usages impactent la consommation car ils peuvent nécessiter des mises à jour du réseau.

La troisième session regardait du côté des pistes d'amélioration, plus précisement de celles sur lesquelles l'IETF pouvait agir. Le premier point est celui des mesures (insuffisantes et parfois contradictoires). Le deuxième point concernait l'influence de phénomènes comme la gigue (RFC 4689) ou l'élongation du trajet (RFC 7980) sur la consommation énergétique (si on réduit ces phénomènes grâce à de meilleurs protocoles, est-ce qu'on diminue la consommation ?). Parmi les autres optimisations possibles, le choix de meilleurs formats, plus optimisés (CBORRFC 8949 - est cité). Notez qu'un des articles acceptés pour l'atelier faisait le point sur toutes les activités de l'IETF liées à l'énergie, draft-eckert-ietf-and-energy-overview.

Et la quatrième et dernière session portait sur les étapes suivantes du travail ; en résumé, il y a du travail et, même si l'IETF ne peut pas tout, elle doit en prendre sa part. Il faut toujours garder en tête que le but n'est pas de réduire l'empreinte environnementale de l'Internet mais de réduire celle de l'ensemble de la société. Éteindre l'Internet diminuerait certainement son empreinte environnementale mais pourrait avoir des effets négatifs sur d'autres secteurs, comme les transports. Pour améliorer l'Internet sans le supprimer, plusieurs axes ont été mis en avant :

  • Mesurer, toujours mesurer, on manque d'informations,
  • Pouvoir diminuer la consommation quand il n'y a pas de travail en cours, voire hiberner, serait utile (comme dans beaucoup de cas, le problème est complexe : un routeur ne peut pas hiberner car il ne sait pas quand arrivera le prochain paquet, et il ne peut pas se réveiller instantanément),
  • Adopter de meilleurs formats de données. Comme le disait un des articles présentés, « CBOR est plus vert ».

Et les actions concrètes (section 2.4.3) ?

  • Faut-il imposer dans chaque RFC une section Environmental considerations comme il y a déjà une Security considerations obligatoire ? (La question s'était déjà posée pour une éventuelle Human rights considerations.) Pour l'instant, ce n'est pas prévu notamment parce que, dans l'état actuel des choses, la plupart des auteurs de RFC n'ont pas de connaissances suffisamment approfondies ur le sujet pour écrire une section utile.
  • Plusieurs groupes à l'IETF sont déjà au travail sur ce sujet environnemental : NMRG travaille sur les métriques (et a, par exemple, un Internet-Draft « Challenges and Opportunities in Management for Green Networking »), même chose à OPSAWG et le nouveau groupe TVR (Time-Variant Routing), comme il travaille sur le routage intermittent, a également un rôle, par exemple lorsqu'on coupe des liens pour économiser de l'énergie. Là, on est pile dans le rôle de l'IETF.
  • Curieusement, le RFC parle aussi d'éviter les NFT, qui ne sont pourtant plus guère à la mode (et n'ont jamais suscité beaucoup d'intérêt à l'IETF, de toute façon). En outre, la plupart d'entre eux tournent sur Ethereum, qui n'utilise plus la preuve de travail et n'est donc pas crucial du point de vue environnemental.

Si vous voulez participer au nouveau programme de l'IAB E-IMPACT, tout est là.

Puisqu'on parlait de la section sur la sécurité qui est obligatoire dans les RFC, notre RFC en a une, qui rappelle que :

  • Tout mécanisme par lequel on donne des pénalités (financières ou bien en terme de diminution des ressources allouées) en fonction de métriques d'empreinte environnementale doit se préparer à ce qu'il y ait des tricheurs (cf. P. McDaniel, « Sustainability is a Security Problem », à la conférence SIGSAC en 2022), qui faussent les mesures pour échapper à ces pénalités.
  • De même, tout mécanisme qui va agir sur le réseau, par exemple pour diminuer l'empreinte environnementale, peut être détourné pour une attaque par déni de service, et doit donc prévoir le problème.

Téléchargez le RFC 9547


L'article seul

RFC 9537: Redacted Fields in the Registration Data Access Protocol (RDAP) Response

Date de publication du RFC : Mars 2024
Auteur(s) du RFC : J. Gould, D. Smith (VeriSign), J. Kolker, R. Carney (GoDaddy)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 14 juillet 2024


RDAP est le protocole recommandé pour accéder aux données sociales sur un nom de domaine, comme le nom du titulaire ou son adresse postale. Pour d'évidentes raisons de vie privée, certains registres ne renvoient pas la totalité de l'information dont ils disposent. Que doit-on mettre dans RDAP dans ce cas ? La question n'était pas tranchée et chaque registre faisait différemment. Désormais, il existe un solution normalisée.

Au passage, oui, d'accord, il n'y a pas que RDAP pour obtenir les données sociales (cet article vous en indiquera d'autres). Mais c'est le service le plus moderne et le plus adapté aux programmeur·ses. Contacté en HTTPS, le serveur RDAP va renvoyer du JSON que le client n'aura plus qu'à filtrer et formatter. Voici par exemple une partie de la réponse RDAP obtenue en se renseignant sur nouveaufrontpopulaire.fr :

% curl -s https://rdap.nic.fr/domain/nouveaufrontpopulaire.fr | jq
…
          [
            "fn",
            {},
            "text",
            "Parti Socialiste"
          ],
          [
            "org",
            {},
            "text",
            "Parti Socialiste"
          ],
          [
            "adr",
            {},
            "text",
            [
              "",
              "",
              "99 Rue Moliere",
              "Ivry-sur-Seine",
              "",
              "94200",
              "FR"
            ]
          ],
…
  

Ici, il s'agit d'une personne morale donc les données sont toutes envoyées. Et s'il s'agissait d'une personne physique, pour laquelle la loi Informatique & et Libertés s'applique, depuis 1978 ? La solution évidente est de ne pas envoyer les données qu'on ne veut pas diffuser mais attention, il y a un piège, il ne faut pas casser la syntaxe JSON. Par exemple, RDAP utilise (c'est en cours de changement, cf. RFC 9553) jCard pour formater les adresses (RFC 7095) et les champs dans jCard ne sont pas étiquetés, c'est leur position dans le tableau qui indique leur rôle (c'est un des nombreux inconvénients de jCard). On ne peut donc pas supprimer, par exemple, la rue, en indiquant :

[
            "adr",
            {},
            "text",
            [
              "",
              "",
              "Ivry-sur-Seine",
              "",
              "94200",
              "FR"
            ]
],
          [
            "email",
            {},
            "text",
            "e5d92838d5f0268143ac47d86880b5f7-48916400@contact.gandi.net"
          ],

Car, alors, on ne saurait plus si "Ivry-sur-Seine" est la rue ou bien la ville.

Le principe de notre RFC est donc : si on peut, retirer le membre JSON. Si on ne peut pas (cas du tableau de taille fixe), mettre une valeur vide ou null.

Petit point de terminologie : comment traduire le redacted du titre ? « Censuré » est inadapté (l'intervention ne vient pas d'un tiers mais d'une des deux parties). Je vais dire « élidé », mais « caviardé », « biffé » et « expurgé » sont également de bonnes solutions. Le nom correspondant est « élision ». Évidemment, il ne faut surtout pas dire « anonymisé », il n'y a rien d'anonyme ici, puisque le registre connait toute l'information, il refuse simplement de la diffuser.

La section 1 du RFC expose les grands principes de l'élision. Elle explique notamment qu'en cas d'élision, il faut ajouter un membre (nommé redacted) à la réponse JSON, expliquant les raisons et utilisant le langage JSONPath (RFC 9535) pour désigner de manière formelle la partie élidée.

Compte-tenu des contraintes sur la syntaxe de la réponse JSON (RFC 9083), le RFC normalise quatre façons d'élider (section 3) :

  • Suppression d'un champ, si possible (c'est la méthode préférée). Cela marche dans la plupart des cas. Par exemple, si on ne veut pas publier le titulaire d'un domaine, on ne l'inclut pas dans la réponse, point.
  • Mettre une valeur vide. Quand les contraintes de syntaxe empêchent de supprimer complètement un champ, on lui met une valeur vide. C'est notamment nécessaire pour les tableaux que jCard utilise abondamment : le retrait d'un champ casserait le tableau, qui est censé avoir un nombre d'élements fixe.
  • Une valeur trop détaillée peut être remplacée par une valeur partielle. L'adresse "label":"123 Maple Ave\nSuite 901\nVancouver BC\nCanada" (un exemple du RFC 7095) peut être remplacée par une valeur moins précise comme "label":"Vancouver\nBC\nCanada\n".
  • Enfin, la dernière méthode est de remplacer une valeur par une autre, par exemple une adresse de courrier par une adresse qui masque la vraie, comme e5d92838d5f0268143ac47d86880b5f7-48916400@contact.gandi.net dans l'exemple plus haut.

Et le RFC insiste qu'il ne faut pas utiliser de texte bidon (« XXX », « lorem ipsum dolor » ou « Ano Nymous ») car ce texte ne correspond pas forcément aux règles de syntaxe du champ (et, j'ajoute, peut être difficile à identifier pour le lecteur, qui peut ne pas avoir la référence).

Pour la première méthode, la suppression d'un champ, si on supprime le titulaire, on aura un membre nommé redacted (élidé) ajouté ainsi :

    "redacted": [
     {
       "name": {
         "description": "Remove registrant"
       },
       "prePath": "$.entities[?(@.roles[0]=='registrant')]",
       "method": "removal"
     }
     ]
  

Notez le (difficile à lire) code JSONPath $.entities[?(@.roles[0]=='registrant')].

Le deuxième cas, celui d'une valeur vide, donnerait, pour le cas où on supprime juste le nom du titulaire (qui est en position 1 dans le jCard, et son nom en position 3 - sachant qu'on part de 0) :

   [
     "fn",
     {},
     "text",
     ""
   ]
   …
      "redacted": [
     {
       "name": {
         "description": "Registrant Name"
       },
       "postPath": "$.entities[?(@.roles[0]=='registrant')].
         vcardArray[1][?(@[0]=='fn')][3]",
       "pathLang": "jsonpath",
       "method": "emptyValue",
       "reason": {
         "description": "Server policy"
       }
     }
     ]
     

Troisième technique d'élision, réduire une valeur. Le redacted devient :

   
   "redacted": [
     {
       "name": {
         "description": "Home Address Label"
       },
       "postPath": "$.vcardArray[1][?(@[0]=='adr')][1].label",
       "pathLang": "jsonpath",
       "method": "partialValue",
       "reason": {
         "description": "Server policy"
       }
     }
   ]
  

Et pour finir, la quatrième et dernière méthode, le remplacement :

   "redacted": [
     {
       "name": {
         "description": "Registrant Email"
       },
       "postPath": "$.entities[?(@.roles[0]=='registrant')].
                  vcardArray[1][?(@[0]=='email')][3]",
       "pathLang": "jsonpath",
       "method": "replacementValue",
     }
     ]
  

Ce membre appelé redacted est spécifié en détail dans la section 4 du RFC. (Et il est enregistré à l'IANA parmi les extensions RDAP.) Pour signaler qu'il peut apparaitre, le membre rdapConformance de la réponse JSON va l'indiquer :

{
  "rdapConformance": [
    "itNic",
    "redacted",
    "rdap_level_0"
  ],
…

Dès qu'il y a élision, redacted doit être ajouté. Il contient un tableau JSON d'objets, dont les membres peuvent être (seul le premier est obligatoire) :

  • name : un terme qui décrit le champ élidé,
  • prePath et postPath : des expressions JSONPath (RFC 9535) qui dénotent le membre retiré ou modifié,
  • method : la technique d'élision utilisée (suppression, nettoyage, remplacement, etc),
  • reason : texte libre décrivant la raison de l'élision.

Téléchargez le RFC 9537


L'article seul

RFC 9499: DNS Terminology

Date de publication du RFC : Mars 2024
Auteur(s) du RFC : P. Hoffman (ICANN), K. Fujiwara (JPRS)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 3 mai 2024


Comme beaucoup de protocoles très utilisés sur l'Internet, le DNS est ancien. Très ancien (la première norme, le RFC 882, date de 1983). Les normes techniques vieillissent, avec l'expérience, on comprend mieux, on change les points de vue et donc, pour la plupart des protocoles, on se lance de temps en temps dans une révision de la norme. Mais le DNS est une exception : la norme actuelle reste fondée sur des textes de 1987, les RFC 1034 et RFC 1035. Ces documents ne sont plus à jour, modifiés qu'ils ont été par des dizaines d'autres RFC. Bref, aujourd'hui, pour comprendre le DNS, il faut s'apprêter à lire de nombreux documents. En attendant qu'un courageux et charismatique participant à l'IETF se lance dans la tâche herculéenne de faire un jeu de documents propre et à jour, ce RFC 9499, successeur du RFC 8499 (il y a peu de modifications), se limite à une ambition plus modeste : fixer la terminologie du DNS. Ce n'est pas un tutoriel : vous n'y apprendrez pas le DNS. C'est plutôt une encyclopédie.

En effet, chacun peut constater que les discussions portant sur le DNS sont difficiles : on manque de terminologie standard, et celle des RFC officielles ne suffit pas, loin de là. Souvent, le DNS a tellement changé que le RFC officiel est même trompeur : les mots ne veulent plus dire la même chose. D'autres protocoles ont connu des mises à jour de la norme. Cela a été le cas de SMTP, passé successivement du RFC 772 à l'actuel RFC 5321, en passant par plusieurs révisions successives. Ou de XMPP, qui a vu sa norme originale mise à jour dans le RFC 6120. Et bien sûr de HTTP, qui a connu plusieurs toilettages complets (cf. RFC 9110). Mais personne n'a encore osé faire pareil pour le DNS. Au moins, ce RFC 9499, comme son prédécesseur RFC 8499, traite l'un des problèmes les plus criants, celui du vocabulaire. Le RFC est évidemment en anglais, les traductions proposées dans cet article, et qui n'ont pas de valeur « officielle » sont de moi seul.

Notre RFC 9499 rassemble donc des définitions pour des termes qui étaient parfois précisément définis dans d'autres RFC (et il fournit alors un lien vers ce RFC original), mais aussi qui n'étaient définis qu'approximativement ou parfois qui n'étaient pas définis du tout (et ce RFC fournit alors cette définition). Du fait du flou de certains RFC anciens, et des changements qui ont eu lieu depuis, certaines définitions sont différentes de l'original. Le document a fait l'objet d'un consensus relatif auprès des experts DNS mais quelques termes restent délicats. Notez aussi que d'autres organisations définissent des termes liés au DNS par exemple le WHATWG a sa définition de ce qu'est un domaine, et le RSSAC a développé une terminologie.

Ironiquement, un des termes les plus difficiles à définir est « DNS » lui-même (section 1 de notre RFC). D'accord, c'est le sigle de Domain Name System mais ça veut dire quoi ? « DNS » peut désigner le schéma de nommage (les noms de domaine comme signal.eu.org, leur syntaxe, leurs contraintes), la base de données répartie (et faiblement cohérente) qui associe à ces noms des informations (comme des certificats, des adresses IP, etc), ou le protocole requête/réponse (utilisant le port 53) qui permet d'interroger cette base. Parfois, « DNS » désigne uniquement le protocole, parfois, c'est une combinaison des trois éléments indiqués plus haut (personnellement, quand j'utilise « DNS », cela désigne uniquement le protocole, un protocole relativement simple, fondé sur l'idée « une requête => une réponse »).

Bon, et ces définitions rigoureuses et qui vont mettre fin aux discussions, ça vient ? Chaque section du RFC correspond à une catégorie particulière. D'abord, en section 2, les noms eux-même, ces fameux noms de domaine. Un système de nommage (naming system) a plusieurs aspects, la syntaxe des noms, la gestion des noms, le type de données qu'on peut associer à un nom, etc. D'autres systèmes de nommage que celui présenté dans ce RFC existent, et sont distincts du DNS sur certains aspects. Pour notre système de nommage, le RFC définit :

  • Nom de domaine (domain name) : la définition est différente de celle du RFC 7719, qui avait simplement repris celle du RFC 1034, section 3.1. Elle est désormais plus abstraite, ce qui permet de l'utiliser pour d'autres systèmes de nommage. Un nom de domaine est désormais simplement une liste (ordonnée) de composants (labels). Aucune restriction n'est imposée dans ces composants, simplement formés d'octets (tant pis pour la légende comme quoi le DNS serait limité à ASCII). Comme on peut représenter les noms de domaine sous forme d'un arbre (la racine de l'arbre étant à la fin de la liste des composants) ou bien sous forme texte (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).
  • FQDN (Fully Qualified Domain Name, nom de domaine complet) : apparu dans le RFC 819, ce terme désigne un nom de domaine où tous les composants sont cités (par exemple, 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.)
  • Composant (label) : un nœud de l'arbre des noms de domaine, dans la chaîne qui compose un FQDN. Dans www.laquadrature.net, il y a trois composants, www, laquadrature et net.
  • Nom de machine (host name) : ce n'est pas la même chose qu'un nom de domaine. Utilisé dans de nombreux RFC (par exemple RFC 952) mais jamais défini, un nom de machine est un nom de domaine avec une syntaxe plus restrictive, définie dans la section 3.5 du RFC 1035 et amendée par la section 2.1 du RFC 1123 : uniquement des lettres, chiffres, points et le tiret. Ainsi, 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).
  • TLD (Top Level Domain, domaine de premier niveau ou domaine de tête) : le dernier composant d'un nom de domaine, celui juste avant (ou juste en dessous) de la racine. Ainsi, 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.
  • DNS mondial (Global DNS), nouveauté du RFC 8499, désigne l'ensemble des noms qui obéissent aux règles plus restrictives des RFC 1034 et RFC 1035 (limitation à 255 octets, par exemple). En outre, les noms dans ce « DNS mondial » sont rattachés à la racine officiellement gérée par l'ICANN, via le service Public Technical Identifiers (PTI). Une racine alternative, si elle sert un contenu différent de celui de PTI, n'est donc pas le « DNS mondial » mais ce que le RFC nomme « DNS privé » (private DNS). Évidemment, un système de nommage qui n'utilise pas le DNS du tout mais qui repose, par exemple, sur le pair-à-pair, ne doit pas être appelé DNS (« DNS pair-à-pair » est un oxymore, une contradiction dans les termes.)
  • IDN (Internationalized Domain Name, nom de domaine internationalisé) : un nom de domaine en Unicode, normalisé dans le RFC 5890, qui définit des termes comme U-label (le nom Unicode) et A-label (sa représentation en Punycode). Sur l'internationalisation des noms, vous pouvez aussi consulter le RFC de terminologie RFC 6365.
  • Sous-domaine (subdomain) : domaine situé sous un autre, dans l'arbre des noms de domaines. Sous forme texte, un domaine est sous-domaine d'un autre si cet autre est un suffixe. Ainsi, 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.
  • Alias (alias) : attention, il y a un piège. Le DNS permet à un nom d'être un alias d'un autre, avec le type d'enregistrement CNAME (voir la définition suivante). L'alias est le terme de gauche de l'enregistrement CNAME. Ainsi, si on met dans un fichier de zone vader IN CNAME anakin, l'alias est vader (et c'est une erreur de dire que c'est « le CNAME »).
  • CNAME (Canonical Name, nom canonique, le « vrai » nom) : le membre droit dans l'enregistrement CNAME. Dans l'exemple de la définition précédente, 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 :

  • NODATA : un mnémonique pour une réponse où le nom de domaine demandé existe bien mais ne contient aucun enregistrement du type souhaité. Le code de retour est 0, NOERROR, et le nombre de réponses (ANCOUNT pour Answer Count) est nul.
  • Réponse négative (negative answer) : le terme recouvre deux choses, une réponse disant que le nom de domaine demandé n'existe pas (code de retour NXDOMAIN), ou bien une réponse indiquant que le serveur ne peut pas répondre (code de retour SERVFAIL ou REFUSED). Voir le RFC 2308.
  • Renvoi (referral) : le DNS étant décentralisé, il arrive qu'on pose une question à un serveur qui ne fait pas autorité pour le domaine demandé, mais qui sait vous renvoyer à un serveur plus proche. Ces renvois sont indiqués dans la section Authority d'une réponse.

Voici un renvoi depuis la racine vers .fr :


% dig @l.root-servers.net A blog.imirhil.fr 
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16572
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 5, ADDITIONAL: 11
...
;; AUTHORITY SECTION:
fr.			172800 IN NS d.ext.nic.fr.
fr.			172800 IN NS d.nic.fr.
fr.			172800 IN NS e.ext.nic.fr.
fr.			172800 IN NS f.ext.nic.fr.
fr.			172800 IN NS g.ext.nic.fr.

    

La section 4 s'intéresse aux transactions DNS. Un des termes définis est celui de QNAME (Query NAME), nouveauté du RFC 8499. Il y a trois sens possibles (le premier étant de très loin le plus commun) :

  • Le sens original, le QNAME est la question posée (le nom de domaine demandé),
  • Le sens du RFC 2308, la question finale (qui n'est pas la question posée s'il y a des CNAME sur le trajet),
  • Un sens qui n'apparait pas dans les RFC mais est parfois présent dans les discussions, une question intermédiaire (dans une chaîne de CNAME).

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) :

  • RR (Resource Record) : un enregistrement DNS.
  • Enregistrements d'adresses (Address records) : enregistrements DNS A (adresses IPv4) et AAAA (adresses IPv6). Beaucoup de gens croient d'ailleurs que ce sont les seuls types d'enregistrements possibles (« le DNS sert à traduire les noms en adresses IP » est une des synthèses erronées du DNS les plus fréquentes).
  • Ensemble d'enregistrements (RRset pour Resource Record set) : un ensemble d'enregistrements ayant le même nom (la même clé d'accès à la base), la même classe, le même type et le même TTL. Cette notion avait été introduite par le RFC 2181. Notez que la définition originale parle malheureusement de label dans un sens incorrect. La terminologie du DNS est vraiment compliquée !
  • Fichier maître (master file) : un fichier texte contenant des ensembles d'enregistrement. Également appelé fichier de zone, car son utilisation la plus courante est pour décrire les enregistrements que chargera le serveur maître au démarrage. (Ce format peut aussi servir à un résolveur quand il écrit le contenu de sa mémoire sur disque.)
  • EDNS (Extension for DNS, également appelé EDNS0) : normalisé dans le RFC 6891, EDNS permet d'étendre l'en-tête du DNS, en spécifiant de nouvelles options, en faisant sauter l'antique limitation de taille de réponses à 512 octets, etc.
  • OPT (pour Option) : une astuce d'EDNS pour encoder les informations de l'en-tête étendu. C'est un enregistrement DNS un peu spécial, défini dans le RFC 6891, section 6.1.1.
  • Propriétaire (owner ou owner name) : le nom de domaine d'un enregistrement. Ce terme est très rarement utilisé, même par les experts.
  • Champs du SOA (SOA field names) : ces noms des enregistrements SOA (comme MNAME ou RNAME) sont peu connus et malheureusement peu utilisés (RFC 1035, section 3.3.13). Notez que la sémantique du champ MINIMUM a complètement changé avec le RFC 2308.
  • TTL (Time To Live) : la durée de vie maximale d'un enregistrement dans les caches des résolveurs. C'est un entier non signé (même si le RFC 1035 dit le contraire), en secondes.

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.

  • Résolveur (resolver) : un client DNS qui va produire une réponse finale (pas juste un renvoi, pas juste une information ponctuelle comme le fait dig). Il existe plusieurs types de résolveurs (voir ci-dessous) et, en pratique, quand on dit « résolveur » tout court, c'est en général un « résolveur complet ».
  • Résolveur minimum (stub resolver) : un résolveur qui ne sait pas suivre les renvois et qui dépend donc d'un ou de plusieurs résolveurs complets pour faire son travail. Ce type est défini dans la section 6.1.3.1 du RFC 1123. C'est ce résolveur minimum qu'appellent les applications lorsqu'elles font un 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.
  • Résolveur complet (full resolver) : un résolveur qui sait suivre les renvois et donc fournir un service de résolution complet. Des logiciels comme Unbound ou PowerDNS Resolver assurent cette fonction. Le résolveur complet est en général situé chez le FAI ou dans le réseau local de l'organisation où on travaille, mais il peut aussi être sur la machine locale. On le nomme aussi « résolveur récursif ». Notez que « résolveur complet » fait partie de ces nombreux termes qui étaient utilisés sans jamais avoir été définis rigoureusement.
  • Resolveur complet avec mémoire (full-service resolver) : un résolveur complet, qui dispose en plus d'une mémoire (cache) des enregistrements DNS récoltés.
  • Serveur faisant autorité (authoritative server et traduire ce terme par « serveur autoritaire » montre une grande ignorance de l'anglais ; un adjudant est autoritaire, un serveur DNS fait autorité) : un serveur DNS qui connait une partie des données du DNS (il « fait autorité » pour une ou plusieurs zones) et peut donc y répondre (RFC 2182, section 2). Ainsi, au moment de l'écriture de cet article, 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.
  • Serveur mixte : ce terme n'est pas dans ce RFC. Autrefois, il était courant que des serveurs DNS fassent à la fois résolveur et serveur faisant autorité. Cette pratique est fortement déconseillée depuis de nombreuses années (entre autres parce qu'elle complique sérieusement le débogage, mais aussi pour des raisons de sécurité parce qu'elle mène à du code plus complexe) et n'est donc pas dans ce RFC.
  • Initialisation (priming) : le processus par lequel un résolveur complet vérifie l'information sur les serveurs de la racine. Ce concept est décrit en détail dans le RFC 8109. Au démarrage, le résolveur ne sait rien. Pour pouvoir commencer la résolution de noms, il doit demander aux serveurs de la racine. Il a donc dans sa configuration leur liste. Mais ces configurations ne sont pas forcément mises à jour souvent. La liste peut donc être trop vieille. La première chose que fait un résolveur est donc d'envoyer une requête « 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.
  • Indications de la racine (root hints) : la liste des noms et adresses des serveurs racine, utilisée pour l'initialisation. L'administrateur d'un résolveur utilisant une racine alternative changera cette liste.
  • Mémorisation des négations (negative caching, ou « cache négatif ») : mémoriser le fait qu'il n'y a pas eu de réponse, ou bien une réponse disant qu'un nom de domaine n'existe pas.
  • Serveur primaire (primary server mais on dit aussi serveur maître, master server) : un serveur faisant autorité qui a accès à la source des données (fichier de zone, base de données, etc). Attention, il peut y avoir plusieurs serveurs primaires (autrefois, ce n'était pas clair et beaucoup de gens croient qu'il y a un serveur primaire, et qu'il est indiqué dans l'enregistrement SOA). Attention bis, le terme ne s'applique qu'à des serveurs faisant autorité, l'utiliser pour les résolveurs (« on met en premier dans /etc/resolv.conf le serveur primaire ») n'a pas de sens.
  • Serveur secondaire (secondary server mais on dit aussi « serveur esclave », slave server) : un serveur faisant autorité qui n'est pas la source des données, qui les a prises d'un serveur primaire (dit aussi serveur maître), via un transfert de zone (RFC 5936).
  • Serveur furtif (stealth server) : un serveur faisant autorité mais qui n'apparait pas dans l'ensemble des enregistrements NS. (Définition du RFC 1996, section 2.1.)
  • Maître caché (hidden master) : un serveur primaire qui n'est pas annoncé publiquement (et n'est donc accessible qu'aux secondaires). C'est notamment utile avec DNSSEC : s'il signe, et donc a une copie de la clé privée, il vaut mieux qu'il ne soit pas accessible de tout l'Internet (RFC 6781, section 3.4.3).
  • Transmission (forwarding) : le fait, pour un résolveur, de faire suivre les requêtes à un autre résolveur (probablement mieux connecté et ayant un cache partagé plus grand). On distingue parfois (mais ce n'est pas forcément clair, même dans le RFC 5625) la transmission, où on émet une nouvelle requête, du simple relayage de requêtes sans modification.
  • Transmetteur (forwarder) : le terme est parfois utilisé confusément. Il désigne tantôt la machine qui transmet une requête et tantôt celle vers laquelle on transmet (c'est dans ce sens qu'il est utilisé dans la configuration de BIND, avec la directive forwarders). La définition de notre RFC est le premier sens.
  • Résolveur politique (policy-implementing resolver) : un résolveur qui modifie les réponses reçues, selon sa politique. On dit aussi, moins diplomatiquement, un « résolveur menteur ». C'est ce que permet, par exemple, le système RPZ. Sur l'utilisation de ces « résolveurs politiques » pour mettre en œuvre la censure, voir entre autres mon article aux RIPE Labs. Notez que le résolveur politique a pu être choisi librement par l'utilisateur (par exemple comme élément d'une solution de blocage des publicités) ou bien qu'il lui a été imposé.
  • Résolveur ouvert (open resolver) : un résolveur qui accepte des requêtes DNS depuis tout l'Internet. C'est une mauvaise pratique (cf. RFC 5358) et la plupart de ces résolveurs ouverts sont des erreurs de configuration. Quand ils sont délibérement ouverts, comme Google Public DNS ou Quad9, on parle plutôt de résolveurs publics.
  • Collecte DNS passive (passive DNS) : désigne les systèmes qui écoutent le trafic DNS, et stockent tout ou partie des informations échangées. Le cas le plus courant est celui où le système de collecte ne garde que les réponses (ignorant donc les adresses IP des clients et serveurs), afin de constituer une base historique du contenu du DNS (c'est ce que font DNSDB ou le système de PassiveDNS.cn).
  • Serveur protégeant la vie privée (privacy-enabled server) : ce terme désigne un serveur, typiquement un résolveur, qui permet les requêtes DNS chiffrées, avec DNS-sur-TLS (RFC 7858) ou avec DoH (RFC 8484).
  • Anycast : le fait d'avoir un service en plusieurs sites physiques, chacun annonçant la même adresse IP de service (RFC 4786). Cela résiste mieux à la charge, et permet davantage de robustesse en cas d'attaque par déni de service. Les serveurs de la racine, ceux des « grands » TLD, et ceux des importants hébergeurs DNS sont ainsi « anycastés ». Chaque machine répondant à l'adresse IP de service est appelée une instance.
  • DNS divisé (split DNS) : le fait de donner des réponses différentes selon que le client est interne à une organisation ou externe. Le but est, par exemple, de faire en sorte que 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) :

  • Zone : un groupe de domaines contigus dans l'arbre des noms, et gérés ensemble, par le même ensemble de serveurs de noms (ma définition, celle du RFC étant très abstraite). Beaucoup de gens croient que tout domaine est une zone, mais c'est faux. Ainsi, au moment de la publication de ce RFC, 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).
  • Parent : le domaine « du dessus ». Ainsi, le parent de wikipedia.org est org, le parent de .org est la racine.
  • Apex : le sommet d'une zone, là où on trouve les enregistrements NS et SOA. Si la zone ne comprend qu'un domaine, l'apex est ce domaine. Si la zone est plus complexe, l'apex est le domaine le plus court. (On voit parfois des gens utiliser le terme erroné de racine pour parler de l'apex.)
  • Coupure de zone (zone cut) : l'endroit où on passe d'une zone à l'autre. Au-dessus de la coupure, la zone parente, en dessous, la zone fille.
  • Délégation (delegation) : un concept évidemment central dans le DNS, qui est un système décentralisé. En ajoutant un ensemble d'enregistrements NS pointant vers les serveurs de la zone fille, une zone parente délègue une partie de l'arbre des noms de domaine à une autre entité. L'endroit où se fait la délégation est donc une coupure de zone.
  • Colle (glue records) : lorsqu'une zone est déléguée à des serveurs dont le nom est dans la zone fille, la résolution DNS se heurte à un problème d'œuf et de poule. Pour trouver l'adresse de 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.)
  • Délégation boiteuse (lame delegation) : un ou plusieurs des serveurs à qui la zone est déléguée ne sont pas configurés pour servir cette zone. La délégation peut avoir été boiteuse depuis le début (parce que le titulaire a indiqué des noms un peu au hasard) ou bien l'être devenue par la suite. C'est certainement une des erreurs techniques les plus courantes. Mais le RFC note que l'usage du terme a dérivé et qu'aujourd'hui, il est devenu plus flou, désignant hélas des erreurs très variées.
  • Dans le bailliage (in bailiwick) : terme absent des textes DNS originaux et qui peut désigner plusieurs choses. (La définition du RFC 8499, embrouillée, a été heureusement remplacée.) « Dans le bailliage » est (très rarement, selon mon expérience) parfois utilisé pour parler d'un serveur de noms dont le nom est dans la zone servie (et qui nécessite donc de la colle, voir la définition plus haut), mais le sens le plus courant désigne des données pour lesquelles le serveur qui a répondu fait autorité, soit pour la zone, soit pour un ancêtre de cette zone. L'idée est qu'il est normal dans la réponse d'un serveur de trouver des données situées dans le bailliage et, par contre, que les données hors-bailliage sont suspectes (elles peuvent être là suite à une tentative d'empoisonnement DNS). Un résolveur DNS prudent ignorera donc les données hors-bailliage.
  • ENT (Empty Non-Terminal pour nœud non-feuille mais vide) : un domaine qui n'a pas d'enregistrements mais a des sous-domaines. C'est fréquent, par exemple, sous ip6.arpa ou sous les domaines très longs de certains CDN. Cela se trouve aussi avec les enregistrements de service : dans _sip._tcp.example.com, _tcp.example.com est probablement un ENT. La réponse correcte à une requête DNS pour un ENT est NODATA (code de réponse NOERROR, liste des répoonses vide) mais certains serveurs bogués, par exemple, à une époque, ceux d'Akamai, répondaient NXDOMAIN.
  • Zone de délégation (delegation-centric zone) : zone composée essentiellement de délégations vers d'autres zones. C'est typiquement le cas des TLD et autres suffixes publics. Il est amusant de noter que les RFC 4956 et RFC 5155 utilisaient ce terme sans le définir.
  • Changement rapide (fast flux) : une technique notamment utilisée par les botnets pour mettre leur centre de commande à l'abri des filtrages ou destructions. Elle consiste à avoir des enregistrements d'adresses IP avec des TTL très courts et à en changer fréquemment.
  • DNS inverse (reverse DNS) : terme qui désigne en général les requêtes pour des enregistrements de type PTR, permettant de trouver le nom d'une machine à partir de son adresse IP. À ne pas confondre avec les vraies requêtes inverses, qui avaient été normalisées dans le RFC 1035, avec le code IQUERY, mais qui, jamais vraiment utilisées, ont été abandonnées dans le RFC 3425.

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 :

  • La section ANSWER est vide, c'est un renvoi.
  • Le serveur indique la colle pour pri.authdns.ripe.net : ce serveur étant dans la zone qu'il sert, sans son adresse IP, on ne pourrait jamais le joindre.
  • Le serveur envoie également les adresses IP d'autres machines comme sec1.apnic.net. Ce n'est pas strictement indispensable (on pourrait l'obtenir par une nouvelle requête), juste une optimisation.
  • Les adresses de ns3.nic.fr et sns-pb.isc.org ne sont pas renvoyées. Le serveur ne les connait probablement pas et, de toute façon, elles seraient hors-bailliage, donc ignorées par un résolveur prudent.

Voyons maintenant, un ENT, gouv.fr (notez que, depuis, ce domaine n'est plus un ENT) :

   
% dig @d.nic.fr ANY gouv.fr 
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42219
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 1

    

Le serveur fait bien autorité pour ce domaine (flag AA dans la réponse), le domaine existe (autrement, le status aurait été NXDOMAIN, pas NOERROR) mais il n'y a aucun enregistrement (ANSWER: 0).

Et, ici, une délégation boiteuse, pour .ni :

% check-soa ni   
dns.nic.cr.
	2001:13c7:7004:1::d100: ERROR: SERVFAIL
	200.107.82.100: ERROR: SERVFAIL
ns.ideay.net.ni.
	186.1.31.8: OK: 2013093010
ns.ni.
	165.98.1.2: ERROR: read udp 10.10.86.133:59569->165.98.1.2:53: i/o timeout
ns.uu.net.
	137.39.1.3: OK: 2013093010
ns2.ni.
	200.9.187.2: ERROR: read udp 10.10.86.133:52393->200.9.187.2:53: i/o timeout
    

Le serveur dns.nic.cr est déclaré comme faisant autorité pour .ni mais il ne le sait pas, et répond donc SERVFAIL.

Les jokers ont une section à eux, la section 8 du RFC. S'appuyant sur le RFC 4592, elle définit, entre autres :

  • Joker (wildcard) : une source de confusion considérable depuis les débuts du DNS. Si on pouvait refaire le DNS en partant de zéro, ces jokers seraient la première chose à supprimer. Pour les résumer, le nom * 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.
  • Ancêtre le plus proche (closest encloser) : c'est le plus long ancêtre existant d'un nom. Si 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) :

  • Registre (registry) : l'organisation ou la personne qui gère les délégations d'une zone. Le terme est parfois employé uniquement pour les gérants de grandes zones de délégation, mais ce n'est pas obligatoire. Je suis à moi tout seul le registre de 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 ».
  • Titulaire (registrant, parfois holder) : la personne ou l'organisation qui a enregistré un nom de domaine.
  • Bureau d'enregistrement (registrar) : dans le modèle RRR (Registry-Registrar-Registrant, mais ce modèle n'est pas obligatoire et pas universel), c'est un intermédiaire entre le titulaire et le registre.
  • Hébergeur DNS (DNS operator) : la personne ou l'organisation qui gère les serveurs DNS. Cela peut être le titulaire, son bureau d'enregistrement, ou encore un acteur spécialisé dans cette gestion.
  • Suffixe public (public suffix) : ce terme pas très officiel est parfois utilisé pour désigner un suffixe de noms de domaine qui est contrôlé par un registre public (au sens où il accepte des enregistrements du public). Mais le terme est trompeur car, par exemple, .aquarelle se trouve dans la Public Suffix List alors qu'il s'agit d'un « .marque », un de ces TLD où une seule entreprise peut enregistrer des noms. Le terme est ancien mais est apparu pour la première fois dans un RFC avec le RFC 6265, section 5.3. com, co.uk et eu.org sont des suffixes publics. Rien dans la syntaxe du nom n'indique qu'un nom de domaine est un suffixe public, puisque ce statut ne dépend que d'une politique d'enregistrement (qui peut changer). Il est parfaitement possible qu'un domaine, et un de ses enfants, soient tous les deux un suffixe public (c'est le cas de .org et eu.org).
  • EPP (Extensible Provisioning Protocol) : normalisé dans le RFC 5730, c'est le protocole standard entre bureau d'enregistrement et registre. Ce protocole n'a pas de lien avec le DNS, et tous les registres ne l'utilisent pas.
  • Whois (nommé d'après la question Who Is?) : un protocole réseau, sans lien avec le DNS, normalisé dans le RFC 3912. Il permet d'interroger les bases de données du registre pour trouver les informations dites « sociales », informations qui ne sont pas dans le DNS (comme le nom du titulaire, et des moyens de le contacter). Les termes de « base Whois » ou de « données Whois » sont parfois utilisés mais ils sont erronés puisque les mêmes bases peuvent être interrogées par d'autres protocoles que Whois, comme RDAP (voir définition suivante).
  • RDAP (Registration Data Access Protocol) : un protocole concurrent de Whois, mais plus moderne. RDAP est décrit notamment dans les RFC 9082 et RFC 9083.

Prenons par exemple le domaine eff.org. Au moment de la publication du RFC :

Enfin, pour terminer, les sections 10 et 11 de notre RFC couvrent DNSSEC. Pas grand'chose de nouveau ici, DNSSEC étant plus récent et donc mieux défini.

L'annexe A de notre RFC indique quelles définitions existaient dans de précédents RFC mais ont été mises à jour par le nôtre. (C'est rare, puisque le but de ce RFC de terminologie est de rassembler les définitions, pas de les changer.) Par exemple, la définition de QNAME du RFC 2308 est corrigée ici.

L'annexe B liste les termes dont la première définition formelle se trouve dans ce RFC (ou dans un de ses prédécesseurs, les RFC 7719 et "RFC 8499). Cette liste est bien plus longue que celle de l'annexe A, vu le nombre de termes courants qui n'avaient jamais eu l'honneur d'une définition stricte.

Notre RFC ne contient pas une liste exhaustive des changements depuis son prédécesseur, le RFC 8499, mais ils sont peu importants. Parmi les changements sérieux :

  • DoT (RFC 7858), DoH (RFC 8484) et DoQ (RFC 9250) ont maintenant une définition complète.
  • Par contraste, le DNS traditionnel (Classic DNS), celui qui tourne directement sur UDP ou TCP, et en clair, a sa définition.
  • L'importance de TLS se traduit aussi par l'arrivée des définitions de DoT vers le résolveur (RDoT, pour Recursive DNS over TLS), de DoT vers le serveur faisant autorité (ADoT pour Authoritative DNS over TLS) et XoT (zone transfer over TLS, normalisé dans le RFC 9103).
  • Le RFC prend également acte du fait que le terme désignant les délégations invalides, lame delegation, a beaucoup dérivé.

Téléchargez le RFC 9499


L'article seul

RFC 9490: Report from the IAB Workshop on Management Techniques in Encrypted Networks (M-TEN)

Date de publication du RFC : Janvier 2024
Auteur(s) du RFC : M. Knodel, W. Hardaker, T. Pauly
Pour information
Première rédaction de cet article le 9 mai 2024


Aujourd'hui, l'essentiel du trafic sur l'Internet est chiffré, et pour d'excellentes raisons. Pas question de revenir là-dessus, mais, ceci dit, il va falloir adapter certaines pratiques de gestion des réseaux. L'IAB a organisé en 2022 un atelier sur ce sujet, dont ce RFC est le compte-rendu.

Comme les autres ateliers de l'IAB, il s'agit de faire s'exprimer diverses personnes, pas forcément d'arriver à une position commune (surtout sur un sujet… sensible). Tenu entièrement en ligne, cet atelier commençait par la soumission d'articles, qui devaient être lus d'abord, et discutés lors de l'atelier. La liste des articles figure dans l'annexe A du RFC, et les articles sont disponibles en ligne.

D'abord, notre RFC réaffirme que la cryptographie est absolument nécessaire à la protection de la vie privée. Les difficultés réelles qu'elle peut poser ne doivent jamais être un prétexte pour réduire ou affaiblir le chiffrement. Qu'il y ait une tension entre l'impératif du chiffrement et certaines méthodes de gestion du réseau, OK, mais la priorité reste la lutte contre la surveillance (RFC 7258).

Ensuite (section 1), le RFC pose le paysage : le trafic étant largement chiffré, des méthodes traditionnelles de gestion du réseau ne fonctionnent plus. tcpdump ne montre plus les données, on ne peut donc pas distinguer différentes méthodes HTTP, par exemple. Avec QUIC (RFC 9000), c'est même une partie de la couche transport qui est chiffrée donc un observateur extérieur ne peut plus, par exemple, évaluer le RTT d'une session qu'il observe (ce qui était possible avec TCP). Il va donc falloir s'adapter notamment en coopérant davantage avec les applications qui, elles, ont accès à tout. Pour l'instant, on a vu surtout passer des récriminations d'acteurs habitués à surveiller le trafic et qui se plaignent que le chiffrement les en empêchent (le RFC 8404 est un bon exemple de ces récriminations). Au contraire, il faut chercher à résoudre une contradiction : permettre aux réseaux d'appliquer leur politique sans céder d'un millimètre sur le principe du chiffrement.

Outre le cas des opérateurs réseau qui veulent examiner le trafic, à des fins louables (améliorer le réseau) ou critiquables (défavoriser certains usages), on peut aussi citer, parmi les intermédiaires qui voudraient observer et/ou interférer avec le trafic réseau, les réseaux d'entreprise qui veulent, par exemple, empêcher les employés d'accéder à certains services, ou bien les mécanismes de contrôle des enfants (appelés à tort « contrôle parental », alors que leur but est justement de remplacer les parents).

Trois thèmes importants étaient discutés à l'atelier : la situation actuelle, les futures techniques et la façon dont on pourrait arriver à déployer ces futures techniques.

Pour la situation actuelle, on sait que, depuis longtemps, les administrateurices réseau comptent sur de l'observation passive, par exemple pour classer le trafic (tant de HTTP, tant de SSH, etc). C'est à la base de techniques comme IPFIX (RFC 7011), qui permet de produire de beaux camemberts. Outre la production de rapports, cette observation passive peut (mais n'est pas forcément) être utilisée pour prévoir les évolutions futures, prioriser certains types de trafic, détecter des comportements malveillants, etc. Les protocoles Internet étaient en effet traditionnellement trop bavards, faisant fuiter des informations vers des observateurs passifs (cf. le concept de « vue depuis le réseau », RFC 8546).

La lutte de l'épée et de la cuirasse étant éternelle, il y a évidemment eu des travaux sur des mécanismes pour empêcher ce genre d'analyses comme l'article soumis pour l'atelier « WAN Traffic Obfuscation at Line Rate ». (Au passage, sur cette idée d'obfuscation, je recommande le livre de Brunton et Nissenbaum.) Le RFC mentionne aussi une discussion sur l'idée d'exiger une permission explicite des utilisateurs pour les analyses du trafic (je ne vous dis pas les difficultés pratiques…). Voir l'Internet-Draft draft-irtf-pearg-safe-internet-measurement.

Dans un monde idéal, le réseau et les applications coopéreraient (et, dans un monde idéal, licornes et bisounours s'embrasseraient tous les jours) mais on voit plutôt une lutte, le réseau essayant d'en savoir plus, les applications de préserver leur intimité. Pourtant, le RFC note qu'une coopération pourrait être dans leur intérêt réciproque. Bien sûr, des applications comme Tor refuseraient certainement toute coopération puisque leur but est de laisser fuiter le moins d'information possible, mais d'autres applications pourraient être tentées, par exemple en échange d'informations du réseau sur la capacité disponible. Après tout, certaines techniques sont justement conçues pour cela, comme ECN (RFC 3168).

OK, maintenant, quelles sont les techniques et méthodes pour résoudre le problème de la gestion des réseaux chiffrés (section 2.2) ? Le RFC exprime bien le fait que le but du chiffrement est d'empêcher tout tiers d'accéder aux informations. Donc, sauf faille de sécurité, par exemple suite à une faiblesse du chiffrement, tout accès à l'information impose la coopération d'une des deux parties qui communiquent. Cette question du consentement est cruciale. Si on n'a pas le consentement d'une des deux extrémités de la communication, on n'a pas le droit d'accéder aux données, point. Cf. la contribution « What’s In It For Me? Revisiting the reasons people collaborate ». Comme le répète souvent Eric Rescorla « Sur l'Internet, tout ce qui n'est pas une des extrémités est un ennemi. » (Cela inclut donc tous les équipements réseaux et les organisations qui les contrôlent.) Bref, les utilisateurs doivent pouvoir donner un consentement éclairé, et juger si les bénéfices de la visibilité l'emportent sur les risques. Évidemment, ce principe correct va poser de sérieuses difficultés d'application, car il n'est pas évident d'analyser proprement les bénéfices, et les risques (songez aux bandeaux cookies ou aux permissions des application sur votre ordiphone). Développer des nouveaux mécanismes de communication avec l'utilisateurice sont nécessaires, sinon, comme le note le RFC, « les ingénieurs devront choisir pour les utilisateurs ». Le RFC estime que les utilisateurs donneront toujours la priorité à leur vie privée (notamment parce qu'ils ne comprennent pas l'administration de réseaux et ses exigences), mais cela ne me semble pas si évident que cela. Personnellement, il me semble que le choix par défaut est crucial, car peu d'utilisateurices le modifieront.

Une des techniques qui permettent d'avoir accès à de l'information sans avoir toute l'information est celle des relais (cf. « Relying on Relays: The future of secure communication ». Le principe est de séparer les fonctions : l'utilisateur parle à un relais qui parle au serveur final. Le relais ne connait pas le contenu de la demande, le serveur ne connait pas le demandeur. C'est utilisé dans des techniques comme Oblivious HTTP (RFC 9458) ou Oblivious DNS (RFC 9230). La préservation de la vie privée est donc bien meilleure qu'avec, par exemple, un VPN, où l'opérateur du VPN voit tout, le demandeur et la demande (contrairement à ce que prétendent les publicités pour NordVPN que de nombreux youtubeurs transmettent avec leurs vidéos). Le relais permet donc un accès à une information limitée, ce qui permet d'assurer certaines fonctions (comme le filtrage de contenu malveillant) sans avoir un accès complet.

Un exemple souvent cité par les opérateurs de l'intérêt d'accéder à la communication et de la modifier est celui de l'optimisation des flux TCP. Des PEP (Performance Enhancing Proxies) violant le modèle en couches (car, normalement, TCP est de bout en bout) sont souvent déployés, notamment sur les liaisons satellites, dont les performances sont plus mauvaises, et les caractéristiques techniques très spéciales. (Cf. l'exposé de Nicolas Kuhn, « QUIC for satellite communications » à la Journée du Conseil Scientifique de l'Afnic en 2021.) Le PEP va modifier les en-têtes TCP, voire parfois boucler la connexion TCP et en faire une autre lui-même. Cette technique ne marche évidemment plus avec QUIC, qui chiffre même la couche transport. Et elle mène à l'ossification de l'Internet puisqu'elle rend plus difficile de faire évoluer TCP, car toute évolution risque de perturber les PEP. QUIC avait en partie été explicitement développé pour empêcher ces intermédiaires de bricoler la session. Une autre approche est celle proposée dans « The Sidecar: "Opting in" to PEP Functions ».

Bon, maintenant, comment arriver à déployer de meilleures méthodes ? Dans un environnement fermé ou semi-fermé, cela doit être possible de faire en sorte que, par exemple, toutes les machines mettent en œuvre telle fonction (cf. RFC 8520 pour un exemple). Mais cela ne marche clairement pas pour l'Internet.

Parmi les moyens proposés de résolution de la contradiction entre visibilité par le réseau et protection de la vie privée, le RFC mentionne les Zero-Knowledge Middleboxes. Décrites dans l'article du même nom, cette méthode utilise les preuves à divulgation nulle de connaissance pour prouver à l'intermédiaire qui fait le filtrage qu'on a bien respecté sa politique de filtrage, sans lui dire ce qu'on a fait. L'article détaille par exemple comment cela peut s'appliquer au DNS, qui est le principal outil de censure de l'Internet dans l'Union européenne. Ces preuves à divulgation nulle de connaissance ayant toujours été mystérieuses pour moi, je ne vous expliquerai pas comment elles marchent, mais notez que les auteurs ont fait un article pédagogique.

Enfin, le RFC mentionne la proposition Red Rover, qui propose encore un arrangement bisounours où les utilisateurs et les opérateurs collaboreraient pour un filtrage géré en commun. Les auteurs disent que ça marcherait car les utilisateurs « ne veulent probablement pas violer les CGU » (ben si : ils veulent utiliser Sci-Hub même si c'est censuré).

En conclusion, le RFC note en effet qu'on peut être sceptique quant aux chances d'une solution négociée. Mais il met aussi en avant le fait qu'il va être très difficile de trouver une solution qui marche pour toute la variété des réseaux. Et que l'expérience prouve largement que toute nouvelle technique va avoir des effets inattendus et que, par exemple, une solution qui visait à donner aux opérateurs des informations utiles pour la gestion de réseaux, va parfois être détournée pour d'autres buts.

Sinon, sur la question du déboguage des applications dans un monde où tout est chiffré, j'avais fait un exposé à Capitole du Libre.


Téléchargez le RFC 9490


L'article seul

RFC 9476: The .alt Special-Use Top-Level Domain

Date de publication du RFC : Septembre 2023
Auteur(s) du RFC : W. Kumari (Google), P. Hoffman (ICANN)
Chemin des normes
Première rédaction de cet article le 30 septembre 2023


Le TLD .alt a été réservé pour les utilisations non-DNS. Si demain je crée une chaine de blocs nommée Catena et que j'enregistre des noms dessus, il est recommandé qu'ils se terminent par catena.alt (mais comme toujours sur l'Internet, il n'y a pas de police mondiale chargée de faire respecter les RFC).

Ce nouveau RFC est l'occasion de rappeler que noms de domaine et DNS, ce n'est pas pareil. Les noms de domaine existaient avant le DNS et, même aujourd'hui, peuvent être résolus par d'autres techniques que le DNS (par exemple votre fichier local /etc/hosts ou équivalent).

Mais le DNS est un tel succès que cette « marque » est utilisée partout. On voit ainsi des systèmes de résolution de noms n'ayant rien à voir avec le DNS se prétendre « DNS pair-à-pair » ou « DNS sur la blockchain », ce qui n'a aucun sens. Ce nouveau RFC, lui, vise clairement uniquement les systèmes non-DNS. Ainsi, des racines alternatives ou des domaines privés comme le .local du RFC 6762 ne rentrent pas dans son champ d'application. De plus, il ne s'applique qu'aux noms de domaine ou en tout cas aux identificateurs qui leur ressemblent suffisamment. Si vous créez un système complètement disruptif où les identificateurs ne ressemblent pas à des noms de domaine, ce RFC ne vous concerne pas non plus. Mais si, pour des raisons techniques (être compatible avec les applications existantes) ou marketing (les noms de domaine, c'est bien, tout le monde les reconnait), vous choisissez des noms de domaine comme identificateurs, lisez ce RFC.

En effet, il existe plusieurs de ces systèmes. Ils permettent, en indiquant un nom de domaine (c'est-à-dire une série de composants séparés par des points comme truc.machin.chose.example) d'obtenir des informations techniques, permettant, par exemple, de trouver un serveur ou de s'y connecter. Un exemple d'un tel système est le mécanisme de résolution utilisé par Tor. Les identificateurs sont construits à partir d'une clé cryptographique et suffixés d'un .onion (réservé par le RFC 7686). Ainsi, ce blog est en http://sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion/. N'essayez pas ce nom dans le DNS : vous ne le trouverez pas, il se résout via Tor.

Pendant longtemps, cette pratique de prendre, un peu au hasard, un nom et de l'utiliser comme TLD a été la façon la plus courante de créer des noms de domaine dans son espace à soi. C'est ainsi que Namecoin a utilisé le .bit, ENS (Ethereum Name Service) le .eth et GNUnet le .gnu. Chacun prenait son nom comme il voulait, sans concertation (il n'existe pas de forum ou d'organisation pour discuter de ces allocations). Cela entraine deux risques, celui de collision (deux systèmes de nommage utilisent le même TLD, ou bien un système de nommage utilise un TLD qui est finalement alloué dans le DNS, ce qui est d'autant plus probable qu'il n'existe pas de liste de ces TLD non-DNS). Il y a plusieurs solutions à ce problème, et l'IETF en a longuement discuté (cf. par exemple l'atelier EName de 2017, ou bien le très contestable RFC 8244). Ce RFC a donc mis des années à sortir. L'ICANN, par exemple, a essayé de diaboliser ces noms, attirant surtout l'attention sur leurs dangers. Autre méthode, il a été suggéré de créer un mécanisme de concertation pour éviter les collisions, création qui n'a jamais eu lieu, et pas pour des raisons techniques. Ce RFC 9476 propose simplement de mettre les noms non-DNS sous un TLD unique, le .alt. Ce TLD est réservé dans le registre des noms spéciaux (créé par le RFC 6761). Ainsi, si le système de résolution de Tor était créé aujourd'hui, on lui proposerait d'être en .onion.alt. Est-ce que les concepteurs de futurs systèmes de résolution non-DNS se plieront à cette demande ? Je suis assez pessimiste à ce sujet. Et il serait encore plus utopique de penser que les TLD non-DNS existants migrent vers .alt.

Comme .alt, par construction, regroupe des noms qui ne sont pas résolvables dans le DNS, un résolveur purement DNS ne peut que répondre tout de suite NXDOMAIN (ce nom n'existe pas) alors qu'un résolveur qui parle plusieurs protocoles peut utiliser le suffixe du nom comme une indication qu'il faut utiliser tel ou tel protocole. Si jamais des noms sous .alt sont réellement utilisés, ils ne devraient jamais apparaitre dans le DNS (par exemple dans les requêtes aux serveurs racine) mais, compte-tenu de l'expérience, il n'y a aucun doute qu'on les verra fuiter dans le DNS. Espérons que des techniques comme celles du RFC 8020, du RFC 8198 ou du RFC 9156 réduiront la charge sur les serveurs de la racine; et préserveront un peu la vie privée (section 4 du RFC).

Le nom .alt est évidemment une référence à alternative mais il rappelera des souvenirs aux utilisateurs d'Usenet. D'autres noms avaient été sérieusement discutés comme de préfixer tous les TLD non-DNS par un tiret bas. Mon catena.alt aurait été _catena :-) Tout ce qui touche à la terminologie est évidemment très sensible, et le RFC prend soin de souligner que le terme de « pseudo-TLD », qu'il utilise pour désigner les TLD non-DNS n'est pas péjoratif…

On note que le risque de collision existe toujours, mais sous .alt. Notre RFC ne prévoit pas de registre des noms sous .alt (en partie parce que l'IETF ne veut pas s'en mêler, son protocole, c'est le DNS, et en partie parce que ce milieu des mécanismes de résolution différents est très individualiste et pas du tout organisé).


Téléchargez le RFC 9476


L'article seul

RFC 9460: Service Binding and Parameter Specification via the DNS (SVCB and HTTPS Resource Records)

Date de publication du RFC : Novembre 2023
Auteur(s) du RFC : B. Schwartz (Meta Platforms), M. Bishop, E. Nygren (Akamai Technologies)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 8 avril 2024


Ces deux nouveaux types d'enregistrement DNS, SVCB et sa variante HTTPS, permettent de donner des informations supplémentaires à un client réseau avant qu'il ne tente de se connecter à un serveur. On peut envoyer ainsi des indications sur les versions des protocoles gérées, des clés cryptographiques ou des noms de serveurs supplémentaires.

Un client d'un service réseau a en effet plein de questions à se poser avant de tenter une connexion. Quelle adresse IP utiliser ? Quel port ? Chiffrement ou pas ? Les anciens mécanismes traitent la question de l'adresse IP (on la trouve par une requête DNS) et celle du port, si on se limite aux ports bien connus (comme 43 pour whois). Mais cela ne dit pas, par exemple, si le serveur HTTP distant accepte ou non HTTP/3 (RFC 9114). Par contre, cet enregistrement HTTPS de Cloudflare va bien nous dire que ce serveur accepte HTTP/2 et 3 :


% dig cloudflare.com HTTPS
…
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28399
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
…
;; ANSWER SECTION:
cloudflare.com.		300 IN HTTPS 1 . alpn="h3,h2" ipv4hint=104.16.132.229,104.16.133.229 ipv6hint=2606:4700::6810:84e5,2606:4700::6810:85e5
…
;; WHEN: Mon Apr 08 09:27:01 CEST 2024
;; MSG SIZE  rcvd: 226

  

Bon, en quoi consiste cet enregistrement SVCB ? Il a deux modes de fonctionnement, alias et service. Le premier mode sert à faire d'un nom une version canonique d'un autre, un peu comme le CNAME mais en étant utilisable à l'apex d'une zone. Le second mode sert à indiquer les paramètres techniques de la connexion. Un enregistrement SVCB (ou HTTPS) a trois champs dans ses données :

  • SvcPriority : quand il vaut zéro, il indique le mode alias. Autrement (par exemple dans le cas ci-dessus), il indique la priorité de ces paramètres.
  • TargetName : en mode alias, il indique le nom canonique, ou autrement un nom alternatif (pour un service accessible via plusieurs noms). Dans l'exemple Cloudflare ci-desssus, il valait la racine (un point) ce qui indique l'absence de nom alternatif (section 2.5).
  • SvcParams : une liste de couples {clé,valeur} pour les paramètres de connexion (uniquement en mode service). Dans le cas avec Cloudflare, c'était alpn="h3,h2" ipv4hint=104.16.132.229,104.16.133.229 ipv6hint=2606:4700::6810:84e5,2606:4700::6810:85e5. (Si vous vous intéressez aux débats à l'IETF, la question de la syntaxe de ces paramètres avait suscité une longue discussion.)

Les enregistrements SVCB ont le type 64 (enregistré à l'IANA) et les HTTPS, qui ont la même syntaxe et le même contenu, mais sont spécifiques à HTTP, ont le 65 (SVCB est générique). Les enregistrements HTTPS (et de futurs enregistrements pour d'autres protocoles) sont dits « compatibles avec SVCB » car ils ont la même syntaxe et la même sémantique.

Notre RFC définit (section 7) une liste de paramètres possibles mais d'autres peuvent être ajoutés dans un registre IANA, via la procédure « Examen par un expert » (RFC 8126). Pour l'instant, il y a, entre autres :

  • alpn : indique l'ALPN (RFC 7301).
  • ech : il servira à indiquer la clé à utiliser pour chiffrer le SNI.
  • port : comme son nom l'indique.
  • ipv4hint et ipv6hint : les adresses IP du service.

L'enregistrement peut (cela dépend des protocoles qui l'utilisent, HTTP ne le fait pas) être placé sur un sous-domaine indiquant le service, par exemple _8765._baz.api.example.com (section 10.4.5).

Idéalement, un serveur faisant autorité devrait renvoyer les SVCB et les HTTPS, s'ils sont présents, dans la section additionnelle de la réponse, lorsque le type demandé était une adresse IP. Mais ceux de Cloudflare ne semblent pas le faire actuellement. (PowerDNS le fait.)

Si vous vous intéressez aux questions opérationnelles, et que vous voulez mettre des enregistrements SVCB/HTTPS dans votre zone, la section 10 du RFC est faite pour vous. J'ai des enregistrements HTTPS pour ce blog :


# Un alias à l'apex (la priorité 0 indique le mode alias)
% dig +short +nodnssec bortzmeyer.org HTTPS
0 www.bortzmeyer.org.

# J'ai HTTP/2 (mais pas encore HTTP/3)
% dig +short +nodnssec www.bortzmeyer.org HTTPS
1 . alpn="h2"

Pour cela, j'ai mis dans le fichier de zone :

; Enregistrements SVCB (HTTPS).     

; HTTP/2 (mais pas encore - au 2024-04-08 - de HTTP/3)               
www IN HTTPS 1 . alpn="h2"

; alias                               
@ IN HTTPS 0 www.bortzmeyer.org.

Les clients HTTP récents, qui gèrent SVCB/HTTPS vont alors se connecter directement en HTTP/2 à https://www.bortzmeyer.org/ même si l'utilisateur demandait originellement http://bortzmeyer.org/ (le type d'enregistrement HTTPS, comme son nom l'indique, sert aussi à annoncer qu'on accepte HTTPS, ce qui permettra d'abandonner HSTS). Les clients HTTP plus anciens, évidemment, ne connaissent pas le système SVCB/HTTPS et il faut donc garder une configuration pour eux (par exemple des adresses IP à l'apex). Il y a aussi les autres méthodes, comme le Alt-Svc: du RFC 7838. La section 9.3 du RFC décrit le comportement attendu lorsque les différentes méthodes coexistent.

Faites attention toutefois, lorsque vous mettez ce type d'enregistrements dans votre zone, je ne connais pas encore d'outils de test permettant de vérifier la syntaxe des enregistrements, encore moins leur correspondance avec la réalité (par exemple, SSLLabs ne semble pas le faire). C'est un problème général de la signalisation sur l'Internet, quand on signale (notamment via le DNS) les capacités d'un serveur : le logiciel client doit de toute façon être prêt à tout, car il ne peut jamais être sûr que le signal est conforme aux faits.

En parlant d'anciens logiciels (clients et serveurs), vous pouvez trouver une liste de mises en œuvre de SVCB/HTTPS. Attention, elle est incomplète et pas à jour. Notez qu'il y a parfois des contraintes particulières, ainsi, il semble que Firefox ne demande des enregistrements HTTPS que s'il utilise DoH. iOS envoie des requêtes HTTPS depuis iOS 14, publié en septembre 2020, ce qui avait étonné, à l'époque.

En parlant de Firefox, s'il est assez récent, et s'il est configuré pour faire du DoH, vous pouvez tester le SVCB/HTTPS en allant dans about:networking#dnslookuptool. En entrant un nom de domaine, le champ « RR HTTP » doit renvoyer l'enregistrement HTTPS.

Avec un tcpdump récent, voici le trafic DNS utilisant le nouvel enregistrement DNS, qu'on peut observer sur un serveur faisant autorité pour bortzmeyer.org :

09:49:23.354974 IP6 2a04….31362 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 13024% [1au] HTTPS? www.bortzmeyer.org. (47)
09:52:06.094314 IP6 2a00….56551 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 40948% [1au] HTTPS? wWw.bOrTZmEyER.ORg. (62)
10:06:21.501437 IP6 2400….11624 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 59956 [1au] HTTPS? doh.bortzmeyer.fr. (46)
10:06:21.999608 IP6 2400….36887 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 17231 [1au] HTTPS? radia.bortzmeyer.org. (49)
10:25:53.947096 IP6 2001….54476 > 2001:4b98:dc0:41:216:3eff:fe27:3d3f.53: 26123% [1au] HTTPS? www.bortzmeyer.org. (47)
  

Si votre tcpdump est plus ancien, vous verrez Type65 au lieu de HTTPS.

Sinon, si vous aimez les bricolages (et celui-ci sera de moins en moins utile avec le temps, au fur et à mesure que les serveurs géreront ce type), pour fabriquer les enregistrements, vous pouvez utiliser cet outil, qui va fabriquer la forme binaire, directement chargeable par les serveurs faisant autorité :

% perl type65_https.pl 'example.net HTTPS 1 . alpn="h3,h2" ipv4hint="192.0.2.42" ipv6hint="2001:db8::42"'
example.net. TYPE65 ( \# 41 00010000010006026833026832000400
	04c000022a0006001020010db8000000 000000000000000042 )
  

(Il faut un Net::DNS récent sinon « unknown type "HTTPS" at /usr/share/perl5/Net/DNS/RR.pm line 671. in new Net::DNS::RR( www.bortzmeyer.org HTTPS 1 . alpn="h2" ) at type65_https.pl line 30. ».)

Quelques articles pas mal :


Téléchargez le RFC 9460


L'article seul

RFC 9432: DNS Catalog Zones

Date de publication du RFC : Juillet 2023
Auteur(s) du RFC : P. van Dijk (PowerDNS), L. Peltan (CZ.NI), O. Sury (Internet Systems Consortium), W. Toorop (NLnet Labs), C.R. Monshouwer, P. Thomassen (deSEC, SSE - Secure Systems Engineering), A. Sargsyan (Internet Systems Consortium)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 29 mars 2024


L'idée de base de ces « zones catalogue » est d'automatiser la configuration d'une nouvelle zone DNS sur les serveurs secondaires, en publiant dans le DNS les caractéristiques des zones qu'ils devront servir. Cela concerne donc surtout les gros hébergeurs qui ont beaucoup de zones.

Petit rappel : une zone, dans le DNS, est une partie contigüe de l'arbre des noms de domaine, gérée comme un tout (mêmes serveurs faisant autorité). Ainsi, si vous venez de louer le nom machin.example, un hébergeur DNS va configurer ses serveurs pour faire autorité pour ce nom. Par exemple, avec le logiciel NSD, sur le primaire (serveur maitre) :

zone:
        name: "machin.example"
        zonefile: "primary/machin.example"
        # Les serveurs secondaires :
        notify: 2001:db8:1::53
        provide-xfr: 2001:db8:1::53
        …

Et, sur un serveur secondaire (serveur esclave), qui transférera la zone depuis le primaire (RFC 5936) :

zone:
   name: "machin.example"
   # Le primaire :
   allow-notify: 2001:db8:cafe::1 NOKEY
   request-xfr: AXFR 2001:db8:cafe::1 NOKEY

Si on gère beaucoup de zones, avec des ajouts et des retraits tout le temps, l'avitaillement manuel est long et risqué (et si on oublie un serveur ?). Éditer ces fichiers sur tous les serveurs secondaires devient vite pénible. Et si les logiciels sur les secondaires sont différents les uns des autres (ce qui est recommandé, pour la robustesse), il faut se souvenir des différentes syntaxes. Pourquoi faire manuellement ce qu'on peut automatiser ? C'est le principe des zones catalogue.

Le principe est simple : une zone catalogue est une zone comme une autre, produite par les mêmes mécanismes (par exemple emacs) et qui sera servie par un serveur primaire à tous les secondaires, qui changeront alors automatiquement leur configuration en fonction du contenu de la zone catalogue. Chaque zone à configurer est un enregistrement de type PTR, dont la partie gauche est une étiquette interne et la partie droite indique le nom de la zone. Ici, on configure la zone rutaba.ga, l'étiquette (qui doit être unique) label1 est à usage interne (section 4.1 du RFC) :

label1.zones.catalog.example. IN PTR rutaba.ga.

Le reste est listé sous forme de propriétés (section 4.2). Une propriété évidente est l'adresse IP du primaire. Pour l'instant, elle doit être indiquée via le composant ext qui désigne les propriétés pas encore normalisées :

primaries.ext.catalog.example.    IN AAAA 2001:db8:bad:dcaf::42

La liste des propriétés figure dans un registre IANA.

À l'heure actuelle, de nombreux logiciels gèrent ces zones catalogues. Le site Web du projet (pas mis à jour depuis très longtemps) en liste plusieurs.

Voici un exemple complet de zone catalogue :

; -*- zone -*-
catalog.example.    IN SOA ns4.bortzmeyer.org. stephane.bortzmeyer.org. 2023120902 900 600 86400 1
catalog.example.    IN NS invalid.   ; Le NS est inutile mais
                         ; obligatoire et "invalid" est la valeur
                         ; recommandée (section 4).
version.catalog.example.    IN TXT "2"  ; Obligatoire, section 4.2.1

label1.zones.catalog.example. IN PTR rutaba.ga.
primaries.ext.catalog.example.    IN AAAA 2001:db8:bad:dcaf::42

La configuration de BIND pour l'utiliser :

# La zone catalogue se charge comme n'importe quelle zone. Ceci dit,
# vu le caractère critique de la zone catalogue, la section 7 du RFC
# insiste sur l'importance de sécuriser ce transfert, par exemple avec
# TSIG (RFC 8945) :
zone "catalog.example" {
        type slave;
        file "catalog.example";
        masters {
              2001:db8:666::;
        };            

};

# Et, ici, on la désigne comme spéciale :
options {
   ...      
   catalog-zones {
     zone "catalog.example"
              in-memory no;
   };
};

Naturellement, comme toujours lorsque on automatise, on risque le syndrome de l'apprenti sorcier. Attention donc en générant la zone catalogue. Comme le note le RFC (section 6) : « Great power comes with great responsibility. Catalog zones simplify zone provisioning by orchestrating zones on secondary name servers from a single data source: the catalog. Hence, the catalog producer has great power and changes must be treated carefully. For example, if the catalog is generated by some script and this script generates an empty catalog, millions of member zones may get deleted from their secondaries within seconds, and all the affected domains may be offline in a blink of an eye. »


Téléchargez le RFC 9432


L'article seul

RFC 9340: Architectural Principles for a Quantum Internet

Date de publication du RFC : Mars 2023
Auteur(s) du RFC : W. Kozlowski, S. Wehner (QuTech), R. Van Meter (Keio University), B. Rijsman, A. S. Cacciapuoti, M. Caleffi (University of Naples Federico II), S. Nagayama (Mercari)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF qirg
Première rédaction de cet article le 3 avril 2024


Voici un RFC assez futuriste qui explore à quoi pourrait ressembler un futur « Internet » quantique. Je divulgâche tout de suite : ce ne sera pas de si tôt.

Quelques avertissements s'imposent d'abord. Avant tout, rappelez-vous que la quantique produit des résultats qui sont parfaitement cohérents théoriquement et très bien vérifiés expérimentalement mais qui sont hautement non-intuitifs. Avant d'aborder le monde merveilleux de la quantique, n'oubliez pas d'oublier tout ce que vous croyez savoir sur le monde physique. Et ne comptez pas trop sur moi comme guide, on sort nettement ici de mon domaine de compétence. Ensuite, « quantique » est un terme à très forte charge marketing (moins que « IA » mais davantage que « métavers » ou même « blockchain », qui semblent bien passés de mode). Il faut donc être prudent chaque fois qu'un commercial ou un éditorialiste va dire que « c'est quantique » ou que « la quantique va bouleverser tel ou tel domaine ». Enfin, il y a loin de la coupe aux lèvres : de même qu'on n'a pas encore d'ordinateur quantique utile, on n'a pas encore de réseau quantique. Le RFC est à juste titre prudent, pointant les différents obstacles qui restent sur le chemin de l'Internet quantique.

Bon, ces précautions étant posées, qu'est-ce qu'un réseau quantique et pourquoi y consacrer un RFC ? La section 1 du RFC le résume : un réseau quantique est un réseau qui ferait communiquer des dispositifs quantiques pour faire des choses inimaginables avec un réseau classique. Il s'appuierait sur des propriétés spécifiques au monde quantique, notamment l'intrication, propriétés qui n'ont pas d'équivalent dans le monde classique. Attention, et le RFC insiste bien là-dessus, personne n'envisage de remplacer l'Internet classique par un Internet quantique (de la même façon que les futurs ordinateurs quantiques, étant loin d'être généralistes, ne remplaceront pas les ordinateurs classiques). Au contraire, le scénario envisagé est celui d'un réseau hybride, partiellement quantique. (Une lecture recommandée par le RFC est « The quantum internet ».)

Un exemple typique qui ne serait pas possible avec un réseau classique est celui de la distribution quantique de clés (parfois appelée du terme erroné de « cryptographie quantique »), dont l'utilité pratique est douteuse mais qui est assez spectaculaire et, contrairement à d'autres applications, est assez avancée techniquement. D'autres applications sont envisageables à plus long terme. C'est le cas par exemple du blind quantum computation, qui n'a pas encore d'article Wikipédia mais est expliqué dans cet article.

En laboratoire, beaucoup de résultats ont été obtenus. Les chercheurs et chercheuses ont déjà mis au point bien des dispositifs physiques étonnants. Mais à l'échelle du réseau, il n'y a pas encore eu beaucoup de travaux. Le RFC compare cette situation à celle d'un réseau classique où on aurait des fibres optiques et des lasers pour les illuminer mais aucun protocole de transport, aucun mécanisme de routage, encore moins de moyens de gérer le réseau. Développer une application pour un réseau quantique revient à toucher directement au matériel, comme, pour un réseau classique, s'il fallait que chaque application parle aux interfaces physiques, sans avoir d'interface de plus haut niveau comme les prises.

La section 2 du RFC est un rappel sur la quantique. Comme dit plus haut, c'est un domaine riche et complexe, où l'intuition ordinaire ne sert pas à grand'chose. Donc, lire ce rappel est une bonne idée mais n'espérez pas tout comprendre si vous n'êtes pas spécialiste de la question. Cette section est conçue pour des gens qui ne connaissent rien à la physique quantique, elle recommande, pour aller plus loin, le livre de Sutor Dancing with Qubits ou bien celui de Nielsen et Chuang, Quantum Computation and Quantum Information.

Le rappel commence avec la notion d'état quantique. Vous avez sans doute déjà entendu dire qu'un bit classique peut prendre deux valeurs, 0 ou 1, alors que son équivalent quantique, le qubit, a un état qui est une superposition de valeurs possibles, avec des probabilités. Lorsqu'on le mesure, on trouve un 0 ou un 1. (Oui, comme le célèbre chat qui est à la fois vivant et mort.) Attention, ces non-certitudes ne sont pas la conséquence d'un manque d'information mais sont une propriété fondamentale du monde quantique (Alain Aspect a eu un prix Nobel pour avoir prouvé cela). Notez que les versions HTML ou PDF du RFC sont recommandées ici, car il y a quelques équations. Comme un qubit est dans un état qui superpose les deux valeurs possibles, les opérations quantiques agissent sur tout l'état, par exemple l'équivalent quantique d'une porte NOT va inverser les probabilités du 0 et du 1 mais pas transformer un 0 en 1.

Le terme « qubit » (et cette distinction revient souvent dans le RFC) peut désigner aussi bien le concept abstrait que le truc physique qui va le mettre en œuvre (il existe plusieurs techniques pour fabriquer un engin qui gérera des qubits).

On peut ensuite assembler des qubits et, très vite, le nombre de possibilités croît. Mais l'intérêt de mettre des qubits ensemble est qu'on peut les intriquer et ce concept est au cœur de beaucoup de solutions quantiques, notamment du réseau quantique. Une fois intriqués, les deux qubits verront leur sort lié. Une mesure sur l'un affectera l'autre. (Rappel : la quantique n'est pas intuitive et l'intrication n'a pas d'équivalent dans le monde non-quantique, celui sur lequel a été bâtie notre intuition.) La mesure, comme toujours en quantique, est « destructive » au sens où elle ramène à un système classique (le qubit vaut 0 ou 1 quand on le mesure, pas un mélange des deux, et le chat est vivant ou mort quand on ouvre la boite).

Cette intrication est au cœur des réseaux quantiques (section 3 du RFC). Tous les projets de réseaux quantiques utilisent cette propriété (qui, rappelons-le, n'a pas d'équivalent non-quantique). L'intrication permet de corréler deux nœuds du réseau. Par exemple, pour se mettre d'accord sur une valeur, deux machines n'ont pas besoin de faire tourner des algorithmes de consensus, elles peuvent utiliser deux qubits intriqués, chacune en gardant un. Quand une machine lira la valeur de son qubit, elle sera certaine de la valeur lue par l'autre. Et l'intrication ne peut pas être partagée : un tiers ne peut pas s'intriquer avec une intrication existante, ce qui peut avoir des applications en sécurité.

Un réseau quantique est donc défini par notre RFC comme un ensemble de nœuds qui peuvent échanger des qubits intriqués.

Bon, tout ça, c'est très joli, mais comment on le réalise, ce réseau quantique ? La section 4 se penche sur les défis :

  • Comme toute mesure détruit le caractère quantique du qubit et le transforme en un bit ordinaire, des opérations banales dans le monde classique, comme la copie d'un bit, deviennent non triviales.
  • Et copier un qubit sans le mesurer ? On ne peut pas (c'est le théorème d'impossibilité du clonage).
  • Tout cela rend très difficile la correction d'erreurs. Un réseau réel, reposant sur des objets physiques, va forcément voir des erreurs (un rayon cosmique passe et paf, un 0 est transformé en 1) et de nombreuses techniques existent pour gérer ce problème dans le monde classique. Mais elles ne peuvent en général pas s'appliquer dans le monde quantique, qui va donc avoir un problème de fidélité : la fidélité est la conformité à ce qu'on souhaitait (elle va de 0 à 1), et les applications doivent en tenir compte.

Distribuer sur le réseau des qubits quelconques n'est pas forcément facile, donc le RFC suggère de plutôt distribuer des paires de Bell. On peut alors plus facilement (tout est relatif) faire de la téléportation, c'est-à-dire « transporter » un qubit d'un point à un autre. Ce n'est pas une violation du théorème d'impossibilité du clonage puisque le qubit n'est pas copié (il disparait de son point de départ). Notez que le terme de « téléportation » est surtout marketing : vous ne pourrez pas déplacer votre chat ou vous-même de cette façon.

Dernier problème, amplifier le signal (sans le copier !) pour tenir compte de sa dégradation avec la distance. Il existe une astuce, l'échange d'intrication, que je ne vais pas essayer d'expliquer, mais qui permet des réseaux quantiques sur des distances importantes.

Revenons à la correction d'erreurs. Les réseaux quantiques ne sont pas complètement démunis, et ont des solutions possibles, comme les codes quantiques.

OK, on a vu que le monde quantique était très spécial. Donc, le réseau quantique va être bizarre aussi, aux yeux de quelqu'un qui a l'habitude des réseaux classiques (section 5 du RFC). Par exemple, il fera face à ces problèmes :

  • C'est bien joli, les paires de Bell, mais ce n'est pas l'équivalent d'un paquet portant une charge utile. Il n'y a pas l'équivalent de l'en-tête du paquet, et le réseau quantique va donc devoir utiliser un réseau classique pour l'information de contrôle. On ne fera pas un réseau purement quantique.
  • Toute action sur des qubits intriqués doit être coordonnée entre les nœuds puisque l'action sur un qubit va déterminer le résultat d'une action sur les autres (il faut donc que chaque nœud sache contacter les autres).

Répétons-le, chaque nœud du réseau quantique devra également être relié à un réseau classique. Le réseau sera donc complexe et son administration pas évidente.

Une fois qu'on a accepté cela, le réseau classique pourra s'occuper d'opérations comme la construction des tables de routage, pour laquelle les algorithmes et méthodes classiques semblent suffire. On n'aura donc peut-être qu'un seul plan de contrôle (le classique) mais deux plans de données, le classique et le quantique.

Que faut-il construire comme machines pour le plan de données quantique ? D'abord, des répéteurs quantiques qui vont pouvoir créer les intrications, les échanger et contrôler la fidélité. Ensuite :

  • Des routeurs quantiques, qui, en plus des fonctions ci-dessus, participeront au routage.
  • Les nœuds ordinaires, des répéteurs qui ne participent pas au routage.
  • Les nœuds terminaux, qui pourront émettre et recevoir des qubits mais pas faire d'échange d'intrication. C'est là que tourneront les applications.

Facile, me direz-vous ? Non, construire ces machines va nécessiter de s'attaquer à quelques problèmes physiques :

  • Stocker un qubit est un défi ! Le monde classique n'aime pas les objets quantiques et essaie régulièrement de les rappeler à ses lois. Les bruits divers de l'environnement font rapidement perdre aux qubits leurs propriétés quantiques. (C'est d'ailleurs pour cela que vous ne rencontrez pas de chats de Schrödinger dans la vraie vie.) Cela se nomme la décohérence et c'est l'un des principaux obstacles sur la route des réseaux quantiques (ou des calculateurs quantiques). Les durées de vie qu'on atteint étaient, lors de la rédaction du RFC, de l'ordre de la seconde (ou de la minute si on n'est pas connecté au réseau, source de perturbations).
  • La capacité des liens quantiques est un autre problème. Générer des qubits intriqués prend du temps. Quand on en fabrique dix par seconde, on est contents. Bien sûr, la technique progresse sans cesse (pas mal des références du RFC sont un peu datées, car il a mis du temps à sortir) mais pas assez.
  • On l'a dit, on peut réaliser des qubits intriqués par différentes méthodes physiques, avec des performances différentes selon la métrique utilisée. Mais ces méthodes ne communiquent pas entre elles, ce qui veut dire que tous les nœuds du réseau doivent utiliser la même.

Si vous n'êtes pas découragé·e (mais il ne faut pas l'être : même si les difficultés sont colossales, le chemin est rigolo), il faut maintenant, en supposant qu'on aura les composants de base d'un réseau, les assembler. (À moins que le choix décrit dans le RFC des paires de Bell et de l'échange d'intrication ne soit remis en cause par les futurs progrès…) La section 6 se penche sur la question. Elle démarre par un bel excès d'optimisme, en expliquant que, contrairement à ce qui s'est passé avec l'Internet classique, on a de l'expérience sur la construction de réseau, et qu'on pourra donc ne pas faire d'erreur comme la taille trop réduite des adresses IPv4.

Des services essentiels pour un réseau réel seront difficiles à assurer sur un réseau quantique. Par exemple, l'impossibilité du clonage interdira d'utiliser un logiciel équivalent à tcpdump (remarquez, pour la sécurité, c'est un plus). Le RFC liste les principes de base d'un réseau quantique :

  • Le service de base sera l'intrication.
  • La fidélité est aussi un service (contrairement au réseau classique où on peut espérer une fidélité parfaite).
  • Le temps va être un facteur crucial, compte tenu de la décohérence. Pas question de faire patienter des qubits trop longtemps dans une file d'attente, par exemple.
  • Il faudra être flexible, notamment parce que le matériel va continuer à évoluer.

Et le RFC se termine par une exploration d'une architecture de réseau quantique possible, inspirée de MPLS. Dans ce réseau (pour l'instant) imaginaire, tout fonctionne en mode connecté (comme MPLS) : on doit d'abord créer un circuit virtuel (car créer les paires de Bell et les utiliser va nécessiter de la coordination, donc il vaut mieux établir d'abord une connexion). Ce QVC (Quantum Virtual Circuit) a des caractéristiques comme une qualité de service choisie, qui se décline en, par exemple, une capacité mesurée en nombre de paires de Bell par seconde et bien sûr une fidélité (toutes les applications des réseaux quantiques n'ont pas les mêmes exigences en terme de fidélité). La signalisation peut être décentralisée (comme avec RSVP) ou centralisée (comme avec OpenFlow). Comme vous le verrez en lisant cette conclusion du RFC, les détails sont encore approximatifs.

Ce RFC a mis longtemps à être écrit, vous pouvez trouver une description ancienne du projet sur le blog de l'IETF. Notez que l'écriture de ce RFC a été en partie financée par la Quantum Internet Alliance européenne.

N'hésitez pas à vous plonger dans la bibliographie très détaillée de ce RFC, vous y trouverez beaucoup de lectures passionnantes. Il y a même déjà des livres entiers sur les réseaux quantiques comme celui de Van Meter.


Téléchargez le RFC 9340


L'article seul

RFC 9309: Robots Exclusion Protocol

Date de publication du RFC : Septembre 2022
Auteur(s) du RFC : M. Koster, G. Illyes, H. Zeller, L. Sassman (Google)
Chemin des normes
Première rédaction de cet article le 14 septembre 2022


Quand vous gérez un serveur Internet (pas forcément un serveur Web) prévu pour des humains, une bonne partie du trafic, voire la majorité, vient de robots. Ils ne sont pas forcément malvenus mais ils peuvent être agaçants, par exemple s'ils épuisent le serveur à tout ramasser. C'est pour cela qu'il existe depuis longtemps une convention, le fichier robots.txt, pour dire aux robots gentils ce qu'ils peuvent faire et ne pas faire. La spécification originale était très limitée et, en pratique, la plupart des robots comprenait un langage plus riche que celui du début. Ce nouveau RFC documente plus ou moins le format actuel.

L'ancienne spécification, qui date de 1996, est toujours en ligne sur le site de référence (qui ne semble plus maintenu, certaines informations ont des années de retard). La réalité des fichiers robots.txt d'aujourd'hui est différente, et plus riche. Mais, comme souvent sur l'Internet, c'est assez le désordre, avec différents robots qui ne comprennent pas la totalité du langage d'écriture des robots.txt. Peut-être que la publication de ce RFC aidera à uniformiser les choses.

Donc, l'idée de base est la suivante : le robot qui veut ramasser des ressources sur un serveur va d'abord télécharger un fichier situé sur le chemin /robots.txt. (Au passage, si cette norme était faite aujourd'hui, on utiliserait le /.well-known du RFC 8615.) Ce fichier va contenir des instructions pour le robot, lui disant ce qu'il peut récupérer ou pas. Ces instructions concernent le chemin dans l'URI (et robots.txt n'a donc de sens que pour les serveurs utilisant ces URI du RFC 3986, par exemple les serveurs Web). Un exemple très simple serait ce robots.txt :

User-Agent: *
Disallow: /private
  

Il dit que, pour tous les robots, tout est autorisé sauf les chemins d'URI commençant par /private. Un robot qui suit des liens (RFC 8288) doit donc ignorer ceux qui mènent sous /private.

Voyons maintenant les détails pratiques (section 2 du RFC). Un robots.txt est composé de groupes, chacun s'appliquant à un robot ou un groupe de robots particulier. Les groupes sont composés de règles, chaque règle disant que telle partie de l'URI est interdite ou autorisée. Par défaut, tout est autorisé (même chose s'il n'y a pas de robots.txt du tout, ce qui est le cas de ce blog). Un groupe commence par une ligne User-Agent qui va identifier le robot (ou un astérisque pour désigner tous les robots) :

User-Agent: GoogleBot
  

Le robot va donc chercher le ou les groupes qui le concernent. En HTTP, normalement, l'identificateur que le robot cherche dans le robots.txt est une sous-chaine de l'identificateur envoyé dans le champ de l'en-tête HTTP User-Agent (RFC 9110, section 10.1.5).

Le robot doit combiner tous les groupes qui s'appliquent à lui, donc on voit parfois plusieurs groupes avec le même User-Agent.

Le groupe est composé de plusieurs règles, chacune commençant par Allow ou Disallow (notez que la version originale de la spécification n'avait pas Allow). Si plusieurs règles correspondent, le robot doit utiliser la plus spécifique, c'est-à-dire celle dont le plus d'octets correspond. Par exemple, avec :

Disallow: /private
Allow: /private/notcompletely
  

Une requête pour /private/notcompletely/foobar sera autorisée. L'ordre des règles ne compte pas (mais certains robots sont bogués sur ce point). C'est du fait de cette notion de correspondance la plus spécifique qu'on ne peut pas compiler un robots.txt en une simple expression rationnelle, ce qui était possible avec la spécification originelle. Si une règle Allow et une Disallow correspondent, avec le même nombre d'octets, l'accès est autorisé.

Le robot a le droit d'utiliser des instructions supplémentaires, par exemple pour les sitemaps.

En HTTP, le robot peut rencontrer une redirection lorsqu'il essaie de récupérer le robots.txt (code HTTP 301, 302, 307 ou 308). Il devrait dans ce cas la suivre (mais pas infinement : le RFC recommande cinq essais au maximum). S'il n'y a pas de robots.txt (en HTTP, code de retour 404), tout est autorisé (c'est le cas de mon blog). Si par contre il y a une erreur (par exemple 500 en HTTP), le robot doit attendre et ne pas se croire tout permis. Autre point HTTP : le robot peut suivre les instructions de mémorisation du robots.txt données, par exemple, dans le champ Cache-control de l'en-tête.

Avant de passer à la pratique, un peu de sécurité. D'abord, il faut bien se rappeler que le respect du robots.txt dépend de la bonne volonté du robot. Un robot malveillant, par exemple, ne tiendra certainement pas compte du robots.txt. Mais il peut y avoir d'autres raisons pour ignorer ces règles. Par exemple, l'obligation du dépôt légal fait que la BNF annonce explicitement qu'elle ignore ce fichier. (Et un programme comme wget a une option, -e robots=off, pour débrayer la vérification du robots.txt.) Bref, un robots.txt ne remplace pas les mesures de sécurité, par exemple des règles d'accès aux chemins définies dans la configuration de votre serveur HTTP. Le robots.txt peut même diminuer la sécurité, en indiquant les fichiers « intéressants » à un éventuel attaquant.

Passons maintenant à la pratique. On trouve plein de mises en œuvre de robots.txt un peu partout mais en trouver une parfaitement conforme au RFC est bien plus dur. Disons-le clairement, c'est le bordel, et l'auteur d'un robots.txt ne peut jamais savoir comment il va être interprété. Il ne faut donc pas être trop subtil dans son robots.txt et en rester à des règles simples. Du côté des robots, on a un problème analogue : bien des robots.txt qu'on rencontre sont bogués. La longue période sans spécification officielle est largement responsable de cette situation. Et tout n'est pas clarifié par le RFC. Par exemple, la grammaire en section 2.2 autorise un Disallow ou un Allow à être vide, mais sans préciser clairement la sémantique associée.

Pour Python, il y a un module standard, mais qui est loin de suivre le RFC. Voici un exemple d'utilisation :

import urllib.robotparser
import sys

if len(sys.argv) != 4:
    raise Exception("Usage: %s robotstxt-file user-agent path" % sys.argv[0])
input = sys.argv[1]
ua = sys.argv[2]
path = sys.argv[3]
parser = urllib.robotparser.RobotFileParser()
parser.parse(open(input).readlines())
if parser.can_fetch(ua, path):
    print("%s can be fetched" % path)
else:
    print("%s is denied" % path)

Et, ici, on se sert de ce code :

% cat ultra-simple.txt 
User-Agent: *
Disallow: /private

% python test-robot.py ultra-simple.txt foobot /public/test.html         
/public/test.html can be fetched

% python test-robot.py ultra-simple.txt foobot /private/test.html
/private/test.html is denied

Mais ce module ne respecte pas la précédence des règles :

% cat precedence.txt
User-Agent: *
Disallow: /
Allow: /bar.html

% python3 test-robot.py precedence.txt foobot /bar.html
/bar.html is denied

En application de la règle la plus spécifique, /bar.html aurait dû être autorisé. Si on inverse Allow et Disallow, on obtient le résultat attendu. Mais ce n'est pas normal, l'ordre des règles n'étant normalement pas significatif. Autre problème du module Python, il ne semble pas gérer les jokers, comme l'exemple *.gif$ du RFC. Il existe des modules Python alternatifs pour traiter les robots.txt mais aucun ne semble vraiment mettre en œuvre le RFC.

La situation n'est pas forcément meilleure dans les autres langages de programmation. Essayons avec Elixir. Il existe un module pour cela. Écrivons à peu près le même programme qu'en Python :

usage = "Usage: test robotstxtname useragent path"
filename =
  case Enum.at(System.argv(), 0) do
    nil -> raise RuntimeError, usage
    other -> other
  end
content =
  case File.read(filename) do
    {:ok, data} -> data
    {:error, reason} -> raise RuntimeError, "#{filename}: #{reason}"
  end
ua =
  case Enum.at(System.argv(), 1) do
    nil -> raise RuntimeError, usage
    other -> other
  end
statuscode = 200
{:ok, rules} = :robots.parse(content, statuscode) 
path =
  case Enum.at(System.argv(), 2) do
    nil -> raise RuntimeError, usage
    other -> other
  end
IO.puts(
  case :robots.is_allowed(ua, path, rules) do
    true -> "#{path} can be fetched"
    false -> "#{path} can NOT be fetched"
  end)
  

Et testons-le :

% mix run test-robot.exs ultra-simple.txt foobot /public/test.html   
/public/test.html can be fetched

% mix run test-robot.exs ultra-simple.txt foobot /private/test.html  
/private/test.html can NOT be fetched
  

Il semble avoir moins de bogues que le module Python mais n'est quand même pas parfait.

Comme dit plus haut, le robots.txt n'est pas réservé au Web. Il est utilisé par exemple pour Gemini. Ainsi, le robot de collecte Lupa lit les robots.txt et ne va pas ensuite récupérer les URI interdits. En septembre 2022, 11 % des capsules Gemini avaient un robots.txt.


Téléchargez le RFC 9309


L'article seul

RFC 9299: An Architectural Introduction to the Locator/ID Separation Protocol (LISP)

Date de publication du RFC : Octobre 2022
Auteur(s) du RFC : A. Cabellos (UPC-BarcelonaTech), D. Saucez (INRIA)
Pour information
Réalisé dans le cadre du groupe de travail IETF lisp
Première rédaction de cet article le 3 novembre 2022


Le protocole réseau LISP (Locator/ID Separation Protocol, rien à voir avec le langage de programmation du même nom) est normalisé dans le RFC 6830 et plusieurs autres qui l'ont suivi. Ce RFC 6830 est un peu long à lire et ce nouveau RFC propose donc une vision de plus haut niveau, se focalisant sur l'architecture de LISP. Cela peut donc être un bon point de départ vers les RFC LISP.

L'idée de base de LISP est de séparer l'identificateur du localisateur, comme recommandé par le RFC 4984. Un identificateur désigne une machine, un localisateur sa position dans l'Internet. Aujourd'hui, les adresses IP servent pour les deux, ne satisfaisant parfaitement aucun des deux buts : les identificateurs devraient être stables (une machine qui change de réseau ne devrait pas en changer), les localisateurs devraient être efficaces et donc être liés à la topologie, et agrégeables.

LISP a donc deux classes : les identificateurs, ou EID (End-host IDentifier) et les localisateurs, ou RLOC (Routing LOCators). Les deux ont la syntaxe des adresses IP (mais pas leur sémantique). Un système de correspondance permet de passer de l'un à l'autre (par exemple, je connais l'EID de mon correspondant, je cherche le RLOC pour lui envoyer un paquet). LISP est plutôt prévu pour être mis en œuvre dans les routeurs, séparant un cœur de l'Internet qui n'utilise que les RLOC, d'une périphérie qui utiliserait les EID (avec, entre les deux, les routeurs LISP qui feraient la liaison).

En résumé (accrochez-vous, c'est un peu compliqué) :

  • LISP peut être vu comme un réseau virtuel (overlay) au-dessus de l'Internet existant (underlay),
  • Les RLOC n'ont de sens que dans le réseau sous-jacent, l'underlay,
  • Les EID n'ont de sens que dans le réseau virtuel overlay,
  • Entre les deux, le système de correspondance,
  • Dans le réseau sous-jacent, les RLOC servent de localisateur et d'identificateur,
  • Dans un site à la périphérie, les EID servent d'identificateur et de localisateur pour les autres machines du site.

C'est ce côté « solution dans les routeurs » et donc le fait que les machines terminales ne savent même pas qu'elles font du LISP, qui distingue LISP des autres solutions fondées sur la séparation de l'identificateur et du localisateur, comme ILNP (RFC 6740).

Au passage, pourquoi avoir développé LISP ? Quel était le problème à résoudre ? Outre la beauté conceptuelle de la chose (il n'est pas esthétique de mêler les fonctions d'identificateur et de localisateur), le principal problème à traiter était celui du passage à l'échelle du système de routage de l'Internet, décrit dans le RFC 4984. En gros, le nombre de routes distinctes augmente trop vite et menace la stabilité des routeurs de la DFZ. Le but était donc, par une nouvelle architecture, de rendre inutile certains choix qui augmentent la taille de la table de routage (comme la désagrégation des préfixes IP, afin de faire de l'ingénierie de trafic). Le RFC 7215 décrit comment LISP aide dans ce cas.

La section 7 du RFC décrit les différents scénarios d'usage de LISP :

  • Ingénierie de trafic : aujourd'hui, dans l'Internet classique BGP, quand on veut orienter le trafic entrant, le faire passer par un chemin donné, on utilise souvent la désagrégation des préfixes. Comme indiqué plus haut, cela aggrave la pression sur la table de routage globale. L'indirection que permet LISP dispense de cette solution : on peut publier dans le système de correspondance les RLOC qu'on veut.
  • Transition vers IPv6 : les EID et les RLOC ont la forme syntaxique d'une adresse IP, v4 ou v6, et il n'y a pas d'obligation qu'EID et RLOC aient la même version. On peut avoir des EID IPv6 et des RLOC IPv4, et cela fournit un mécanisme de tunnel permettant de connecter deux sites LISP IPv6 au-dessus de réseaux qui seraient purement IPv4. L'avantage par rapport aux techniques de transition actuelles est l'intégration dans une solution plus générale et plus « propre ».
  • LISP permet également de faire des VPN, ou de gérer la mobilité de réseaux entiers (un réseau qui se déplace, et donc change de RLOC, c'est juste la correspondance dans le sous-système de contrôle qu'il faut changer, et tout les partenaires routeront vers les nouveaux RLOC).

La section 3 du RFC décrit en détail l'architecture de LISP (après, vous serez mûr·e·s pour lire les RFC LISP eux-mêmes, en commençant par le RFC 6830). Elle repose sur quatre principes :

  • La séparation entre l'identificateur (le EID) et le localisateur (le RLOC),
  • Deux sous-systèmes différents, le contrôle et les données, utilisant des protocoles différents, et pouvant évoluer séparement (enfin, dans une certaine mesure),
  • Une architecture overlay : LISP est déployé sur un réseau virtuel au-dessus de l'Internet existant, ce qui évite les approches « table rase », qui ont une probabilité de déploiement à peu près nulle,
  • Un protocole déployable de manière incrémentale, pas besoin d'attendre que tout le monde s'y mette, il y a des avantages à déployer LISP même pour les premiers à l'adopter (ce problème de déploiement est une des plaies de l'Internet actuel, comme on le voit avec IPv6).

La séparation entre identificateur et localisateur n'est pas faite au niveau de la machine individuelle, comme avec ILNP, mais à la frontière entre la périphérie de l'Internet (the edge) et son cœur (the core, en gros, la DFZ et quelques routeurs en plus). La périphérie travaille avec des EID (elle ne sait même pas que LISP est utilisé), le cœur avec des RLOC. Contrairement à ILNP, il n'y a donc pas une stricte séparation entre identificateurs et localisateurs : ils ont la même syntaxe (qui est celle d'une adresse IP, v4 ou v6) et, à part les routeurs d'entrée et de sortie des tunnels LISP, personne ne sait s'il utilise un EID ou un RLOC : les machines terminales manipulent des EID, les routeurs du cœur des RLOC, tout en croyant que ce sont des adresses IP ordinaires. Seuls les routeurs à la frontière entre les deux mondes connaissent LISP (et auront donc besoin d'un logiciel adapté).

Un Internet LISP est donc une série de « sites LISP » (des réseaux de périphérie accessibles par LISP) connectés par des tunnels entre eux, au-dessus du cœur actuel. Le routeur d'entrée du tunnel se nomme ITR (pour Ingress Tunnel Router) et celui de sortie ETR (pour Egress Tunnel Router). Le terme de xTR (pour « ITR ou bien ETR ») est parfois utilisé pour désigner un routeur LISP, qu'il soit d'entrée ou de sortie lisp-arch

Le sous-système des données (data plane) se charge d'encapsuler et de décapsuler les paquets, puis de les transmettre au bon endroit. (Sa principale qualité est donc la rapidité : il ne faut pas faire attendre les paquets.) Les ITR encapsulent un paquet IP qui vient d'un site LISP (dans un paquet UDP à destination du port 4341), puis l'envoient vers l'ETR. À l'autre bout du tunnel, les ETR décapsulent le paquet. Dans le tunnel, les paquets ont donc un en-tête intérieur (un en-tête IP normal, contenant les EID source et destination), qui a été placé par la machine d'origine, et un en-tête extérieur (contenant le RLOC source, celui de l'ITR, et le RLOC de destination, celui de l'ETR puis, après l'en-tête UDP, l'en-tête spécifique de LISP). Rappelez-vous que les routeurs du cœur ne connaissent pas LISP, ils font suivre ce qui leur semble un paquet IP ordinaire. Les routeurs LISP utilisent des tables de correspondance entre EID et RLOC pour savoir à quel ETR envoyer un paquet.

Ces tables ont été apprises du sous-système de contrôle (control plane, qui contient la fonction de correspondance - mapping), le routeur ayant un cache des correspondances les plus récentes. Cette fonction de correspondance, un des points les plus délicats de LISP (comme de tout système de séparation de l'identificateur et du localisateur) est décrite dans le RFC 6833. Son rôle peut être comparé à celui du DNS et de BGP dans l'Internet classique.

Une correspondance est une relation entre un préfixe d'identificateurs (rappelez-vous que les EID sont, syntaxiquement, des adresses IP ; on peut donc utiliser des préfixes CIDR) et un ensemble de localisateurs, les RLOC des différents routeurs possibles pour joindre le préfixe convoité.

Le RFC 6833 normalise une interface avec ce système de correspondance. Il y a deux sortes d'entités, le Map Server, qui connait les correspondances pour certains préfixes (car les ETR lui ont raconté les préfixes qu'ils servent), et le Map Resolver, qui fait les requêtes (il est typiquement dans l'ITR, ou proche). Quatre messages sont possibles (les messages de contrôle LISP sont encpasulés en UDP, et vers le port 4342) :

  • Map-Register : un ETR informe son Map Server des préfixes EID qu'il sait joindre,
  • Map-Notify : la réponse de l'ETR au message précédent,
  • Map-Request : un ITR (ou bien un outil de débogage comme lig, cf. RFC 6835) cherche les RLOC correspondant à un EID,
  • Map-Reply : un Map Server ou un ETR lui répond.

Un point important de LISP est qu'il peut y avoir plusieurs mécanismes de correspondance EID->RLOC, du moment qu'ils suivent les messages standard du RFC 6833. On pourra donc, dans le cadre de l'expérience LISP, changer de mécanisme pour voir, pour tester des compromis différents. Notre RFC rappele l'existence du système ALT (RFC 6836, fondé, comme BGP sur un graphe. Mais aussi celle d'un mécanisme utilisant une base « plate » (NERD, RFC 6837), un mécanisme arborescent nommé DDT (RFC 8111), des DHT, etc. On pourrait même, dans des déploiements privés et locaux, avoir une base centralisée avec un seul serveur.

ALT, normalisé dans le RFC 6836, est le système de correspondance « historique » de LISP, et il est souvent présenté comme le seul dans les vieux documents. Il repose sur BGP, protocole bien maitrisé par les administrateurs de routeurs, ceux qui auront à déployer LISP. L'idée de base est de connecter les serveurs ALT par BGP sur un réseau virtuel au-dessus de l'Internet.

DDT, dans le RFC 8111, lui, ressemble beaucoup plus au DNS, par sa structuration arborescente des données, et sa racine.

Évidemment, tout l'Internet ne va pas migrer vers LISP instantanément. C'est pour cela que notre RFC mentionne les problèmes de communication avec le reste du monde. Les EID ne sont typiquement pas annoncés dans la table de routage globale de l'Internet. Alors, comment un site pourra-t-il communiquer avec un site LISP ? Le mécanisme décrit dans le RFC 6832 utilise deux nouvelles sortes de routeurs : les PITR (Proxy Ingress Tunnel Router) et les PETR (Proxy Egress Tunnel Router). Le PITR annonce les EID en BGP vers l'Internet, en les agrégeant le plus possible (l'un des buts de LISP étant justement d'éviter de charger la table de routage globale). Il recevra donc les paquets envoyés par les sites Internet classiques et les fera suivre par les procédures LISP normales. A priori, c'est tout : le site LISP peut toujours envoyer des paquets vers l'Internet classiques en ayant mis un EID en adresse IP source. Mais cela peut échouer pour diverse raisons (uRPF, par exemple) donc on ajoute le PETR : il recevra le paquet du site LISP et le transmettra.

Voici pour les principes de LISP. Mais, si vous travaillez au quotidien comme administrateur d'un réseau, vous avez sans doute à ce stade plein de questions concrètes et opérationnelles. C'est le moment de lire la section 4 de ce RFC. Par exemple, la gestion des caches : un routeur LISP ne peut pas faire appel au système de correspondance pour chaque paquet qu'il a à transmettre. Le sous-système des données tuerait complètement le sous-système de contrôle, si un routeur s'avisait de procéder ainsi. Il faut donc un cache, qui va stocker les réponses aux questions récentes. Qui dit cache dit problèmes de cohérence des données, puisque l'information a pu changer entre la requête, et l'utilisation d'une réponse mise en cache. Pour gérer cette cohérence, LISP dispose de divers mécanismes, notamment un TTL (Time To Live) : l'ETR le définit, indiquant combien de temps les données peuvent être utilisées (c'est typiquement 24 h, aujourd'hui).

Autre problème pratique cruciale, la joignabilité des RLOC. C'est bien joli de savoir que telle machine a tel RLOC mais est-ce vrai ? Peut-on réellement lui parler ou bien tous les paquets vont-ils finir dans un trou noir ? Un premier mécanisme pour transporter l'information de joignabilité est les LSB (Locator Status Bits). Transportés dans les paquets LISP, ces bits indiquent si un RLOC donné est joignable par l'ETR qui a envoyé le paquet. Évidemment, eux aussi peuvent être faux, donc, s'il existe une communication bi-directionnelle, il est préférable d'utiliser le mécanisme des numniques. Quand un ITR écrit à un ETR, il met un numnique dans le paquet, que l'ETR renverra dans son prochain paquet. Cette fois, plus de doute, l'ETR est bien joignable. Si l'ITR est impatient et veut une réponse tout de suite, il peut tester activement la joignabilité, en envoyant des Map-Request.

LISP est souvent présenté avec un modèle simplifié où chaque site est servi par un seul ETR, qui connait les EID du site et les annonce au Map Server. Mais, dans la réalité, les sites sérieux ont plusieurs ETR, pour des raisons de résilience et de répartition de charge. Cela soulève le problème de leur synchronisation : ces ETR doivent avoir des configurations compatibles, pour annoncer les mêmes RLOC pour leurs EID. Pour l'instant, il n'existe pas de protocole pour cela, on compte sur une synchronisation manuelle par l'administrateur réseaux.

Enfin, comme LISP repose sur des tunnels, il fait face à la malédiction habituelle des tunnels, les problèmes de MTU. Du moment qu'on encapsule, on diminue la MTU (les octets de l'en-tête prennent de la place) et on peut donc avoir du mal à parler avec les sites qui ont une MTU plus grande, compte-tenu de la prévalence d'erreurs grossières de configuration, comme le filtrage d'ICMP. La section 4.4 de notre RFC décrit le traitement normal de la MTU dans LISP et ajoute que des mécanismes comme celui du RFC 4821 seront peut-être nécessaires.

Dans l'Internet d'aujourd'hui, une préoccupation essentielle est bien sûr la sécurité : d'innombrables menaces pèsent sur les réseaux (section 8 du RFC). Quelles sont les problèmes spécifiques de LISP en ce domaine ? Par exemple, certains systèmes de correspondance, comme DDT, sont de type pull : on n'a pas l'information à l'avance, on va la chercher quand on en a besoin. Cela veut dire qu'un paquet de données (sous-système des données) peut indirectement déclencher un événement dans le sous-système de contrôle (par la recherche d'une correspondance EID->RLOC afin de savoir où envoyer le paquet). Cela peut affecter la sécurité.

D'autant plus que le sous-système de contrôle sera typiquement mis en œuvre dans le processeur généraliste des routeurs, beaucoup moins rapide que les circuits électroniques spécialisés qui servent à la transmission des données. Un attaquant qui enverrait des tas de paquets vers des EID différents pourrait, à un coût très faible pour lui, déclencher plein de demandes à DDT, ralentissant ainsi sérieusement les routeurs LISP. Une mise en œuvre naïve de LISP où toute requête pour un EID absent du cache déclencherait systématiquement une MAP-Request serait très vulnérable. Une limitation du trafic est donc nécessaire.

En parlant du système de correspondance, il représente évidemment un talon d'Achille de LISP. Si son intégrité est compromise, si des fausses informations s'y retrouvent, les routeurs seront trahis et enverront les paquets au mauvais endroit. Et si le système de correspondance est lent ou en panne, par exemple suite à une attaque par déni de service, le routage ne se fera plus du tout (à part pour les EID encore dans les caches des routeurs). On peut donc comparer ce système de correspondance au DNS dans l'Internet classique, par son côté crucial pour la sécurité.

Il faut donc des « bons » Map Server, qui suivent bien le RFC 6833 (notamment sa section 6) et, peut-être dans le futur, des Map Servers qui gèrent l'extension de sécurité LISP-Sec (si son RFC est publié un jour).

Dernier point de sécurité, le fait que LISP puisse faire du routage asymétrique (le chemin d'Alice à Bob n'est pas le même que celui de Bob à Alice). Rien d'extraordinaire à cela, c'est pareil pour lee routage Internet classique, mais il faut toujours se rappeler que cela a des conséquences de sécurité : par exemple, un pare-feu ne verra, dans certains cas, qu'une partie du trafic.

On trouvera plus de détails sur les attaques qui peuvent frapper LISP dans le RFC 7835.

Pour ceux qui sont curieux d'histoire des technologies, l'annexe A du RFC contient un résumé de LISP. Tout avait commencé à Amsterdam en octobre 2006, à l'atelier qui avait donné naissance au RFC 4984. Un groupe de participants s'était alors formé, avait échangé, et le premier Internet-Draft sur LISP avait été publié en janvier 2007. En même temps, et dans l'esprit traditionnel de l'Internet (running code), la programmation avait commencé et les premiers routeurs ont commencé à gérer des paquets LISP en juin 2007.

Le groupe de travail IETF officiel a été ensuite créé, en mars 2009. Les premiers RFC sont enfin sortis en 2013.

LISP n'a pas toujours été comme aujourd'hui ; le protocole initial était plutôt une famille de protocoles, désignés par des numéros, chacun avec des variantes sur le concept de base. Cela permettait de satisfaire tous les goûts mais cela compliquait beaucoup le protocole. On avait LISP 1, où les EID étaient routables dans l'Internet normal (ce qui n'est plus le cas), LISP 1.5 où ce routage se faisait dans un réseau séparé, LISP 2 où les EID n'étaient plus routables, et où la correspondance EID->RLOC se faisait avec le DNS, et enfin LISP 3 où le système de correspondance était nouveau (il était prévu d'utiliser une DHT...). Le LISP final est proche de LISP 3.


Téléchargez le RFC 9299


L'article seul

RFC 9297: HTTP Datagrams and the Capsule Protocol

Date de publication du RFC : Août 2022
Auteur(s) du RFC : D. Schinazi (Google), L. Pardue (Cloudflare)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF masque
Première rédaction de cet article le 8 septembre 2022


Ce RFC est le premier du groupe de travail MASQUE qui développe des techniques pour relayer du trafic IP. Dans ce RFC, il s'agit de faire passer des datagrammes sur HTTP.

Pourquoi, sur HTTP ? Parce que c'est le seul protocole dont on est raisonnablement sûr qu'il passera partout. En bâtissant un service de datagrammes sur HTTP (et non pas directement sur IP), on arrivera à le faire fonctionner à tous les endroits où HTTP fonctionne.

En fait, ce RFC décrit deux mécanismes assez différents, mais qui visent le même but, la transmission de datagrammes sur HTTP. Le premier mécanisme est le plus simple, utiliser HTTP juste pour lancer une session qui utilisera les datagrammes de QUIC, normalisés dans le RFC 9221. (QUIC seul ne passe pas partout, d'où la nécessité d'insérer une couche HTTP.) Ce premier mécanisme dépend de QUIC donc ne marche qu'avec HTTP/3 (RFC 9114). Un second mécanisme est normalisé, dans notre RFC ; nommé Capsule, il permet de faire passer des datagrammes (ou à peu près) sur HTTP/2 (RFC 9113) et HTTP/1 (RFC 9112). Capsule est plus général, mais moins efficace.

Ces mécanismes ne sont pas vraiment prévus pour des utilisations par des applications, mais plutôt pour des extensions à HTTP (RFC 9110, section 16). Par exemple, le service CONNECT (RFC 9110, section 9.3.6) pourrait être doublé par un service équivalent, mais utilisant des datagrammes, au lieu du transfert fiable que fait CONNECT (cf. RFC 9298). Même chose pour les Web sockets du RFC 6455.

La section 2 de notre RFC explique de quels datagrammes on parle. Il s'agit de paquets de données dont l'ordre d'arrivée et l'acheminement ne sont pas garantis, et qui vont sans doute consommer moins de ressources. Sur HTTP/3 (qui utilise QUIC), ils vont utiliser les trames QUIC de type DATAGRAM (RFC 9221). Ce sont les « meilleurs » datagrammes HTTP, ceux qui collent le plus à la sémantique des datagrammes. Sur HTTP/2, tout acheminement de données est garanti. Si on veut faire des datagrammes, il va falloir utiliser le protocole Capsule, décrit dans la section 3. (Notez que HTTP/2 ne garantit pas l'ordre d'arrivée si les datagrammes sont transportés dans des ruisseaux différents.) Et pour HTTP/1 ? Le protocole ne convient guère puisqu'il garantit l'ordre d'arrivée et l'acheminement, justement les propriétés auxquelles on était prêt à renoncer. Là aussi, on se servira de Capsule.

HTTP utilise différentes méthodes pour faire ses requêtes (les deux plus connues sont GET et POST). Les datagrammes ne sont utilisables qu'avec des méthodes qui les acceptent explicitement. Donc, pas de GET ni de POST, plutôt du CONNECT.

Sur HTTP/3, le champ de données du datagramme QUIC aura le format :

HTTP/3 Datagram {
     Quarter Stream ID (i),
     HTTP Datagram Payload (..),
   }
  

Le quarter stream ID est l'identificateur du ruisseau QUIC du client où a été envoyée la requête HTTP, divisé par 4 (ce qu'on peut toujours faire, vu la façon dont sont générés ces identificateurs, RFC 9000, section 2.1).

Ah, et comme les datagrammes ne sont pas acceptés par défaut en HTTP/3, il faudra envoyer au début de la session un paramètre SETTINGS_H3_DATAGRAM (enregistré à l'IANA).

Les capsules, maintenant. Inutiles en HTTP/3, elles sont la seule façon de faire du datagramme en HTTP/1 et HTTP/2. Le protocole Capsule permet d'envoyer des données encodées en TLV sur une connexion HTTP. On indique qu'on va l'utiliser, en HTTP/1 avec le champ Upgrade: (RFC 9110, section 7.8) et en HTTP/2 avec un CONNECT étendu (cf. RFC 8441). Les upgrade tokens (les identificateurs de protocoles utilisés, enregistrés à l'IANA) sont décrits dans la section 16.7 du RFC 9110.

Le format des capsules est décrit en section 3.2 :

Capsule {
     Capsule Type (i),
     Capsule Length (i),
     Capsule Value (..),
   }
  

Les différents types possibles de capsules figurent dans un registre IANA. Une fois le protocole Capsule sélectionné via un upgrade token, on passe du HTTP classique au protocole Capsule. Un premier type de capsule est DATAGRAM (type 0) dont le nom indique bien la sémantique. D'autres types pourront être ajoutés en suivant la procédure « spécification nécessaire » du RFC 8126, sauf les valeurs basses du type (de 0 à 63) qui exigent une action de normalisation, ou bien une approbation par l'IESG.

Si on utilise Capsule, la requête HTTP est accompagnée du champ Capsule-Protocol (un champ structuré, cf. RFC 8941), champ désormais enregistré à l'IANA.

Il existe plusieurs mises en œuvre de ces datagramms HTTP. En logiciel libre, on a :

Notez que le protocole Capsule n'est a priori pas accessible au code JavaScript chargé dans le navigateur Web (il faut pouvoir accéder aux upgrade tokens). Mais rappelez-vous que tout ce mécanisme de datagrammes sur HTTP est conçu pour des extensions de HTTP, pas pour l'application finale.


Téléchargez le RFC 9297


L'article seul

RFC 9293: Transmission Control Protocol (TCP)

Date de publication du RFC : Août 2022
Auteur(s) du RFC : W. Eddy (MTI Systems)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 20 août 2022


Le protocole de transport TCP est l'un des piliers de l'Internet. La suite des protocoles Internet est d'ailleurs souvent appelée TCP/IP, du nom de ses deux principaux protocoles. Ce nouveau RFC est la norme technique de TCP, remplaçant l'antique RFC 793, qui était vieux de plus de quarante ans, plus vieux que la plupart des lecteurices de ce blog. Il était temps de réviser et de rassembler en un seul endroit tous les détails sur TCP.

TCP est donc un protocole de transport. Rappelons brièvement ce qu'est un protocole de transport et à quoi il sert. L'Internet achemine des paquets IP de machine en machine. IP ne garantit pas leur arrivée : les paquets peuvent être perdus (par exemple parce qu'un routeur n'avait plus de place dans ses files d'attente), peuvent être dupliqués, un paquet peut passer devant un autre, pourtant envoyé avant, etc. Pour la grande majorité des applications, ce n'est pas acceptable. La couche de transport est donc chargée de remédier à cela. Première (en partant du bas) couche à être de bout en bout (les routeurs et autres équipements intermédiaires n'y participent pas, ou plus exactement ne devraient pas y participer), elle se charge de suivre les paquets, de les remettre dans l'ordre, et de demander aux émetteurs de ré-émettre si un paquet s'est perdu. C'est donc un rôle crucial, puisqu'elle évite aux applications de devoir gérer ces opérations très complexes. (Certaines applications, par exemple de vidéo, n'ont pas forcément besoin que chaque paquet arrive, et n'utilisent pas TCP.) Le RFC 8095 contient une comparaison détaillée des protocoles de transport de l'IETF.

Avant de décrire TCP, tel que spécifié dans ce RFC 9293, un petit mot sur les normes techniques de l'Internet. TCP avait originellement été normalisé dans le RFC 761 en 1980 (les couches 3 - IP et 4 étaient autrefois mêlées en une seule). Comme souvent sur l'Internet, le protocole existait déjà avant sa normalisation et était utilisé. La norme avait été rapidement révisée dans le RFC 793 en 1981. Depuis, il n'y avait pas eu de révision générale, et les RFC s'étaient accumulés. Comprendre TCP nécessitait donc de lire le RFC 793 puis d'enchainer sur plusieurs autres. Un RFC, le RFC 7414, avait même été écrit dans le seul but de fournir un guide de tous ces RFC à lire. Et il fallait également tenir compte de l'accumulation d'errata. Désormais, la situation est bien plus simple, tout TCP a été consolidé dans un RFC central, notre RFC 9293. (Notez que ce travail de consolidation a été aussi fait pour HTTP et SMTP, qui ne sont plus décrits par les RFC d'origine mais par des versions à jour, mais pas pour le DNS, qui reste le principal ancien protocole qui aurait bien besoin d'une remise à jour complète des documents qui le spécifient.) Ne vous faites toutefois pas d'illusion : malgré ses 114 pages, ce RFC 9293 ne couvre pas tout, et la lecture d'autres RFC reste nécessaire. Notamment, TCP offre parfois des choix multiples (par exemple pour les algorithmes de contrôle de la congestion, une partie cruciale de TCP), qui ne sont pas décrits intégralement dans la norme de base. Voyez par exemple le RFC 7323, qui n'a pas été intégré dans le RFC de base, et reste une extension optionnelle.

Attaquons-nous maintenant à TCP. (Bien sûr, ce sera une présentation très sommaire, TCP est forcément plus compliqué que cela.) Son rôle est de fournir aux applications un flux d'octets fiable et ordonné : les octets que j'envoie à une autre machine arriveront dans l'ordre et arriveront tous (ou bien TCP signalera que la communication est impossible, par exemple en cas de coupure du câble). TCP découpe les données en segments, chacun voyageant dans un datagramme IP. Chaque octet est numéroté (attention : ce sont bien les octets et pas les segments qui sont numérotés) et cela permet de remettre dans l'ordre les paquets, et de détecter les pertes. En cas de perte, on retransmet. Le flux d'octets est bidirectionnel, la même connexion TCP permet de transmettre dans les deux sens. TCP nécessite l'établissement d'une connexion, donc la mémorisation d'un état, par les deux pairs qui communiquent. Outre les tâches de fiabilisation des données (remettre les octets dans l'ordre, détecter et récupérer les pertes), TCP fournit du démultiplexage, grâce aux numéros de port. Ils permettent d'avoir plusieurs connexions TCP entre deux machines, qui sont distinguées par ces numéros de port (un pour la source et un pour la destination) dans l'en-tête TCP. Le bon fonctionnement de TCP nécessite d'édicter un certain nombre de règles et le RFC les indique avec MUST-n où N est un entier. Par exemple, MUST-2 et MUST-3 indiqueront que la somme de contrôle est obligatoire, aussi bien en envoi qu'en vérification à la réception.

Après ces concepts généraux, la section 3 de notre RFC rentre dans les détails concrets, que je résume ici. D'abord, le format de cet en-tête que TCP ajoute derrière l'en-tête IP et devant les données du segment. Il inclut les numéros de port, le numéro de séquence (le rang du premier octet dans les données), celui de l'accusé de réception (le rang du prochain octet attendu), un certain nombre de booléens (flags, ou bits de contrôle, qui indiquent, par exemple, s'il s'agit d'une connexion déjà établie, ou pas encore, ou la fin d'une connexion), la taille de la fenêtre (combien d'octets peuvent être envoyés sans accusé de réception, ce qui permet d'éviter de surcharger le récepteur), une somme de contrôle, et des options, de taille variable (l'en-tête contient un champ indiquant à partir de quand commencent les données). Une option de base est MSS (Maximum Segment Size) qui indique quelle taille de segment on peut gérer. Il existe d'autres options comme les accusés de réception sélectifs du RFC 2018 ou comme le facteur multiplicatif de la taille de fenêtre du RFC 7323.

Avant d'expliquer la dynamique de TCP, le RFC définit quelques variables indispensables, qui font partie de l'état de la connexion TCP. Pour l'envoi de données, ce sont par exemple SND.UNA (ces noms sont là pour comprendre le RFC, mais un programme qui met en œuvre TCP peut évidemment nommer ces variables comme il veut), qui désigne le numéro de séquence du début des données envoyées, mais qui n'ont pas encore fait l'objet d'un accusé de réception, ou SND.NXT, le numéro de séquence du début des données pas encore envoyées ou encore SND.WND, la taille de la fenêtre d'envoi. Ainsi, on ne peut pas envoyer d'octets dont le rang serait supérieur à SND.UNA + SND.WND, il faut attendre des accusés de réception (qui vont incrémenter SND.UNA) ou une augmentation de la taille de la fenêtre. Pour la réception, on a RCV.NXT, le numéro de séquence des prochains paquets si tout est normal, et RCV.WND, la taille de la fenêtre de réception.

TCP est un protocole à état et il a donc une machine à états, décrite en section 3.3.2. Parmi les états, LISTEN, qui indique un TCP en train d'attendre un éventuel pair, ESTAB (ou ESTABLISHED), où TCP envoie et reçoit des données, et plusieurs états qui apparaissent lors de l'ouverture ou de la fermeture d'une connexion.

Et les numéros de séquence ? Un point important de TCP est qu'on ne numérote pas les segments mais les octets. Chaque octet a son numéro et les accusés de réception référencent ces numéros. On n'accuse évidemment pas réception de chaque octet individuel. Les accusés de réception sont cumulatifs ; ils désignent un octet et, implicitement, tous les octets précédents. Quand un récepteur prévient qu'il a bien reçu l'octet N, cela veut dire que tous ceux avant N ont aussi été reçus.

Les numéros ne partent pas de 1 (ni de zéro), ils sont relatifs à un ISN (Initial Sequence Number, rappelez-vous qu'il y a un glossaire, en section 4 du RFC) qui est choisi aléatoirement (pour des raisons de sécurité, cf. RFC 6528) au démarrage de la session. Des logiciels comme Wireshark savent cela et peuvent calculer puis afficher des numéros de séquence qui partent de 1, pour faciliter la lecture.

Les numéros de séquence sont stockés sur 32 bits et il n'y a donc que quatre milliards (et quelques) valeurs possibles. C'était énorme quand TCP a été créé mais cela devient de plus en plus petit, relativement aux capacités des réseaux modernes. Comme TCP repart de zéro quand il atteint le numéro de séquence maximum, il y a un risque qu'un « vieux » paquet soit considéré comme récent. À 1 Gb/s, cela ne pouvait se produire qu'au bout de 34 secondes. Mais à 100 Gb/s, il suffit d'un tiers de seconde ! Les extensions du RFC 7323 deviennent alors indispensables.

Tout le monde sait bien que TCP est un protocole orienté connexion. Cela veut dire qu'il faut pouvoir créer, puis supprimer, les connexions. La section 3.5 de notre RFC décrit cet établissement de connexion, via la fameuse « triple poignée de mains ». En gros, un des TCP (TCP n'est pas client-serveur, et la triple poignée de mains marche également quand les deux machines démarrent la connexion presque en même temps) envoie un paquet ayant l'option SYN (pour synchronization), le second répond avec un paquet ayant les options ACK (accusé de réception du SYN) et SYN, le premier TCP accuse réception. Avec trois paquets, les deux machines sont désormais d'accord qu'elles sont connectées. En d'autres termes (ici, machine A commence) :

  • Machine A ⇒ Machine B : je veux te parler,
  • B ⇒ A : j'ai noté que tu voulais me parler et je veux te parler aussi,
  • A ⇒ B : j'ai noté que tu voulais me parler.

Pour mettre fin à la connexion, un des deux TCP envoie un paquet ayant l'option FIN (pour finish), auquel l'autre répond de même.

Conceptuellement, TCP gère un flux d'octets sans séparation. TCP n'a pas la notion de message. Si une application envoie (en appelant print, write ou autre fonction du langage de programmation utilisé) les octets "AB" puis "CD", TCP transmettra puis livrera à l'application distante les quatre octets "ABCD" dans l'ordre, sans indiquer qu'il y avait eu deux opérations d'écriture sur le réseau. Mais, pour envoyer les données, TCP doit les segmenter en paquets IP distincts. La fragmentation IP n'étant pas bonne pour les performances, et étant peu fiable de toute façon, l'idéal est que ces paquets IP aient une taille juste inférieure à la MTU du chemin. Cela nécessite de découvrir cette MTU (section 3.7.2), ou bien d'adopter la solution paresseuse de faire des paquets de 1 280 octets (la MTU minimum, en IPv6). Comme la solution paresseuse ne serait pas optimale en performances, les mises en œuvre de TCP essaient toutes de découvrir la MTU du chemin. Cela peut se faire avec la méthode PMTUD, décrite dans les RFC 1191 et RFC 8201, mais qui a l'inconvénient de nécessiter qu'ICMP ne soit pas bloqué. Or, beaucoup de middleboxes programmées avec les pieds, ou configurées par des incompétents, bloquent ICMP, empêchant le fonctionnement de PMTUD. Une alternative est PLPMTUD, normalisée dans le RFC 4821, qui ne dépend plus d'ICMP.

Une fois la connexion établie, on peut envoyer des données. TCP va les couper en segments, chaque segment voyageant dans un paquet IP.

Une mise en œuvre de TCP n'envoie pas forcément immédiatement les octets qu'on lui a confiés. Envoyer des petits paquets n'est pas efficace : s'il y a peu de données, les en-têtes du paquet forment la majorité du trafic et, de toute façon, le goulet d'étranglement dans le réseau n'est pas forcément lié au nombre d'octets, il peut l'être au nombre de paquets. Un des moyens de diminuer le nombre de paquets contenant peu d'octets est l'algorithme de Nagle. Le principe est simple : quand TCP reçoit un petit nombre d'octets à envoyer, il ne les transmet pas tout de suite mais attend un peu pour voir si l'application ne va pas lui confier des octets supplémentaires. Décrit dans le RFC 896, et recommandé dans le RFC 1122, cet algorithme est aujourd'hui présent dans la plupart des mises en œuvres de TCP. Notre RFC 9293 le recommande, mais en ajoutant que les applications doivent pouvoir le débrayer. C'est notamment indispensable pour les applications interactives, qui n'aiment pas les délais, même courts. (Sur Unix, cela se fait avec l'option TCP_NODELAY passée à setsockopt.)

Un des rôles les plus importants de TCP est de lutter contre la congestion. Il ne faut pas envoyer le plus d'octets possible, ou alors on risque d'écrouler le réseau (RFC 2914). Plusieurs mécanismes doivent être déployés : démarrer doucement (au début de la connexion, TCP ne connait pas le débit que peut supporter le réseau et doit donc être prudent), se calmer rapidement si on constate que des paquets sont perdus (cela peut être un signe de congestion en aval), etc. Ces mesures pour éviter la congestion sont absolument obligatoires (comme tous les biens communs, l'Internet est vulnérables aux parasites, un TCP égoïste qui ne déploierait pas ces mesures anti-congestion serait un mauvais citoyen du réseau). Elles sont décrites plus précisément dans les RFC 1122, RFC 2581, RFC 5681 et RFC 6298. Notez qu'ils énoncent des principes, mais pas forcément les algorithmes exacts à utiliser, ceux-ci pouvant évoluer (et ils l'ont fait depuis la sortie du RFC 793, voir les RFC 5033 et RFC 8961).

Un point qui surprend souvent les programmeur·ses qui écrivent des applications utilisant TCP est que rien n'indique qu'une connexion TCP est coupée, par exemple si un câble est sectionné. C'est seulement lorsqu'on essaie d'écrire qu'on l'apprendra (ce comportement est une conséquence de la nature sans connexion d'IP ; pourquoi prévenir d'une coupure alors qu'IP va peut-être trouver un autre chemin ?). Si on veut être mis au courant tout de suite, on peut utiliser des keep-alives, des segments vides qui seront envoyés de temps en temps juste pour voir s'ils arrivent. Cette pratique est contestée (entre autre parce qu'elle peut mener à abandonner une connexion qui n'est que temporairement coupée, et aussi parce que, même si les keep-alives passent, cela ne garantit pas que la prochaine écriture de données passera). Notre RFC demande donc que les keep-alives soient désactivés par défaut, et que l'application puisse contrôler leur activation (sur Unix, c'est l'option SO_KEEPALIVE à utiliser avec setsockopt).

Pendant les quarante ans écoulés depuis la sortie du RFC 793, bien des choses ont changé. Des options ont été ajoutées mais on a vu aussi certaines options qui semblaient être des bonnes idées être finalement abandonnées. C'est le cas du mécanisme de données urgentes, qui n'a jamais vraiment marché comme espéré. Une mise en œuvre actuelle de TCP doit toujours le gérer, pour pouvoir parler avec les anciennes, mais on ne peut pas compter dessus (RFC 6093).

On a parlé plusieurs fois de l'utilisation de TCP par les applications. Pour cela, il faut que TCP offre une API à ces applications. Notre RFC ne décrit pas d'API concrète (cela dépend de toute façon du langage de programmation, et sans doute du système d'exploitation). Il se contente d'une description fonctionnelle de l'interface : les services qu'elle doit offrir à l'application. On y trouve :

  • L'ouverture de la connexion, en indiquant l'adresse et le port distant, mais aussi l'adresse locale (MUST-43), pour pouvoir choisir l'adresse si la machine en a plusieurs.
  • L'envoi de données, dont TCP garantit qu'elles arriveront toutes et dans l'ordre.
  • La lecture de données. Rappelez-vous que TCP fournit un flot continu d'octets, deux envois de données feront peut-être une seule lecture ou bien un envoi deux lectures. C'est une des erreurs les plus courante des programmeurs débutants que de penser que s'ils font un write d'un côté, un read de l'autre lira tout ce que le write a écrit et seulement cela.
  • La fermeture propre (avec envoi, et réémission si nécessaire, des données écrites) de la connexion.
  • Et quelques autres services auxiliaires que je ne détaille pas ici (voir la section 3.9.1).

Rappelez-vous que l'API « socket » n'est qu'un des moyens pour l'application de parler à TCP. Et, à propos de cette API, notez que le RFC utilise le terme de socket avec un sens très différent (cf. le glossaire en section 4).

Et l'autre interface, « sous » TCP, l'interface avec la couche réseau (section 3.9.2) ? TCP peut fonctionner avec des couches réseau variées mais, en pratique, c'est toujours IP, dans une de ses deux versions, v4 ou v6. Un des points délicats est le traitement des messages ICMP que TCP peut recevoir. Au minimum, ils doivent être assignés à une connexion existante. Mais cela ne veut pas dire qu'il faut systématiquement agir lors de la réception d'un de ces messages (RFC 5461). Par exemple, il faut ignorer les messages ICMP de répression de la source (RFC 6633). Et il ne faut pas fermer la connexion juste parce qu'on a reçu une erreur ICMP (MUST-56), car elles peuvent être temporaires.

TCP étant un protocole orienté connexion, la connexion va avoir un état, représenté par un automate fini. Ainsi, l'ouverture d'une connexion par le premier TCP va le faire passer par les états CLOSED, SYN-SENT puis enfin ESTABLISHED. Récupérée sur Wikimedia Commons, voici une représentation de cet automate : TCP_state_diagram.jpg

La section 5 de notre RFC décrit les changements depuis le RFC 793. TCP reste le même et des TCP d'« avant » peuvent interopérer avec ceux qui suivent notre RFC récent. Les principaux changements sont :

  • La résolution d'un grand nombre d'errata accumulés depuis quarante ans.
  • La « démobilisation » du mécanisme de données urgentes (cf. RFC 6093).
  • L'incorporation d'une bonne partie des RFC comme le RFC 1011 ou le RFC 1122.

Le RFC 793 avait été écrit par Jon Postel (comme tant de RFC de cette époque), et notre nouveau RFC 9293 lui rend hommage, en estimant que la longue durée de vie du RFC 793 montre sa qualité.

En application des changements de ce nouveau RFC sur TCP, un registre IANA, celui des bits de l'en-tête comme URG (dont on a vu qu'il était désormais démobilisé), a été mis à jour.

La section 7 de notre nouveau RFC 9293 n'avait pas d'équivalent dans l'ancien RFC 793. Elle concerne la sécurité de TCP. TCP, par lui-même, fournit peu de sécurité (il protège quand même un peu contre l'usurpation d'adresse IP et l'injection de trafic, surtout contre un attaquant qui n'est pas sur le chemin, cf. RFC 5961). Par exemple, il ne fournit pas de services cryptographiques, donc pas de confidentialité ou de vraie garantie d'intégrité. (Son concurrent QUIC, lui, intègre systématiquement la cryptographie.) Si on veut davantage de sécurité pour TCP, il faut mettre IPsec en dessous de TCP ou bien TLS au-dessus. TCP a aussi des mécanismes de sécurité qui lui sont propres, comme AO, normalisé dans le RFC 5925 (mais très peu déployé en pratique). Autre expérience qui n'a pas été un succès, tcpcrypt (RFC 8548).

Il reste des attaques contre lesquelles la cryptographie ne fournit pas vraiment de solution. C'est le cas du SYN flooding, ou d'autres attaques par déni de service.

Mëme si on chiffre les données applicatives avec TLS, TCP expose quand même au réseau tout son fonctionnement. Cette vue depuis le réseau (RFC 8546) soulève plusieurs problèmes (elle peut par exemple faciliter des actions contraires à la neutralité du réseau) et c'est pour cela que le plus récent QUIC masque une grande partie de son fonctionnement (ce qui n'a pas été sans entrainer des polémiques). Toujours côté vie privée, le comportement des mises en œuvre de TCP est suffisamment différent pour qu'il soit possible dans certains cas d'identifier le système d'exploitation d'une machine à distance (ce n'est pas forcément grave, mais il faut le savoir).

Pour les programmeurs et programmeuses, l'annexe A du RFC contient différentes observations utiles pour la mise en œuvre concrète de TCP. Par exemple, cette annexe explique que la validation des numéros de séquence des segments TCP entrants, validation qui est nécessaire pour empêcher un grand nombre d'attaques, peut rejeter des segments légitimes. Ce problème n'a pas de solution idéale.

Un autre point intéressant concerne l'algorithme de Nagle. Cet algorithme représente un compromis entre la réactivité de l'application (qui nécessite d'envoyer les données le plus vite possibles) et l'optimisation de l'occupation du réseau (qui justifie d'attendre un peu pour voir si on ne va pas recevoir de nouvelles données à envoyer). Une modification de l'algorithme de Nagle, décrite dans le document draft-minshall-nagle peut rendre le choix moins douloureux.

Enfin, l'annexe B résume sous forme d'un tableau quelles sont les parties de la norme TCP qui doivent être mises en œuvre et quelles sont celles qu'on peut remettre à plus tard.

Si vous voulez sérieusement apprendre TCP, vous pouvez bien sûr lire le RFC en entier, mais il est sans doute plus sage de commencer par un bon livre comme le CNP3.

Un pcap d'une connexion TCP typique est utilisé ici pour illustrer le fonctionnement de TCP (fichier tcp-typique.pcap). Vu par tshark, cela donne :

8.445772 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 94 33672 → 443 [SYN] Seq=0 Win=64800 Len=0 MSS=1440 SACK_PERM=1 TSval=285818465 TSecr=0 WS=128
8.445856 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TCP 94 443 → 33672 [SYN, ACK] Seq=0 Ack=1 Win=64260 Len=0 MSS=1440 SACK_PERM=1 TSval=3778761402 TSecr=285818465 WS=128
8.534636 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [ACK] Seq=1 Ack=1 Win=64896 Len=0 TSval=285818554 TSecr=3778761402
8.534897 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 SSLv3 236 Client Hello
8.534989 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TCP 86 443 → 33672 [ACK] Seq=1 Ack=151 Win=64128 Len=0 TSval=3778761491 TSecr=285818554
8.548101 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TLSv1.2 1514 Server Hello
8.548111 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TCP 1514 443 → 33672 [PSH, ACK] Seq=1429 Ack=151 Win=64128 Len=1428 TSval=3778761504 TSecr=285818554 [TCP segment of a reassembled PDU]
8.548265 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TLSv1.2 887 Certificate, Server Key Exchange, Server Hello Done
8.636922 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [ACK] Seq=151 Ack=2857 Win=63616 Len=0 TSval=285818656 TSecr=3778761504
8.636922 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [ACK] Seq=151 Ack=3658 Win=62848 Len=0 TSval=285818656 TSecr=3778761504
8.649899 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [FIN, ACK] Seq=151 Ack=3658 Win=64128 Len=0 TSval=285818669 TSecr=3778761504
8.650160 2605:4500:2:245b::42 → 2a01:4f8:c010:7eb7::106 TCP 86 443 → 33672 [FIN, ACK] Seq=3658 Ack=152 Win=64128 Len=0 TSval=3778761606 TSecr=285818669
8.739117 2a01:4f8:c010:7eb7::106 → 2605:4500:2:245b::42 TCP 86 33672 → 443 [ACK] Seq=152 Ack=3659 Win=64128 Len=0 TSval=285818758 TSecr=3778761606
  

Ici, 2a01:4f8:c010:7eb7::106 (une sonde RIPE Atlas) veut faire du HTTPS (donc, port 443) avec le serveur 2605:4500:2:245b::42. Tout commence par la triple poignée de mains (les trois premiers paquets, SYN, SYN+ACK, ACK). Une fois la connexion établie, tshark reconnait que les machines font du TLS mais, pour TCP, ce ne sont que des données applicatives (contrairement à QUIC, TCP sépare connexion et chiffrement). On note qu'il n'y a pas eu d'optimisation des accusés de réception. Par exemple, l'accusé de réception du ClientHello TLS voyage dans un paquet séparé, alors qu'il aurait pu être inclus dans la réponse applicative (le ServerHello), probablement parce que celle-ci a mis un peu trop de temps à être envoyée. À la fin de la connexion, chaque machine demande à terminer (FIN). Une analyse automatique plus complète, avec tshark -V figure dans tcp-typique.txt.

Ah, au fait, si vous avez pu lire cet article, c'est certainement grâce à TCP… (Ce blog ne fait pas encore de HTTP sur QUIC…).


Téléchargez le RFC 9293


L'article seul

RFC 9287: Greasing the QUIC bit

Date de publication du RFC : Août 2022
Auteur(s) du RFC : M. Thomson (Mozilla)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF quic
Première rédaction de cet article le 30 août 2022


Dans un monde idéal, nos flux de données sur l'Internet passeraient sans problème et sans être maltraitées par les éléments intermédiaires du réseau. Mais dans le monde réel, de nombreuses middleboxes examinent le trafic et interfèrent avec lui. Les machines émettrices de trafic doivent donc se protéger et une des techniques les plus courantes est le chiffrement : si la middlebox ne peut pas comprendre ce qui est envoyé, elle ne pourra pas interférer. Le protocole de transport QUIC chiffre donc le plus possible. Mais une partie du trafic reste non chiffrée. Une des techniques pour empêcher que les middleboxes ne le lisent et ne prennent des décisions déplorables fondées sur ces informations transmises en clair est le graissage : on fait varier les données le plus possible. Ce RFC normalise une possibilité de graisser un bit de l'en-tête QUIC qui était fixe auparavant.

L'un des principes de conception de QUIC est de diminuer le plus possible la vue depuis le réseau (RFC 8546) ; les éléments intermédiaires, comme les routeurs ne doivent avoir accès qu'à ce qui leur est strictement nécessaire, le reste étant chiffré, pour leur éviter toute tentation. QUIC définit (dans le RFC 8999) des invariants, des informations qui seront toujours vraies même pour les futures versions de QUIC (le RFC 9000 définit QUIC version 1, pour l'instant la seule version). En dehors de ces invariants, tout peut… varier et les équipements intermédiaires du réseau ne doivent pas en tenir compte. Si tous suivaient ce principe, on préserverait la neutralité du réseau, et on éviterait l'ossification (cf. RFC 9170). Mais, en pratique, on sait que ce n'est pas le cas et que toute information visible depuis le réseau sera utilisée par les middleboxes, ce qui peut rendre difficile les évolutions futures (TLS a par exemple beaucoup souffert de ce problème, d'où le RFC 8701, le premier à avoir formalisé ce concept de graissage).

Or, QUIC a un bit qui n'est pas cité dans les invariants du RFC 8999 mais qui est fixe et peur servir à différencier QUIC d'autres protocoles. On l'appele d'ailleurs souvent le « bit QUIC » (même si ça n'est pas son nom officiel, qui est fixed bit, cf. RFC 9000, sections 17.2 et 17.3). Si les middleboxes s'en servent pour appliquer un traitement particulier à QUIC, les futures versions de QUIC ne pourront pas utiliser ce bit pour autre chose (rappelez-vous qu'il ne fait pas partie des invariants).

Notre RFC ajoute donc à QUIC une option pour graisser ce bit, c'est-à-dire pour le faire varier au hasard, décourageant ainsi les middleboxes d'en tenir compte. Un nouveau paramètre de transport QUIC (ces paramètres sont définis dans le RFC 9000, section 7.4) est créé, grease_quic_bit (et ajouté au registre IANA). Lorsqu'il est annoncé à l'ouverture de la connexion QUIC, il indique que ce bit est ignoré et que le pair à tout intérêt à le faire varier.

Les paquets initiaux de la connexion (Initial et Handshake) doivent garder le bit QUIC à 1 (puisqu'on n'a pas encore les paramètres de la connexion), sauf si on reprend une ancienne connexion (en envoyant un jeton, cf. RFC 9000, section 19.7).

Tout le but de ce graissage est de permettre aux futures versions de QUIC (v2, 3, etc) d'utiliser ce bit qui était originellement fixe. Ces futures versions, ou tout simplement des extensions à QUIC négociées au démarrage de la connexion, pourront donc utiliser ce bit (lui donner une sémantique). Le RFC leur recommande d'annoncer quand même le paramètre grease_quic_bit, ce qui permettra de graisser même si la future extension n'est pas gérée par le partenaire.

Le RFC note enfin que ce graissage rend plus difficile d'identifier les flux de données QUIC au milieu de tout le trafic. C'est bien sûr le but, mais cela peut compliquer certains activités d'administration réseau. Un futur RFC explique plus en détail cette situation.

Apparemment, plusieurs mises en œuvre de QUIC gèrent déjà ce graissage.


Téléchargez le RFC 9287


L'article seul

RFC 9285: The Base45 Data Encoding

Date de publication du RFC : Août 2022
Auteur(s) du RFC : P. Faltstrom (Netnod), F. Ljunggren (Kirei), D. van Gulik (Webweaving)
Pour information
Première rédaction de cet article le 10 août 2022


Ce RFC décrit un encodage en texte de données binaires, l'encodage Base45. Proche conceptuellement d'encodages comme Base64, il est, par exemple, beaucoup utilisé pour le code QR.

Ces codes QR ne permettent pas de stocker directement des données binaires quelconques car le contenu sera toujours interprété comme du texte. Il est donc nécessaire d'encoder et c'est le rôle de Base45. Il existe bien sûr d'innombrables autres mécanismes d'encodage en texte, comme Base16, Base32 et Base64, spécifiés dans le RFC 4648 mais Base45 est plus efficace pour les codes QR, qui réencodent ensuite.

Base45 utilise un sous-ensemble d'ASCII, de 45 caractères (d'où son nom). Deux octets du contenu binaire sont encodés en trois caractères de ce sous-ensemble. Par exemple, deux octets nuls donneront la chaine de caractères "000". Regardons tout de suite avec le module Python base45 :


% pip3 install base45
% python3             
>>> import base45
>>> base45.b45encode(b'Bonjour, tout le monde')
b'.H86/D34ENJES4434ESUETVDL44-3E6VC'
>>> 

  

Et décodons avec le module Elixir base45 :

    
% iex -S mix
iex(1)> Base45.decode(".H86/D34ENJES4434ESUETVDL44-3E6VC")
"Bonjour, tout le monde"

C'est parfait, tout le monde est d'accord. Ici, évidemment, la chaine originale n'avait pas vraiment besoin d'être encodée donc on va essayer avec du binaire, les trois octets 42, 1 et 6, encodage en Elixir, décodage en Python :

    
iex(1)> Base45.encode(<<42, 1, 6>>)
"/D560"

>>> base45.b45decode("/D560")
b'*\x01\x06'

  

Encore une fois, tout va bien, Elixir a encodé les trois octets du binaire en quatre caractères, que Python a su décoder (l'astérisque est affiché car son code ASCII est 42).

Je vous laisse découvrir l'algorithme complet (il est assez simple) dans la section 3 du RFC. Si vous le programmez vous-même (ce qui n'est sans doute pas une bonne idée, il existe déjà de nombreuses mises en œuvre), attention aux cas limites comme un binaire d'un seul octet, ou comme une chaine à décoder qui compte des caractères invalides. Ici, un essai avec un caractère invalide, le signe égal :


% python3
>>> import base45
>>> base45.b45decode("AAAA=")
Traceback (most recent call last):
  File "/home/stephane/.local/lib/python3.8/site-packages/base45/__init__.py", line 30, in b45decode
    buf = [BASE45_DICT[c] for c in s.rstrip("\n")]
  File "/home/stephane/.local/lib/python3.8/site-packages/base45/__init__.py", line 30, in <listcomp>
    buf = [BASE45_DICT[c] for c in s.rstrip("\n")]
KeyError: '='

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/stephane/.local/lib/python3.8/site-packages/base45/__init__.py", line 54, in b45decode
    raise ValueError("Invalid base45 string")
ValueError: Invalid base45 string

  

Pensez à tester votre code avec de nombreux cas variés. Le RFC cite l'exemple des chaines "FGW" (qui se décode en une paire d'octets valant chacun 255) et "GGW" qui, quoique proche de la précédente et ne comportant que des caractères de l'encodage Base45 est néanmoins invalide (testez-la avec votre décodeur). Le code de test dans le module Elixir donne des idées de tests utiles (certains viennent de bogues détectées pendant le développement). Regardez test/base45_test.exs.

Et, comme toujours, lorsque vous recevez des données venues de l'extérieur, soyez paranoïaques dans le décodage ! Un code QR peut être malveillant et chercher à activer une bogue dans le décodeur (section 6 du RFC).

Enfin, le RFC rappelle qu'une chaine encodée n'est pas forcément directement utilisable comme URL, elle peut comporter des caractères qui ne sont pas sûrs, il faut donc encore une étape d'encodage si on veut en faire un URL.

Compte tenu de l'importance des codes QR, on trouve des mises en œuvre de Base45 un peu partout. J'ai par exemple cité celle en Python. Si vous programmez en Elixir, il y a une bibliothèque sur Hex (écrite en Erlang) mais aussi ma bibliothèque (aux performances très basses).

Sinon, si vous cherchez un bon article d'explication sur Base45, je recommande celui-ci.


Téléchargez le RFC 9285


L'article seul

RFC 9267: Common Implementation Anti-Patterns Related to Domain Name System (DNS) Resource Record (RR) Processing

Date de publication du RFC : Juillet 2022
Auteur(s) du RFC : S. Dashevskyi, D. dos Santos, J. Wetzels, A. Amri (Forescout Technologies)
Pour information
Première rédaction de cet article le 14 septembre 2022


Comme chacun·e sait, l'Internet est une jungle (les politiciens ajouteraient « une jungle Far-West de non-droit qu'il faut civiliser »). Par exemple, les logiciels qui parlent avec les vôtres ne sont pas toujours correctement écrits, voire ils sont franchement malveillants. Le code de votre côté doit donc être robuste, voire paranoïaque, et penser à tout. Ce RFC décrit quelques problèmes qui ont été observés dans des logiciels mettant en œuvre le DNS et explique comment ne pas refaire la même erreur.

Le RFC ne parle pas de failles DNS mais de failles dans les programmes DNS, ce qui est très différent (mais la différence est souvent oubliée dans les médias). Le protocole lui-même n'était pas en cause dans ces cas, ce sont juste les logiciels qui avaient des bogues. Les cas sont nombreux, par exemple SIGRed (CVE-2020-1350) ou DNSpooq (CVE-2020-25681 à CVE-2020-25687). Ces problèmes frappent notamment souvent dnsmasq (personnellement, je n'ai jamais compris pourquoi ce logiciel était si utilisé, mais c'est une autre histoire).

Plusieurs vulnérabilités ont concerné l'analyse des enregistrements DNS (RR = Resource Record). Les risques lors de cette analyse devraient être bien connus, la première faille documentée l'ayant été en 2000 (CVE-2000-0333) ! Tout logiciel qui analyse des enregistrements DNS (client, serveurs, mais aussi pare-feux, IDS, etc) peut tomber dans ces pièges, d'où l'importance de les documenter. C'était déjà fait dans l'Internet-Draft draft-ietf-dnsind-local-compression et dans le RFC 5625 mais c'était perdu au milieu d'autres choses donc notre RFC choisit d'enfoncer le clou.

Il commence par le grand classique des bogues de logiciels DNS : le traitement des pointeurs de compression. Pour gagner quelques octets, à l'époque où ça comptait, le DNS prévoit (RFC 1035, section 4.1.4) qu'on peut, dans un enregistrement DNS, remplacer tout ou partie d'un nom de domaine par un pointeur vers un autre endroit du paquet. Ainsi, si on a tous ses serveurs qui se terminent par les mêmes composants (comme c'est le cas, par exemple, de la racine), on peut ne mettre ces composants qu'une fois, et pointer vers eux partout ailleurs. Évidemment, si vous êtes programmeuse ou programmeur, vous avez déjà vu le piège : les pointeurs, c'est dangereux. Ils peuvent pointer en dehors du paquet, ou pointer vers eux-même, par exemple. Si on suit aveuglément un pointeur, un déni de service est possible, si on tape en dehors de la mémoire allouée ou bien si on se lance dans une boucle sans fin.

Rentrons dans les détails. Le RFC 1035 nous dit que l'octet qui indique la longueur d'un composant doit valoir moins de 64 (la taille maximale d'un composant), donc avoir les deux bits de plus fort poids à zéro. S'ils sont tous les deux à un, cela indique qu'on a un pointeur. Cet octet et le suivant (privés des deux bits de plus fort poids) sont alors un pointeur, le nombre d'octets depuis le début du message. On voit qu'on peut atteindre 16 383 octets (2 ** 14 - 1), largement assez pour sortir de la mémoire allouée pour le paquet, avec les conséquences qu'on imagine. Cela s'est produit en vrai (CVE-2020-25767, CVE-2020-24339 et CVE-2020-24335).

Autre exemple amusant cité par le RFC, le cas d'un pointeur pointant sur lui-même. Soit un message DNS minimal. Le premier composant est à 12 octets du début du message. Si on y met les octets 0xC0 et 0x0C, on aura un pointeur (0xC0 = 11000000, les deux bits de plus fort poids à un) qui vaut 12 (0xC0 0x0C moins les deux bits les plus significatifs = 0000000000001100 = 12). Le pointeur pointera alors sur lui-même, entrainant le logiciel DNS imprudent dans une boucle sans fin. Ça aussi, ça s'est déjà produit (CVE-2017-9345).

Dernier exemple amusant avec des pointeurs, le pointeur qui va mener à un nombre infini de composants. L'attaquant (ou le programmeur maladroit) met dans le message un composant suivi d'un pointeur qui revient au début de ce composant. Si le composant était test, un analyseur DNS imprudent va créer le nom de domaine test.test.test.test… avant de tomber à court de mémoire, ou bien, dans un langage de programmation comme C, d'écrire dans une autre zone de la mémoire, ce qui peut mener à un RCE. Notez que les pointeurs ne devraient pas pointer vers un pointeur : ça n'a aucun intérêt pratique et c'est dangereux, mais le RFC 1035 ne l'interdit pas explicitement. Une alternative est de tester le nombre de fois qu'on a suivi un pointeur ou, encore mieux, de vérifier que le pointeur ne pointe qu'en avant de lui-même.

Notez que le problème de la répétition infinie d'un composant pourrait également être évité en s'assurant que le nom de domaine reste en dessous de sa taille maximale, 255 octets (section 3 du RFC).

Mais il n'y a pas que les pointeurs. Un nom de domaine doit être terminé par un octet nul, indiquant la racine. Ainsi, le nom afnic.fr est encodé 0x05 0x61 0x66 0x6E 0x69 0x63 ("afnic") 0x02 0x66 0x72 ("fr") 0x00 (la racine). Que se passe-t-il si l'octet nul final manque ? Certains programmes en C ont été assez imprudents pour tenter de traiter le nom de domaine avec des routines comme strlen, qui comptent sur un octet nul pour terminer les données (section 4 de notre RFC). Le développeur qui ne veut pas refaire des failles comme CVE-2020-25107, CVE-2020-17440, CVE-2020-24383 ou CVE-2020-27736 devrait utiliser strnlen ou une autre méthode sûre.

On n'a pas terminé. Lorsqu'on traite une réponse DNS, les enregistrements incluent des données. Leur longueur est donné par un champ RDLENGTH de deux octets, qui précède les données (section 5). Si un analyseur DNS ne fait pas attention, la longueur indiquée peut être supérieure à la taille du paquet. Un programme qui, bêtement, lirait RDLENGTH puis ferait un read() de la longueur indiquée aurait de fortes chances de taper en dehors de la mémoire accessible (CVE-2020-25108, CVE-2020-24336 et CVE-2020-27009).

Ah, et le RFC n'en parle pas, mais l'analyse des options EDNS (RFC 6891) offre également ce genre de pièges. Une option est encodée en TLV et une longueur invalide peut mener à lire les valeurs en dehors du paquet.

Un problème du même genre est décrit dans la section 6 du RFC. Un message DNS comprend plusieurs sections (Question, Réponse, Autorité, Additionnelle) et le nombre d'enregistrements par section est indiqué au début du message DNS. Ces nombres (count) peuvent également être faux et ne doivent pas être utilisés aveuglément, sinon, on aura des malheurs du genre CVE-2020-25109, CVE-2020-24340, CVE-2020-24334 ou CVE-2020-27737.

C'est en raison de tous ces risques qu'il faut tester que ses programmes résistent bien à des messages DNS mal formés. Par exemple, le serveur faisant autorité Drink, écrit en Elixir, a dans ses jeux de tests de tels messages. Regardez par exemple drink_parsing_test.exs. Un exemple d'un tel test, pour le problème de boucle sans fin sur des pointeurs, décrit en section 2 du RFC, est :

    
  test "pointerloop1" do # RFC 9267, section 2
    result = Drink.Parsing.parse_request(<<@id::unsigned-integer-size(16),
      @request::unsigned-integer-size(16),
      1::unsigned-integer-size(16), # QDcount
      0::unsigned-integer-size(16), # ANcount
      0::unsigned-integer-size(16), # NScount
      0::unsigned-integer-size(16), # ARcount
      0xc0::unsigned-integer-size(8), # First label of the question
				      # section starts with a
				      # compression pointer to itself.
      0x0c::unsigned-integer-size(8),
      @mx::unsigned-integer-size(16),
      @in_class::unsigned-integer-size(16)
      >>, 
      false)
    assert result == {:error, :badDNS}    
  end

  

On fabrique un paquet DNS avec le pointeur invalide, et on l'analyse. Le sous-programme parse_request ne doit pas tourner sans fin, et doit renvoyer une erreur.

Un test du même genre en Python, où on envoie un message DNS avec un pointeur pointant sur lui-même (et on espère que ça n'aura pas tué le serveur qui le reçoit) :

id = 12345
misc = 0 # opcode 0, all flags zero 
data = struct.pack(">HHHHHHBB", id, misc, 1, 0, 0, 0, 0xc0, 0x0c)
s.sendto(data, sockaddr)
  

Téléchargez le RFC 9267


L'article seul

RFC 9260: Stream Control Transmission Protocol

Date de publication du RFC : Juin 2022
Auteur(s) du RFC : R. Stewart (Netflix), M. Tüxen (Münster Univ. of Appl. Sciences), K. Nielsen (Kamstrup A/S)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF twvwg
Première rédaction de cet article le 6 juin 2022


Une des particularités du protocole IP est que vous avez plusieurs protocoles de transport disponibles. TCP et UDP sont les plus connus mais SCTP, normalisé dans notre RFC, est également intéressant. C'est un protocole déjà ancien (il date de 2000) et ce RFC remplace son prédécesseur, le RFC 4960. Beaucoup de changements de détails, mais rien de crucial.

SCTP ressemble plutôt à TCP, notamment par le fait qu'il fournit un transport fiable. Mais il a plusieurs différences :

  • il ne gère pas un flot d'octets continu mais une série de messages, bien séparés,
  • il gère plusieurs flux de données séparés, qui partagent le même contrôle de congestion mais gèrent à part les pertes et retransmissions (un peu comme QUIC),
  • il gère le cas où la machine a plusieurs adresses IP, ce qui lui fournit normalement plus de redondance, si on est connecté à plusieurs réseaux.

Cette dernière possibilité le rapproche des protocoles qui séparent l'identificateur et le localisateur et lui permet de gérer proprement le multihoming. Cela se fait en indiquant, au début du contact, toutes les adresses IP (autrefois, il y avait même les noms de domaines, cf. la section 3.3.2.1.4) de la machine.

SCTP tient également compte de l'expérience acquise avec TCP. par exemple, l'établissement d'une connexion (que SCTP nomme association) se fait avec un échange de quatre paquets (et non pas trois comme avec TCP), pour offrir une meilleure protection contre les dénis de service. Les SYN cookies, un ajout plus ou moins bancal en TCP, sont ici partie intégrante du protocole.

SCTP est surtout issu des demandes du monde de la téléphonie sur IP, qui avait besoin d'un tel protocole pour la signalisation mais il peut être aussi utilisé dans d'autres domaines.

Un excellent article du Linux Journal explique bien SCTP.

SCTP est depuis longtemps mis en œuvre dans Linux et dans FreeBSD. De même, des programmes de débogage comme Wireshark sont capables de décoder et d'afficher le SCTP. Voici par exemple un pcap entre deux machines dont l'une a envoyé (trame 3) la chaîne de caractères « toto ». (Vous avez également la version texte de ce dialogue.) Des exemples de programmes de tests SCTP se trouvent dans mon article sur le RFC 3286.

Comme tout « nouveau » protocole de transport, SCTP est handicapé par des coupe-feux mal programmés et/ou mal configurés. L'Internet s'ossifiant de plus en plus, il devient très difficile de déployer un nouveau protocole de transport. D'où le RFC 6951, pour faire tourner SCTP sur UDP (comme ce que fait QUIC).

Les changements qu'introduit ce nouveau RFC ne modifient pas en profondeur le protocole mais corrigent les nombreux problèmes survenus pendant ses premières années d'utilisation. Le RFC 8540 donne la liste complète des problèmes corrigés.


Téléchargez le RFC 9260


L'article seul

RFC 9255: The 'I' in RPKI Does Not Stand for Identity

Date de publication du RFC : Juin 2022
Auteur(s) du RFC : R. Bush (Arrcus & IIJ Research), R. Housley (Vigil Security)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF sidrops
Première rédaction de cet article le 15 juin 2022


Un très court RFC pour un simple rappel, qui ne devrait même pas être nécessaire : les identités utilisées dans la RPKI, la base des identités qui sert aux techniques de sécurisation de BGP, n'ont pas de lien avec les identités attribuées par l'État et ne doivent pas être utilisées pour, par exemple, signer des documents « officiels » ou des contrats commerciaux.

Le RFC 6480 décrit cette RPKI : il s'agit d'un ensemble de certificats et de signatures qui servent de base pour des techniques de sécurité du routage comme les ROA du RFC 6482 ou comme le BGPsec du RFC 8205. Les objets de la RPKI servent à établir l'autorité sur des ressources Internet (INR, Internet Number Resources, comme les adresses IP et les numéros d'AS). Le RFC 6480, section 2.1, dit clairement que cela ne va pas au-delà et que la RPKI ne prétend pas être la source d'identité de l'Internet, ni même une source « officielle ».

Prenons un exemple concret, un certificat choisi au hasard dans les données du RIPE-NCC (qu'on peut récupérer avec rsync en rsync://rpki.ripe.net/repository). Il est au format DER donc ouvrons-le :

%  openssl x509 -inform DER -text -in  ./DEFAULT/ztLYANxsM7afpHKR4vFbM16jYA8.cer
Certificate:
    Data:
        ...    
        Serial Number: 724816122963 (0xa8c2685453)
        Validity
            Not Before: Jan  1 14:04:04 2022 GMT
            Not After : Jul  1 00:00:00 2023 GMT
        Subject: CN = ced2d800dc6c33b69fa47291e2f15b335ea3600f
	...
        X509v3 extensions:
            ...
            sbgp-ipAddrBlock: critical
                IPv4:
                  51.33.0.0/16
                  ...
            sbgp-autonomousSysNum: critical
                Autonomous System Numbers:
                  206918
                  ...
  

Je n'ai pas tout montré, mais seulement les choses importantes :

  • Il n'y a pas de vraie identité dedans, le sujet a manifestement été généré automatiquement,
  • Il couvre un certain nombre de ressources (INR, Internet Number Resources), comme le préfixe IP 51.33.0.0/16, utilisant les extensions du RFC 3779.

Ce certificat dit simplement que l'entité qui a la clé privée (RFC 5280) correspondante (une administration britannique, dans ce cas) a autorité sur des ressources comme l'AS 206918. Rien d'autre.

Mais, apparemment, certaines personnes n'avaient pas bien lu le RFC 6480 et croyaient que cet attirail PKIesque leur permettait également de signer des documents sans lien avec les buts de la RPKI, voire n'ayant rien à voir avec le routage. Et d'autant plus que des gens croient que le I dans RPKI veut dire Identity (il veut en fait dire Infrastructure). Il a ainsi été suggéré que les clés de la RPKI pouvaient être utilisées pour signer des LOA demandant l'installation d'un serveur dans une baie.

Pourquoi est-ce que cela serait une mauvaise idée ? Il y a plusieurs raisons mais la principale est qu'utiliser la RPKI pour des usages en dehors du monde restreint du routage Internet est que cela exposerait les AC de cette RPKI à des risques juridiques qu'elles n'ont aucune envie d'assumer. Et cela compliquerait les choses, obligeant sans doute ces AC à déployer des processus bureaucratiques bien plus rigides. Si on veut connaitre l'identité officielle (que le RFC nomme à tort « identité dans le monde réel ») d'un titulaire de ressources Internet, on a les bases des RIR, qu'on interroge via RDAP ou autres protocoles. (C'est ainsi que j'ai trouvé le titulaire de 51.33.0.0/16.) Bien sûr, il y a les enregistrements Ghostbusters du RFC 6493 mais ils sont uniquement là pour aider à trouver un contact opérationnel, pas pour indiquer l'identité étatique du titulaire. Même les numéros d'AS ne sont pas l'« identité » d'un acteur de l'Internet (certains en ont plusieurs).

Notons qu'il y a d'autres problèmes avec l'idée de se servir de la RPKI pour signer des documents à valeur légale. Par exemple, dans beaucoup de grandes organisations, ce ne sont pas les mêmes personnes qui gèrent le routage et qui gèrent les commandes aux fournisseurs. Et, au RIPE-NCC, les clés privées sont souvent hébergées par le RIPE-NCC, pas par les titulaires, et le RIPE-NCC n'a évidemment pas le droit de s'en servir pour autre chose que le routage.

Bref, n'utilisez pas la RPKI pour autre chose que ce pour quoi elle a été conçue.


Téléchargez le RFC 9255


L'article seul

RFC 9250: DNS over Dedicated QUIC Connections

Date de publication du RFC : Mai 2022
Auteur(s) du RFC : C. Huitema (Private Octopus), S. Dickinson (Sinodun IT), A. Mankin (Salesforce)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dprive
Première rédaction de cet article le 22 mai 2022


Ce nouveau RFC complète la série des mécanismes de protection cryptographique du DNS. Après DoT (RFC 7858) et DoH (RFC 8484), voici DoQ, DNS sur QUIC. On notera que bien que QUIC ait été conçu essentiellement pour les besoins de HTTP, c'est le DNS qui a été la première application normalisée de QUIC.

Fonctionnellement, DoQ est très proche de ses prédécesseurs, et offre les mêmes services de sécurité, confidentialité, grâce au chiffrement, et authentification du serveur, via le certificat de celui-ci. L'utilisation du transport QUIC permet quelques améliorations, notamment dans le « vrai » parallélisme. Contrairement à DoT (RFC 7858) et DoH (RFC 8484), DoQ peut être utilisé pour parler à un serveur faisant autorité aussi bien qu'à un résolveur.

Notez que le terme de DoQ (DNS over QUIC) n'apparait pas dans le RFC de terminologie DNS, le RFC 8499. Il sera normalement dans son successeur.

Logiquement, DoQ devrait faire face aux mêmes problèmes politiques que ceux vécus par DoH (voir aussi mon article dans Terminal) mais cela n'a pas été encore le cas. Il faut dire qu'il y a peu de déploiements (ne comptez pas installer DoQ comme serveur ou client tout de suite, le code est loin d'être disponible partout).

Le cahier des charges de DoQ est (sections 1 et 3 du RFC) :

  • Protection contre les attaques décrites dans le RFC 9076.
  • Même niveau de protection que DoT (RFC 7858, avec notamment les capacités d'authentification variées du RFC 8310). Rappelez-vous que QUIC est toujours chiffré (actuellement avec TLS, cf. RFC 9001).
  • Meilleure validation de l'adresse IP source qu'avec le classique DNS sur UDP, grâce à QUIC.
  • Pas de limite de taille des réponses, quelle que soit la MTU du chemin.
  • Diminution de la latence, toujours grâce à QUIC et notamment ses possibilités de connexion 0-RTT (RFC 9000, section 5), ses procédures de récupération en cas de perte de paquets (RFC 9002) et la réduction du head-of-line blocking, chaque couple requête/réponse étant dans son propre ruisseau.

En revanche, DoQ n'esssaie pas de :

  • Contourner le blocage de QUIC que font certaines middleboxes (DoH est meilleur, de ce point de vue). La section 3.3 du RFC détaille cette limitation de DoQ : il peut être filtré trop facilement, son trafic se distinguant nettement d'autres trafics.
  • Ou de permettre des messages du serveur non sollicités (pour cela, il faut utiliser le RFC 8490).

DoQ utilise QUIC. QUIC est un protocole de transport généraliste, un concurrent de TCP. Plusieurs protocoles applicatifs peuvent utiliser QUIC et, pour chacun de ces protocoles, cela implique de définir comment sont utilisés les ruisseaux (streams) de QUIC. Pour HTTP/3, cela a été fait dans le RFC 9113, publié plus tard. Ironiquement, le DNS est donc le premier protocole à être normalisé pour une utilisation sur QUIC. Notez qu'une autre façon de faire du DNS sur QUIC est de passer par DoH et HTTP/3 (c'est parfois appelé DoH3, terme trompeur) mais cette façon n'est pas couverte dans notre RFC, qui se concentre sur DoQ, du DNS directement sur QUIC, sans HTTP.

La spécification de DoQ se trouve en section 4. Elle est plutôt simple, DoQ étant une application assez directe de QUIC. Comme DoQ utilise QUIC, il y a forcément un ALPN, en l'occurrence via l'identificateur doq.

Le port, quant à lui, est par défaut 853, également nommé domain-s. C'est donc le même que DoT, et aussi le même que le DNS sur DTLS (RFC 8094, protocole expérimental, jamais vraiment mis en œuvre et encore moins déployé, et qui est de toute façon distinguable du trafic QUIC, cf. section 17.2 du RFC 9000). Bien sûr, un client et un serveur DNS majeurs et vaccinés peuvent toujours se mettre d'accord sur un autre port (mais pas 53, réservé au DNS classique). Cette utilisation d'un port spécifique à DoQ est un des points qui le rend vulnérable au filtrage, comme vu plus haut. Utiliser 443 peut aider. (Le point a été chaudement discuté à l'IETF, entre défenseurs de la vie privée et gestionnaires de réseau qui voulaient pouvoir filtrer facilement les protocoles de leur choix. Si on utilise le port 443, il faut se rappeler que l'ALPN est pour l'instant en clair, la lutte de l'épée et de la cuirasse va donc continuer.)

Plus important, la question de la correspondance entre les messages DNS et les ruisseaux QUIC. La règle est simple : pour chaque requête DNS, un ruisseau est créé. La première requête sur une connexion donnée sera donc sur le ruisseau 0 (RFC 9000, section 2.1), la deuxième sur le ruisseau 4, etc. Si les connexions QUIC sont potentiellement de longue durée, en revanche, les ruisseaux ne servent qu'une fois. Le démultiplexage des réponses est assuré par QUIC, pas par le DNS (et l'identificateur de requête DNS est donc obligatoirement à zéro, puisqu'inutile). Le parallélisme est également fourni par QUIC, client et serveur n'ont rien de particulier à faire. Il est donc recommandé de ne pas attendre une réponse pour envoyer les autres questions. Les messages DNS sont précédés de deux octets indiquant leur longueur, comme avec TCP.

Comme avec TCP, client et serveur ont le droit de garder un œil sur les ruisseaux et les connexions inactifs, et de les fermer d'autorité. La section 5.5 donne des détails sur cette gestion de connexion, mais le RFC 7766 est également une bonne lecture (voir aussi le RFC 9103 pour le cas des transferts de zones).

DoQ introduit quelques codes d'erreur à lui (RFC 9000, section 20.2), comme DOQ_INTERNAL_ERROR (quelque chose s'est mal passé), DOQ_PROTOCOL_ERROR (quelqu'un n'a pas lu le RFC, par exemple l'identificateur de requête était différent de zéro) ou DOQ_EXCESSIVE_LOAD (trop de travail tue le travail).

Un des gros avantages de QUIC est le 0-RTT, où des données sont envoyées dès le premier paquet, ce qui réduit nettement la latence. Cela implique que le client ait déjà contacté le serveur avant, mémorisant les informations qui seront données au serveur pour qu'il retrouve la session à reprendre. C'est cool, mais cela pose d'évidents problèmes de vie privée (ces informations mémorisées sont une sorte de cookie, et permettent le suivi à la trace d'un client). D'ailleurs, en parlant de vie privée, le RFC signale aussi (section 5.5.4) que la possibilité de migrer une connexion QUIC d'une adresse IP à l'autre a également des conséquences pour la traçabilité.

Autre piège avec le 0-RTT, il ne protège pas contre le rejeu et il ne faut donc l'utiliser que pour des requêtes DNS idempotentes comme QUERY (le cas le plus courant) ou NOTIFY (qui change l'état du serveur, mais est idempotent). Bon, de toute façon, un serveur peut toujours être configuré pour ne pas accepter de 0-RTT, et répondre alors REFUSED (avec un code d'erreur étendu - RFC 8914 - Too Early).

La section 5 du RFC liste les exigences auxquelles doivent se soumettre les mises en œuvre de DoQ. Le client doit authentifier le serveur (cf. RFC 7858 et RFC 8310, mais aussi RFC 8932, sur les bonnes pratiques). Comme tous les serveurs ne géreront pas DoQ, en tout cas dans un avenir prévisible, les clients doivent être préparés à ce que ça échoue et à réessayer, par exemple en DoT.

QUIC ne dissimule pas forcément la taille des messages échangés, et celle-ci peut être révélatrice. Malgré le chiffrement, la taille d'une requête ou surtout d'une réponse peut donner une idée sur le nom demandé. Le RFC impose donc l'utilisation du remplissage, soit par la méthode QUIC générique de la section 19.1 du RFC 9000, soit par la méthode spécifique au DNS du RFC 7830. La première méthode est en théorie meilleure car elle dissimule d'autres métadonnées, et qu'elle tient compte de davantage d'éléments, mais les bibliothèques QUIC n'exposent pas forcément dans leur API de moyen de contrôler ce remplissage. Le RFC recommande donc aux clients et serveurs DoQ de se préparer à faire le remplissage eux-mêmes (en relisant bien le RFC 8467 avant).

Un petit retour sur la protection de la vie privée en section 7. Cette section rappele l'importance de lire et suivre le RFC 8932 si vous gérez un service de résolution DNS sécurisé (DoQ ou pas DoQ). Et elle insiste sur le fait que des fonctions de QUIC très pratiques pour diminuer la latence à l'établissement de la connexion, notamment le 0-RTT, ont des conséquences pour la vie privée, en permettant de suivre un même utilisateur (plus exactement une même machine). Et cela marche même si la machine a changé d'adresse IP entretemps. On peut aussi dire que le problème de QUIC est de permettre des sessions de très longue durée (que ce soit une vraie session unique, ou bien une « session virtuelle », formée en reprenant une session existante avec le 0-RTT) et que de telles sessions longues, excellentes pour les performances, le sont forcément moins pour l'intimité.

Les mises en œuvre de DoQ, maintenant. La société AdGuard a produit du code, dont un serveur et un client en Go, dnslookup. (Regardez l'exposé du directeur technique de cette société à l'OARC.) Voici un exemple de compilation, puis d'utilisation de dnslookup :


% git clone https://github.com/ameshkov/dnslookup.git
%  cd dnslookup
% make
% ./dnslookup www.bortzmeyer.org quic://dns.adguard.com

On utilisait le serveur DoQ public d'AdGuard. Officiellement, NextDNS en a également un mais je n'arrive pas à l'utiliser :

% ./dnslookup www.bortzmeyer.org quic://3e4935.dns.nextdns.io
...
2022/05/22 08:16:33 Cannot make the DNS request: opening quic session to quic://3e4935.dns.nextdns.io:853: timeout: no recent network activity
  

Notez que si vous voulez utiliser dnslookup avec un serveur dont le certificat n'est pas valide, il faut mettre la variable d'environnement VERIFY à 0 (cela ne semble pas documenté).

Il existe une autre mise en œuvre de DoQ, quicdoq, écrite en C. Elle dépend de la bibliothèque QUIC picoquic qui, à son tour, dépend de picotls, dont la compilation n'est pas toujours évidente. D'une manière générale, les bibliothèques QUIC sont souvent récentes, parfois expérimentales, et ne marchent pas toujours du premier coup. Mais si vous arrivez à compiler les dépendances de quicdoq, vous pouvez lancer le serveur ainsi :

% ./quicdoq_app -p 8053 -d 9.9.9.9
  

Puis le client :

% ./quicdoq_app ::1 8053 foobar:SOA bv:NS www.bortzmeyer.org:AAAA
  

Le logiciel de test de performances Flamethrower, écrit en C++ a une branche DoQ en cours de développement.

Le logiciel en Python aioquic ne semble pouvoir interagir qu'avec lui-même. Vu les messages d'erreur (quelque chose à propos de not enough bytes), je soupçonne qu'il utilise l'encodage des débuts de DoQ, quand il n'y avait pas encore le champ de deux octets au début des messages. Même avec lui-même, il a des exigences pénibles en matière de certificat (pas de certificats auto-signés, obligation que le nom du serveur soit dans le SAN - Subject Alternative Name, pas seulement dans le sujet).

Pour PowerDNS, il n'y a pour l'instant qu'un ticket. Et pour Unbound, c'est en cours, ainsi que pour Knot.

Dans les bibliothèques DNS, un travail est en cours pour go-dns.

Pour finir, vous pouvez regarder la présentation de DoQ par une des auteures du RFC au RIPE 84. Et un des premiers articles de recherche sur DoQ est « One to Rule them All? A First Look at DNS over QUIC ».


Téléchargez le RFC 9250


L'article seul

RFC 9239: Updates to ECMAScript Media Types

Date de publication du RFC : Mai 2022
Auteur(s) du RFC : M. Miller, M. Borins (GitHub), M. Bynens (Google), B. Farias
Pour information
Réalisé dans le cadre du groupe de travail IETF dispatch
Première rédaction de cet article le 11 mai 2022


Ce RFC documente les types de médias pour le langage de programmation JavaScript (dont le nom officiel est ECMAScript, qu'on retrouve dans le titre de ce RFC). Il remplace le RFC 4329 et le principal changement est que le type recommandé est désormais text/javascript et non plus application/javascript.

Si vous voulez vous renseigner en détail sur JavaScript, notre RFC recommande de lire la norme ECMA, en https://262.ecma-international.org/12.0/. Cette norme est développée en dehors de l'IETF et le choix des types de médias (aussi appelés types MIME, cf. RFC 2046) n'est donc pas forcément en accord avec les règles de l'IETF (RFC 6838). C'est pour cela que l'IESG a ajouté une note d'avertissement au RFC. Mais, bon, ce n'est pas trop grave en pratique. Le type recommandé est donc désormais text/javascript. D'autres types existent, application/ecmascript, application/javascript, etc, mais ils sont maintenant considérés comme dépassés.

Il existe plusieurs versions de la norme JavaScript et d'autres apparaitront peut-être dans le futur. Mais le type officiel n'indique pas de version (il a existé des propositions comme text/javascript1.4) et compte que toutes ces versions sont suffisamment compatibles pour qu'on ne gère pas la complexité d'avoir plusieurs types. Normalement, ECMA s'engage à ne pas bouleverser le langage.

Le choix de text/ est contestable car la définition originale de text/ dans le RFC 2045 prévoyait plutôt application/ pour les programmes. Le RFC 4329 enregistrait les types text/javascript et application/javascript, et recommandait le application/javascript (en accord avec ce que dit le RFC 6838). Aujourd'hui, c'est le contraire, text/javascript est le préféré. D'une manière générale, application/ n'a guère été utilisé (pas seulement dans le cas de JavaScript). (Personnellement, cela me convient très bien : pour moi, si ça peut s'afficher avec cat et s'éditer avec vi, c'est du texte.)

JavaScript est donc officiellement du texte, et la section 4 de notre RFC précise : du texte en UTF-8. Le paramètre charset est donc facultatif (même si le RFC 6838 dit le contraire). Si vous aimez les détails d'encodage, la section 4 vous ravira (c'est l'un des points qui a suscité le plus de discussion à l'IETF).

Le type text/javascript est enregistré à l'IANA ; les types comme application/javascript sont marqués comme dépassés. Même chose pour text/ecmascript. Le nom officiel de JavaScript est ECMAscript, puisque normalisé à l'ECMA mais personne n'utilise ce terme. (Il faut quand même noter que JavaScript est un terme publicitaire mensonger puisqu'il avait été choisi par le marketing pour profiter de la popularité - à l'époque - de Java, un langage avec lequel il n'a rien à voir.) Enfin, les types comme text/x-javascript qu'on voit parfois trainer datent d'avant le RFC 6648, qui abandonnait les identificateurs semi-privés commençant par x-.

La section 5 couvre les questions de sécurité. Elle est très longue car l'envoi de JavaScript à un programme qui l'exécutera (ce qui est très courant sur le Web) pose plein de problèmes de sécurité. Le RFC rappelle que l'exécution automatique d'un programme fourni par un tiers est évidemment intrinsèquement dangereuse, et doit se faire dans un bac à sable. Parmi les dangers (mais il y en a beaucoup d'autres !) le déni de service puisque JavaScript est un langage de Turing et permet donc, par exemple, des boucles infinies.

L'annexe B du RFC résume les changements depuis le RFC 4329 : le principal est bien sûr l'abandon de application/javascript au profit de text/javascript. Il y a aussi l'ajout de quelques détails comme une faille de sécurité nouvelle, et le cas des modules JavaScript.

Ah, un point de détail cité au détour d'un paragraphe du RFC : il n'y a pas de norme pour les identificateurs de fragments dans un URI pointant vers une ressource de type text/javascript. Si https://code.example/js/foreach.js#l23 pointe vers du JavaScript, la signification du #l23 (l'identificateur de fragment) n'est pas spécifiée.


Téléchargez le RFC 9239


L'article seul

RFC 9234: Route Leak Prevention and Detection Using Roles in UPDATE and OPEN Messages

Date de publication du RFC : Mai 2022
Auteur(s) du RFC : A. Azimov (Qrator Labs & Yandex), E. Bogomazov (Qrator Labs), R. Bush (IIJ & Arrcus), K. Patel (Arrcus), K. Sriram (USA NIST)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 25 mai 2022


Vous savez sans doute que l'Internet repose sur le protocole de routage BGP et que ce protocole a été cité dans des accidents (voire des attaques) spectaculaires, par exemple lorsqu'un routeur BGP annonçait ou réannonçait des routes qu'il n'aurait pas dû annoncer (ce qu'on nomme une fuite de routes). Si BGP lui-même est pair-à-pair, en revanche, les relations entre les pairs ne sont pas forcément symétriques et des règles sont largement admises ; par exemple, on n'est pas censé annoncer à un transitaire des routes apprises d'un autre transitaire. Ce RFC étend BGP pour indiquer à l'ouverture de la session le type de relations qu'on a avec son pair BGP, ce qui permet de détecter plus facilement certaines fuites de routes.

La solution proposée s'appuie sur un modèle des relations entre opérateurs, le modèle « sans vallée » (valley-free, même si ce terme n'est pas utilisé dans le RFC). On le nomme aussi modèle Gao-Rexford. Ce modèle structure ces relations de telle façon à ce que le trafic aille forcément vers un opérateur aussi ou plus important (la « montagne »), avant de « redescendre » vers la destination ; le trafic ne descend pas dans une « vallée », sauf si elle est la destination finale. Le but est de permettre un routage optimum et d'éviter boucles et goulets d'étranglement, mais ce modèle a aussi une conséquence politico-économique, maintenir la domination des gros acteurs (typiquement les Tier-1). Les relations entre participants à une session BGP sont de type Client-Fournisseur ou Pair-Pair, et le trafic va toujours dans le sens du client vers le fournisseur, sauf pour les destinations finales (on n'utilise donc pas un pair pour du transit, c'est-à-dire pour joindre d'autres réseaux que ceux du pair).

En raison de ce modèle, un routeur BGP n'est pas censé annoncer à un transitaire des routes apprises d'un autre transitaire, ou annoncer à un pair des routes apprises d'un transitaire, ou bien apprises d'un autre pair (RFC 7908). Une fuite de routes est une annonce qui viole cette politique (qu'elle soit explicite ou implicite).

Traditionnellement, on empêche ces fuites par des règles dans la configuration du routeur. Par exemple, le client d'un transitaire va bloquer l'exportation par défaut, puis lister explicitement les routes qu'il veut annoncer. Et le transitaire va bloquer l'importation par défaut, et lister ensuite les routes qu'il acceptera de son client. Ce nouveau RFC ajoute un autre mécanisme, où la relation entre les deux partenaires BGP est explicitement marquée comme Client-Fournisseur ou Pair-Pair, facilitant la détection des fuites. Un concept qui était auparavant purement business, le type de relation entre acteurs BGP, passe ainsi dans le protocole.

Le RFC utilise donc ces termes, pour désigner la place d'un acteur BGP dans le processus de routage :

  • Fournisseur (Provider) : peut envoyer n'importe quelle route (un transitaire est un Fournisseur).
  • Client (Customer) : ne peut envoyer que ses propres routes, ou bien celles apprises d'un de ses clients (l'opérateur qui est Client pour une relation BGP peut être Fournisseur pour une autre).
  • Serveur de routes (Route Server) : peut envoyer toutes les routes qu'il connait à ses clients.
  • Client d'un serveur de routes (Route Server Client) : ne peut envoyer au serveur de routes que ses propres routes, ou bien celles apprises d'un de ses clients.
  • Pair (Peer) : ne peut envoyer à un de ses pairs que ses propres routes, ou bien celles apprises d'un de ses clients.

Une violation de ces règles est une fuite de routes (RFC 7908). Le RFC précise qu'il s'agit de relations « techniques », qu'elles n'impliquent pas forcément une relation business mais, en pratique, l'échange entre pairs est souvent gratuit et la relation Client-Fournisseur payante. Le RFC précise aussi qu'il peut exister des cas plus complexes, comme une relation Client-Fournisseur pour certains préfixes IP et Pair-Pair pour d'autres.

Bon, assez de politique et de business, place à la technique et au protocole. Le RFC définit une nouvelle capacité (RFC 5492) BGP, Role, code 9 et dont la valeur, stockée sur un octet, indique un des rôles listés ci-dessus (0 pour Fournisseur, 3 pour Client, 4 pour Pair, etc). D'autres rôles pourront être ajoutés dans le futur (politique « Examen par l'IETF », cf. RFC 8126). Ce rôle indique la place de l'AS et est configuré avec la session BGP (rappelez-vous qu'un AS peut être Client d'un côté et Fournisseur de l'autre). À l'ouverture de la connexion, les routeurs vérifient la compatibilité des rôles (si tous les deux annoncent qu'ils sont Fournisseur, c'est un problème, d'où un nouveau code d'erreur BGP, Role mismatch).

Une fois que les deux routeurs sont d'accord sur les rôles respectifs de leurs AS, les routes annoncées peuvent être marquées avec un nouvel attribut, OTC (Only To Customer, code 35), dont la valeur est un numéro d'AS. Une route ainsi marquée ne sera acceptée que si elle vient d'un Fournisseur, ou d'un pair dont le numéro d'AS coïncide. Autrement, elle sera considérée comme résultant d'une fuite.

Ah, et pour les cas compliqués dont on a parlé plus haut, comme les rôles différents pour chaque préfixe ? Dans ce cas-là, le mécanisme des rôles de ce RFC n'est pas adapté et ne doit pas être utilisé.

Ce système des rôles est apparemment mis en œuvre dans des patches (non encore intégrés ?) pour FRRouting et BIRD. Par exemple, pour FRRouting, on configurerait :

neighbor 2001:db8:999::1 local-role customer
  

Pour BIRD, cela serait :

configuration
    ...
    protocol bgp {
        ...
        local_role customer;
    }
  

Et la gestion des rôles devrait être bientôt dans BGPkit. Et une version antérieure à la sortie du RFC tourne sur les routeurs MikroTik (voir leur documentation, local.role ebgp-customer…). Par contre, je ne sais pas pour Cisco ou Juniper. Si quelqu'un a des informations…


Téléchargez le RFC 9234


L'article seul

RFC 9233: IDNA2008 and Unicode 12.0.0

Date de publication du RFC : Mars 2022
Auteur(s) du RFC : P. Faltstrom (Netnod)
Chemin des normes
Première rédaction de cet article le 24 avril 2022


La norme pour les noms de domaine en Unicode, dite « IDNA 2008 », prévoit une révision à chaque nouvelle version d'Unicode pour éventuellement s'adapter à des changements dus à ces nouvelles versions. Ce processus de révision a pas mal cafouillé (euphémisme), et ce RFC doit donc traiter d'un coup les versions 6 à 12 d'Unicode.

Le fond du problème est que l'ancienne norme sur les IDN (RFC 3490) était strictement liée à une version donnée d'Unicode et qu'il fallait donc une nouvelle norme pour chacune des versions annuelles d'Unicode. Vu le processus de publication d'une norme à l'IETF, ce n'était pas réaliste. La seconde norme IDN, « IDN 2 » ou « IDN 2008 » (bien qu'elle soit sortie en 2010) remplaçait les anciennes tables fixes de caractères autorisés ou interdits par un algorithme, à faire tourner à chaque sortie d'une version d'Unicode pour produire les tables listant les caractères qu'on peut accepter dans un nom de domaine internationalisé (le mécanisme exact, utilisant les propriétés des caractères listées dans la norme Unicode, figure dans le RFC 5892). En théorie, c'était très bien. En pratique, malgré les règles de stabilité d'Unicode, il se produisait parfois des problèmes. Comme le documente le RFC 8753, un caractère peut ainsi passer d'interdit à autorisé, ce qui n'est pas grave mais aussi dans certains cas d'autorisé à interdit ce qui est bien plus embêtant : que faire des noms déjà réservés qui utilisent ce caractère ? En général, il faut ajouter une exception manuelle, ce qui justifie un mécanisme de révision de la norme IDN, mis en place par ce RFC 8753. Ce nouveau RFC 9233 est le premier RFC de révision. Heureusement, cette fois, aucune exception manuelle n'a été nécessaire.

La précédente crise était due à Unicode version 6 qui avait créé trois incompatibilités (RFC 6452). Une seule était vraiment gênante, le caractère , qui, autorisé auparavant, était devenu interdit suite au changement de ses propriétés dans la norme Unicode. Le RFC 6452 avait documenté la décision de ne rien faire pour ce cas, ce caractère n'ayant apparemment jamais été utilisé. Unicode 7 a ajouté un autre problème, le , qui était un cas différent (la possibilité de l'écrire de plusieurs façons), et la décision a été prise de faire désormais un examen formel de chaque nouvelle version d'Unicode. Mais cet examen a été souvent retardé et voilà pourquoi, alors qu'Unicode 13 est sorti (ainsi qu'Unicode 14 depuis), ce nouveau RFC ne traite que jusqu'à la version 12.

Passons maintenant à l'examen des changements apportés par les versions 7 à 12 d'Unicode, fait en section 3 du RFC :

  • À part le cas du cité plus haut, Unicode 7 n'a pas apporté de changements gênants pour IDN (par exemple, U+17B4 (caractère non visible) a changé de propriétés mais il était interdit pour IDN et le reste),
  • Unicode 8, Unicode 9 et Unicode 10 n'ont appporté aucun changement gênant,
  • Unicode 11 a changé les propriétés de certains caractères existants mais le résultat pour IDN ne change pas (par exemple, 𑨇, qui était autorisé, le reste),
  • Et Unicode 12 ? Rien de problématique.

En Unicode 11, 𑇉 qui passe d'interdit à autorisé, était un cas peu gênant. Le RFC prend donc la décision de ne pas ajouter d'exception pour ce caractère peu commun.

Voilà, arrivé ici, vous pensez peut-être que cela fait beaucoup de bruit pour rien puisque finalement les différentes versions d'Unicode n'ont pas créé de problème. Mais c'est justement pour s'assurer de cela que cet examen était nécessaire.

Pour compliquer davantage les choses, on notera qu'il existe encore sans doute (section 2.3 du RFC) des déploiements d'IDN qui en sont restés à la première version (celle du RFC 3490) voire qui sont un mélange des deux versions d'IDN, en ce qui concerne l'acceptation ou le refus des caractères.

En avril 2022, le travail pour Unicode 13 ou Unicode 14 n'a apparemment pas encore commencé…


Téléchargez le RFC 9233


L'article seul

RFC 9230: Oblivious DNS over HTTPS

Date de publication du RFC : Juin 2022
Auteur(s) du RFC : E. Kinnear (Apple), P. McManus (Fastly), T. Pauly (Apple), T. Verma, C.A. Wood (Cloudflare)
Expérimental
Première rédaction de cet article le 9 juin 2022


Des protocoles de DNS sécurisés comme DoH protègent la liaison entre un client et un résolveur DNS. Mais le résolveur, lui, voit tout, aussi bien le nom de domaine demandé que l'adresse IP du client. Ce nouveau RFC décrit un mécanisme expérimental de relais entre le client et le serveur DoH, permettant de cacher l'adresse IP du client au serveur, et la requête au relais. Une sorte de Tor minimum.

Normalement, DoH (normalisé dans le RFC 8484) résout le problème de la surveillance des requêtes DNS. Sauf qu'évidemment le serveur DoH lui-même, le résolveur, voit le nom demandé, et la réponse faite. (Les utilisateurices de l'Internet oublient souvent que la cryptographie, par exemple avec TLS, protège certes contre le tiers qui écoute le réseau mais pas du tout contre le serveur avec qui on parle.) C'est certes nécessaire à son fonctionnement mais, comme il voit également l'adresse IP du client, il peut apprendre pas mal de choses sur le client. Une solution possible est d'utiliser DoH (ou DoT) sur Tor (exemple plus loin). Mais Tor est souvent lent, complexe et pas toujours disponible (cf. annexe A du RFC). D'où les recherches d'une solution plus légère, Oblivious DoH, décrit dans ce nouveau RFC. L'idée de base est de séparer le routage des messages (qui nécessite qu'on connaisse l'adresse IP du client) et la résolution DNS (qui nécessite qu'on connaisse la requête). Si ces deux fonctions sont assurées par deux machines indépendantes, on aura amélioré la protection de la vie privée.

Les deux machines vont donc être :

  • L'Oblivious Proxy, le relais qui connait l'adresse IP du client mais ne voit passer qu'une requête DNS chiffrée ; ce n'est pas un relais HTTP complet, il est spécialisé dans DoH.
  • Et l'Oblivious Target, le serveur DoH (le résolveur, donc) ; il a une clé publique avec laquelle le client chiffrera les requêtes à envoyer.

Un schéma simplifié : oblivious-doh

Il est important de répéter que, si le relais et le serveur sont complices, Oblivious DoH perd tout intérêt. Ils doivent donc être gérés par des organisations différentes.

La découverte du relais, et de la clé publique du serveur, n'est pas traité dans ce RFC. Au moins au début, elle se fera « manuellement ».

Le gros morceau du RFC est sa section 4. Elle explique les détails du protocole. Le client qui a une requête DNS à faire doit la chiffrer avec la clé publique du serveur. Ce client a été configuré avec un gabarit d'URI (RFC 6570) et avec l'URL du serveur DoH, serveur qui doit connaitre le mécanisme Oblivious DoH. Le gabarit doit contenir deux variables, targethost (qui sera incarné avec le nom du serveur DoH) et targetpath (qui sera incarné avec le chemin dans l'URL du serveur DoH). Par exemple, un gabarit peut être https://dnsproxy.example/{targethost}/{targetpath}. Le client va ensuite faire une requête (méthode HTTP POST) vers le relais. La requête aura le type application/oblivious-dns-message (la réponse également, donc le client a intérêt à indiquer Accept: application/oblivious-dns-message).

En supposant un serveur DoH d'URL https://dnstarget.example/dns, la requête sera donc (en utilisant la syntaxe de description de HTTP/2 et 3) :

:method = POST
:scheme = https
:authority = dnsproxy.example
:path = /dnstarget.example/dns
accept = application/oblivious-dns-message
content-type = application/oblivious-dns-message
content-length = 106

[La requête, chiffrée]
  

Le relais enverra alors au serveur :

:method = POST
:scheme = https
:authority = dnstarget.example
:path = /dns
accept = application/oblivious-dns-message
content-type = application/oblivious-dns-message
content-length = 106    

[La requête, chiffrée]
  

Le relais ne doit pas ajouter quelque chose qui permettrait d'identifier le client, ce qui annulerait tout l'intérêt d'Oblivious DoH. Ainsi, le champ forwarded (RFC 7239) est interdit. Et bien sûr, on n'envoie pas de cookies, non plus.

La réponse utilise le même type de médias :

:status = 200
content-type = application/oblivious-dns-message
content-length = 154

[La réponse, chiffrée]
  

Le relais, en la transmettant, ajoutera un champ proxy-status (RFC 9209).

Les messages de type application/oblivious-dns-message sont encodés ainsi (en utilisant le langage de description de TLS, cf. RFC 8446, section 3) :


struct {
      opaque dns_message<1..2^16-1>;
      opaque padding<0..2^16-1>;
} ObliviousDoHMessagePlaintext;

  

Bref, un message DNS binaire, et du remplissage. Le remplissage est nécessaire car TLS, par défaut, n'empêche pas l'analyse de trafic. (Lisez le RFC 8467.) Le tout est ensuite chiffré puis mis dans :


struct {
      uint8  message_type;
      opaque key_id<0..2^16-1>;
      opaque encrypted_message<1..2^16-1>;
} ObliviousDoHMessage;

  

Le key_id servant à identifier ensuite le matériel cryptographique utilisé.

J'ai parlé à plusieurs reprises de chiffrement. Le client doit connaitre la clé publique du serveur DoH pour pouvoir chiffrer. (Le RFC ne prévoit pas de mécanisme pour cela.) Comme avec la plupart des systèmes de chiffrement, le chiffrement sera en fait réalisé avec un algorithme symétrique. La transmission ou la génération de la clé de cet algorithme utlisera le mécanisme HPKE (Hybrid Public Key Encryption, spécifié dans le RFC 9180).

Outre l'analyse de trafic, citée plus haut, une potentielle faille d'Oblivious DoH serait le cas d'un serveur indiscret qui essaierait de déduire des informations de l'établissement des connexions DoH vers lui. Le RFC recommande que les relais établissent des connexions de longue durée et les réutilisent pour plusieurs clients, rendant cette éventuelle analyse plus complexe.

Notez aussi que le relais est évidemment libre de sa politique. Les relais peuvent par exemple ne relayer que vers certains serveurs DoH connus.

Le serveur DoH ne connait pas l'adresse IP du client (c'est le but) et ne peut donc pas transmettre aux serveurs faisant autorité les informations qui peuvent leur permettre d'envoyer une réponse adaptée au client final. Si c'est un problème, le client final peut toujours utiliser ECS (EDNS Client Subnet, RFC 7871), en indiquant un préfixe IP assez générique pour ne pas trop en révéler. Mais cela fait évidemment perdre une partie de l'intérêt d'Oblivious DoH.

Bon, et questions mises en œuvre de ce RFC ? Il y en a une dans DNScrypt (et ils publient une liste de serveurs et de relais).

Oblivious DoH est en travaux depuis un certain temps (voyez par exemple cet article de Cloudflare). Notez que la technique de ce RFC est spécifique à DoH. Il y a aussi des travaux en cours à l'IETF pour un mécanisme plus général, comme par exemple un Oblivious HTTP, qui serait un concurrent « low cost » de Tor.

À propos de Tor, j'avais dit plus haut qu'on pouvait évidemment, si on ne veut pas révéler son adresse IP au résolveur DoH, faire du DoH sur Tor. Voici un exemple de requête DoH faite avec le client kdig et le programme torsocks pour faire passer les requêtes TCP par Tor :

% torsocks kdig +https=/ @doh.bortzmeyer.fr ukraine.ua
;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM)
;; HTTP session (HTTP/2-POST)-(doh.bortzmeyer.fr/)-(status: 200)
...
;; ANSWER SECTION:
ukraine.ua.         	260	IN	A	104.18.7.16
ukraine.ua.         	260	IN	A	104.18.6.16
...
;; Received 468 B
;; Time 2022-06-02 18:45:15 UTC
;; From 193.70.85.11@443(TCP) in 313.9 ms
  

Et ce résolveur DoH ayant un service .onion, on peut même :

% torsocks kdig +https=/ @lani4a4fr33kqqjeiy3qubhfx2jewfd3aeaepuwzxrx6zywp2mo4cjad.onion ukraine.ua
;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM)
;; HTTP session (HTTP/2-POST)-(lani4a4fr33kqqjeiy3qubhfx2jewfd3aeaepuwzxrx6zywp2mo4cjad.onion/)-(status: 200)
...
;; ANSWER SECTION:
ukraine.ua.         	212	IN	A	104.18.6.16
ukraine.ua.         	212	IN	A	104.18.7.16
...
;; Received 468 B
;; Time 2022-06-02 18:46:00 UTC
;; From 127.42.42.0@443(TCP) in 1253.8 ms
  

Pour comparer les performances (on a dit que la latence de Tor était franchement élevée), voici une requête non-Tor vers le même résolveur :

% kdig +https=/ @doh.bortzmeyer.fr ukraine.ua                                             
;; TLS session (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM)
;; HTTP session (HTTP/2-POST)-([2001:41d0:302:2200::180]/)-(status: 200)
...
;; ANSWER SECTION:
ukraine.ua.         	300	IN	A	104.18.6.16
ukraine.ua.         	300	IN	A	104.18.7.16
...
;; Received 468 B
;; Time 2022-06-02 18:49:44 UTC
;; From 2001:41d0:302:2200::180@443(TCP) in 26.0 ms
  

Téléchargez le RFC 9230


L'article seul

RFC 9229: IPv4 Routes with an IPv6 Next Hop in the Babel Routing Protocol

Date de publication du RFC : Mai 2022
Auteur(s) du RFC : J. Chroboczek (IRIF, University of Paris)
Expérimental
Réalisé dans le cadre du groupe de travail IETF babel
Première rédaction de cet article le 11 mai 2022


Voici une extension sympathique qui réjouira tous les amateurs de routage IP. Elle permet à un routeur qui parle le protocole de routage Babel d'annoncer un préfixe IPv4 où le routeur suivant pour ce préfixe n'a pas d'adresse IPv4 sur cette interface.

Un petit rappel de routage : les annonces d'un routeur qui parle un protocole de routage dynamique comme Babel (RFC 8966) comprennent un préfixe IP (comme 2001:db8:543::/48) et l'adresse IP du routeur suivant (next hop dans la langue de Radia Perlman). Traditionnellement, le préfixe et l'adresse du routeur suivant étaient de la même version (famille) : tous les deux en IPv4 ou tous les deux en IPv6. Mais, si on veut router IPv6 et IPv4, cela consomme une adresse IP par interface sur chaque routeur, or les adresses IPv4 sont désormais une denrée très rare. D'où la proposition de ce RFC : permettre des annonces d'un préfixe IPv4 avec une adresse de routeur suivant en IPv6. (Notez que cela ne concerne pas que Babel, cela pourrait être utilisé pour d'autres protocoles de routage dynamique.)

La section 1 du RFC résume ce que fait un protocole de routage. Son but est de construire des tables de routage, où chaque entrée de la table est indexée par un préfixe d'adresses IP et a une valeur, l'adresse du routeur suivant. Par exemple :

   destination                      next hop
2001:db8:0:1::/64               fe80::1234:5678%eth0
203.0.113.0/24                  192.0.2.1    
  

Lorsqu'un routeur doit transmettre un paquet, il regarde dans cette table et trouve l'adresse du routeur suivant. Il va alors utiliser un protocole de résolution d'adresses (ARPRFC 826 - pour IPv4, NDRFC 4861 - pour IPv6) pour trouver l'adresse MAC du routeur suivant. Rien dans cette procédure n'impose que le préfixe de destination et l'adresse du routeur suivant soient de la même version d'IP. Un routeur qui veut transmettre un paquet IPv4 vers un routeur dont il ne connait que l'adresse IPv6 procède à la résolution en adresse MAC, puis met simplement le paquet IPv4 dans une trame portant l'adresse MAC en question (l'adresse IPv6 du routeur suivant n'apparait pas dans le paquet).

En permettant des annonces de préfixes IPv4 avec un routeur suivant en IPv6, on économise des adresses IPv4. Un réseau peut fournir de la connectivité IPv4 sans avoir d'adresses IPv4, à part au bord. Et comme les adresses IPv6 des routeurs sont des adresses locales au lien allouées automatiquement (cf. RFC 7404), on peut avoir un réseau dont le cœur n'a aucune adresse statique, ce qui peut faciliter sa gestion.

Notre RFC documente donc une extension au protocole Babel (qui est normalisé dans le RFC 8966), nommée v4-via-v6. (Comme dit plus haut, le principe n'est pas spécifique à Babel, voir le RFC 5549 pour son équivalent pour BGP.)

Bon, le concret, maintenant. Les préfixes annoncés en Babel sont précédés de l'AE (Address Encoding), un entier qui indique la version (famille) du préfixe. Ce RFC crée un nouvel AE, portant le numéro 4, qui a le même format que l'AE 1 qui servait à IPv4. Une annonce d'un préfixe ayant l'AE 4 devra donc contenir un préfixe IPv4 et un routeur suivant en IPv6. Un routeur qui sait router IPv4 mais n'a pas d'adresse IPv4 sur une de ses interfaces peut donc quand même y annoncer les préfixes connus, en les marquant avec l'AE 4 et en mettant comme adresse l'adresse IPv6 pour cette interface. (Cet AE est uniquement pour les préfixes, pas pour l'indication du routeur suivant.)

Les routeurs qui ne gèrent pas l'extension v4-via-v6 ignoreront cette annonce, comme ils doivent ignorer toutes les annonces portant un AE inconnu (RFC 8966). Pour cette raison, si un routeur a une adresse IPv4 sur une interface, il faut qu'il utilise des annonces traditionnelles, avec l'AE 1, pour maximiser les chances que son annonce soit acceptée.

Arrivé ici, mes lectrices et mes lecteurs, qui sont très malins, se demandent depuis longtemps « mais un routeur doit parfois émettre des erreurs ICMP (RFC 792), par exemple s'il n'a pas d'entrée dans sa table pour cette destination ». Comment peut-il faire s'il n'a pas d'adresse sur l'interface où il a reçu le paquet coupable ? Ne rien émettre n'est pas acceptable, certains protocoles en dépendent. C'est par exemple le cas de la découverte de la MTU du chemin, documentée dans le RFC 1191, qui a besoin des messages ICMP Packet too big. Certes, il existe un algorithme de découverte de cette MTU du chemin qui est entièrement de bout en bout, et ne dépend donc pas des routeurs et de leurs messages (RFC 4821). Mais ICMP sert à d'autres choses, on ne peut pas y renoncer.

Si le routeur Babel a une adresse IPv4 quelque part, sur une de ses interfaces, il peut l'utiliser comme adresse IP source pour ses messages ICMP. S'il n'en a pas, il peut toujours utiliser 192.0.0.8 comme suggéré par la section 4.8 du RFC 7600. Bien sûr, si tout le monde le fait, des outils d'administration du réseau très pratiques comme traceroute seront moins utiles.

La nouvelle extension peut soulever des questions de sécurité (section 6 du RFC). Par exemple, si l'administrateur réseaux croyait que, parce que les routeurs n'avaient pas d'adresse IPv4 sur une interface, les paquets IPv4 ne seraient pas traités sur cette interface, cette supposition n'est plus vraie. Ainsi, une île de machines IPv4 qui se croyaient séparées de l'Internet IPv4 par un ensemble de routeurs qui n'avaient pas d'adresse IPv4 de leur côté peut se retrouver subitement connectée. Si ce n'est pas ce qu'on veut, il faut penser à ne pas se contenter du protocole de routage pour filtrer, mais aussi à filtrer explicitement IPv4.

Question mise en œuvre, cette extension figure dans babeld (voir ici un compte-rendu d'expérience) à partir de la version 1.12, ainsi que dans BIRD.


Téléchargez le RFC 9229


L'article seul

RFC 9228: Delivered-To Email Header Field

Date de publication du RFC : Avril 2022
Auteur(s) du RFC : D. Crocker (Brandenburg InternetWorking)
Expérimental
Première rédaction de cet article le 14 avril 2022


Le courrier électronique est souvent plus compliqué et plus varié que ne le croient certains utilisateurs. Par exemple, rien ne garantit que l'adresse à laquelle un message est livré soit listée dans les champs de l'en-tête (RFC 5322) comme To: ou Cc:. Cette adresse de destination est en effet séparément gérée, dans le protocole SMTP (RFC 5321) et ses comandes RCPT TO. Et puis un message peut être relayé, il peut y avoir plusieurs livraisons successives. Bref, quand on a un message dans sa boite aux lettres, on ne sait pas forcément quelle adresse avait servi. D'où ce RFC expérimental qui propose d'élargir le rôle du champ Delivered-To: pour en faire un bon outil d'information.

Ce champ existe déjà et on le voit parfois apparaitre dans les en-têtes, par exemple dans un message que je viens de recevoir :

Delivered-To: monpseudo@sources.org
  

Mais il n'est pas vraiment normalisé, son contenu exact peut varier et, en pratique, lorsqu'il y a eu plusieurs délivrances successives suivies de transmissions à une autre adresse, il ne reste parfois qu'une occurrence de ce champ, la plus récente. Difficile donc de compter dessus.

Mais c'est quoi, la livraison d'un message ? Comme définie par le RFC 5598, dans sa section 4.3.3, c'est un transfert de responsabilité d'un MTA vers un MDA comme procmail. Il peut y avoir plusieurs délivrances successives par exemple si le MDA, au lieu de simplement écrire dans la boite aux lettres de l'utilisateur, fait suivre le message à une autre adresse. Un exemple typique est celui d'une liste de diffusion, où le message va être délivré à la liste puis, après transmission par le logiciel de gestion de listes, à chaque utilisateur (dont les MDA renverront peut-être le message, créant de nouvelles livraisons).

Pourquoi est-il utile d'avoir de l'information sur ces livraisons successives ? Outre la curiosité, une motivation importante est le débogage. S'il y a des problèmes de livraison, la traçabilité est essentielle à l'analyse. Une autre motivation est la détection automatique de boucles, un problème souvent compliqué.

On l'a dit, Delivered-To: existe déjà, même s'il n'était normalisé dans aucun RFC, bien qu'il soit cité dans des exemples, comme dans le RFC 5193. Ce RFC 9228 enregistre le champ (selon la procédure du RFC 3864), et prévoit d'étendre son application. (Une tentative de normalisation est en cours dans draft-duklev-deliveredto.) Du fait de cette absence de normalisation, il existe de nombreuses variations dans l'utilisation de Delivered-To:. Par exemple, si la plupart des logiciels mettent comme valeur une adresse de courrier et rien d'autre, certains ajoutent des commentaires. Notons que le champ Received: est souvent utilisé de la même façon, via sa clause for. On peut ainsi trouver (regardez le for) :

    
Received: from bendel.debian.org (bendel.debian.org [82.195.75.100])
        (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
        key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)
        (No client certificate requested)
        by ayla.bortzmeyer.org (Postfix) with ESMTPS id 6D245A00D9
        for <monpseudo@sources.org>; Wed, 16 Feb 2022 13:22:03 +0100 (CET)

  

Là encore, il n'y a pas de normalisation et il semble qu'on ne puisse pas compter sur cette clause for. Par exemple, elle n'indique pas forcément une livraison mais peut être utilisée pour une simple transmission d'un serveur à un autre.

Venons-en à la définition de l'utilisation de Delivered-To: proposée par notre RFC dans sa section 4 :

  • Le champ Delivered-To: est ajouté uniquement lors d'une livraison,
  • sa valeur est l'adresse de courrier utilisée pour cette livraison (une adresse unique, jamais de liste),
  • si une réécriture de l'adresse est faite, on rajoute un Delivered-To: avec l'adresse réécrite,
  • comme il peut y avoir plusieurs Delivered-To: (en cas de livraisons multiples), le logiciel qui en ajoute un doit le mettre en haut (comme pour les autres champs de trace de l'en-tête, par exemple Received:, cf. RFC 5321, section, 4.1.1.4), l'ordre chronologique des Delivered-To: sera donc de bas en haut,
  • et donc les logiciels ne doivent pas réordonner les Delivered-To: ou en supprimer, sous peine de casser cette chronologie.

En conséquence, si un logiciel qui va ajouter un Delivered-To: avec une certaine adresse voit un Delivered-To: avec la même adresse, il peut être sûr qu'il y a une boucle. Voici un exemple de deux Delivered-To: différents, en raison du passage par une liste de diffusion (le message comportait évidemment bien d'autres champs dans son en-tête). La liste a été la première à traiter le message, la livraison finale ayant eu lieu après (donc plus haut dans le message, cf. section 5 du RFC) :

Delivered-To: monpseudo@sources.org
Delivered-To: lists-debian-user-french@bendel.debian.org
  

Le RFC donne un exemple imaginaire mais plus détaillé (j'ai raccourci les exemples du RFC, référez-vous à lui pour avoir les Received: et autres détails). D'abord, le message est émis par Ann :


From: Ann Author <aauthor@com.example>
Date: Mon, 25 Jan 2021 18:29:00 -0500
To: list@org.example
Subject: Sending through a list and alias
Sender: Ann Author <aauthor@com.example>   

  

Ce message a été envoyé à une liste de diffusion. Il est donc désormais :


Delivered-To: list@org.example
From: Ann Author <aauthor@com.example>
Date: Mon, 25 Jan 2021 18:29:06 -0500
To: list@org.example
Subject: Sending through a list and alias

  

Un des abonnés à la liste est alumn@edu.example. Chez lui, le message sera :


Delivered-To: alumn@edu.example
Delivered-To: list@org.example
From: Ann Author <aauthor@com.example>
Date: Mon, 25 Jan 2021 18:29:06 -0500
To: list@org.example
Subject: Sending through a list and alias

Et cet abonné fait suivre automatiquement son courrier à theRecipient@example.net. Le message, dans son état final, sera :

  

Delivered-To: theRecipient@example.net
Delivered-To: alumn@edu.example
Delivered-To: list@org.example
From: Ann Author <aauthor@com.example>
Date: Mon, 25 Jan 2021 18:29:06 -0500
To: list@org.example
Subject: Sending through a list and alias

Voilà, vous savez tout désormais sur l'extension proposée de l'utilisation de Delivered-To:. La section 6 attire toutefois notre attention sur quelques risques. Delivered-To: a comme valeur une donnée personnelle. Donc, attention, cela peut poser des questions de vie privée. Par exemple, la liste des adresses par lesquelles est passé un message peut en révéler plus que ce que le destinataire connaissait. Le problème risque surtout de se poser si quelqu'un fait suivre manuellement un message en incluant tous les en-têtes.

Autre problème potentiel, certains systèmes stockent les messages identiques en un seul exemplaire. Si on écrit à alice@example.com et bob@example.com, le serveur d'example.com pourrait décider de ne stocker qu'un seul exemplaire du message, avec des liens depuis les boites aux lettres d'Alice et Bob. Ce serait évidemment incompatible avec Delivered-To: (et pas question de mettre deux Delivered-To:, on ne veut pas révéler à Alice que Bob a reçu le message et réciproquement).


Téléchargez le RFC 9228


L'article seul

RFC 9224: Finding the Authoritative Registration Data Access Protocol (RDAP) Service

Date de publication du RFC : Mars 2022
Auteur(s) du RFC : M. Blanchet (Viagenie)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 25 avril 2022


Le protocole RDAP d'accès à l'information sur les objets (noms de domaines, adresses IP, etc) stockés dans un registre fonctionne en interrogeant le serveur en HTTP. Encore faut-il trouver le serveur à interroger. Comment le client RDAP qui veut des informations sur 2001:67c:288::2 sait-il à quel serveur demander ? Ce RFC décrit le mécanisme choisi. En gros, l'IANA gère des « méta-services » qui donnent la liste de serveurs RDAP, mais il peut aussi y avoir redirection d'un serveur RDAP vers un autre. Ce RFC remplace le premier RFC sur le sujet, le RFC 7484, avec très peu de changements.

Le protocole RDAP est décrit entre autres dans le RFC 7480 qui précise comment utiliser HTTP pour transporter les requêtes et réponses RDAP. Comme indiqué plus haut, il ne précise pas comment trouver le serveur RDAP responsable d'un objet donné. Le prédécesseur de RDAP, whois, avait exactement le même problème, résolu par exemple en mettant en dur dans le logiciel client tous les serveurs whois connus. En pratique, beaucoup de gens font simplement une requête Google et accordent une confiance aveugle auu premier résultat venu, quel que soit sa source. Au contraire, ce RFC vise à fournir un mécanisme pour trouver la source faisant autorité.

Avant d'exposer la solution utilisée, la section 1 de notre RFC rappelle comment ces objets (noms de domaine, adresses IP, numéros d'AS, etc) sont alloués et délégués. L'IANA délégue les TLD aux registres de noms de domaines, des grands préfixes IP et les blocs de numéros d'AS aux RIR. Il est donc logique que RDAP suive le même chemin : pour trouver le serveur responsable, on commence par demander à l'IANA, qui a déjà les processus pour cela, et maintient les registres correspondants (adresses IPv4, adresses IPv6, numéros d'AS et domaines).

Pour permettre à RDAP de fonctionner, ce RFC demande donc à l'IANA quelques fichiers supplémentaires, au format JSON (RFC 8259), dont le contenu dérive des registres cités plus haut. Et l'IANA doit aussi prévoir des serveurs adaptés à servir ces fichiers, nommés RDAP Bootstrap Service, à de nombreux clients RDAP.

Le format de ces fichiers JSON est décrit dans la section 3 de notre RFC. Chaque bootstrap service contient des métadonnées comme un numéro de version et une date de publication, et il contient un membre nommé services qui indique les URL où aller chercher l'information (la syntaxe formelle figure en section 10). Voici un extrait d'un des fichiers actuels :

 
% curl https://data.iana.org/rdap/ipv4.json
  "description": "RDAP bootstrap file for IPv4 address allocations",
  "publication": "2019-06-07T19:00:02Z",
  "services": [
    [
      [
        "41.0.0.0/8",
        "102.0.0.0/8",
        "105.0.0.0/8",
        "154.0.0.0/8",
        "196.0.0.0/8",
        "197.0.0.0/8"
      ],
      [
        "https://rdap.afrinic.net/rdap/",
        "http://rdap.afrinic.net/rdap/"
      ]
    ],
    ...

On voit que le contenu est un tableau donnant, pour des objets donnés, les URL des serveurs RDAP à contacter pour ces objets indiqués, par exemple pour l'adresse IP 154.3.2.1, on ira demander au serveur RDAP d'AfriNIC en https://rdap.afrinic.net/rdap/.

Le membre publication indique la date de parution au format du RFC 3339. Les URL indiqués se terminent par une barre oblique, le client RDAP a juste à leur ajouter une requête formulée selon la syntaxe du RFC 9082. Ainsi, cherchant des informations sur 192.0.2.24, on ajoutera ip/192.0.2.24 à l'URL de base.

Pour les adresses IP (section 5), les entrées sont des préfixes, comme dans le premier exemple montré plus haut et la correspondance se fait avec le préfixe le plus spécifique (comme pour le routage IP). Les préfixes IPv4 suivent la syntaxe du RFC 4632 et les IPv6 celle du RFC 5952. Voici un exemple IPv6 (légèrement modifié d'un cas réel) :

   [
      [
        "2001:200::/23",
        "2001:4400::/23",
        "2001:8000::/19",
        "2001:a000::/20",
        "2001:b000::/20",
        "2001:c00::/23",
        "2001:e00::/23",
        "2400::/12"
      ],
      [
        "https://rdap.apnic.net/"
      ]
    ],
    [
      [
        "2001:200:1000::/36"
      ],
      [
        "https://example.net/rdaprir2/"
      ]

Si on cherche de l'information sur le préfixe 2001:200:1000::/48, il correspond à deux entrées dans le tableau des services (2001:200::/23 et 2001:200:1000::/36) mais la règle du préfixe le plus long (le plus spécifique) fait qu'on va utiliser 2001:200:1000::/36, et la requête finale sera donc https://example.net/rdaprir2/ip/2001:200:1000::/48.

Pour les domaines (section 4), les entrées des services sont des noms de domaine :

...
       "services": [
         [
           ["net", "com", "org"],
           [
             "https://registry.example.com/myrdap/"
           ]
         ],
         [
           ["foobar.org", "mytld"],
           [
             "https://example.org/"
           ]
         ],
...

L'entrée sélectionnée est la plus longue (en nombre de composants du nom, pas en nombre de caractères) qui correspond. Dans l'exemple ci-dessus, si on cherche des informations sur foobar.org, on ira voir le serveur RDAP en https://example.org/, si on en cherche sur toto.org, on ira en https://registry.example.com/myrdap/. (En pratique, aujourd'hui, le tableau des serveurs RDAP ne contient que des TLD.)

Pour les numéros d'AS (section 5.3), on se sert d'intervalles de numéros et de la syntaxe du RFC 5396 :

"services": [
         [
           ["2045-2045"],
           [
             "https://rir3.example.com/myrdap/"
           ]
         ],
         [
           ["10000-12000", "300000-400000"],
           [
             "http://example.org/"
           ]
         ],
...

Les entités (section 6 de notre RFC) posent un problème particulier, elles ne se situent pas dans un espace arborescent, contrairement aux autres objets utilisable avec RDAP. (Rappel : les entités sont les contacts, les titulaires, les BE…) Il n'existe donc pas de bootstrap service pour les entités (ni, d'ailleurs, pour les serveurs de noms, cf. section 9). En pratique, si une requête RDAP renvoie une réponse incluant un handle pour une entité, il n'est pas idiot d'envoyer les requêtes d'information sur cette entité au même serveur RDAP mais il n'est pas garanti que cela marche.

Notez (section 7) que le tableau services ne sera pas forcément complet et que certains objets peuvent ne pas s'y trouver. Par exemple, dans un tableau pour les TLD, les registres n'ayant pas encore de serveur RDAP ne seront logiquement pas cités. On peut toujours tenter un autre serveur, en espérant qu'il utilisera les redirections HTTP. Par exemple, ici, on demande au serveur RDAP de l'APNIC pour une adresse RIPE. On est bien redirigé avec un code 301 (RFC 7231, section 6.4.2) :


%  curl -v http://rdap.apnic.net/ip/2001:67c:288::2
...
> GET /ip/2001:67c:288::2 HTTP/1.1
> User-Agent: curl/7.38.0
> Host: rdap.apnic.net
> Accept: */*
> 
< HTTP/1.1 301 Moved Permanently
< Date: Wed, 01 Apr 2015 13:07:00 GMT
< Location: http://rdap.db.ripe.net/ip/2001:67c:288::2
< Content-Type: application/rdap+json
...

La section 8 couvre quelques détails liés au déploiement de ce service. C'est que le Bootstrap Service est différent des autres registres IANA. Ces autres registres ne sont consultés que rarement (par exemple, lors de l'écriture d'un logiciel) et jamais en temps réel. Si le serveur HTTP de l'IANA plante, ce n'est donc pas trop grave. Au contraire, le Bootstrap Service doit marcher en permanence, car un client RDAP en a besoin. Pour limiter la pression sur les serveurs de l'IANA, notre RFC recommande que les clients RDAP ne consultent pas ce service à chaque requête RDAP, mais qu'au contraire ils mémorisent le JSON récupéré, en utilisant le champ Expires: de HTTP (RFC 7234, section 5.3) pour déterminer combien de temps doit durer cette mémorisation. Néanmoins, l'IANA a dû ajuster ses serveurs HTTP et louer les services d'un CDN pour assurer ce rôle relativement nouveau.

Le client RDAP doit d'autre part être conscient que le registre n'est pas mis à jour instantanément. Par exemple, si un nouveau TLD est ajouté par le gouvernement états-unien, via Verisign, TLD dont le registre a un serveur RDAP, cette information ne sera pas disponible immédiatement pour tous les clients RDAP.

Comme son ancêtre whois, RDAP soulève plein de questions de sécurité intéressantes, détaillées plus précisement dans le RFC 7481.

La section 12 de notre RFC détaille les processus IANA à l'œuvre. En effet, et c'est une autre différence avec les registres IANA habituels, il n'y a pas de mise à jour explicite des registres du bootstrap service, ils sont mis à jour implicitement comme effet de bord des allocations et modifications d'allocation dans les registres d'adresses IPv4, adresses IPv6, numéros d'AS et domaines. Il a juste fallu modifier les procédures de gestion de ces registres existants, pour permettre d'indiquer le serveur RDAP. Ainsi, le formulaire de gestion d'un TLD par son responsable a été modifié pour ajouter un champ "serveur RDAP" comme il y avait un champ "serveur Whois".

Aujourd'hui, les fichiers de ce service sont :

  • https://data.iana.org/rdap/dns.json
  • https://data.iana.org/rdap/ipv4.json
  • https://data.iana.org/rdap/ipv6.json
  • https://data.iana.org/rdap/asn.json

Voici, par exemple, celui d'IPv6 (notez le champ Expires: dans la réponse) :


%  curl -v https://data.iana.org/rdap/ipv6.json
...
* Connected to data.iana.org (2606:2800:11f:bb5:f27:227f:1bbf:a0e) port 80 (#0)
> GET /rdap/ipv6.json HTTP/1.1
> Host: data.iana.org
> User-Agent: curl/7.68.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Expires: Tue, 26 Apr 2022 12:30:52 GMT
< Last-Modified: Wed, 06 Nov 2019 19:00:04 GMT
...
{
  "description": "RDAP bootstrap file for IPv6 address allocations",
  "publication": "2019-11-06T19:00:04Z",
  "services": [
    [
      [
        "2001:4200::/23",
        "2c00::/12"
      ],
      [
        "https://rdap.afrinic.net/rdap/",
        "http://rdap.afrinic.net/rdap/"
...

Et celui des TLD :


% curl -v http://data.iana.org/rdap/dns.json
...
  "description": "RDAP bootstrap file for Domain Name System registrations",
  "publication": "2022-04-22T16:00:01Z",
  "services": [
    [
      [
        "nowruz",
        "pars",
        "shia",
        "tci",
        "xn--mgbt3dhd"
      ],
      [
        "https://api.rdap.agitsys.net/"
      ]
    ],

Testons si cela marche vraiment :

% curl -v https://api.rdap.agitsys.net/domain/www.nowruz
...
 "nameservers": [
    {
      "objectClassName": "nameserver",
      "ldhName": "ns1.aftermarket.pl.",
      "unicodeName": "ns1.aftermarket.pl."
    },
    {
      "objectClassName": "nameserver",
      "ldhName": "ns2.aftermarket.pl.",
      "unicodeName": "ns2.aftermarket.pl."
    }
  ]

Parfait, tout marche.

Il y a très peu de changements depuis le RFC précédent, le RFC 7484, quelques nettoyages seulement, et un changement de statut sur le chemin des normes.

Si vous aimez lire des programmes, j'ai fait deux mises en œuvre de ce RFC, la première en Python est incluse dans le code utilisé pour mon article « Récupérer la date d'expiration d'un domaine en RDAP ». Le code principal est ianardap.py mais vous noterez que, contrairement à ce que demande le RFC dans sa section 8, la mémorisation du bootstreap registry n'utilise pas le champ HTTP Expires:. La deuxième, en Elixir, est plus correcte et est disponible dans find-rdap-elixir.tar. Lorsque la constante @verbose est vraie, le programme affiche les étapes. S'il n'a pas mémorisé de données :

% mix run find_rdap.exs quimper.bzh
Starting
No expiration known
Retrieving from https://data.iana.org/rdap/dns.json
Updating the cache
RDAP bootstrap file for Domain Name System registrations published on 2022-04-22T16:00:01Z
RDAP server for quimper.bzh is https://rdap.nic.bzh/
Retrieving RDAP info at https://rdap.nic.bzh/domain/quimper.bzh
{"rdapConformance":["rdap_level_0","...
  

S'il en a mémorisé :

% mix run find_rdap.exs quimper.bzh
Starting
Expiration is 2022-04-26 12:44:48Z
Using the cache ./iana-rdap-bootstrap.json
RDAP bootstrap file for Domain Name System registrations published on 2022-04-22T16:00:01Z
RDAP server for quimper.bzh is https://rdap.nic.bzh/
Retrieving RDAP info at https://rdap.nic.bzh/domain/quimper.bzh
{"rdapConformance":["rdap_level_0", ...

Mais il existe évidemment une mise en œuvre de ce RFC dans tous les clients RDAP comme :

  • L'application de Viagenie,
  • Celle de l'ICANN (application Web),
  • L'ARIN a un service de redirection, https://rdap-bootstrap.arin.net/bootstrap, qui lit la base IANA et envoie les redirections appropriées, quand un client RDAP l'interroge.

Téléchargez le RFC 9224


L'article seul

RFC 9221: An Unreliable Datagram Extension to QUIC

Date de publication du RFC : Mars 2022
Auteur(s) du RFC : T. Pauly, E. Kinnear (Apple), D. Schinazi (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF quic
Première rédaction de cet article le 1 avril 2022


Le protocole QUIC fournit, comme son concurrent TCP, un canal fiable d'envoi de données. Mais certaines applications n'ont pas impérativement besoin de cette fiabilité et se contenteraient très bien d'un service de type datagramme. Ce nouveau RFC ajoute donc un nouveau type de trame QUIC, DATAGRAM, pour fournir un tel service.

Dit comme cela, évidemment, ça semble drôle, de bâtir un service non fiable au-dessus du service fiable que rend QUIC. Mais cela permet plusieurs choses très intéressantes, notamment si deux machines qui communiquent ont besoin des deux types de service : elles pourront utiliser une seule session QUIC pour les deux.

Un petit rappel (mais relisez le RFC 9000 pour les détails) : dans une connexion QUIC sont envoyés des paquets et chaque paquet QUIC contient une ou plusieurs trames. Chaque trame a un type et ce type indique si la trame sera retransmise ou non en cas de perte. Le service habituel (utilisé par exemple par HTTP/3) se sert de trames de type STREAM, qui fournissent un service fiable (les données perdues sont retransmises par QUIC). Mais toutes les applications ne veulent pas d'une telle fiabilité ou, plus précisement, ne sont pas prêtes à en payer le coût en termes de performance. Ces applications sont celles qui préféraient utiliser UDP (RFC 768) plutôt que TCP. Pour rajouter de la sécurité, elles utilisaient DTLS (RFC 6347). Si QUIC remplace TCP, comment remplacer UDP+DTLS ?

D'ailleurs, faut-il le remplacer ? La section 2 du RFC donne les raisons suivantes :

  • Les applications qui ont besoin à la fois d'un canal fiable et d'un canal qui ne l'est pas (par exemple une application de communication qui veut un canal fiable pour la signalisation et un pas forcément fiable pour les données multimédia) peuvent avec QUIC n'avoir qu'une seule session (et donc ne « payer » l'établissement de la session qu'une fois, ce qui diminuera la latence),
  • QUIC a un meilleur mécanisme de récupération des pertes que DTLS, lors de l'établissement de la connexion,
  • contrairement à UDP+DTLS, QUIC a un mécanisme de contrôle de la congestion, ce qui simplifie la tâche de l'application (cf. RFC 8085).

Ces raisons sont particulièrement importantes pour le streaming audio/vidéo, pour les jeux en ligne, etc. Les datagrammes dans QUIC peuvent aussi être utiles pour un service de VPN. Là aussi, le VPN a besoin à la fois d'un canal fiable pour la configuration de la communication, mais peut se satisfaire d'un service de type datagramme ensuite (puisque les machines connectées via le VPN auront leur propre mécanisme de récupération des données perdues). Permettre la création de VPN au-dessus de QUIC est le projet du groupe de travail MASQUE.

Bien, normalement, maintenant, vous êtes convaincu·es de l'intérêt des datagrammes au-dessus de QUIC. Pour en envoyer, il faut toutefois que les deux parties qui communiquent soient d'accord. C'est le rôle du paramètre de transport QUIC max_datagram_frame_size, à utiliser lors de l'établissement de la session. (Ce paramètre est enregistré à l'IANA.) S'il est absent, on ne peut pas envoyer de datagrammes (et le partenaire coupe la connexion si on le fait quand même). S'il est présent, il indique la taille maximale acceptée, typiquement 65 535 octets. Sa valeur peut être stockée dans la mémoire d'une machine, de façon à permettre son utilisation lors du 0-RTT (commencer une session QUIC directement sans perdre du temps en négociations). Un paquet QUIC 0-RTT peut donc inclure des trames de type DATAGRAM.

Ah, justement, ce type DATAGRAM. Désormais enregistré à l'IANA parmi les types de trames QUIC, il sert à indiquer des trames qui seront traitées comme des datagrammes, et qui ont la forme suivant :

DATAGRAM Frame {
     Type (i) = 0x30..0x31,
     [Length (i)],
     Datagram Data (..),
}
  

Le champ de longueur est optionnel, sa présence est indiquée par le dernier bit du type (0x30, pas de champ Longueur, 0x31, il y en a un). S'il est absent, la trame va jusqu'au bout du paquet QUIC (rappelons qu'un paquet QUIC peut contenir plusieurs trames).

Maintenant qu'on a des datagrammes comme QUIC, comment les utilise-t-on (section 5 du RFC) ? Rien d'extraordinaire, l'application envoie une trame DATAGRAM et l'application à l'autre bout la recevra. Si la trame n'est pas arrivée, l'émetteur ne le saura pas. La trame peut se retrouver avec d'autres trames (du même type ou pas) dans un même paquet QUIC (et plusieurs paquets QUIC peuvent se retrouver dans un même datagramme IP). La notion de ruisseau (stream) n'existe pas pour les trames-datagrammes, si on veut pouvoir les démultiplexer, c'est à l'application de se débrouiller. (Une version préliminaire de cette extension à QUIC prévoyait un mécanisme de démultiplexage, finalement abandonné.) Comme toutes les trames QUIC, les trames DATAGRAM sont protégées par la cryptographie. Le service est donc équivalent à celui d'UDP+DTLS, pas UDP seul.

Un service de datagrammes est non fiable, des données peuvent se perdre. Mais si les trames de type DATAGRAM ne sont pas réémises (pas par QUIC, en tout cas), elles entrainent néanmoins l'émission d'accusés de réception (RFC 9002, sections 2 et 3). Une bibliothèque QUIC peut ainsi (mais ce n'est pas obligatoire) notifier l'application des pertes. De la même façon, elle a la possibilité de dire à l'application quelles données ont été reçues par le QUIC à l'autre bout (ce qui ne garantit pas que l'application à l'autre bout les aient bien reçues, si on a besoin d'informations sûres, il faut le faire au niveau applicatif).

Comme l'émetteur n'estime pas crucial que toutes les données arrivent, une optimisation possible pour QUIC est que l'accusé de réception d'un paquet ne contenant que des trames DATAGRAM ne soit pas émis tout de suite, il peut attendre le paquet suivant.

Cette extension existe déjà dans plusieurs mises en œuvre de QUIC, mais je ne l'ai pas testée.


Téléchargez le RFC 9221


L'article seul

RFC 9211: The Cache-Status HTTP Response Header Field

Date de publication du RFC : Juin 2022
Auteur(s) du RFC : M. Nottingham (Fastly)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 9 juin 2022


Ce RFC normalise enfin un en-tête HTTP pour permettre aux relais HTTP qui mémorisent les réponses reçues, les caches, d'indiquer au client comment ils ont traité une requête et si, par exemple, la réponse vient de la mémoire du relais ou bien s'il a fallu aller la récupérer sur le serveur HTTP d'origine.

Il existait déjà des en-têtes de débogage analogues mais non-standards, variant aussi bien dans leur syntaxe que dans leur sémantique. Le nouvel en-tête standard, Cache-Status: va, espérons-le, mettre de l'ordre à ce sujet. Sa syntaxe suit les principes des en-têtes structurés du RFC 8941. Sa valeur est donc une liste de relais qui ont traité la requête, séparés par des virgules, dans l'ordre des traitements. Le premier relais indiqué dans un Cache-Status: est donc le plus proche du serveur d'origine et le dernier relais étant le plus proche de l'utilisateur. Voici un exemple avec un seul relais, nommé cache.example.net :

Cache-Status: cache.example.net; hit
  

et un exemple avec deux relais, OriginCache puis CDN Company Here :

Cache-Status: OriginCache; hit; ttl=1100, "CDN Company Here"; hit; ttl=545  
  

La description complète de la syntaxe figure dans la section 2 de notre RFC. D'abord, il faut noter qu'un relais n'est pas obligé d'ajouter cet en-tête. Il le fait selon des critères qui lui sont propres. Le RFC déconseille fortement d'ajouter cet en-tête si la réponse a été générée localement par le relais (par exemple un 400 qui indique que le relais a détecté une requête invalide). En tout cas, le relais qui s'ajoute à la liste ne doit pas modifier les éventuels relais précédents. Chaque relais indique son nom, par exemple par un nom de domaine mais ce n'est pas obligatoire. Ce nom peut ensuite être suivi de paramètres, séparés par des point-virgules.

Dans le premier exemple ci-dessus, hit était un paramètre du relais cache.example.net. Sa présence indique qu'une réponse était déjà mémorisée par le relais et qu'elle a été utilisée (hit : succès, on a trouvé), le serveur d'origine n'ayant pas été contacté. S'il y avait une réponse stockée, mais qu'elle était rassise (RFC 9111, section 4.2) et qu'il a fallu la revalider auprès du serveur d'origine, hit ne doit pas être utilisé.

Le deuxième paramètre possible est fwd (pour Forward) et il indique qu'il a fallu contacter le serveur HTTP d'origine. Plusieurs raisons sont possibles pour cela, entre autres :

  • bypass, quand le relais a été configuré pour que cette requête soit systématiquement relayée,
  • method parce que la méthode HTTP utilisée doit être relayée (c'est typiquement le cas d'un PUT, cf. RFC 9110, section 9.3.4),
  • miss, un manque, la ressource n'était pas dans la mémoire du relais (notez qu'il existe aussi deux paramètres plus spécifiques, uri-miss et vary-miss),
  • stale, car la ressource était bien dans la mémoire du relais mais, trop ancienne et rassise, a dû être revalidée (RFC 9111, section 4.3) auprès du serveur d'origine.

Ainsi, cet en-tête :

Cache-Status: ExampleCache; fwd=miss; stored
  

indique que l'information n'était pas dans la mémoire de ExampleCache et qu'il a donc dû faire suivre au serveur d'origine (puis qu'il a stocké cette information dans sa mémoire, le paramètre stored).

Autre paramètre utile, ttl, qui indique pendant combien de temps l'information peut être gardée (cf. RFC 9111, section 4.2.1), selon les calculs du relais. Quant à detail, il permet de donner des informations supplémentaires, de manière non normalisée (la valeur de ce paramètre ne peut être interprétée que si on connait le programme qui l'a produite).

Les paramètres sont enregistrés à l'IANA, dans un nouveau registre, et de nouveaux paramètres peuvent y être ajoutés, suivant la politique « Examen par un expert » du RFC 8126. La recommandation est de n'enregistrer que des paramètres d'usage général, non spécifiques à un programme particulier, sauf à nommer le paramètre d'une manière qui identifie clairement sa spécificité.

Questions mises en œuvre, Squid sait renvoyer cet en-tête (cf. le code qui commence à SBuf cacheStatus(uniqueHostname()); dans le fichier src/client_side_reply.cc). Ce code n'est pas encore présent dans la version 5.2 de Squid, qui n'utilise encore que d'anciens en-têtes non-standards, il faut attendre de nouvelles versions :

< X-Cache: MISS from myserver
< X-Cache-Lookup: NONE from myserver:3128
< Via: 1.1 myserver (squid/5.2)
  

Je n'ai pas regardé les autres logiciels.


Téléchargez le RFC 9211


L'article seul

RFC 9210: DNS Transport over TCP - Operational Requirements

Date de publication du RFC : Mars 2022
Auteur(s) du RFC : J. Kristoff (Dataplane.org), D. Wessels (Verisign)
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 15 avril 2022


Ce nouveau RFC décrit les exigences opérationnelles pour le DNS, plus précisement pour son protocole de transport TCP. Le RFC 7766 décrivait la norme technique, et s'imposait donc aux programmeur·ses, ce RFC 9210 est plus opérationnel et concerne donc ceux et celles qui installent et configurent les serveurs DNS et les réseaux qui y mènent.

Les messages DNS sont historiquement surtout transportés sur UDP. Mais le DNS a toujours permis TCP et, de nos jours, il est fréquemment utilisé notamment en conjonction avec TLS (cf. RFC 7858). Il est d'autant plus important que TCP marche bien qu'il est parfois nécessaire pour certains usages, par exemple pour des enregistrements de grande taille (ce qui arrive souvent avec DNSSEC). Une mise en œuvre du DNS doit donc inclure TCP, comme clarifié par le RFC 7766 (les RFC précédents n'étaient pas sans ambiguité). Mais cela ne signifiait pas forcément que les serveurs DNS avaient activé TCP ou que celui-ci marchait bien (par exemple, une stupide middlebox pouvait avoir bloqué TCP). Ce nouveau RFC 9210 ajoute donc que l'obligation d'avoir TCP s'applique aussi aux serveurs effectifs, pas juste au logiciel.

DNS sur TCP a une histoire compliquée (section 2 du RFC). Franchement, le fait que le DNS marche sur TCP aussi bien que sur UDP n'a pas toujours été clairement formulé, même dans les RFC. Et, en dehors de l'IETF, beaucoup de gens ont raconté n'importe quoi dans des articles et des documentations, par exemple que TCP n'était utile qu'aux transferts de zone, ceux normalisés dans le RFC 5936. Cette légende a été propagée par certains auteurs (comme Cheswick et Bellovin dans leur livre Firewalls and Internet Security: Repelling the Wily Hacker) ou par des gens qui ne mesuraient pas les limites de leurs compétences DNS comme Dan Bernstein. Pourtant, TCP a toujours été nécessaire, par exemple pour transporter des données des grande taille (que DNSSEC a rendu bien plus fréquentes). Le RFC 1123, en 1989, insistait déjà sur ce rôle. Certes, EDNS (RFC 6891) permettait déjà des données de taille supérieure aux 512 octets de la norme originelle. Mais il ne dispense pas de TCP. Par exemple, des données de taille supérieure à 1 460 octets (le maximum qui peut tenir avec le MTU typique) seront fragmentées et les fragments, hélas, ne passent pas partout sur l'Internet. (Cf. le « DNS Flag Day » de 2020 et lire « DNS XL » et « Dealing with IPv6 fragmentation in the DNS ».) Et la fragmentation pose également des problèmes de sécurité, voir par exemple « Fragmentation Considered Poisonous ».

Bon, en pratique, la part de TCP reste faible mais elle augmente. Mais, trop souvent, le serveur ne répond pas en TCP (ou bien ce protocole est bloqué par le réseau), ce qui entraine des tas de problèmes, voir par exemple le récit dans « DNS Anomalies and Their Impacts on DNS Cache Servers ».

Notons enfin que TCP a toujours permis que plusieurs requêtes se succèdent sur une seule connexion TCP, même si les premières normes n'étaient pas aussi claires qu'il aurait fallu. L'ordre des réponses n'est pas forcément préservé (cf. RFC 5966), ce qui évite aux requêtes rapides d'attendre le résultat des lentes. Et un client peut envoyer plusieurs requêtes sans attendre les réponses (RFC 7766). Par contre, la perte d'un paquet va entrainer un ralentissement de toute la connexion, et donc des autres requêtes (QUIC résout ce problème et il y a un projet de DNS sur QUIC).

Vous pouvez tester qu'un serveur DNS accepte de faire passer plusieurs requêtes sur une même connexion TCP avec dig et son option +keepopen (qui n'est pas activée par défaut). Ici, on demande à ns4.bortzmeyer.org les adresses IP de www.bortzmeyer.org et mail.bortzmeyer.org :

% dig +keepopen +tcp @ns4.bortzmeyer.org www.bortzmeyer.org AAAA mail.bortzmeyer.org AAAA
  

Vous pouvez vérifier avec tcpdump qu'il n'y a bien eu qu'une seule connexion TCP, ce qui ne serait pas le cas sans +keepopen.

Les exigences opérationnelles pour le DNS sur TCP figurent dans la section 3 du RFC. Il est désormais obligatoire non seulement d'avoir la possibilité de TCP dans le code mais en outre que cela soit activé partout. (En termes du RFC 2119, on est passé de SHOULD à MUST.)

La section 4 discute de certaines considérations opérationnelles que cela pourrait poser. D'abord, certains serveurs ne sont pas joignables en TCP. C'était déjà mal avant notre RFC mais c'est encore pire maintenant. Mais cela arrive (ce n'est pas forcément la faute du serveur, cela peut être dû à une middlebox boguée sur le trajet). Les clients doivent donc être préparés à ce que TCP échoue (de toute façon, sur l'Internet, tout peut toujours échouer). D'autre part, diverses attaques par déni de service sont possibles, aussi bien contre le serveur (SYN flooding, contre lequel la meilleure protection est l'utilisation de cookies, cf. RFC 4987), que contre des machines tierces, le serveur étant utilisé comme réflecteur.

Aussi bien le client DNS que le serveur n'ont pas des ressources illimitées et doivent donc gérer les connexions TCP. Dit plus brutalement, il faut être prêt à couper les connexions qui semblent inutilisées. Bien sûr, il faut aussi laisser les connexions ouvertes suffisamment longtemps pour amortir leur création sur le plus grand nombre de requêtes possibles, mais il y a des limites à tout. (Le RFC suggère une durée maximale dans les dizaines de secondes, pouvant être abaissée à quelques secondes pour les serveurs très chargés.)

TCP est particulièrement intéressant pour le DNS quand TLS est inséré (RFC 7858). Mais il va alors consommer davantage de temps de CPU et, dans certains cas, l'établissement de connexion sera plus lent.

Le RFC donne quelques conseils quantitatifs (à ne pas appliquer aveuglément, bien sûr). Un maximum de 150 connexions TCP simultanées semble raisonnable pour un serveur ordinaire, avec un maximum de 25 par adresse IP source. Un délai de garde après inactivité de 10 secondes est suggéré. Par contre, le RFC ne propose pas de valeur maximale au nombre de requêtes par connexion TCP, ni de durée maximale à une connexion.

Les fanas des questions de sécurité noteront que les systèmes de journalisation et de surveillance peuvent ne pas être adaptés à TCP. Par exemple, un méchant pourrait mettre la requête DNS dans plusieurs segments TCP et donc plusieurs paquets IP. Un système de surveillance qui considérerait qu'une requête = un paquet serait alors aveugle. Notez qu'un logiciel comme dnscap a pensé à cela, et réassemble les paquets. Mais il est sans doute préférable, de nos jours, de se brancher sur le serveur, par exemple avec dnstap, notamment pour éviter de faire le réassemblage deux fois. (Ceux qui lisent mes articles depuis longtemps savent que je donnais autrefois le conseil inverse. Mais le déploiement de plus en plus important de TCP et surtout de TLS impose de changer de tactique.)


Téléchargez le RFC 9210


L'article seul

RFC 9209: The Proxy-Status HTTP Response Header Field

Date de publication du RFC : Juin 2022
Auteur(s) du RFC : M. Nottingham (Fastly), P. Sikora (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 14 juin 2022


Le protocole HTTP, qui est à la base du Web, n'est pas forcément de bout en bout, entre client et serveur. Il y a souvent passage par un relais et ce relais a parfois des choses à signaler au client HTTP, notamment en cas d'erreur. Ce RFC spécifie le champ d'en-tête Proxy-Status pour cela.

La norme HTTP, le RFC 9110 décrit ces relais, ces intermédiaires, dans sa section 3.7. On en trouve fréquemment sur le Web. Il y a depuis longtemps des codes d'erreur pour eux, comme 502 si le serveur d'origine répond mal et 504 pour les cas où il ne répond pas du tout. Mais ce n'est pas forcément assez précis, d'où le nouveau champ dans l'en-tête (ou dans le pied). Il utilise la syntaxe des champs structurés du RFC 8941. Voici un exemple :


Proxy-Status: proxy.example.net; error="http_protocol_error"; details="Malformed response header: space before colon",
  "Example CDN"

  

Cet exemple se lit ainsi : le premier (en partant du serveur d'origine) s'identifie comme proxy.example.net et il signale que le serveur d'origine n'avait pas bien lu le RFC 9112. Puis la réponse est passée par un autre intermédiaire, qui s'identifie comme "Example CDN" (l'identificateur n'est pas forcément un nom de domaine), et n'a rien de particulier à raconter. Le champ Proxy-Status est désormais dans le registres des champs d'en-tête (ou de pied).

Vous avez vu dans l'exemple ci-dessus le paramètre error. Il peut s'utiliser, par exemple, avec le code de retour 504 :

HTTP/1.1 504 Gateway Timeout
Proxy-Status: foobar.example.net; error=connection_timeout
  

Ici, le relais foobar.example.net n'a eu aucune réponse du serveur d'origine (ou, plus rigoureusement, du serveur qu'il essayait de contacter, qui peut être un autre intermédiaire).

Mais il existe d'autres paramètres possibles, comme :

  • next-hop : nom ou adresse du serveur contacté, par exemple Proxy-Status: cdn.example.org; next-hop=backend.example.org:8001.
  • next-protocol : protocole (ALPN, RFC 7301) utilisé avec le serveur, par exemple Proxy-Status: "proxy.example.org"; next-protocol=h2 pour du HTTP/2 (RFC 9113).
  • received-status : le code de retour du serveur, comme dans Proxy-Status: ExampleCDN; received-status=200, pour un cas où tout s'est bien passé.

Et on peut définir de nouveaux paramètres par la procédure d'examen par un expert (RFC 8126).

Le paramètre error prend comme valeur un type d'erreur. Il en existe plusieurs, chacun avec un code de retour recommandé dont (je ne les indique pas tous, ils sont très nombreux !) :

  • dns_timeout (pour le code de retour 504) et dns_error (code 502) : c'est la faute du DNS. Le second type permet en outre d'indiquer le paramètre rcode (code de retour DNS, comme NXDOMAIN) et le paramètre info-code, le code étendu du RFC 8914.
  • connection_timeout ou connection_refused.
  • destination_ip_prohibited : le pare-feu ne veut pas.
  • tls_protocol_error : là, c'est TLS qui ne veut pas.
  • tls_certificate_error : certificat du serveur problématique, par exemple expiré.
  • http_protocol_error : la réponse du serveur n'était pas du bon HTTP.
  • proxy_internal_error : le relais a un problème interne.

Là aussi, on peut enregistrer de nouveaux types avec la procédure d'examen par un expert du RFC 8126.

La section 4 du RFC détaille les questions de sécurité. Comme toute information, Proxy-Status peut aider un attaquant, par exemple en lui donnant des idées sur comment joindre directement un intermédiaire. Pour cette raison, les logiciels doivent fournir un moyen de contrôler l'ajout (ou pas) de Proxy-Status, qu'on peut aussi n'inclure que dans certains cas. Notez aussi qu'un intermédiaire peut mentir (ou se tromper) et que le Proxy-Status ne vaut donc que ce que vaut l'intermédiaire.

Je n'ai pas de liste des logiciels qui mettent en œuvre ce champ Proxy-Status.


Téléchargez le RFC 9209


L'article seul

RFC 9205: Building Protocols with HTTP

Date de publication du RFC : Juin 2022
Auteur(s) du RFC : M. Nottingham
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 10 juin 2022


Aujourd'hui, la grande majorité des API accessibles via le réseau fonctionnent au-dessus de HTTP. Ce nouveau RFC, qui remplace le RFC 3205, décrit les bonnes pratiques pour la conception de telles API, notamment pour les protocoles IETF bâtis sur HTTP, comme DoH ou RDAP.

Il y a plein d'applications qui fonctionnent au-dessus de HTTP. Ce nouveau RFC se concentre sur celles qui sont d'usage général et qui ont plusieurs mises en œuvre et déploiements. (Si vous faites un service centralisé qui n'a qu'un seul déploiement de son API spécifique, ce RFC ne va pas forcément être pertinent pour vous.) Si vous avez déjà lu le RFC 3205, il faudra tout recommencer, les changements sont nombreux. Ces applications utilisant HTTP sont parfois qualifiées de REST mais, en toute rigueur, toutes ne suivent pas rigoureusement les principes de REST. Notez aussi un sous-ensemble, CRUD, pour les applications dont l'essentiel du travail est de créer/supprimer/gérer des objets distants.

Normalement, HTTP avait été conçu pour le Web et ses usages. Mais on voit aujourd'hui de très nombreuses API réseau être fondées sur HTTP pour diverses raisons :

  • Parce que le développeur ou la développeuse ne connait que ça,
  • parce qu'il existe un très grand nombre de bibliothèques pour faire les clients (comme l'excellente Requests pour le langage Python, qui sera utilisée ici dans plusieurs exemples), et de cadriciels pour les serveurs,
  • parce que dans certains cas, il sera même possible d'utiliser un navigateur Web pour y accéder, et que tout le monde a un navigateur Web et est familier avec ce logiciel,
  • parce que certains services comme l'authentification sont déjà disponibles dans les logiciels existants,
  • parce que HTTP est le seul protocole dont on peut être sûr qu'il passera même dans les réseaux les plus coincés comme les hotspots des hôtels et des aéroports.

Mais tout n'est pas forcément rose et HTTP peut ne pas être bien adapté à ce qu'envisage le développeur de l'API. Et cette développeuse peut faire des erreurs lors de la conception de l'API, erreurs que ce RFC vise à éviter.

En effet, quand on développe une API sur HTTP, il y a plusieurs décisions à prendre :

  • Définir un nouveau plan d'URL ? (C'est quand même rare, presque tout le monde utilise http: ou, aujourd'hui, https:.)
  • Utiliser un port « non-standard » ?
  • Comment coexister avec les autres utilisations de HTTP, comme la navigation sur le Web ?

La section 2 précise l'applicabilité de ce RFC. Il concerne les protocoles qui utilisent HTTP (ports 80 ou 443, plans d'URI http: ou https:). Ceux qui utiliseraient une version modifiée de HTTP ne comptent pas, et cette pratique est d'ailleurs déconseillée, puisque ces versions modifiées feraient probablement perdre les avantages d'utiliser HTTP, notamment la réutilisation des logiciels et infrastructures existants.

Section 3, maintenant. Quelles sont les caractéristiques importantes de HTTP, qui gouvernent ce que peuvent faire les applications qui l'utilisent ? D'abord, sa sémantique très générale : on peut tout faire avec HTTP. Notammment, HTTP est indépendant du type de ressources sur lesquelles il agit. Ainsi, des composants HTTP génériques (bibliothèques, serveurs, relais) peuvent être développés et déployés pour des applications très différentes, même des applcations qui n'existent pas encore. (Voilà d'alleurs pourquoi la section précédente insistait sur le fait q'il ne faut pas modifier HTTP.) Plus subtile serait l'erreur qui consisterait à spécifier un certain profil de HTTP, en restreignant ce que HTTP peut faire ou pas (« la réponse à un POST doit être 201 »). Une telle restriction, là encore, empêcherait d'utiliser cerrtains composants génériques, en faisant perdre à HTTP de sa généralité.

Une autre erreur courante est de s'attribuer tout ou partie de l'espace de nommage fourni par les liens hypertextes. C'est par exemple le cas lorsqu'une application estime qu'elle peut contrôler tout le chemin dans l'URI et décider que /truc/machin est forcément à elle (RFC 8820). Cela complique le déploiement, par exemple si on veut installer cette application sous /chose et excusivement sous ce chemin (cf. section 4.4). L'application devrait au contraire permettre de la souplesse et utiliser les possibilités qu'offre le système de liens (RFC 8288).

Enfin, HTTP dispose de nombreuses possibilités comme le multiplexage que permettent HTTP/2 (RFC 9113) et HTTP/3 (RFC 9114), l'intégration avec TLS, la possibilité de relayage, la négociation de contenu, la disponibilité de nombreux clients, et l'application qui utilise HTTP doit donc veiller à ne pas casser cet écosystème, et en tout cas à ne pas réinventer la roue, alors que HTTP offre déjà de nombreuses solutions éprouvées.

Bref, compte-tenu de tout cela, comment faire pour bien utiliser HTTP dans sa nouvelle application ? La section 4 est là pour répondre à cette question.

D'abord, bien définir la dépendance de l'application à HTTP, en donnant comme référence RFC 9110 (et surtout pas une version spécifique de HTTP, toujours afin de profiter au maximum de l'écosystème existant). On notera quand même que DoH (RFC 8484) impose (section 5.2 de son RFC) au moins HTTP/2, pour être sûr d'avoir du multiplexage. Notre RFC permet explicitement ce genre d'exceptions.

Le RFC recommande également, quand on montre un dialogue HTTP titre d'exemple, d'utiliser plutôt les conventions de HTTP/1 (RFC 9112), plus lisibles. Donc, par exemple, GET /truc HTTP/1.1 plutôt que le :method = GET :path = /resource de HTTP/2. C'est ce que fait curl :


% curl -v http://www.example
> GET / HTTP/2
> Host: www.example
> user-Agent: curl/7.68.0
> accept: */*
> 
< HTTP/2 200 
< content-Type: text/plain
< content-Length: 13

  

On l'a dit, il ne faut pas modifier le comportement de base de HTTP. Ce qu'on peut faire, par contre :

Et le client ? L'application qui utilise HTTP ne devrait pas exiger de comportement trop spécifique du client ; idéalement, un navigateur Web normal devrait pouvoir interagir avec l'application. On peut par exemple s'appuyer sur les principes FETCH. Il est également préférable que l'application qui va utiliser HTTP soit claire sur le traitement attendu pour les redirections HTTP, ou pour les cookies (RFC 6265), et rappeler que la vérification des certificats doit se faire selon les principes de la section 4.3.4 du RFC 9110.

Le client doit, idéalement, pouvoir se configurer avec uniquement un URL. (Par exemple, un serveur DoH est annoncé ainsi, comme https://doh.bortzmeyer.fr/, la seule information dont vous avez besoin pour l'utiliser.) Et si on ne connait qu'un nom de domaine ? La solution du chemin d'URL fixe qu'on s'alloue (« obligatoirement /app ») étant interdite (RFC 8820), il y a le choix :

  • Utiliser un chemin sous /.well-known (RFC 8615),
  • Utiliser les gabarits du RFC 6570, pour générer les URI (un tel mécanisme de découverte est plus souple mais sans doute moins rapide).

Et le plan d'URI (le premier composant de l'URI) ? http: et surtout https: sont évidemment recommandés mais on peut aussi choisir un plan spécifique. Cela va évidemment rendre l'application inutilisable par un navigateur Web ordinaire. Certains navigateurs permettent d'enregistrer un mécanisme de gestion de ces plans non standards (comme le registerProtocolHandler() du WHATWG) mais cela ne marche pas partout. Et on aura le même problème avec tout l'écosystème logiciel de HTTP. Bref, utiliser un plan autre que http: ou https: fera perdre une bonne partie des avantages qu'il y avait à utiliser le protocole HTTP. D'autres problèmes se poseront comme l'impossibilité d'utiliser le concept d'origine (RFC 6454) par exemple dans la Same Origin Policy, ou comme d'autres fonctions utiles de HTTP (cookies, authentification, mémorisation - RFC 9111, HSTS - RFC 6797, etc). Si vous tenez encore, après tout ça, à créer un plan à vous, consultez le RFC 7595.

Et les ports ? HTTP utilise par défaut les ports 80 pour le trafic en clair et 443 pour le traffic chiffré. Utiliser un autre port est possible (https://machin.example:666) mais rend le trafic de l'application distinguable, ce qui peut être gênant pour la vie privée. (C'est un des choix de conception de DoH que d'utiliser HTTPS sur le port 443, pour ne pas être distinguable, et donc être plus difficile à filtrer.) Le RFC 7605 donne des détails sur ce choix des ports.

Maintenant, quelles méthodes HTTP utiliser ? Le RFC exige que les applications utilisant HTTP se servent uniquement des méthodes enregistrées, comme GET ou PUT. Certes, une procédure existe pour enregistrer de nouvelles méthodes mais l'IETF insiste que ces nouvelles méthodes doivent être génériques, et non pas limitées aux besoins d'une seule application. (Le RFC 4791 avait créé des méthodes spécifiques, mais c'était avant. C'est maintenant interdit.)

Donc, pas de méthodes nouvelles. Mais quelle(s) méthode(s) utiliser ? GET est le choix le plus évident. Cette méthode est idempotente (et permet donc, entre autres, la mémorisation), et a une sémantique simple et compréhensible. Elle a quelques limites (comme le fait que tous les éventuels paramètres doivent être dans l'URL, ce qui peut nécessiter un encodage spécial, et peut empêcher des paramètres de grande taille) mais rien de bien grave. Si c'est trop gênant pour une application donnée, il ne reste plus qu'à utiliser POST.

Et pour récupérer des métadonnées sur le service ? Le RFC note que la méthode OPTIONS n'est pas très pratique, par exemple parce qu'elle ne permet pas de donner comme documentation un simple URL (la méthode par défaut étant GET). Il recommande plutôt un URL dans /.well-known (RFC 8615), en créant un nouveau nom, ou bien avec les URL host-meta (RFC 6415). Pour des métadonnées sur une ressource particulière, il est recommandé d'utiliser les liens (RFC 8288). Le RFC note que, dans ce dernier cas, l'en-tête Link: marche même avec la méthode HEAD donc pas besoin de récupérer la ressource pour avoir des informations sur ses métadonnées.

Et les codes de retour HTTP comme 403 ou 404, comment les utiliser ? D'abord, une application qui utilise HTTP n'a pas forcément un complet contrôle sur ces codes de retour, qui peuvent être générés par des composants logiciels différents. Donc, le client HTTP doit se méfier, le code reçu n'est pas forcément significatif de l'application. Ensuite, une application peut avoir davantage de messages différents qu'il n'existe de codes de retour HTTP, ce qui peut pousser à de mauvaises pratiques, comme l'utilisation de codes de retour non standard, ou commme l'utilisation de codes certes standard mais utilisés d'une manière très éloignée de ce qui était prévu. Bref, le RFC conseille de découpler les erreurs applicatives des erreurs HTTP, de ne pas chercher à tout prix un code de retour HTTP pour chaque erreur applicative, et de ne pas hésiter à utiliser les codes de retour génériques (comme 500, pour « quelque chose ne va pas dans le serveur »). Pour envoyer des informations plus détaillées sur l'erreur, il est préférable d'utiliser les techniques du RFC 7807. Autre avertissement du RFC, les raisons envoyées par le serveur après un code de retour (404 File not found) ne sont pas significatives. Dans certains cas (message HTTP dans un message HTTP), elles ne sont pas transmises du tout, contrairement au code de retour, la seule information sur laquelle on peut compter. L'application ne doit donc pas espérer que le client recevra ces raisons.

Autre difficulté pour le concepteur ou la conceptrice d'applications utilisant HTTP, les redirections. Il y a quatre redirections différentes en HTTP, chacune pouvant être temporaire ou définitive, et permettant de changer de méthode ou pas (une requête POST indiquant une redirection suivie d'une requête GET par le client). On a donc :

  • 301, définitive et permettant de changer de méthode,
  • 302, temporaire et permettant de changer de méthode,
  • 307, temporaire et ne permettant pas de changer de méthode,
  • 308, définitive et ne permettant pas de changer de méthode.

Et il faut donc réfléchir un peu avant de choisir un code de redirection (le RFC privilégie 301 et 302, plus souples).

Et les champs dans l'en-tête ? Une application a souvent envie d'en ajouter, que ce soit dans la requête ou dans la réponse. Mais le RFC n'est pas très chaud, demandant qu'on mette les informations plutôt dans l'URL ou dans le corps du message HTTP. Si on crée de nouveaux en-têtes, en théorie (c'est très théorique…), il faut les enregistrer à l'IANA (RFC 9110, section 16.3). Si ces en-têtes ont une structure, il est très recommandé qu'elles suivent les règles du RFC 8941.

Et le corps du message, justement ? L'application doit spécifier quel format est attendu. C'est souvent JSON (RFC 8259) mais cela peut être aussi XML, CBOR (RFC 8949), etc.

Une des grandes forces d'HTTP est la possibilité de mémorisation, décrite en détail dans le RFC 9111. La mémorisation améliore les performances, rend le service moins sensible aux perturbations, et permet le passage à l'échelle. Les applications qui utilisent HTTP ont donc tout intérêt à permettre et à utiliser cette mémorisation. Il est donc recommandé d'indiquer dans la réponse une durée de vie, de préférence avec Cache-Control: max-age=… ou bien, si c'est nécessaire, d'indiquer explicitement que la réponse ne doit pas être mémorisée (Cache-Control: no-store).

Un autre avantage pour une application d'utiliser HTTP est l'existence d'un cadre général pour l'authentification (RFC 9110, section 11). Attention, certains mécanismes ne doivent être utilisés qu'au-dessus d'HTTPS, comme la basic authentication du RFC 7617. HTTPS permet également d'utiliser les certificats client pour l'authentification. (Attention, avec TLS ≤ 1.2, ces certificats, qui contiennent des données personnelles, sont transmis en clair.)

Conséquence de l'utilisation de HTTP, l'application est utilisable via un navigateur Web. Cela peut être vu comme un avantage (tout le monde a un navigateur Web sous la main) ou comme un inconvénient (si la sémantique de l'application ne permet pas réellement un usage pratique depuis un navigateur). Mais quoi qu'on en pense, l'application sera accessible aux navigateurs, et il est donc important de s'assurer que cela ne provoquera pas de problème. Par exemple, si on peut changer un état avec une requête POST, l'application pourrait être attaquée assez facilement par CSRF. Si l'application tire une partie des données qu'elle renvoie en réponse d'une source que l'attaquant peut contrôler, on risque le XSS. Il est donc recommandé, même si l'application n'est pas prévue pour être utilisée par un navigateur, de suivre les mêmes règles de développement sécurisé que si elle devait être accédée depuis un navigateur, notamment :

  • utiliser des champs dans l'en-tête comme X-Content-Type-Options: nosniff,
  • utiliser CSP,
  • utiliser Referrer-Policy:,
  • utiliser l'option HttpOnly sur les cookies (RFC 6265, section 5.2.6).

Voici un exemple d'une réponse suivant ces principes :

HTTP/1.1 200 OK
Content-Type: application/example+json
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'
Cache-Control: max-age=3600
Referrer-Policy: no-referrer    
  

Il reste à régler la question des frontières de l'application. Le plus simple pour l'application est d'avoir son propre nom de domaine et donc une origine (RFC 6454) unique. Cela simplifie par exemple des problèmes comme les cookies. Mais cela complique le déploiement, empêchant de mettre plusieurs applications derrière le même nom. Le RFC conseille donc plutôt de concevoir des applications pouvant coexister avec d'autres applications sous le même nom (RFC 8820).

Un mot sur la sécurité pour finir (section 6 du RFC). D'abord, une application qui utilise HTTP va évidemment hériter des questions de sécurité générale de HTTP, comme détaillées dans la section 17 du RFC 9110. Vu le caractère sensible des données traitées par beaucoup d'applications, le RFC recommande l'utilisation de HTTPS. Mais il développe aussi la question de la vie privée. HTTP est très bavard et le serveur en apprend beaucoup, souvent beaucoup trop, sur son client. Ainsi, les cookies, l'adresse IP source, les ETags, les tickets de session TLS sont très utiles au serveur qui voudrait suivre un client. Et le RFC rappelle que HTTP donne assez d'informations « auxiliaires » pour pouvoir reconnaitre un client (ce qu'on nomme le fingerprinting). Bref, le maintien de son intimité va être aussi difficile que sur le Web.

Ce RFC remplace l'ancien RFC 3205. Comme le note l'annexe A de notre RFC, il y a trop de changements pour les lister ; ce document est très différent de son prédécesseur (qui date de 2002 !).

Voyons maintenant quelques exemples d'application utilisant HTTP. Dans le monde IETF, il y a évidemment RDAP (RFC 7480, RFC 9082 et RFC 9083). RDAP suit bien les principes de ce RFC. Par exemple, les chemins d'URL comme /domain ou /ip ne sont pas forcément à la « racine » du serveur HTTP. Autre exemple, DoH (RFC 8484), également fidéle (heureusement !) aux recommandations de l'IETF. Notez que ces recommandations laissent des choix. Ainsi, lorsque le nom de domaine cherché n'est pas trouvé, RDAP renvoie le code 404 (RFC 7480, section 5.3) alors que DoH préfère renvoyer un 200 (le serveur HTTP a bien été joignable et a bien répondu), gardant le signal de non-existence uniquement dans la réponse DNS (RFC 8484, section 4.2.1) transportée sur HTTP (l'argument est que DoH ne fait que transporter les requêtes d'un autre protocole, contrairement à RDAP).

J'ai parlé plus haut de la possibilité d'utilisation d'un navigateur Web ordinaire pour accéder aux applications utilisant HTTP. Mais comme ces applications envoient souvent des données structurées en JSON, il faut un navigateur qui gère bien le JSON. Et c'est justement ce que fait Firefox, qui sait l'afficher de manière pratique : rdap-json-firefox.png

Terminons avec quelques exemples d'API « finales » (donc pas le sujet principal du RFC, qui parle de protocoles IETF). Commençons modestement par l'API du DNS looking glass. A priori, elle suit tous les principes de ce RFC. En tout cas, elle essaie. Mais si vous constatez des différences avec le RFC, n'hésitez pas à faire un rapport. Autre API intéressante, celle des sondes RIPE Atlas. Elle utilise toutes les possibilités de HTTP, notamment les multiples méthodes (DELETE pour supprimer une mesure en cours, par exemple). J'aurais juste trouvé plus logique d'utiliser PUT au lieu de POST pour créer une mesure. L'API de Mastodon (cf. sa documentation) est encore plus incohérente, utilisant POST pour créer un pouète, mais PUT pour le mettre à jour.


Téléchargez le RFC 9205


L'article seul

RFC 9204: QPACK: Header Compression for HTTP/3

Date de publication du RFC : Juin 2022
Auteur(s) du RFC : C. Krasic (Netflix), M. Bishop (Akamai Technologies), A. Frindell (Facebook)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF quic
Première rédaction de cet article le 7 juin 2022


QPACK, normalisé dans ce RFC est un mécanime de compression des en-têtes HTTP, prévu spécifiquement pour HTTP/3. Il est proche du HPACK de HTTP/2, mais adapté aux particularités de QUIC.

Car le HPACK du RFC 7541 a un défaut qui n'était pas un problème en HTTP/2 mais le devient avec HTTP/3 : il supposait que les trames arrivent dans l'ordre d'émission, même si elles circulent sur des ruisseaux différents. Ce n'est plus vrai en HTTP/3 qui, grâce à son transport sous-jacent, QUIC, a davantage de parallélisme, et où une trame peut en doubler une autre (si elles n'étaient pas dans le même ruisseau). QPACK ressemble à HPACK, mais en ayant corrigé ce problème. (Au fait, ne cherchez pas ce que veut dire QPACK, ce n'est pas un acronyme.)

Donc, le principe de QPACK. Comme HPACK, on travaille avec deux tables, qui vont associer aux en-têtes HTTP un nombre (appelé index). L'une des tables est statique, définie dans ce RFC (annexe A) et donc identique pour tout le monde. L'autre est dynamique et construite par un échange de messages transmis dans des ruisseaux QUIC. Le fonctionnement avec la table statique est simple : l'encodeur regarde si ce qu'il veut écrire est dans la table, si oui, il le remplace par l'index. Le décodeur, recevant un index, le remplace par le contenu de la table. Par exemple, l'en-tête HTTP if-none-match (RFC 7232, section 3.2) est dans la table, index 9. L'encodeur remplacera donc un if-none-match par 9 (12 octets de gagnés si tout était sous forme de caractères de 8 bits, mais peut-être un peu plus ou un peu moins, avec l'encodage de QPACK), et le décodeur fera l'inverse.

J'ai un peu simplifié en supposant que la table ne contenait que les noms des en-têtes. Elle peut aussi contenir leur valeur si celle-ci est très courante. Ainsi, accept: application/dns-message est dans la table, index 30, vu son utilisation intensive par DoH (RFC 8484). Même chose pour content-type: text/html;charset=utf-8 à l'index 52.

La table dynamique est évidemment bien plus complexe. Encodeur (celui qui comprime) et décodeur doivent cette fois partager un état. En outre, le parallélisme inhérent à QUIC fait qu'un message d'ajout d'une entrée dans la table pourrait arriver après le message utilisant cette entrée. QPACK fonctionne de la façon suivante :

  • L'encodeur peut envoyer (sur un ruisseau QUIC dédié) des instructions de construction de la table dynamique, notamment pour insérer une nouvelle entrée (mais pas en supprimer).
  • Le décodeur accuse réception de ces instructions.
  • L'encodeur envoie l'index le plus élevé de sa table, ce qui permet au décodeur, en regardant sa propre table, de savoir s'il est synchronisé ou pas. S'il ne l'est pas, le décodeur se bloque en attendant les instructions d'insertion qui vont faire grandir sa table.

QUIC a un système de contrôle de flux, et des inconvénients peuvent en résulter, par exemple un blocage des messages de contrôle de la table. Pour éviter tout blocage, un encodeur peut n'utiliser que des entrées de la table qui ont déjà fait l'objet d'un accusé de réception. Il comprimera moins mais ne risquera pas d'être bloqué.

Le RFC détaille les précautions à prendre pour éviter l'interblocage. Ainsi, les messages modifiant la table risquent d'être bloqués par ce système alors que le récepteur n'autorise pas de nouvelles trames tant qu'il n'a pas traité des trames qui ont besoin de ces nouvelles entrées dans la table dynamique. L'encodeur a donc pour consigne de ne pas tenter de modifier la table s'il ne lui reste plus beaucoup de « crédits » d'envoi de données. (D'une manière générale, quand il y a des choses compliquées à faire, QPACK demande à l'encodeur de les faire, le décodeur restant plus simple.)

L'encodage des messages QPACK est spécifié dans la section 4. QPACK utilise deux ruisseaux QUIC unidirectionnels, un dans chaque direction. Ils sont enregistrés à l'IANA.

Notez aussi qu'il y a deux instructions d'ajout d'une entrée dans la table dynamique, une qui ajoute une valeur litérale (comme « ajoute accept-language: fr ») et une qui ajoute une valeur exprimée sous forme d'une référence à une entrée d'une table (qui peut être la statique ou la dynamique). Par exemple, comme accept-language est dans la table statique, à l'index 72, on peut dire simplement « ajoute 72: fr ». Encore quelques octets gagnés.

Dans la trame SETTINGS de HTTP/3, deux paramètres concernent spécialement QPACK, pour indiquer la taille maximale de la table dynamique, et le nombre maximal de ruisseaux bloqués. Ils sont placés dans un registre IANA.

Quelques mots sur la sécurité : dans certaines conditions, un observateur peut obtenir des informations sur l'état des tables (cf. l'attaque CRIME) même s'il ne peut déchiffrer les données protégées par TLS, celui-ci ne masquant pas la taille. Bien sûr, on pourrait remplir avec des données bidons mais cela annulerait l'avantage de la compression. La section 7 du RFC donne quelques idées sur des mécanismes de limitation du risque.

L'annexe A du RFC spécifie la table statique et ses 98 entrées. Elle a été composée à partir de l'analyse de trafic HTTP en 2018. L'ordre des entrées n'est pas arbitraire : vu comment sont représentés les entiers, donc les index, dans QPACK, les entrées les plus fréquentes sont en premier, car QPACK utilise moins de bits pour les nombres les plus petits. Notez aussi que cette table comprend les en-têtes HTTP « classiques », comme content-length ou set-cookie mais aussi ce que HTTP/2 appelle les « pseudo-en-têtes », qui commencent par deux-points. C'est par exemple le cas de la méthode HTTP (GET, PUT, etc), notée :method ou, dans les réponses, du code de retour, noté :status (tiens, la table statique a une entrée pour le code 403 mais pas pour le 404).

Si vous envisagez de programmer QPACK, l'annexe B contient des exemples de dialogue entre encodeur et décodeur, et l'annexe C du pseudo-code pour l'encodeur.


Téléchargez le RFC 9204


L'article seul

RFC 9199: Considerations for Large Authoritative DNS Servers Operators

Date de publication du RFC : Mars 2022
Auteur(s) du RFC : G.C.M. Moura (SIDN Labs/TU Delft), W. Hardaker, J. Heidemann (USC/Information Sciences Institute), M. Davids (SIDN Labs)
Pour information
Première rédaction de cet article le 22 mars 2022


On sait que le DNS est un des services d'infrastructure les plus essentiels de l'Internet. Il est, par exemple, crucial que les serveurs DNS faisant autorité soient correctement gérés, et robustes face aux problèmes qui peuvent survenir. Ce RFC donne un certain nombre de conseils (juste des conseils, il ne s'agit pas d'une norme) aux opérateurs de ces serveurs.

Il n'est pas obligatoire de suivre ces conseils mais il faut noter qu'ils sont tous fondés sur des articles publiés dans des revues techniques avec examen par les pairs. Donc, c'est quand même sérieux, même si ce RFC n'a pas le statut de norme. Le RFC vise surtout les « gros » serveurs faisant autorité, par exemple les serveurs de TLD importants. Ces serveurs sont typiquement anycastés (RFC 1546 et RFC 4786). Si le vôtre est unicast et ne sert qu'à trois visiteurs de temps en temps, vous n'êtes pas forcément concerné·e.

Au passage, cette distinction entre serveurs faisant autorité et résolveurs est cruciale dès qu'on parle du DNS. La section 2 du RFC commence d'ailleurs par un rappel de cette distinction. D'autre part, cette même section insiste sur l'importance du DNS pour le vécu de l'utilisatrice. Par exemple la latence perçue par celle-ci dépend en bonne partie du DNS. Voici un exemple :

% curl-timing https://www.nic.af/
DNS: 0,920891 s
TCP connect: 1,124164 s
TLS connect: 1,455578 s
Start transfer: 1,598288 s
Total: 1,735741 s
  

On voit que la résolution DNS a pris plus de la moitié du temps total. (La commande curl-timing est définie par alias curl-timing='curl --silent --output /dev/null --write-out "DNS: %{time_namelookup} s\nTCP connect: %{time_connect} s\nTLS connect: %{time_appconnect} s\nStart transfer: %{time_starttransfer} s\nTotal: %{time_total} s\n"'.) Sur ce sujet de la latence, voir aussi l'article « The Internet at the Speed of Light ». La latence dépend en grande partie de la distance géographique, et la réduire impose donc une présence mondiale.

Et ces performances, et cette robustesse sont menacées, entre autres, par les nombreuses attaques par déni de service qui visent le DNS, comme celle décrite dans l'article « Anycast vs. DDoS: Evaluating the November 2015 Root DNS Event ».

La section 3 est le cœur du RFC, contenant les recommandations, numérotées de C1 à C6. Commençons par C1, le conseil de n'avoir que des serveurs anycast. L'anycast est indispensable pour répartir très largement les serveurs, assurant la performance et la robustesse de la résolution DNS. Bien sûr, on peut se contenter d'avoir plusieurs enregistrements NS mais cela ne suffit pas : diverses raisons font qu'on ne peut pas avoir des centaines d'enregistrements dans un ensemble NS, alors que certains nuages anycast atteignent cette taille. Donc, avoir plusieurs enregistrements NS est nécessaire mais cela ne dispense pas de l'anycast.

C'est d'autant plus vrai que, parmi les enregistrements NS, la sélection de celui utilisé est faite par le résolveur. Les résolveurs corrects mesurent la latence et choisissent le serveur faisant autorité le plus rapide. Mais il y a d'autres résolveurs qui n'utilisent pas de bons algorithmes de sélection (cf. « Recursives in the Wild: Engineering Authoritative DNS Servers »). Avec l'anycast, la sélection de l'instance utilisée est faite par le réseau, via BGP, et peut donc être meilleure. Du fait de ces résolveurs sous-performants, un opérateur a donc intérêt à ce que tous les serveurs faisant autorité soient anycastés. S'il y a un « maillon faible », unicast et mal connecté, il sera quand même consulté par certains résolveurs, et dégradera donc la latence dans certains cas. Par exemple, le TLD .nl est passé en tout anycast en 2018.

Maintenant, C2, le conseil de travailler son routage. Une fois qu'on a décidé de mettre de l'anycast partout, il reste à optimiser. BGP ne garantit pas du tout, contrairement à ce qu'on lit parfois, que la route la plus rapide sera choisie. BGP fait ce que les administrateurs réseau lui demandent de faire. On voit régulièrement des traceroute qui montrent qu'on ne va pas à l'instance anycast la plus proche. Certains opérateurs DNS compensent en ajoutant davantage d'instances mais l'article « Anycast Latency: How Many Sites Are Enough? » montre, en testant des serveurs de la racine depuis des sondes RIPE Atlas, que le nombre d'instances compte moins que le soin apporté au routage. (On ne parle ici que de performances ; pour la résistance aux attaques par déni de service, le nombre d'instances reste essentiel.) Ici, le temps de réponse des serveurs faisant autorité pour .fr, depuis une machine en France (il y a une instance proche pour chaque serveur) :

% check-soa -i fr            
d.nic.fr.
	2001:678:c::1: OK: 2230506452 (4 ms)
	194.0.9.1: OK: 2230506452 (0 ms)
e.ext.nic.fr.
	193.176.144.22: OK: 2230506434 (15 ms)
	2a00:d78:0:102:193:176:144:22: OK: 2230506434 (15 ms)
f.ext.nic.fr.
	194.146.106.46: OK: 2230506434 (6 ms)
	2001:67c:1010:11::53: OK: 2230506452 (11 ms)
g.ext.nic.fr.
	2001:678:4c::1: OK: 2230506434 (2 ms)
	194.0.36.1: OK: 2230506434 (2 ms)    
  

Le conseil C3 porte sur un concept très important lorsqu'on fait de l'anycast, celui de bassin d'attraction. Ce terme désigne l'ensemble des réseaux qui vont envoyer leurs paquets vers une instance donnée. Si toutes les instances ont à peu près les mêmes capacités de traitement des requêtes, on souhaite en général placer les instances et configurer BGP de manière à ce que toutes les instances reçoivent à peu près la même quantité de requêtes. Mais en pratique c'est rarement le cas : le réseau est une chose compliquée. Il existe des méthodes pour améliorer les choses (cf. l'article « Broad and Load-Aware Anycast Mapping with Verfploeter », qui décrit l'outil Verfploeter) mais ce n'est jamais parfait. Au moins, un outil comme Verfploeter permet de procéder plus scientifiquement qu'au pifomètre, et d'avoir une idée de ce que produiront des changements de configuration BGP pour faire du traffic engineering (par exemple, allonger le chemin d'AS dans les annonces, pour diminuer le trafic d'une instance).

Le conseil C4, quant à lui, porte sur les attaques par déni de service (comme celle décrite dans « Anycast vs. DDoS: Evaluating the November 2015 Root DNS Event ») et sur le comportement des serveurs soumis à un stress intense. Que peut faire l'opérateur lorsque Mirai ou un de ses semblables attaque ?

  • Essayer de diminuer le trafic entrant, soit en faisant du traffic engineering BGP pour envoyer le trafic vers une autre instance, si certaines ont davantage de capacité, ou bien ne sont pas attaquées, voire, si on n'a pas de réserves, en arrêtant complètement d'annoncer les routes (ce qui arrête le service - ce qui était le but de l'attaquant - mais cela peut être nécessaire s'il y a d'autres services au même endroit et qu'on veut les épargner),
  • Ou bien encaisser en silence, sachant qu'on perdra des requêtes et qu'on ne pourra pas répondre à tout. Si l'attaque ne frappait que certaines instances, cela peut permettre d'épargner les instances non attaquées.

Le conseil C4 est donc d'avoir des plans prêts à l'avance, permettant de prendre une décision en cas de crise, et de l'appliquer.

Et les TTL ? Ils ne sont pas oubliés. La mémorisation des réponses, pendant une durée maximale égale au TTL, est un point essentiel du DNS, et un facteur de performance crucial. Même si un serveur faisant autorité répond souvent en moins de 50 ms, la réponse de la mémoire d'un résolveur, qui peut arriver en 1 ms, sera encore préférable. Ce rôle a été largement étudié (voir par exemple « Modeling TTL- based Internet Caches » ou, plus récemment, « When the Dike Breaks: Dissecting DNS Defenses During DDoS »). Une étude des conséquences du choix du TTL sur les performances est la base du conseil C5, l'étude « Cache Me If You Can: Effects of DNS Time-to- Live ». Elle montre que :

  • Des TTL plus longs diminuent le temps de réponse (puisque les données sont conservées plus longtemps dans la mémoire des résolveurs), cf. par exemple l'expérience faite sur .uy citée dans l'article ci-dessus,
  • Ces TTL plus longs diminuent le trafic DNS, pour la même raison, (logiquement, les gens qui s'inquiètent de l'empreinte environnementale du numérique devraient mettre des TTL longs sur leurs domaines),
  • Et ils améliorent la robustesse face aux attaques par déni de service (même si tous les serveurs faisant autorité ont du mal à répondre, les résolveurs pourront continuer à servir les données),
  • D'un autre côté, et comme il n'existe pas de solution parfaite, juste des choix et des compromis, des TTL plus longs empêchent de changer rapidement sa configuration ; si le changement était planifié, ce n'est pas grave (on abaisse le TTL avant le changement et on le remonte après), mais si on veut le faire en urgence, c'est plus gênant,
  • C'est d'autant plus gênant que certains services d'atténuation des attaques par déni de service fonctionnent en utilisant le DNS pour envoyer le trafic vers l'atténuateur ; des TTL longs limitent cette possibilité (et la planification ne change rien dans ce cas, puisque les attaquants ne se signalent pas forcément à l'avance),
  • Même problème avec la répartition de charge utilisant le DNS.

Bref, on ne peut pas faire une recommandation quantitative appliquable à tous les cas. Le conseil C5 est donc plus complexe :

  • Pour un domaine ordinaire, des TTL d'au moins une heure, et de préférence d'au moins huit heures sont recommandés,
  • Pour les gros registres (par exemple registres de TLD), la plupart des résolveurs utiliseront de toute façon les TTL indiqués dans la zone fille ; pour le bien des autres résolveurs, le RFC recommande un TTL d'au moins une heure,
  • Pour les zones « terminales » qui utilisent des répartiteurs de charge ou des services d'atténuation d'attaques qui reposent sur le DNS, on peut se contenter de TTL de 5 minutes, mais le RFC recommande quand même au moins 15 minutes.

Et si les TTL entre la zone parente et la zone fille diffèrent ? La question se pose typiquement pour les enregistrements NS. Par exemple, les enregistrements NS dans la zone racine ont un TTL de 48 heures, alors que dans les zones des TLD, l'ensemble NS a presque toujours un TTL plus faible (une heure pour .cl, par exemple). Selon que le résolveur est parent-centric (une minorité) ou child-centric (la grande majorité), il utilisera le TTL de la zone parente ou bien celui de la zone fille. Le conseil C6 : on ne peut pas compter que le TTL qu'on a mis soit respecté partout, si l'enregistrement existe aussi dans la zone parente avec des TTL différents. Attention donc lorsqu'on veut changer quelque chose, à intégrer le TTL de la zone parente.

Un bon résumé en anglais de ce RFC a été écrit par un des auteurs. Il inclut un récit de la création du RFC et du processus suivi.


Téléchargez le RFC 9199


L'article seul

RFC 9184: BGP Extended Community Registries Update

Date de publication du RFC : Janvier 2022
Auteur(s) du RFC : C. Loibl (next layer Telekom)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 21 janvier 2022


Les « communautés étendues » de BGP, des attributs d'une annonce de route avec BGP, sont enregistrées dans des registres IANA, décrits par le RFC 7153. Notre nouveau RFC 9184 met légèrement à jour les procédures d'enregistrement dans ces registres.

Le principal changement est simple : les types 0x80, 0x81 et 0x82 du registre des communautés étendues transitives sont désormais utilisables selon une politique d'enregistrement « premier arrivé, premier servi » (cf. RFC 8126 sur ces politiques d'enregistrement à l'IANA). Ils sont en effet utilisés par le RFC 8955 alors qu'ils étaient dans une plage prévue pour les expérimentations. L'erreur est donc désormais rectifiée.


Téléchargez le RFC 9184


L'article seul

RFC 9172: Bundle Protocol Security (BPSec)

Date de publication du RFC : Janvier 2022
Auteur(s) du RFC : E. Birrane, K. McKeever (JHU/APL)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dtn
Première rédaction de cet article le 1 février 2022


Le protocole Bundle, normalisé dans le RFC 9171, permet la communication entre des machines dont la connectivité est faible et/ou intermittente, par exemple dans le domaine spatial. L'absence de synchronicité interdit beaucoup de solutions de sécurité couramment utilisées sur l'Internet. Ce nouveau RFC présente donc un mécanisme spécifique pour assurer la sécurité (confidentialité et intégrité) des communications faites avec Bundle : ce mécanisme se nomme BPsec (Bundle Protocol security). Notez que le mécanisme est très général, et laisse de côté de nombreux détails.

BPsec peut assurer une sécurité de bout en bout. Ce n'est pas évident dans le contexte d'utilisation de Bundle (RFC 4838), les DTN (Delay Tolerant Networks). Bundle est un protocole qui fonctionne en « enregistre et fais suivre » (store and forward), sans liaisons directes entre émetteur et récepteur, et avec des réseaux lents, imprévisibles, à fort taux de perte de paquets. On ne peut donc pas garantir, par exemple, qu'émetteur et récepteur pourront communiquer à la demande avec un tiers de confiance, genre autorité de certification. On ne peut même pas supposer que toutes les liaisons seront bidirectionnelles, ce qui veut dire, entre autres, qu'un échange de clés Diffie-Hellman ne sera pas possible. Le protocole suppose évidemment que ledit réseau n'est pas de confiance : des méchants peuvent regarder et modifier les bits qui circulent. Cette supposition est classique en sécurité.

BPsec ne va pas essayer de garantir une authentification de chaque étape intermédiaire. D'abord, on ne sait pas si le nœud suivant est réellement adjacent dans l'espace physique (il peut y avoir des intermédiaires « invisibles ») et puis les différents nœuds par lequel le message va passer peuvent avoir des choix de sécurité incompatibles, les réseaux DTN pouvant, comme l'Internet, être composés de nœuds gérés par des organisations différentes.

Si les logiciels sont tous bien programmés, et que les clés privées utilisées n'ont pas été compromises, BPsec va garantir l'intégrité et la confidentialité des messages. BPsec est la continuation du cadre défini dans le RFC 6257, en le simplifiant et en le rendant plus réaliste.

Un peu de terminologie pour suivre ce RFC : d'abord, un rappel qu'un bundle est composé de blocs (RFC 9171, section 4.3). Ensuite :

  • Bloc de sécurité (security block) : un bloc d'extension dans un bundle, une unité de données.
  • Security acceptor : un nœud qui traite un bloc de sécurité, et le supprime ensuite. Ce n'est pas forcément le destinataire final (sauf évidemment pour les services de sécurité de bout en bout).
  • Service de sécurité (security service) : un service particulier, par exemple la confidentialité.
  • Source de sécurité (security source) : un nœud qui ajoute un bloc de sécurité. Ce n'est pas forcément l'émetteur initial.
  • Vérificateur de sécurité (security verifier) : un nœud qui lit et vérifie un bloc de sécurité mais qui ne le supprime pas.

La section 2 du RFC résume les points importants de la conception de BPsec :

  • Sécurité au niveau du bloc, pas du bundle entier.
  • Plusieurs sources ont pu mettre des blocs de sécurité dans le bundle.
  • Les différents nœuds ont des politiques de sécurité différentes.
  • BPsec a une notion de contexte de sécurité (qui définit les techniques admissibles, par exemple les algorithmes de cryptographie), notion détaillée dans la section 9.
  • Traitement déterministe des différents blocs de sécurité, notamment de l'ordre de traitement.

Il existe deux sortes de blocs de sécurité (les types de bloc figurent dans un registre IANA, défini dans le RFC 6255), les BIB (Block Integrity Block) et les BCB (Block Confidentiality Block). Les sources de sécurité ajoutent ces blocs et les acceptors les traitent. Dans le cas d'un chiffrement de bout en bout, par exemple, l'émetteur met un BCB que le destinataire déchiffrera. Dans un autre cas, on peut voir un nœud de départ ne mettre aucun bloc de sécurité (peut-être parce que ce nœud est un objet contraint, avec trop peu de ressources de calcul) mais un nœud intermédiaire ajouter un BIB pour protéger le contenu avant un voyage sur un lien qu'on sait dangereux. Ces blocs sont encodés comme spécifié dans le RFC 9171. L'ajout d'un BIB ne modifie pas le contenu du bundle mais celui d'un BCB va le faire, puisque les données seront chiffrées.

Le bloc contient notamment l'identificateur du nœud qui l'a ajouté.

Le bloc doit être mis sous forme canonique de CBOR (RFC 8949, section 4.2). Comme toujours en cryptographie, pour que les signatures soient vérififiables, il faut que les données soient sous une forme canonique.

La section 5 du RFC décrit le traitement des blocs de sécurité. Le nœud peut les passer tels quels, sans les interpréter, s'il n'est qu'un intermédiaire. Ou bien, s'il veut les valider, il va alors vérifier leurs signatures, déchiffrer, etc. En cas d'erreur, notre RFC ajoute cinq codes supplémentaires pour les signaler.

Pas de cryptographie sans clés et la gestion des clés est souvent le point difficile. La section 6 rappelle le problème. Comme les réseaux de type DTN seront très variés, avec des caractéristiques bien différentes, notre RFC ne décrit pas une méthode unique de gestion de clés. Disons que cette gestion est repoussée aux mises en œuvre ultérieures.

Enfin, la section 7 du RFC décrit les différentes politiques possibles, et la section 8 analyse en détail les caractéristiques de sécurité de ce système, face aux différentes menaces. Parmi les points amusants, le RFC note qu'avec les DTN, on peut s'attendre à ce que des bundles restent dans le réseau très longtemps, peut-être même des années (!) et que la cryptographie (choix des algorithmes, par exemple) doit donc être pensée pour durer.

Ce mécanisme de sécurité est mis en œuvre dans le logiciel libre ION.


Téléchargez le RFC 9172


L'article seul

RFC 9171: Bundle Protocol Version 7

Date de publication du RFC : Janvier 2022
Auteur(s) du RFC : S. Burleigh (JPL, Calif. Inst. Of Technology), K. Fall (Roland Computing Services), E. Birrane (APL, Johns Hopkins University)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dtn
Première rédaction de cet article le 1 février 2022


Après plusieurs itérations, le protocole Bundle, qui sert à transmettre des messages dans des situations où la connectivité est intermittente et la latence importante (par exemple dans l'espace), a atteint sa version 7, la première qui soit officiellement norme de l'Internet. Ce nouveau RFC décrit cette version 7, assez modifiée par rapport à la version 6 qui faisait l'objet du RFC 5050. Les deux versions n'interopèrent pas.

« Dans l'espace, personne ne vous entend crier », disait l'annonce du film Alien. Peut-être, mais on veut quand même communiquer, échanger des messages et des fichiers. Les particularités de cet environnement (et de quelques autres analogues comme par exemple des étendues peu connectées sur Terre ou bien des situations post-catastrophe) ont suscité la création du groupe DTN (Delay-Tolerant Networking), qui travaille à normaliser des techniques pour envoyer une photo de chat à un autre vaisseau spatial. La version 6 du protocole Bundle, spécifié dans le RFC 5050, avait été programmée dans de nombreux langages, et l'expérience acquise avec Bundle v6 a mené cette version 7. Dans les environnements concernés, avec leur latence élevée (et parfois très variable), leur taux de pertes de paquets souvent important, et leur connectivité intermittente, les protocoles de transport comme TCP et les protocoles applicatifs qui les accompagnent ne conviennent pas. Il n'est pas possible de faire du synchrone (on ne peut pas rester à attendre l'autre pendant on ne sait pas combien de temps). D'où la nécessité de protocoles spécifiques, architecturés autour de l'idée de « enregistre et fais suivre » (store and forward), où chaque machine intermédiaire va stocker les messages en attendant une occasion de les livrer. (Oui, ça ressemble à UUCP et c'est logique, les cahiers des charges étant analogues.) Notre RFC résume ce qu'on attend d'un tel protocole :

  • Capacité à utiliser des déplacements physiques des données (en gros, la clé USB dans un camion, mécanisme dont on sait qu'il a une latence désastreuse mais une énorme capacité ; cf. l'anecdote amusante de l'Allemand et son cheval, ou bien l'offre SnowMobile d'Amazon).
  • Capacité d'un envoyeur à oublier, une fois qu'il a transmis le message au nœud suivant (alors que TCP doit garder les données, en attendant l'accusé de réception de la destination finale).
  • Capacité à gérer les connectivités intermittentes, voire les cas où émetteur et récepteur ne sont jamais joignables en même temps.
  • Capacité à utiliser les cas où la connectivité arrive à un moment prédit à l'avance, aussi bien que ceux où il faut être opportuniste, utilisant la connectivité quand elle arrive.

Des détails sur ce cahier des charges sont dans les RFC 4838 et dans l'article de K. Fall à SIGCOMM « A delay-tolerant network architecture for challenged internets ».

La section 3 du RFC rappelle les termes utilisés, notamment :

  • Bundle (j'ai été trop paresseux pour traduire ce terme mais Bertand Petit suggère « ballot ») : une unité de données transmises. Un message (tel que compris par l'utilisateur humain) va être découpé en bundles qui seront transmis sur le réseau.
  • BPA (Bundle Protocol Agent) : le logiciel qui, sur un nœud, met en œuvre le protocole décrit dans ce RFC.
  • CLA (Convergence Layer Adapter) : la partie du protocole qui parle aux couches basses, pour effectuer l'envoi et la réception. Cela peut, par exemple, être TCP (RFC 9174).
  • Nœud (node) : une machine qui peut envoyer et recevoir des bundles mais aussi relayer les bundles des autres. Chaque nœud a un identificateur, le node ID, qui est un URI (RFC 3986), en général de plan dtn (comme dtn://example.com/foobar) ou, de plus bas niveau et de signification seulement locale, ipn, qui avait été créé par le RFC 6260 (comme ipn:9.37). Les plans utilisés ont été mis dans le registre IANA.
  • Transmission (Forwarding) : faire suivre le message au nœud suivant (tout ce protocole est « saut par saut », avec des nœuds qui se refilent les bundles).
  • Terminal (endpoint) : un ou plusieurs nœuds qui mettent en œuvre un service. Une sorte de nœud virtuel, quoi. Comme les nœuds, ils ont un identificateur qui est un URI.

La section 4 du RFC décrit le format des bundles. Ce sera du CBOR (RFC 8949). Un bundle comprend un certain nombre de booléens qui indiquent si le récepteur doit accuser réception de l'arrivée du bundle, de la transmission au nœud suivant, etc. (Leur liste figure dans un registre IANA.) Il indique également la destination (sous le forme d'un endpoint ID) et la source (facultative).

Un bundle est en fait composé de blocs, chaque bloc étant un tableau CBOR. Le premier bloc porte les métadonnées indispensables pour les nœuds qui relaieront le bundle (comme l'identificateur de destination) ou comme la durée de vie du bundle (analogue au TTL d'IP). Chaque bloc contient un type et des données. La charge utile du bundle est dans un bloc de type 1, les autres types servant à divers détails (qui, dans IP, seraient mis dans les options du paquet en IPv4 et dans un en-tête d'extension en IPv6). Par exemple, un bloc de type 7 sert à transporter l'âge du bundle, alors qu'un bloc de type 10 à indiquer le nombre de sauts maximum et le nombre déjà effectués. Les différents types figurent dans un registre IANA.

La section 5 du RFC précise comment on traite les bundles. Lors de la réception d'un bundle, le nœud qui n'est pas destination (dont l'identificateur n'apparait pas dans le champ de destination) doit décider s'il transmet le bundle ou pas. S'il décide de le transmettre, il doit ensuite regarder s'il « connait » le nœud de destination (s'il sait le joindre directement) ou bien s'il doit le transmettre à un nœud mieux placé. Comment trouve-t-on ces nœuds mieux placés ? De même que le RFC sur IP ne décrit pas les protocoles de routage, le Bundle Protocol ne précise pas. Cela est fait par des protocoles externes comme SABR.

Une fois prise la décision d'envoyer le bundle à un autre nœud, on sélectionne un CLA adapté à cet autre nœud et on lui transmet. (Puis on le supprime du stockage local.) Bref, tout cela ressemble beaucoup au traitement d'un paquet IP par un routeur IP classique, avec le CLA jouant le rôle de la couche 2. La plus grosse différence est sans doute que le nœud peut garder le paquet pendant un temps assez long, s'il ne peut pas le transmettre tout de suite. La gestion des files d'attentes et des retransmissions sera donc assez différente de celle d'un routeur IP.

En cas d'échec, le nœud peut décider de renvoyer le bundle à l'expéditeur ou de le jeter. Si le bundle a expiré (durée de vie dépassée), il est jeté.

À la réception d'un bundle, le nœud regarde aussi si ce bundle nécessitait un accusé de réception et, si nécessaire, il en envoie. Si le bundle a une destination locale, il est livré à la machine. (Le bundle a pu être fragmenté et, dans ce cas, il faudra attendre que tous les fragments soient là, pour le réassemblage, cf. section 5.8).

La section 6 du RFC couvre le cas des enregistrements administratifs, des messages (envoyés évidemment sous forme de bundles) qui servent à gérer le bon fonctionnement du protocole (un peu comme ICMP pour IP). Ces enregistrements sont un tableau CBOR de deux entrées, un type (un nombre) et les données, un seul type d'enregistrement administratif existe actuellement, de type 1, le rapport d'état, qui va servir à indiquer ce que le message est devenu. Son format et son contenu sont détaillés dans la section 6.1.1. Il dispose d'une liste de codes, comme 1 pour « durée de vie dépassée » ou 5 pour « je n'arrive pas à joindre la destination ».

On a vu que BP, le Bundle Protocol, dépend d'un CLA (Convergence Layer Adapter) pour parler aux couches basses. La section 7 précise ce qu'on attend de ce convergence layer :

  • Il doit évidemment permettre d'envoyer un bundle d'un nœud à l'autre,
  • il doit rendre des comptes au BP, lui disant s'il a pu transmettre le bundle ou pas,
  • il doit pouvoir recevoir des bundles et les livrer localement.
  • Certaines extensions peuvent ajouter des exigences au CLA.

Le CLA doit faire tout cela tout en respectant le réseau sous-jacent, par exemple en évitant la congestion. Notez que dans la plupart des cas d'usage de BP, la latence est telle qu'on ne peut pas compter sur les réactions du récepteur, et il faut donc se débrouiller autrement. Un exemple d'un CLA est celui avec TCP du RFC 9174 (mais dans la plupart des scénarios envisagés pour le Bundle Protocol, TCP ne sera pas utilisable).

Il y a actuellement pas moins de sept mises en œuvres différentes de ce BP (Bundle Protocol) :

Notez que deux mises en œuvre de BP ne peuvent interopérer que si elles utilisent le même CLA.

Par défaut, BP n'offre aucune sécurité (cf. section 8). Un attaquant peut suivre le trafic, le modifier, etc. Certes, notre RFC recommande un CLA « sûr » mais cela ne suffit pas. La sécurité doit être fournie par des extensions, comme celle du RFC 9172. Notons aussi que le protocole utilise à plusieurs endroits des estampilles temporelles, donc sa sécurité dépend de celle de son horloge.

L'annexe A détaille les changements depuis le RFC 5050, qui normalisait la version 6 du protocole. Outre le changemente de statut (BP est désormais une norme et plus une « expérience »), les changements techniques sont importants à tel point qu'il n'y a pas d'interopérabilité entre les deux versions. Entre autres, les concepts de node ID et endpoint ID sont désormais séparés, le premier bloc d'un bundle est désormais immuable, de nouveaux types de blocs ont été créés, l'encodage abandonne l'ancien SDNV (RFC 6256) pour CBOR, etc. (Notez qu'il existe un registre IANA des versions de BP.)

Un des avantages de CBOR est l'existence du langage de description CDDL (RFC 8610), ce qui permet de donner une description formelle des bundles dans l'annexe B. Par exemple, voici la description du premier bloc de chaque bundle, en CDDL (eid étant l'Endpoint ID) :

primary-block = [
     version: 7,
     bundle-control-flags,
     crc-type,
     destination: eid,
     source-node: eid,
     report-to: eid,
     creation-timestamp,
     lifetime: uint,
     ...
     ? crc-value,
   ]    
  

Avec un cahier des charges présentant beaucoup de ressemblances avec celui du BP, notez l'existence du système NNCP, qui est également de type « enregistre et fais suivre ». Ça semble très intéressant mais je ne l'ai jamais testé.


Téléchargez le RFC 9171


L'article seul

RFC 9170: Long-term Viability of Protocol Extension Mechanisms

Date de publication du RFC : Décembre 2021
Auteur(s) du RFC : M. Thomson (Mozilla), T. Pauly (Apple)
Pour information
Première rédaction de cet article le 1 janvier 2022


Après des années de déploiement d'un protocole sur l'Internet, lorsqu'on essaie d'utiliser des fonctions du protocole parfaitement standards et légales, mais qui avaient été inutilisées jusqu'à présent, on découvre souvent que cela ne passe pas : des programmes bogués, notamment dans les middleboxes, plantent de manière malpropre lorsqu'ils rencontrent ces (toutes relatives) nouveautés. C'est ce qu'on nomme l'ossification de l'Internet. Ce RFC de l'IAB fait le point sur le problème et sur les solutions proposées, par exemple le graissage, l'utilisation délibérée et précoce de variations, pour ne pas laisser d'options inutilisées.

Lorsqu'un protocole a du succès (cf. RFC 5218, sur cette notion de succès), on va vouloir le modifier pour traiter des cas nouveaux. Cela n'est pas toujours facile, comme le note le RFC 8170. Tout protocole a des degrés de liberté (extension points) où on pourra l'étendre. Par exemple, le DNS permet de définir de nouveaux types de données (contrairement à ce qu'on lit souvent, le DNS ne sert pas qu'à « trouver des adresses IP à partir de noms ») et IPv6 permet de définir de nouvelles options pour la destination du paquet, voire de nouveaux en-têtes d'extension. En théorie, cela permet d'étendre le protocole. Mais en pratique, utiliser ces degrés de liberté peut amener des résultats imprévus, par exemple, pour le cas du DNS, un pare-feu programmé avec les pieds qui bloque les paquets utilisant un type de données que le pare-feu ne connait pas. Ce RFC se focalise sur des couches relativement hautes, où tout fonctionne de bout en bout (et où, en théorie, les intermédiaires doivent laisser passer ce qu'ils ne comprennent pas). Les couches basses impliquent davantage de participants et sont donc plus problématiques. Ainsi, pour IPv6, l'en-tête « options pour la destination » doit normalement être ignoré et relayé aveuglément par les routeurs alors que l'en-tête « options pour chaque saut » doit (enfin, devait, avant le RFC 8200) être compris et analysé par tous les routeurs du chemin, ce qui complique sérieusement son utilisation.

Notre RFC a été développé par l'IAB dans le cadre du programme « Evolvability, Deployability, & Maintainability (EDM) ».

Déployer une nouvelle version d'un protocole, ou simplement utiliser des options qui n'avaient pas été pratiquées avant, peut donc être très frustrant. Prenons le cas imaginaire mais très proche de cas réels d'un protocole qui a un champ de huit bits inutilisé et donc le RFC d'origine dit que l'émetteur du paquet doit mettre tous ces bits à zéro, et le récepteur du paquet, en application du principe de robustesse, doit ignorer ces bits. Un jour, un nouveau RFC sort, avec une description du protocole où ces bits ont désormais une utilité. Les premiers logiciels qui vont mettre en œuvre cette nouvelle version, et mettre certains bits à un, vont fonctionner lors de tests puis, une fois le déploiement sur l'Internet fait, vont avoir des problèmes dans certains cas. Un pare-feu programmé par des incompétents va jeter ces paquets. Dans son code, il y a un test que le champ vaut zéro partout, car le programmeur n'a jamais lu le RFC et il a juste observé que ce champ était toujours nul. Cette observation a été mise dans le code et bonne chance pour la corriger. (Les DSI qui déploient ce pare-feu n'ont pas regardé s'il était programmé correctement, ils ont juste lu la brochure commerciale.) À partir de là, les programmeurs qui mettent en œuvre le protocole en question ont le choix de foncer et d'ignorer les mines, acceptant que leur programme ne marche pas si les paquets ont le malheur de passer par un des pare-feux bogués (et seront donc perdus), ou bien de revenir à l'ancienne version, ce second choix étant l'ossification : on n'ose plus rien changer de peur que ça casse quelque chose quelque part. Comme les utilisateurs ignorants attribueraient sans doute les problèmes de timeout à l'application, et pas au pare-feu mal écrit, le choix des programmeurs est vite fait : on ne déploie pas la nouvelle version. C'est d'autant plus vrai si le protocole est particulièrement critique pour le bon fonctionnement de l'Internet (BGP, DNS…) ou bien s'il y a de nombreux acteurs non coordonnés (cf. section 2.3). Bien sûr, le blocage de certains paquets peut être volontaire mais, bien souvent, il résulte de la négligence et de l'incompétence des auteurs de middleboxes (cf. section 5).

Les exemples de tels problèmes sont innombrables (l'annexe A du RFC en fournit plusieurs). Ainsi, TLS a eu bien des ennuis avec des nouvelles valeurs de l'extension signature_algorithms.

Si le protocole en question était conçu de nos jours, il est probable que ses concepteurs prendraient la précaution de réserver quelques valeurs non nulles pour le champ en question, et de demander aux programmes d'utiliser de temps en temps ces valeurs, pour être sûr qu'elles sont effectivement ignorées. C'est ce qu'on nomme le graissage et c'est une technique puissante (mais pas parfaite) pour éviter la rouille, l'ossification. (Notez que les valeurs utilisées pour le graissage ne doivent pas être consécutives, pour limiter les risques qu'un programmeur de middlebox paresseux ne les teste facilement. Et qu'il faut les réserver pour que, plus tard, l'utilisation de vraies valeurs pour ce champ ne soit pas empêchée par les valeurs de graissage.) Le graissage ne résout pas tous les problèmes puisqu'il y a toujours le risque que les valeurs utilisées pour graisser finissent par bénéficier d'un traitement de faveur, et soient acceptées, alors que les valeurs réelles poseront des problèmes.

On pourrait penser qu'une meilleure conception des protocoles éviterait l'ossification. (C'est le discours des inventeurs géniaux et méconnus qui prétendent que leur solution magique n'a aucun inconvénient et devrait remplacer tout l'Internet demain.) Après tout, le RFC 6709 contient beaucoup d'excellents conseils sur la meilleure façon de concevoir des protocoles pour qu'ils puissent évoluer. Par exemple, il insiste sur le fait que le mécanisme de négociation qui permet aux deux parties de se mettre d'accord sur une option ou une version doit être simple, pour qu'il y ait davantage de chances qu'il soit mis en œuvre correctement dans tous les programmes. En effet, en l'absence de graissage, ce mécanisme ne sera testé en vrai que lorsqu'on introduira une nouvelle version ou une nouvelle option et, alors, il sera trop tard pour modifier les programmes qui n'arrivent pas à négocier ces nouveaux cas, car leur code de négociation est bogué. Le RFC 6709 reconnait ce problème (tant qu'on n'a pas utilisé un mécanisme, on ne sait pas vraiment s'il marche) et reconnait que le conseil d'un mécanisme simple n'est pas suffisant.

Notre RFC 9170 contient d'ailleurs une petite pique contre QUIC en regrettant qu'on repousse parfois à plus tard le mécanisme de négociation de version, pour arriver à publier la norme décrivant le protocole (exactement ce qui est arrivé à QUIC, qui n'a pas de mécanisme de négociation des futures versions). Le problème est que, une fois la norme publiée et le protocole déployé, il sera trop tard…

Bref, l'analyse du RFC (section 3) est qu'il faut utiliser tôt et souvent les mécanismes d'extension. Une option ou un moyen de négocier de nouvelles options qui n'a jamais été utilisé depuis des années est probablement ossifié et ne peut plus être utilisé. C'est très joli de dire dans le premier RFC d'un protocole « cet octet est toujours à zéro dans cette version mais les récepteurs doivent ignorer son contenu car il pourra servir dans une future version » mais l'expérience prouve largement qu'une telle phrase est souvent ignorée et que bien des logiciels planteront, parfois de façon brutale, le jour où on commencer à utiliser des valeurs non nulles. Il faut donc utiliser les mécanismes ou bien ils rouillent. Par exemple, si vous concevez un mécanisme d'extension dans votre protocole, il est bon que, dès le premier jour, au moins une fonction soit mise en œuvre via ce mécanisme, pour forcer les programmeurs à en tenir compte, au lieu de simplement sauter cette section du RFC. Les protocoles qui ajoutent fréquemment des options ou des extensions ont moins de problèmes que ceux qui attendent des années avant d'exploiter leurs mécanismes d'extension. Et plus on attend, plus c'est pire. C'est pour cela que, par exemple, il faut féliciter le RIPE NCC d'avoir tenté l'annonce de l'attribut BGP 99, même si cela a cassé des choses, car si on ne l'avait pas fait (comme l'avaient demandé certaines personnes qui ne comprenaient pas les enjeux), le déploiement de nouveaux attributs dans BGP serait resté quasi-impossible.

Tester tôt est d'autant plus important qu'il peut être crucial, pour la sécurité, qu'on puisse étendre un protocole (par exemple pour l'agilité cryptographique, cf. RFC 7696). L'article de S. Bellovin et E. Rescorla, « Deploying a New Hash Algorithm » montre clairement que les choses ne se passent pas aussi bien qu'elles le devraient.

Comme dit plus haut, la meilleure façon de s'assurer qu'un mécanisme est utilisable est de l'utiliser effectivement. Et pour cela que ce mécanisme soit indispensable au fonctionnement normal du protocole, qu'on ne puisse pas l'ignorer. Le RFC cite l'exemple de SMTP : le principal mécanisme d'extension de SMTP est d'ajouter de nouveaux en-têtes (comme le Archived-At: dans le RFC 5064) or, SMTP dépend d'un traitement de ces en-têtes pour des fonctions mêmes élémentaires. Un MTA ne peut pas se permettre d'ignorer les en-têtes. Ainsi, on est sûr que toute mise en œuvre de SMTP sait analyser les en-têtes. Et, comme des en-têtes nouveaux sont assez fréquemment ajoutés, on sait que des en-têtes inconnus des vieux logiciels ne perturbent pas SMTP. Ce cas est idéal : au lieu d'un mécanisme d'extension qui serait certes spécifié dans le RFC mais pas encore utilisé, on a un mécanisme d'extension dont dépendent des fonctions de base du protocole.

Le RFC cite également le cas de SIP, qui est moins favorable : les relais ne transmettent en général pas les en-têtes inconnus, ce qui ne casse pas la communication, mais empêche de déployer de nouveaux services tant que tous les relais n'ont pas été mis à jour.

Bien sûr, aucune solution n'est parfaite. Si SMTP n'avait pas ajouté de nombreux en-têtes depuis sa création, on aurait peut-être des programmes qui certes savent analyser les en-têtes mais plantent lorsque ces en-têtes ne sont pas dans une liste limitée. D'où l'importance de changer souvent (ici, en ajoutant des en-têtes).

Souvent, les protocoles prévoient un mécanisme de négociation de la version utilisée. On parle de version différente lorsque le protocole a suffisamment changé pour qu'on ne puisse pas interagir avec un vieux logiciel. Un client SMTP récent peut toujours parler à un vieux serveur (au pire, le serveur ignorera les en-têtes trop récents) mais une machine TLS 1.2 ne peut pas parler à une machine TLS 1.3. Dans le cas le plus fréquent, la machine récente doit pouvoir parler les deux versions, et la négociation de version sert justement à savoir quelle version utiliser. Le problème de cette approche est que, quand la version 1 est publiée en RFC, avec son beau mécanisme de négociation de version, il n'y a pas encore de version 2 pour tester que cette négociation se passera bien. Celle-ci ne sortira parfois que des années plus tard et on s'apercevra peut-être à ce moment que certains programmes ont mal mis en œuvre la négociation de version, voire ont tout simplement négligé cette section du RFC…

Première solution à ce problème, utiliser un mécanisme de négociation de version située dans une couche plus basse. Ainsi, IP a un mécanisme de négociation de version dans l'en-tête IP lui-même (les quatre premiers bits indiquent le numéro de version, cf. RFC 8200, section 3) mais ce mécanisme n'a jamais marché en pratique. Ce qui marche, et qui est utilisé, c'est de se servir du mécanisme de la couche 2. Par exemple, pour Ethernet (RFC 2464), c'est l'utilisation du type de protocole (EtherType), 0x800 pour IPv4 et 0x86DD pour IPv6. Un autre exemple est l'utilisation d'ALPN (RFC 7301) pour les protocoles au-dessus de TLS. (Le RFC cite aussi la négociation de contenu de HTTP.)

Une solution récente au problème de l'ossification est le graissage, présenté en section 3.3. Décrit à l'origine pour TLS, dans le RFC 8701, il est désormais utilisé dans d'autres protocoles comme QUIC. Son principe est de réserver un certain nombre de valeurs utilisant des extensions du protocole et de s'en servir, de façon à forcer les différents logiciels, intermédiaires ou terminaux, à lire tout le RFC et à gérer tous les cas. Dans l'exemple cité plus haut d'un protocole qui aurait un champ de huit bits « cet octet est toujours à zéro dans cette version mais les récepteurs doivent ignorer son contenu car il pourra servir dans une future version », on peut réserver les valeurs 3, 21, 90 et 174 comme valeurs de graissage et l'émetteur les mettra de temps en temps dans le champ en question. Dans le cas de TLS, où le client propose des options et le serveur accepte celles qu'il choisit dans ce lot (oui, je sais, TLS est plus compliqué que cela, par exemple lorsque le serveur demande une authentification), le client annonce au hasard des valeurs de graissage. Ainsi, une middlebox qui couperait les connexions TLS serait vite détectée et, on peut l'espérer, rejetée par le marché. Le principe du graissage est donc « les extensions à un protocole s'usent quand on ne s'en sert pas ». Cela évoque les tests de fuzzing qu'on fait en sécurité, où on va essayer plein de valeurs inhabituelles prises au hasard, pour s'assurer que le logiciel ne se laisse pas surprendre. On voit aussi un risque du graissage : si les programmes bogués sont nombreux, le premier qui déploie un mécanisme de graissage va essuyer les plâtres et se faire parfois rejeter. Il est donc préférable que le graissage soit utilisé dès le début, par exemple dans un nouveau protocole.

Évidemment, la solution n'est pas parfaite. On peut imaginer un logiciel mal fait qui reconnait les valeurs utilisées pour le graissage (et les ignore) mais rejette quand même les autres valeurs pourtant légitimes. (La plupart du temps, les valeurs réservées pour le graissage ne sont pas continues, comme dans les valeurs 3, 21, 90 et 174 citées plus haut, pour rendre plus difficile un traitement spécifique au graissage.)

Bien sûr, même sans valeurs réservées au graissage, un programme pourrait toujours faire à peu près l'équivalent, en utilisant les valeurs réservées pour des expérimentations ou bien des usages privés. Mais le risque est alors qu'elles soient acceptées par l'autre partie, ce qui n'est pas le but.

Tous les protocoles n'ont pas le même style d'interaction que TLS, et le graissage n'est donc pas systématiquement possible. Et puis il ne teste qu'une partie des capacités du protocole, donc il ne couvre pas tous les cas possibles. Le graissage est donc utile mais ne résout pas complètement le problème.

Parfois, des protocoles qui ne permettaient pas facilement l'extensibilité ont été assez sérieusement modifiés pour la rendre possible. Ce fut le cas du DNS avec EDNS (RFC 6891). Son déploiement n'a pas été un long fleuve tranquille et, pendant longtemps, les réactions erronées de certains serveurs aux requêtes utilisant EDNS nécessitaient un mécanisme de repli (re-essayer sans EDNS). Il a ensuite fallu supprimer ce mécanisme de repli pour être sûr que les derniers systèmes erronés soient retirés du service, le tout s'étalant sur de nombreuses années. Un gros effort collectif a été nécessaire pour parvenir à ce résultat, facilité, dans le cas du DNS, par le relativement petit nombre d'acteurs et leur étroite collaboration.

La section 4 du RFC cite d'autres techniques qui peuvent être utilisées pour lutter contre l'ossification. D'abord, ne pas avoir trop de possibilités d'étendre le protocole car, dans ce cas, certaines possibilités seront fatalement moins testées que d'autres et donc plus fragiles si on veut s'en servir un jour.

Le RFC suggère aussi l'utilisation d'invariants. Un invariant est une promesse des auteurs du protocole, assurant que cette partie du protocole ne bougera pas (et, au contraire, que tout le reste peut bouger et qu'il ne faut pas compter dessus). Si les auteurs de logiciels lisent les RFC (une supposition audacieuse, notamment pour les auteurs de middleboxes), cela devrait éviter les problèmes avec les évolutions futures. (Il est donc toujours utile de graisser les parties du protocole qui ne sont pas des invariants.) Le RFC 5704, dans sa section 2.2, définit plus rigoureusement ce que sont les invariants. Deux exemples d'utilisation de ce concept sont le RFC 8999 (décrivant les invariants de QUIC) et la section 9.3 du RFC 8446 sur TLS. Notre RFC conseille aussi de préciser explicitement ce qui n'est pas invariant (comme le fait l'annexe A du RFC 8999), ce que je trouve contestable (il y a un risque que cette liste d'exemples de non-invariants soit interprétée comme limitative, alors que ce ne sont que des exemples).

Une autre bonne technique, que recommande notre RFC, est de prendre des mesures techniques pour empêcher les intermédiaires de tripoter la communication. Moins il y a d'entités qui analysent et interprètent les paquets, moins il y aura de programmes à vérifier et éventuellement à modifier en cas d'extension du protocole. Un bon exemple de ces mesures techniques est évidemment la cryptographie. Ainsi, chiffrer le fonctionnement du protocole, comme le fait QUIC, ne sert pas qu'à préserver la vie privée de l'utilisateur, cela sert aussi à tenir à l'écart les intermédiaires, ce qui limite l'ossification. Le RFC 8558 est une bonne lecture à ce sujet.

Bien sûr, si bien conçu que soit le protocole, il y aura des problèmes. Le RFC suggère donc aussi qu'on crée des mécanismes de retour, permettant d'être informés des problèmes. Prenons par exemple un serveur TLS qui refuserait des clients corrects, mais qui utilisent des extensions que le serveur ne gère pas correctement. Si le serveur ne journalise pas ces problèmes, ou bien que l'administrateur système ne lit pas ces journaux, le problème pourra trainer très longtemps. Idéalement, ces retours seront récoltés et traités automatiquement, et envoyés à celles et ceux qui sont en mesure d'agir. C'est plus facile à dire qu'à faire, et cela dépend évidemment du protocole utilisé. Des exemples de protocoles ayant un tel mécanisme sont DMARC (RFC 7489, avec son étiquette rua, section 7), et SMTP (avec le TLSRPT du RFC 8460).

Enfin, pour terminer, l'annexe A du RFC présente quelques exemples de protocoles et comment ils ont géré ce problème. Elle commence par le DNS (RFC 1034 et RFC 1035). Le DNS a une mauvaise expérience avec le déploiement de nouveaux types d'enregistrement qui, par exemple, provoquaient des rejets violents des requêtes DNS par certains équipements. C'est l'une des raisons pour lesquelles SPF (RFC 7208) a finalement renoncé à utiliser son propre type d'enregistrement, SPF (cf. RFC 6686) et pour lesquelles tout le monde se sert de TXT. Le problème s'est heureusement amélioré depuis la parution du RFC 3597, qui normalise le traitement des types inconnus. (Mais il reste d'autres obstacles au déploiement de nouveaux types, comme la mise à jour des interfaces « conviviales » d'avitaillement d'une zone DNS, interfaces qui mettent de nombreuses années à intégrer les nouveaux types.)

HTTP, lui, a plutôt bien marché, question extensibilité mais il a certains mécanismes d'extension que personne n'ose utiliser et qui ne marcheraient probablement pas, comme les extensions des chunks (RFC 7230, section 4.1.1) ou bien comme l'utilisation d'autres unités que les octets dans les requêtes avec intervalles (RFC 7233, section 2.2).

Et IP lui-même ? Par exemple, IP avait un mécanisme pour permettre aux équipements réseau de reconnaitre la version utilisée, permettant donc de faire coexister plusieurs versions d'IP, le champ Version (les quatre premiers bits du paquet). Mais il n'a jamais réellement fonctionné, les équipements utilisant à la place les indications données par la couche inférieure (l'« ethertype », dans le cas d'Ethernet, cf. RFC 2464). D'autres problèmes sont arrivés avec IP, par exemple l'ancienne « classe E ». Le préfixe 224.0.0.0/3 avait été réservé par le RFC 791, section 3.2, mais sans précisions (« The extended addressing mode is undefined. Both of these features are reserved for future use. »). Le RFC 988 avait ensuite pris 224.0.0.0/4 pour le multicast (qui n'a jamais été réellement déployé sur l'Internet), créant la « classe D », le reste devenant la « classe E », 240.0.0.0/4 dont on ne sait plus quoi faire aujourd'hui, même si certains voudraient la récupérer pour prolonger l'agonie d'IPv4. Son traitement spécial par de nombreux logiciels empêche de l'affecter réellement. Une solution souvent utilisée pour changer la signification de tel ou tel champ dans l'en-tête est la négociation entre les deux parties qui communiquent, mais cela ne marche pas pour les adresses (la communication peut être unidirectionnelle).

SNMP n'a pas eu trop de problèmes avec le déploiement de ses versions 2 et 3. La norme de la version 1 précisait clairement (RFC 1157) que les paquets des versions supérieures à ce qu'on savait gérer devaient être ignorés silencieusement, et cela était vraiment fait en pratique. Il a donc été possible de commencer à envoyer des paquets des versions 2, puis 3, sans casser les vieux logiciels qui écoutaient sur le même port.

TCP, par contre, a eu bien des problèmes avec son mécanisme d'extension. (Lire l'article de Honda, M., Nishida, Y., Raiciu, C., Greenhalgh, A., Handley, M., et H. Tokuda, « Is it still possible to extend TCP? ».) En effet, de nombreuses middleboxes se permettaient de regarder l'en-tête TCP sans avoir lu et compris le RFC, jetant des paquets qui leur semblaient anormaux, alors qu'ils avaient juste des options nouvelles. Ainsi, le multipath TCP (RFC 6824) a été difficile à déployer (cf. l'article de Raiciu, C., Paasch, C., Barre, S., Ford, A., Honda, M., Duchêne, F., Bonaventure, O., et M. Handley, «  How Hard Can It Be? Designing and Implementing a Deployable Multipath TCP »). TCP Fast Open (RFC 7413) a eu moins de problèmes sur le chemin, mais davantage avec les machines terminales, qui ne comprenaient pas ce qu'on leur demandait. Comme le but de TCP Fast Open était d'ouvrir une connexion rapidement, ce problème était fatal : on ne peut pas entamer une négociation lorsqu'on veut aller vite.

Et enfin, dernier protocole étudié dans cette annexe A, TLS. Là, la conclusion du RFC est que le mécanisme de négociation et donc d'extension de TLS était correct, mais que la quantité de programmes mal écrits et qui réagissaient mal à ce mécanisme l'a, en pratique, rendu inutilisable. Pour choisir une version de TLS commune aux deux parties qui veulent communiquer de manière sécurisée, la solution était de chercher la plus haute valeur de version commune (HMSV pour highest mutually supported version, cf. RFC 6709, section 4.1). Mais les innombrables bogues dans les terminaux, et dans les middleboxes (qui peuvent accéder à la négociation TLS puisqu'elle est en clair, avant tout chiffrement) ont fait qu'il était difficile d'annoncer une version supérieure sans que les paquets soient rejetés. (Voir l'expérience racontée dans ce message.)

TLS 1.3 (RFC 8446) a donc dû abandonner complètement le mécanisme HMSV et se présenter comme étant du 1.2, pour calmer les middleboxes intolérantes, dissimulant ensuite dans le ClientHello des informations qui permettent aux serveurs 1.3 de reconnaitre un client qui veut faire du 1.3 (ici, vu avec tshark) :

    TLSv1 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 665
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 661
            Version: TLS 1.2 (0x0303)

[Ce champ Version n'est là que pour satisfaire les middleboxes.]
	    
            Extension: supported_versions (len=9)
                Type: supported_versions (43)
                Length: 9
                Supported Versions length: 8
                Supported Version: TLS 1.3 (0x0304)
                Supported Version: TLS 1.2 (0x0303)
                Supported Version: TLS 1.1 (0x0302)
                Supported Version: TLS 1.0 (0x0301)

[La vraie liste des versions acceptées était dans cette extension.]
  

Toujours à propos de TLS, SNI (Server Name Indication, cf. RFC 6066, section 3) est un autre exemple où la conception était bonne mais le manque d'utilisation de certains options a mené à l'ossification et ces options ne sont plus, en pratique, utilisables. Ainsi, SNI permettait de désigner le serveur par son nom de domaine (host_name dans SNI) mais aussi en utilisant d'autres types d'identificateurs (champ name_type) sauf que, en pratique, ces autres types n'ont jamais été utilisés et ne marcheraient probablement pas. (Voir l'article de A. Langley, « Accepting that other SNI name types will never work ».)


Téléchargez le RFC 9170


L'article seul

RFC 9167: Registry Maintenance Notification for the Extensible Provisioning Protocol (EPP)

Date de publication du RFC : Décembre 2021
Auteur(s) du RFC : T. Sattler, R. Carney, J. Kolker (GoDaddy)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 26 décembre 2021


Un registre, par exemple un registre de noms de domaine, utilise parfois le protocole EPP pour la communication avec ses clients. Ce RFC décrit comment utiliser ce protocole pour informer les clients des périodes d'indisponibilité du registre, par exemple lors d'une opération de maintenance.

Aujourd'hui, un registre prévient de ses periodes d'indisponibilité prévues par divers moyens : courriers aux BE, messages sur des réseaux sociaux, page Web dédiée comme : afnic-maintenances.png

Chaque registre le fait de façon différente, il n'existe pas de règles communes, et le côté non-structuré de ces annonces fait qu'il faut une interventon humaine pour les analyser et les mettre dans un agenda. Et un BE peut devoir interagir avec de nombreux registres ! Notre RFC propose d'utiliser EPP (RFC 5730) pour ces annonces.

Donc, premier principe, puisqu'on va souvent manipuler des dates, les dates et heures seront toutes représentées en UTC et dans le format du RFC 3339. Ensuite, les annonces seront dans un élément XML <item>, de l'espace de noms urn:ietf:params:xml:ns:epp:maintenance-1.0 (enregistré à l'IANA). Parmi les sous-éléments de cet élément :

  • id, un identificateur de l'évènement,
  • systems, qui permettra de désigner les systèmes affectés,
  • environment, pour dire si l'évènement concerne la production ou bien un banc de test,
  • start et end, qui indiquent le début et la fin (prévue…) de l'évènement,
  • et plusieurs autres éléments.

Un exemple d'évènement, une intervention sur le serveur EPP epp.registry.example de production, peut être :


<maint:item>
  <maint:id>2e6df9b0-4092-4491-bcc8-9fb2166dcee6</maint:id>
  <maint:systems>
    <maint:system>
      <maint:name>EPP</maint:name>
      <maint:host>epp.registry.example</maint:host>
      <maint:impact>full</maint:impact>
    </maint:system>
  </maint:systems>
  <maint:environment type="production"/>
  <maint:start>2021-12-30T06:00:00Z</maint:start>
  <maint:end>2021-12-30T07:00:00Z</maint:end>
  <maint:reason>planned</maint:reason>
  <maint:detail>
    https://www.registry.example/notice?123
  </maint:detail>
  <maint:tlds>
    <maint:tld>example</maint:tld>
    <maint:tld>test</maint:tld>
  </maint:tlds>
</maint:item>
    
  

On voit que le serveur EPP sera arrêté pendant une heure (<impact>full</impact> indiquant une indisponibilité totale) et que cela affectera les TLD .example et .test. Une telle information, étant sous une forme structurée, peut être analysée par un programme et automatiquement insérée dans un agenda, ou un système de supervision.

Les commandes EPP exactes, maintenant (section 4 du RFC). La commande <info> peut renvoyer maintenant un élément <maint:info> qui contient l'information de maintenance. Voici l'exemple du RFC. D'abord, la question du client, qui veut de l'information sur l'évènement 2e6df9b0-4092-4491-bcc8-9fb2166dcee6 :


<info>
  <maint:info
    xmlns:maint="urn:ietf:params:xml:ns:epp:maintenance-1.0">
    <maint:id>2e6df9b0-4092-4491-bcc8-9fb2166dcee6</maint:id>
  </maint:info>
</info>

  

Puis la réponse du serveur :

    
 <response>
    <result code="1000">
       <msg>Command completed successfully</msg>
    </result>
    <resData>
      <maint:infData
        xmlns:maint="urn:ietf:params:xml:ns:epp:maintenance-1.0">
        <maint:item>
          <maint:id>2e6df9b0-4092-4491-bcc8-9fb2166dcee6
          </maint:id>
          <maint:type lang="en">Routine Maintenance</maint:type>
          <maint:systems>
            <maint:system>
              <maint:name>EPP</maint:name>
              <maint:host>epp.registry.example
              </maint:host>
              <maint:impact>full</maint:impact>
            </maint:system>
          </maint:systems>
          <maint:environment type="production"/>
          <maint:start>2021-12-30T06:00:00Z</maint:start>
          <maint:end>2021-12-30T07:00:00Z</maint:end>
          <maint:reason>planned</maint:reason>
          <maint:detail>
            https://www.registry.example/notice?123
          </maint:detail>
          <maint:description lang="en">free-text
          </maint:description>
          <maint:description lang="de">Freitext
          </maint:description>
          <maint:tlds>
            <maint:tld>example</maint:tld>
            <maint:tld>test</maint:tld>
          </maint:tlds>
          <maint:intervention>
            <maint:connection>false</maint:connection>
            <maint:implementation>false</maint:implementation>
          </maint:intervention>
          <maint:crDate>2021-11-08T22:10:00Z</maint:crDate>
        </maint:item>
      </maint:infData>
    </resData>
    ...

  

Ici, le client connaissait l'identificateur d'une opération de maintenance particulière. S'il ne le connait pas et veut récupérer une liste d'événements :


<info>
  <maint:info
    xmlns:maint="urn:ietf:params:xml:ns:epp:maintenance-1.0">
    <maint:list/>
  </maint:info>
</info>

  

Il récupérera alors une <maint:list>, une liste d'opérations de maintenance.

Le client EPP peut également être prévenu des maintenances par la commande <poll>, qui dote EPP d'un système de messagerie (RFC 5730, section 2.9.2.3). Ainsi, un message dans la boite aux lettres du client pourra être :


<response>
  <result code="1301">
    <msg>Command completed successfully; ack to dequeue</msg>
  </result>
<msgQ count="1" id="12345">
  <qDate>2021-11-08T22:10:00Z</qDate>
  <msg lang="en">Registry Maintenance Notification</msg>
</msgQ>
<resData>
  <maint:infData
    xmlns:maint="urn:ietf:params:xml:ns:epp:maintenance-1.0">
    <maint:item>
      <maint:id>2e6df9b0-4092-4491-bcc8-9fb2166dcee6</maint:id>
      <maint:pollType>create</maint:pollType>
      <maint:systems>
        <maint:system>
          <maint:name>EPP</maint:name>
          <maint:host>epp.registry.example
          </maint:host>
          <maint:impact>full</maint:impact>
...

  

La section 5 du RFC décrit la syntaxe formelle de cette extension (en XML Schema). Elle est dans le registre IANA des extensions à EPP.

Et question mises en œuvre ? Apparemment, les registres gérés par GoDaddy et Tango envoient déjà ces informations de maintenance.


Téléchargez le RFC 9167


L'article seul

RFC 9165: Additional Control Operators for Concise Data Definition Language (CDDL)

Date de publication du RFC : Décembre 2021
Auteur(s) du RFC : C. Bormann (Universität Bremen TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cbor
Première rédaction de cet article le 25 décembre 2021


Le langage CDDL (Concise Data Definition Language) est un langage de description de schémas de données, notamment pour le format CBOR. Ce nouveau RFC étend CDDL avec de nouveaux opérateurs, permettant entre autres l'addition d'entiers et la concaténation de chaines de caractères.

CDDL est normalisé dans le RFC 8610 (et le format CBOR dans le RFC 8949). Il permet l'ajout de nouveaux opérateurs pour étendre le langage, possibilité utilisée par notre nouveau RFC. Notez que, comme les modèles de données de JSON et CBOR sont très proches, les schémas CDDL peuvent également être utilisés pour JSON, ce que je fais ici pour les exemples car le JSON est plus facile à lire et à écrire.

D'abord, l'opérateur .plus. Il permet par exemple, dans la spécification d'un schéma, de faire dépendre certains nombres d'autres nombres. L'exemple ci-dessous définit un type « intervalle » où la borne supérieure doit être supérieure de 5 à la borne inférieure :

    
top = interval<3>

interval<BASE> = BASE .. (BASE .plus 5)

  

Avec un tel schéma, la valeur 4 sera acceptée mais 9 sera refusée :

% cddl tmp.cddl validate tmp.json
CDDL validation failure (nil for 9):
[9, [:range, 3..8, Integer], ""]
  

Deuxième opérateur, la concaténation, avec le nouvel opérateur .cat :

s = "foo" .cat "bar"
  

Dans cet exemple, évidemment, .cat n'est pas très utile, on aurait pu écrire la chaine complète directement. Mais .cat est plus pertinent quand on veut manipuler des chaines contenants des sauts de ligne :

s = "foo" .cat '
 bar
 baz
'
  

Ce schéma acceptera la chaine de caractère "foo\n bar\n baz\n".

Dans l'exemple ci-dessus, bar et baz seront précédés des espaces qui apparaissaient dans le code source. Souvent, on souhaite mettre ces espaces en début de ligne dans le code source, pour l'indenter joliment, mais les supprimer dans le résultat final. Cela peut se faire avec l'opérateur pour lequel notre RFC invente le joli mot de détentation (dedending), .det, qui fonctionne comme .cat mais « dédente » les lignes :

s = "foo" .det '
 bar
 baz
'
  

Cette fois, le schéma n'acceptera que la chaine "foo\nbar\nbaz\n".

Le RFC note que, comme .det est l'abréviation de dedending cat, on aurait pu l'appeler .dedcat mais cela aurait chagriné les amis des chats.

CDDL est souvent utilisé dans les normes techniques de l'Internet et celles-ci contiennent souvent des grammaires en ABNF (RFC 5234). Pour permettre de réutiliser les règles ABNF dans CDDL, et donc se dispenser d'une ennuyeuse traduction, un nouvel opérateur fait son apparition, .abnf. Le RFC donne l'exemple de la grammaire du RFC 3339, qui normalise les formats de date : abnf-rfc3339.cddl. Avec ce fichier, on peut accepter des chaines comme "2021-12-15" ou "2021-12-15T15:52:00Z". Notons qu'il reste quelques difficultés car les règles d'ABNF ne sont pas parfaitement compatibles avec celles de CDDL. Si .abnf va traiter l'ABNF comme de l'Unicode encodé en UTF-8, un autre opérateur, .abnfb, va le traiter comme une bête suite d'octets. D'autre part, comme ABNF exige souvent des sauts de ligne, les opérateurs .cat et .det vont être très utiles.

Quatrième et dernier opérateur introduit par ce RFC, .feature. À quoi sert-il ? Comme le langage CDDL peut ếtre étendu au-delà de ce qui existait dans le RFC 8610, on court toujours le risque de traiter un schéma CDDL avec une mise en œuvre de CDDL qui ne connait pas toutes les fonctions utilisées dans le schéma. Cela produit en général un message d'erreur peu clair et, surtout, cela mènerait à considérer des données comme invalides alors qu'elles sont parfaitement acceptables pour le reste du schéma. .feature sert donc à marquer les extensions qu'on utilise. Le programme qui met en œuvre CDDL pourra ainsi afficher de l'information claire. Par exemple, si on définit une personne :

person = {
     ? name: text
     ? organization: text
}
  

puis qu'on veut rajouter son groupe sanguin :

{"name": "Jean", "bloodgroup": "O+"}
  

Cet objet sera rejeté, en raison du champ bloodgroup. On va faire un schéma plus ouvert, avec .feature :

person = {
     ? name: text
     ? organization: text
     * (text .feature "further-person-extension") => any
}
  

Et, cette fois, l'objet est accepté avec un message d'avertissement clair :

% cddl person-new-feature.cddl validate tmp.json
** Features potentially used (tmp.json): further-person-extension: ["bloodgroup"]
  

Comme le schéma est assez ouvert, la fonction de génération de fichiers d'exemple de l'outil donne des résultats amusants :

% cddl person-new-feature.cddl generate
{"name": "plain", "dependency's": "Kathryn's", "marvelous": "cleavers"}
  

Les nouveaux opérateurs ont été placés dans le registre IANA. Ils sont mis en œuvre dans l'outil de référence de CDDL (le cddl utilisé ici). Écrit en Ruby, on peut l'installer avec la méthode Ruby classique :

% gem install cddl
  

Il existe une autre mise en œuvre de CDDL (qui porte malheureusement le même nom). Elle est en Rust et peut donc s'installer avec :

% cargo install cddl

Elle n'inclut pas encore les opérateurs de ce RFC :

% /home/stephane/.cargo/bin/cddl validate --cddl plus.cddl plus.json  
Validation of "plus.json" failed

error parsing CDDL: error: lexer error
  ┌─ input:8:12
  │
8 │      (BASE .plus 1) => int   ; upper bound
  │            ^^^^^ invalid control operator
  

Mais un travail est en cours pour cela.


Téléchargez le RFC 9165


L'article seul

RFC 9164: Concise Binary Object Representation (CBOR) Tags for IPv4 and IPv6 Addresses

Date de publication du RFC : Décembre 2021
Auteur(s) du RFC : M. Richardson (Sandelman Software Works), C. Bormann (Universität Bremen TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF cbor
Première rédaction de cet article le 14 décembre 2021


Ce nouveau RFC normalise deux étiquettes CBOR pour représenter des adresses IP et des préfixes d'adresses.

Le format de données CBOR, normalisé dans le RFC 8949, a une liste de types prédéfinis mais on peut en créer d'autres, en étiquetant la donnée avec un entier qui permettre de savoir comment interpréter la donnée en question. Notre RFC introduit les étiquettes 52 (pour les adresses IPv4) et 54 (pour les adresses IPv6). Ah, pourquoi 52 et 54 ? Je vous laisse chercher, la solution est à la fin de l'article

La section 3 de notre RFC décrit le format. Pour chaque famille (IPv4 ou IPv6), il y a trois formats (tous avec la même étiquette) :

  • Les adresses à proprement parler, représentées sous forme d'une byte string CBOR (suite d'octets, cf. RFC 8949, section 3.1, type majeur 2) et donc pas sous la forme textuelle (celle avec les points pour IPv4 et les deux-points pour IPv6),
  • Les préfixes, représentés par un tableau de deux éléments, un entier pour la longueur du préfixe et une suite d'octets pour le préfixe lui-même (les octets nuls à la fin doivent être omis),
  • L'interface, sous forme d'un tableau de deux ou trois éléments, qui comporte une adresse IP, la longueur du préfixe sur cette interface et éventuellement un identificateur d'interface (genre eth0 sur Linux, voir la section 6 du RFC 4007 pour IPv6, et les RFC 4001 et RFC 6991 pour IPv4, mais cela peut aussi être un entier), identificateur qui est local à la machine.

La section 5 du RFC contient une description en CDDL (RFC 8610) de ces données.

J'ai écrit une mise en œuvre en Python de ce RFC, qui renvoie à un client HTTP son adresse IP, et le préfixe annoncé dans la DFZ en BGP (en utilisant pour cela les données du RIS, via le programme WhichASN). Le service est accessible à l'adresse https://www.bortzmeyer.org/apps/addresses-in-cbor, par exemple :

% curl -s https://www.bortzmeyer.org/apps/addresses-in-cbor > tmp.cbor

Le CBOR est du binaire, on peut regarde avec le programme read-cbor :

% read-cbor tmp.cbor                               
Array of 3 items
	String of length 165: Your IP address in CBOR [...]
	Tag 54
		Byte string of length 16
	Tag 54
		Array of 2 items
			Unsigned integer 32
			Byte string of length 4

On voit que le service renvoie un tableau CBOR de trois entrées :

  • Une chaine de caractères de documentation,
  • l'adresse IP (ici, de l'IPv6, d'où l'étiquette 54 et les 16 octets),
  • le préfixe routé, sous la forme d'une longueur (ici, 32 bits) et des octets non nuls dudit préfixe (ici, au nombre de 4).

Vu avec le programme cbor2diag, le même fichier :

% cbor2diag.rb tmp.cbor
["Your IP address in CBOR, done with Python 3.9.2 [...]", 
    54(h'200141D0030222000000000000000180'), 
    54([32, h'200141D0'])]

(Le préfixe du client HTTP était en effet bien 2001:41d0::/32.) Le code source de service est dans les sources du moteur de ce blog, plus précisement en wsgis/dispatcher.py.

Sinon, la raison du choix des étiquettes est que, en ASCII, 52 est le chiffre 4 et 54 est 6. Les deux étiquettes sont désormais dans le registre IANA. À noter que la représentation des adresses IP en CBOR avait été faite initialement avec les étiquettes 260 et 261, en utilisant un encodage complètement différent. 260 désignait les adresses (v4 et v6), 261, les préfixes. (Ces deux étiquettes sont marquées comme abandonnées, dans le registre IANA.) Au contraire, dans notre nouveau RFC, l'étiquette identifie la version d'IP, la distinction entre adresse et préfixe se faisant par un éventuel entier initial pour indiquer la longueur.


Téléchargez le RFC 9164


L'article seul

RFC 9162: Certificate Transparency Version 2.0

Date de publication du RFC : Décembre 2021
Auteur(s) du RFC : B. Laurie, A. Langley, E. Kasper, E. Messeri (Google), R. Stradling (Sectigo)
Expérimental
Réalisé dans le cadre du groupe de travail IETF trans
Première rédaction de cet article le 10 décembre 2021


Le système de gestion de certificats PKIX (dérivé des certificats X.509) a une énorme faiblesse. N'importe quelle AC peut émettre un certificat pour n'importe quel nom de domaine. Il ne suffit donc pas de bien choisir son AC, votre sécurité dépend de toutes les AC. Ce RFC propose une approche pour combler cette faille de sécurité : encourager/obliger les AC à publier « au grand jour » les certificats qu'elles émettent. Un titulaire d'un certificat qui craint qu'une AC n'émette un certificat à son nom sans son autorisation n'a alors qu'à surveiller ces publications. (Il peut aussi découvrir à cette occasion que sa propre AC s'est fait pirater ou bien est devenue méchante et émet des certificats qu'on ne lui a pas demandés. L'idée est aussi d'empêcher l'émission « discrète » de vrais/faux certificats qui seraient ensuite utilisés uniquement à certains endroits.) Ce système, dit « Certificate Transparency » (CT) avait initialement été normalisé dans le RFC 6962, que notre RFC remplace, le protocole passant à une nouvelle version, la v2 (toujours considérée comme expérimentale).

Le principe est donc de créer des journaux des certificats émis. Le journal doit être public, pour que n'importe qui puisse l'auditer (section 4 du RFC). Il doit être en mode « ajout seulement » pour éviter qu'on puisse réécrire l'histoire. Les certificats sont déjà signés mais le journal a ses propres signatures, pour prouver son intégrité. Conceptuellement, ce journal est une liste de certificats dans l'ordre de leur création. Toute AC peut y ajouter des certificats (la liste ne peut pas être ouverte en écriture à tous, de crainte qu'elle ne soit remplie rapidement de certificats bidons). En pratique, le RFC estime que la liste des AC autorisées à écrire dans le journal sera l'union des listes des AC acceptées dans les principaux navigateurs Web (voir aussi les sections 4.2 et 5.7, chaque journal est responsable de ce qu'il accepte ou pas comme soumissions).

À chaque insertion, le journal renvoie à l'AC une estampille temporelle signéee (SCT, pour Signed Certificate Timestamp), permettant à l'AC de prouver qu'elle a bien enregistré le certificat. Si on a cette signature mais que le certificat est absent du journal, l'observateur aura la preuve que le journal ne marche pas correctement. Le format exact de cette estampille temporelle est décrit en section 4.8. Idéalement, elle devra être envoyée au client par les serveurs TLS, dans l'extension TLS transparency_info (désormais enregistrée à l'IANA), comme preuve de la bonne foi de l'AC (cf. section 6 et notamment 6.5, car c'est plus compliqué que cela). Bien sûr, cette validation de l'insertion dans un journal ne dispense pas de la validation normale du certificat (un certificat peut être journalisé et mensonger à la fois). Notez aussi que, si le serveur TLS n'envoie pas toutes les données au client, celui-ci peut les demander au journal (opérations /get-proof-by-hash et get-all-by-hash) mais, ce faisant, il informe le journal des certificats qui l'intéressent et donc, par exemple, des sites Web qu'il visite.

De même, une extension à OCSP (RFC 6960) peut être utilisée pour appuyer les réponses OCSP. On peut même inclure les preuves d'inclusion dans le journal dans le certificat lui-même, ce qui permet d'utiliser des serveurs TLS non modifiés.

Les titulaires de certificats importants, comme Google, mais aussi des chercheurs, des agences de sécurité, etc, pourront alors suivre l'activité de ces journaux publics (section 8.2 du RFC). Ce qu'ils feront en cas de détection d'un certificat anormal (portant sur leur nom de domaine, mais qu'ils n'ont pas demandé) n'est pas spécifié dans le RFC : cela dépend de la politique de l'organisation concernée. Ce RFC fournit un mécanisme, son usage n'est pas de son ressort. Ce journal n'empêchera donc pas l'émission de vrais/faux certificats, ni leur usage, mais il les rendra visibles plus facilement et sans doute plus vite.

Notons que les certificats client, eux, ne sont typiquement pas journalisés (rappelez-vous que les journaux sont publics et que les certificats client peuvent contenir des données personnelles). Le serveur TLS ne peut donc pas utiliser Certificate Transparency pour vérifier le certificat du client. (Le RFC estime que le principal risque, de toute façon, est celui d'usurpation du serveur, pas du client.)

Pour que cela fonctionne, il faudra que les clients TLS vérifient que le certificat présenté est bien dans le journal (autrement, le méchant n'aurait qu'à ne pas enregistrer son vrai/faux certificat, cf. section 8.3 du RFC).

En pratique, la réalisation de ce journal utilise un arbre de Merkle, une structure de données qui permet de mettre en œuvre un système où l'ajout de certificats est possible, mais pas leur retrait, puisque chaque nœud est un condensat de ses enfants (voir aussi le RFC 8391). La section 2 du RFC détaille l'utilisation de ces arbres et la cryptographie utilisée. (Et les exemples en section 2.1.5 aident bien à comprendre comment ces arbres de Merkle sont utilisés.)

Le protocole utilisé entre les AC et le journal, comme celui utilisé entre les clients TLS et le journal, est HTTP et le format des données du JSON (section 5, qui décrit l'API). Ainsi, pour ajouter un certificat nouvellement émis au journal géré sur sunlight-log.example.net, l'AC fera :

POST https://sunlight-log.example.net/ct/v2/submit-entry

et le corps de la requête HTTP sera un tableau JSON de certificats encodés en Base64. La réponse contiendra notamment l'estampille temporelle (SCT pour Signed Certificate Timestamp). S'il y a un problème, le client recevra une des erreurs enregistrées. Pour récupérer des certificats, le programme de surveillance fera par exemple :

GET https://sunlight-log.example.net/ct/v2/get-entries

D'autres URL permettront de récupérer les condensats cryptographiques contenus dans l'arbre de Merkle, pour s'assurer qu'il est cohérent.

Comme il n'existe (en octobre 2021) aucune mise en œuvre de la version 2 du protocole, voici quelques exemples, utilisant des journaux réels, et la version 1 du protocole (notez le v1 dans l'URL). Pour trouver les coordonnées des journaux, j'ai utilisé la liste « officielle » du projet. Notez que tous les journaux qui y figurent ne fonctionnent pas correctement. Notez aussi que, comme pour les AC ou les serveurs de clés PGP, il n'y a pas de « journal de référence », c'est à chacun de choisir les journaux où il va écrire, et qu'il va lire. Le script test-ct-logs-v1.py teste la liste, et trouve :

50 logs are OK, 54 are currently broken
  

Si vous vous demandez pourquoi un même opérateur a plusieurs journaux, c'est en partie parce qu'il n'est pas possible de faire évoluer les algorithmes cryptographiques au sein d'un même journal (cf. section 9 du RFC) et qu'il faut donc de temps en temps créer un nouveau journal. Un journal est identifié par son URL, sa clé publique et (en v2) par son OID. Par exemple, le journal « Nimbus 2021 » de Cloudflare est en https://ct.cloudflare.com/logs/nimbus2021/ et a la clé MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExpon7ipsqehIeU1bmpog9TFo4Pk8+9oN8OYHl1Q2JGVXnkVFnuuvPgSo2Ep+6vLffNLcmEbxOucz03sFiematg== (je ne donne pas l'OID, il n'existe pas encore de journaux qui utilisent la version 2 du protocole). Voici un exemple d'utilisation (le STH est le Signed Tree Head, la valeur de la racine de l'arbre de Merkle, cf. section 4.2.10) :

% curl -s https://ct.cloudflare.com/logs/nimbus2021/ct/v1/get-sth | jq .                   
{
  "tree_size": 408013312,
  "timestamp": 1634739692384,
  "sha256_root_hash": "7GnGjI7L6O5fn8kQKTdJG2riShNTTbcjRP2WbLoZrvQ=",
  "tree_head_signature": "BAMARjBEAiAQ0gb6udc9e28ykUGUzl0HV8U5NlJhPVSTUF4JtXGSeQIgcSbZ9kRgttGzpFETFem4eCv7GgUYPUUnl7lTGGFZSHM="
}
  

Plus de quatre cents millions de certificats, fichtre. Si on veut récupérer les deux premiers certificats journalisés :

% curl -s https://ct.cloudflare.com/logs/nimbus2021/ct/v1/get-entries\?start=0\&end=1 | jq .
{
  "entries": [
    {
      "leaf_input":
    [L'exemple est fait avec un journal v1, l'objet JSON renvoyé est
    différent en v2.]
  

Mais vous pouvez aussi utiliser Certificate Transparency (CT) sans aller regarder du JSON. Un service en ligne comme https://crt.sh vous permet de scruter un journal. Voici par exemple l'actuel certificat de ce blog, ou bien tous les certificats au nom de la Banque Postale (CT est utile pour le renseignement).

On a vu que plusieurs acteurs intervenaient, le gérant du journal, les AC, les gens qui regardent le journal, par exemple pour l'auditer, etc. Une utilisation courante de CT est pour surveiller l'émission de certificats au nom de son entreprise ou de son organisation, pour repérer les AC qui créent des certificats incorrects. Pour éviter de programmer tout cela de zéro en partant du RFC, on peut utiliser le service Certstream, qui sert d'intermédiaire avec plusieurs journaux, et sa bibliothèque Python. Ainsi, le petit script test-certstream.py permet de détecter tous les certificats émis pour les noms de domaine en .fr :

% pip3 install certstream
  
% ./test-certstream.py
...
[2021-10-23T13:21:46] pimpmydrone.fr (SAN: www.pimpmydrone.fr)
[2021-10-23T13:21:51] pascal-goldbach.fr (SAN: www.pascal-goldbach.fr)
[2021-10-23T13:21:52] leginkobiloba.fr (SAN: www.leginkobiloba.fr)
[2021-10-23T13:21:52] promabat-distribution.fr (SAN: www.promabat-distribution.fr)
[2021-10-23T13:21:53] maevakaliciak.fr (SAN: mail.maevakaliciak.fr, www.maevakaliciak.fr)
[2021-10-23T13:21:55] pascal-goldbach.fr (SAN: www.pascal-goldbach.fr)
[2021-10-23T13:21:56] maevakaliciak.fr (SAN: mail.maevakaliciak.fr, www.maevakaliciak.fr)
[2021-10-23T13:21:57] blog.nicolas-buffart.itval.fr (SAN: euromillions-generator.itval.fr, itval.fr, loto-generator.itval.fr, password-generator.itval.fr, www.blog.nicolas-buffart.itval.fr, www.euromillions-generator.itval.fr, www.itval.fr, www.loto-generator.itval.fr, www.password-generator.itval.fr)
...

Bien sûr, cela fait beaucoup (regardez les intervalles entre deux messages). En pratique, on modifierait sans doute ce script pour ne regarder que les noms de son organisation. Ainsi, vous pouvez détecter les certificats et chercher ensuite s'ils sont légitimes (ce qui, dans certaines organisations très cloisonnées n'ira pas de soi…).

À part Certstream, Samuel Bizien Filippi me suggère CertSpotter mais qui me semble uniquement payant. Il a une API. Elle peut être utilisée par le programme check_ct_logs, qui peut être utilisé comme script de test pour les programmes de supervision comme Icinga.

Le projet « Certificate Transparency » (largement impulsé par Google) a un site officiel (lecture recommandée) et, une liste de diffusion (sans compter le groupe de travail IETF TRANS, mais qui se limitait à la normalisation, il ne parle pas des aspects opérationnels, et il a de toute façon été clos en août 2021). Questions logiciels, si vous voulez créer votre propre journal, il y a le programme de Google.

Aujourd'hui, on peut dire que « Certificate Transparency » est un grand succès. La plupart (voire toutes ?) des AC y participent, il existe de nombreux journaux publics, et ils sont fréquemment utilisés pour l'investigation numérique (voire pour le renseignement, puisqu'on peut savoir, via les journaux, les noms de domaine pas encore annoncés, ce qui a parfois été cité comme une objection contre CT). Un bon exemple est celui de l'attaque « moyen-orientale » de 2018 (mais il y a aussi l'affaire du certificat révoqué de la Poste). Par contre, un client TLS ne peut pas encore être certain que tous les certificats seront dans ces journaux, et ne peut donc pas encore refuser les serveurs qui ne signalent pas la journalisation du certificat. Et le navigateur Firefox ne teste pas encore la présence des certificats dans le sjournaux.

Un point amusant : le concept de « Certificate Transparency » montre qu'il est parfaitement possible d'avoir un livre des opérations publiquement vérifiable sans chaine de blocs. La chaine de blocs reste nécessaire quand on veut autoriser l'écriture (et pas juste la lecture) au public.

La section 1.3 du RFC résume les principaux changements entre les versions 1 (RFC 6962) et 2 du protocole :

  • Les algorithmes cryptographiques utilisés sont désormais dans des registres IANA,
  • certains échanges utilisent désormais le format CMS (RFC 5652),
  • les journaux qui étaient auparavant idenfiés par un condensat de leur clé publique le sont désormais par un OID, enregistrés à l'IANA (aucun n'est encore attribué),
  • nouvelle fonction get-all-by-hash dans l'API,
  • remplacement de l'ancienne extension TLS signed_certificate_timestamp (valeur 18) par transparency_info (valeur 52, et voir aussi le nouvel en-tête HTTP Expect-CT: du RFC 9163,
  • et d'autres changements, dans les structures de données utilisées.

CT ne change pas de statut avec la version 2 : il est toujours classé par l'IETF comme « Expérimental » (bien que largement déployé). La sortie de cette v2 n'est pas allée sans mal (le premier document étant sorti en février 2014), avec par exemple aucune activité du groupe pendant la deuxième moitié de 2020.

Une des plus chaudes discussions pour cette v2 avait été la proposition de changer l'API pour que les requêtes, au lieu d'aller à <BASE-URL>/ct/v2/ partent du chemin /.well-known/ du RFC 8615. Cette idée a finalement été rejetée, malgré le RFC 8820, qui s'oppose à cette idée de chemins d'URL en dur.


Téléchargez le RFC 9162


L'article seul

RFC 9157: Revised IANA Considerations for DNSSEC

Date de publication du RFC : Novembre 2021
Auteur(s) du RFC : 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 1 décembre 2021


Rien de très grave dans ce nouveau RFC, qui règle un problème surtout bureaucratique, le fait que les politiques d'inclusion dans les registres IANA pour certains algorithmes utilisés par DNSSEC n'étaient pas parfaitement alignées.

En effet, le RFC 6014 avait modifié la politique d'enregistrement des algorithmes cryptographiques de « Action de normalisation » à la plus laxiste « RFC nécessaire » (rappelez-vous que tous les RFC ne sont pas des normes, voir le RFC 8126 qui décrit ces politiques possibles). Mais cette « libéralisation » laissait de côté certains algorithmes, ceux utilisés pour les enregistrements DS (RFC 4034), et ceux utilisés pour NSEC3 (RFC 5155), qui restaient en « Action de normalisation ». Notre nouveau RFC aligne les politiques d'enregistrement des algorithmes utilisés pour les DS et pour NSEC3 pour qu'ils soient eux aussi « RFC nécessaire ».

Il modifie également le RFC 8624 pour préciser que les algorithmes normalisés dans des RFC qui ne sont pas sur le chemin des normes sont également couverts par les règles du RFC 8624 ; en gros, ils sont facultatifs (MAY dans le langage du RFC 2119).

Les registres concernés sur celui sur NSEC3 et celui sur DS. Ils portent désormais la mention RFC Required.

Comme l'enregistrement d'algorithmes va, du fait de ce RFC, être plus léger, cela facilitera l'enregistrement de bons algorithmes, mais aussi de mauvais. Le programmeur qui met en œuvre DNSSEC, ou l'administratrice système qui le déploie, ne doit donc pas considérer que la présence dans un registre IANA vaut forcément approbation de la solidité cryptographique de l'algorithme. Il faut consulter la littérature technique avant d'utiliser ces algorithmes.


Téléchargez le RFC 9157


L'article seul

RFC 9156: DNS Query Name Minimisation to Improve Privacy

Date de publication du RFC : Novembre 2021
Auteur(s) du RFC : S. Bortzmeyer (AFNIC), R. Dolmans (NLnet Labs), 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 novembre 2021


Protéger la vie privée sur l'Internet nécessite au moins deux techniques : chiffrer les données en transit pour éviter leur lecture par des tiers et minimiser les données qu'on envoie, pour éviter les abus par les récepteurs des données. Ce deuxième point, pourtant bien mis en avant dans la loi Informatique & Libertés ou dans le RGPD est souvent oublié. Ce RFC applique ce principe au DNS : il ne faut pas envoyer aux serveurs faisant autorité le nom de domaine complet mais seulement la partie du nom de domaine qui lui est strictement nécessaire pour répondre, le minimum. Cette norme succède au RFC 7816, qui était purement expérimental alors que cette minimisation de la requête (QNAME minimisation) est désormais une norme. Le principal changement est la recommandation d'utiliser le type de données A (adresse IPv4) et plus NS (serveurs de noms).

Ce principe de minimisation, qui devrait être central dans toute approche de la protection de la vie privée est également exposé dans le RFC 6973, section 6.1. Le DNS violait ce principe puisque, traditionnellement, un résolveur DNS qui recevait une demande d'information sur www.foobar.example transmettait aux serveurs faisant autorité la question complète, alors que, par exemple, les serveurs faisant autorité pour la racine ne connaissent que les TLD et que leur demander simplement des informations sur le TLD .example aurait suffi. (Voir le RFC 7626 pour une analyse complète des relations entre le DNS et la vie privée.) Cette tradition (qui ne s'appuyait sur aucune norme technique) est remise en cause par la QNAME minimisation qui demande au contraire qu'on n'envoie aux serveurs faisant autorité que le nom minimal (example à la racine, foobar.example aux serveurs du TLD .example, etc).

Cette minimisation est unilatérale, elle ne nécessite qu'un changement des résolveurs, sans toucher aux serveurs faisant autorité puisqu'elle ne change pas le protocole DNS. Depuis la sortie du RFC 7816, en 2016, elle a été largement déployée (si le résolveur que vous utilisez ne le fait pas, réclamez-le à votre service informatique !).

Le précédent RFC sur cette technique, le RFC 7816 avait le statut d'expérimentation alors que notre RFC 9156 est désormais une norme. En effet, une expérience considérable a été accumulée depuis le RFC 7816, qui a été mis en œuvre dans pratiquement tous les résolveurs, et souvent activé. Le FUD souvent entendu comme quoi la QNAME minimisation allait tuer Internet et des chatons a été largement réfuté. Les leçons tirées sont documentées dans « DNSThought QNAME minimisation results. Using Atlas probes », « Maximizing Qname Minimization: A New Chapter in DNS Protocol Evolution », « Measuring Query Name Minimization » et « A First Look at QNAME Minimization in the Domain Name System ».

Maintenant, la pratique, comment fait-on de la QNAME minimisation ? La question envoyée par le résolveur au serveur faisant autorité comprend un QNAME (Query Name, le nom demandé) et un QTYPE (Query Type, le type de données, par exemple serveur de courrier, adresse IP, texte libre, etc). Avec la QNAME minimisation, le nom doit être le nom le plus court possible. Quand le résolveur interroge un serveur racine, il n'envoie comme QNAME que le TLD, par exemple. Trouver « le plus court possible » n'est pas forcément trivial en raison des coupures de zone. Dans un nom comme miaou.foo.bar.example, foo.bar.example et bar.example font peut-être partie de la même zone (et ont donc les mêmes serveurs faisant autorité) et peut-être pas. Rien dans la syntaxe du nom ne l'indique. Contrairement à une idée fausse et répandue, il n'y a pas forcément une coupure de zone pour chaque point dans le nom. Trouver les coupures de zone est expliqué dans le RFC 2181, section 6. Un résolveur qui valide avec DNSSEC doit savoir trouver ces coupures, pour savoir à qui demander les enregistrements de type DS. Les autres (mais quelle idée, en 2021, d'avoir un résolveur qui ne valide pas) doivent s'y mettre. Si, par exemple, foo.bar.example et bar.example sont dans la même zone, le résolveur qui veut trouver des données associées à miaou.foo.bar.example va envoyer le QNAME example à la racine, puis bar.example au serveur du TLD, puis miaou.foo.bar.example au serveur de bar.example. (Avant la QNAME minimisation, il aurait envoyé le QNAME miaou.foo.bar.example à tout le monde.)

Cela, c'était pour le QNAME. Et le QTYPE ? On peut choisir celui qu'on veut (à l'exception de ceux qui ne sont pas dans la zone, comme le DS), puisque les délégations de zones ne dépendent pas du type. Mais, et c'est un sérieux changement depuis le RFC 7816, notre RFC recommande le type A (ou AAAA), celui des adresses IP, et plus le type NS (les serveurs de noms), que recommandait le RFC 7816. Deux raisons à ce changement :

  • Certaines middleboxes boguées jettent les questions DNS portant sur des types qu'elles ne connaissent pas. Le type A passe à coup sûr.
  • Le but de la QNAME minimisation étant la protection de la vie privée, il vaut mieux ne pas se distinguer et, notamment, ne pas dire franchement qu'on fait de la QNAME minimisation (les requêtes explicites pour le type NS étaient rares). Un serveur faisant autorité, ou un surveillant qui espionne le trafic, ne peut donc pas déterminer facilement si un client fait de la QNAME minimisation.

Vous voyez ici le schéma de la résolution DNS sans la QNAME minimisation puis avec : resolution-dns-sans-qnamemin resolution-dns-avec-qnamemin

Dans certains cas, la QNAME minimisation peut augmenter le nombre de requêtes DNS envoyées par le résolveur. Si un nom comporte dix composants (ce qui arrive dans des domaines ip6.arpa), il faudra dans certains cas dix requêtes au lieu de deux ou trois. Les RFC 8020 et RFC 8198 peuvent aider à diminuer ce nombre, en permettant la synthèse de réponses par le résolveur. Une autre solution est de ne pas ajouter un composant après l'autre en cherchant le serveur faisant autorité mais d'en mettre plusieurs d'un coup, surtout après les quatre premiers composants.

Un algorithme complet pour gérer la QNAME minimisation figure dans la section 3 du RFC.

Notez que, si vous voulez voir si votre résolveur fait de la QNAME minimisation, vous pouvez utiliser tcpdump pour voir les questions qu'il pose mais il y a une solution plus simple, la page Web de l'OARC (dans les DNS features).

Un test avec les sondes RIPE Atlas semble indiquer que la QNAME minimisation est aujourd'hui largement répandue (les deux tiers des résolveurs utilisés par ces sondes) :

% blaeu-resolve --requested 1000 --type TXT   qnamemintest.internet.nl 
["hooray - qname minimisation is enabled on your resolver :)!"] : 651 occurrences 
["no - qname minimisation is not enabled on your resolver :("] : 343 occurrences 
Test #33178767 done at 2021-11-05T14:41:02Z
  

Il existe aussi une étude récente sur la QNAME minimization en République Tchèque.

Comme son prédécesseur, ce RFC utilise (prétend Verisign) un brevet. Comme la plupart des brevets logiciels, il n'est pas fondé sur une réelle invention (la QNAME minimisation était connue bien avant le brevet).

Ah, et vous noterez que le développement de ce RFC, par trois auteurs, a été fait sur FramaGit.


Téléchargez le RFC 9156


L'article seul

RFC 9155: Deprecating MD5 and SHA-1 signature hashes in (D)TLS 1.2

Date de publication du RFC : Décembre 2021
Auteur(s) du RFC : L.V. Velvindron (cyberstorm.mu), K.M. Moriarty (CIS), A.G. Ghedini (Cloudflare)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 20 décembre 2021


Vous le savez certainement déjà, car toutes les lectrices et tous les lecteurs de ce blog sont très attentif·ves et informé·es, mais les algorithmes de condensation MD5 et SHA-1 ont des failles connues et ne doivent pas être utilisés dans le cadre de signatures. Vous le savez, mais tout le monde ne le sait pas, ou bien certain·es ont besoin d'un document « officiel » pour agir donc, le voici : notre RFC dit qu'on ne doit plus utiliser MD5 et SHA-1 dans TLS.

Si vous voulez savoir pourquoi ces algorithmes sont mauvais, le RFC 6151 vous renseignera (et la section 1 de notre RFC 9155 vous donnera une bibliographie récente).

La section 2 à 5 sont le cœur du RFC et elle est sont très simples : pas de MD5, ni de SHA-1 pour les signatures. Dans le registre IANA, ces algorithmes sont désormais marqués comme déconseillés.

Les fanas de cryptographie noteront qu'on peut toujours utiliser SHA-1 pour HMAC (où ses faiblesses connues n'ont pas de conséquences).


Téléchargez le RFC 9155


L'article seul

RFC 9154: Extensible Provisioning Protocol (EPP) Secure Authorization Information for Transfer

Date de publication du RFC : Décembre 2021
Auteur(s) du RFC : J. Gould, R. Wilhelm (VeriSign)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF regext
Première rédaction de cet article le 31 décembre 2021


Le protocole EPP d'avitaillement des noms de domaine permet, entre autres opérations, de transférer un domaine d'un client (typiquement un BE) à un autre. Cette opération ouvre de sérieux problèmes de sécurité, le transfert pouvant être utilisé pour détourner un nom de domaine. En général, la sécurisation de ce transfert est faite par un mot de passe stocké en clair. Notre RFC décrit une méthode pour gérer ces mots de passe qui évite ce stockage, et qui gêne sérieusement les transferts malveillants.

EPP est normalisé dans le RFC 5730 et c'est ce document qui décrit le cadre général d'autorisation d'un transfert de domaines. La forme exacte que prend l'autorisation dépend du type d'objets qu'on gère avec EPP. Pour les domaines (RFC 5731), c'est un élément <authInfo>, qui peut contenir divers types de sous-élements mais le plus fréquent est un simple mot de passe (parfois appelé « code de transfert » ou « code d'autorisation », ou simplement « authinfo »), comme dans cet exemple, une réponse à une commande EPP <info>, où le mot de passe est « 2fooBAR » :

    
<domain:infData>
     <domain:name>example.com</domain:name>
...
     <domain:crDate>1999-04-03T22:00:00.0Z</domain:crDate>
...
     <domain:authInfo>
       <domain:pw>2fooBAR</domain:pw>
     </domain:authInfo>
</domain:infData>

  

L'utilisation typique de ce mot de passe est que le client (en général un BE) le crée, le stocke en clair, l'envoie au serveur, qui le stocke. Lors d'un transfert légitime, le BE gagnant recevra ce mot de passe (typiquement via le titulaire de l'objet, ici un nom de domaine) et le transmettra au registre (RFC 5731, section 3.2.4). Ici, le BE gagnant demande à son client le code d'autorisation qu'il a normalement obtenu via le BE perdant (si le client est vraiment le titulaire légitime) : gandi-winning-registrar.png Et ici le BE perdant indique à son client le code d'autorisation : gandi-losing-registrar.png (registar = BE)

Notez que la façon d'autoriser les transferts, et d'accéder aux informations d'autorisation, dépend de la politique du registre. Les RFC sur EPP normalisent la technique mais pas la politique. Par exemple, lorsqu'un BE demande un transfert sans fournir d'information d'autorisation, certains registres refusent immédiatement le transfert, tandis que d'autres le mettent en attente d'une acceptation ou d'un refus explicite. De même, certains registres permettent de récupérer l'information d'autorisation, comme dans l'exemple ci-dessus, alors que d'autres (comme CentralNic) refusent.

Même chose pour d'autres types d'objets comme les contacts (RFC 5733) même si en pratique la pratique du transfert est plus rare pour ces types. Le mot de passe étant typiquement stocké en clair chez le client, pour pouvoir être donné en cas de transfert, on voit les risques que cela pose en cas d'accès à la base du BE. Aujourd'hui, stocker un mot de passe en clair est nettement considéré comme une mauvaise pratique de sécurité.

À la place, notre RFC décrit, non pas une modification du protocole EPP, mais une nouvelle procédure, une façon créative de se servir du protocole existant pour gérer ces informations d'autorisation de manière plus sérieuse : l'objet (par exemple le nom de domaine) est créé sans information d'autorisation, le serveur EPP (par exemple le registre de noms de domaine) doit refuser le transfert si cette information est manquante. Lors d'un transfert légitime, le client (par exemple un BE) perdant va générer un mot de passe, le transmettre au registre et à son client (typiquement le titulaire du nom de domaine) et ne pas le stocker. Le registre stockera le mot de passe uniquement sous forme condensée et, lorsqu'il recevra la demande de transfert accompagnée d'un mot de passe, il pourra condenser ce mot et vérifier qu'il correspond bien à celui stocké. Le mot ne servira qu'une fois et l'information d'autorisation est détruite après le succès du transfert. Tout ceci ne nécessite pas de modification du protocole, mais, dans certains cas, une modification des pratiques des différents acteurs (par exemple, le serveur EPP doit accepter qu'un objet soit créé sans information d'autorisation, et doit considérer que cela vaut refus de tout transfert).

Le RFC note que la norme EPP ne décrit, logiquement, que le protocole, c'est-à-dire l'interaction entre les deux machines, mais pas ce que fait chaque machine de son côté. Ainsi, la nécessité de stocker les mots de passe de manière sécurisée n'est pas imposée par EPP (mais est néanmoins une bonne pratique).

D'autre part, EPP ne prévoit pas explicitement de durée de vie pour les mots de passe (mais n'interdit pas non plus de les supprimer au bout d'un temps donné, ce qui va être justement la technique de notre RFC).

Petite révision sur les acteurs de l'avitaillement de noms de domaine en section 2 du RFC. La norme EPP parle de client et de serveur, notions techniques, mais du point de vue business, il y a trois acteurs (cf. la terminologie dans le RFC 8499), le registre (qui gère le serveur EPP), le bureau d'enregistrement (BE, qui gère le client EPP) et le titulaire (qui se connecte à son BE via une interface Web ou une API). Dans beaucoup de domaines d'enregistrement, il n'y a aucun lien direct entre le titulaire et le registre, tout devant passer par le BE.

Maintenant, place à la description de la nouvelle manière de faire des transferts. Bien qu'elle ne change pas le protocole, qu'elle ne soit qu'une nouvelle façon d'utiliser ce qui existe déjà dans EPP, elle doit se signaler lors de la connexion EPP, avec l'espace de noms urn:ietf:params:xml:ns:epp:secure-authinfo-transfer-1.0 (enregistré à l'IANA). En effet, la nouvelle manière a besoin que le serveur accepte des choses qui sont autorisées par EPP mais pas obligatoires, notamment :

  • une information d'autorisation vide (représentée par exemple par le NULL dans une base SQL),
  • la possibilité de supprimer l'information d'autorisation avec la commande EPP <update>,
  • la possibilité de valider l'information d'autorisation avec la commande EPP <info>,
  • le refus de tout transfert si l'information d'autorisation est vide,
  • la remise à zéro de l'information d'autorisation lorsque le transfert a été réalisé (mot de passe à usage unique).

Le serveur, lui, en recevant urn:ietf:params:xml:ns:epp:secure-authinfo-transfer-1.0, peut compter que le client EPP saura :

  • générer une information d'autorisation forte (aléatoire, par exemple),
  • ne le faire que lorsqu'un transfert est demandé.

L'autorisation d'information dans l'élement XML <domain:pw> (RFC 5731, section 3.2.4) est un mot de passe qui doit être difficile à deviner par un attaquant. Idéalement, il doit être aléatoire ou équivalent (RFC 4086). Le RFC calcule que pour avoir 128 bits d'entropie, avec uniquement les caractères ASCII imprimables, il faut environ 20 caractères.

Pour compenser l'absence de la notion de durée de vie de l'information d'autorisation dans EPP, le client ne doit définir une information d'autorisation que lorsqu'un transfert est demandé, et supprimer cette information ensuite. La plupart du temps, le domaine n'aura pas d'information d'autorisation, et les transferts seront donc refusés.

L'information d'autorisation, comme tout mot de passe, ne doit plus être stockée en clair, mais sous forme d'un condensat. Le BE perdant ne doit pas la stocker (il la génère, la passe au titulaire et l'oublie ensuite). Le BE gagnant ne doit la stocker que le temps de finaliser le transfert. Évidemment, toute la communication EPP doit être chiffrée (RFC 5734). Lors d'une demande de transfert, le registre va vérifier qu'un condensat de l'information d'autorisation transmise par le BE gagnant correspond à ce que le BE perdant avait envoyé. L'information vide est un cas particulier, le registre ne doit pas tester l'égalité mais rejeter le transfert.

La section 4 explique en détail le processus de transfert avec cette nouvelle méthode :

  • Quand le domaine est créé, l'information d'autorisation est vide (pas de « authinfo »),
  • quand le titulaire veut transférer le nom à un nouveau BE, il demande au BE perdant l'information d'autorisation,
  • le BE perdant génère un mot de passe, qu'il transmet au titulaire et au registre (qui peut répondre avec le code d'erreur EPP 2202 si ce mot de passe ne lui semble pas assez fort), puis qu'il oublie aussitôt,
  • le titulaire donne le mot de passe au BE gagnant,
  • le BE gagnant demande le transfert au registre, en fournissant le mot de passe, ce qui permet au transfert d'être accepté immédiatement,
  • le registre (ou bien le BE perdant), efface le mot de passe.

Voici en EPP quelques messages pour réaliser ces différentes opérations. D'abord, la création d'un nom (notez le mot de passe vide) :


<create>
  <domain:create
    xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
    <domain:name>example.test</domain:name>
    <domain:authInfo>
      <domain:pw/>
    </domain:authInfo>
  </domain:create>
</create>

  

Ici, la mise à jour de l'information d'autorisation par le BE perdant, lorsque le titulaire lui a annoncé le départ du domaine ; le mot de passe est LuQ7Bu@w9?%+_HK3cayg$55$LSft3MPP (le RFC rappelle fortement l'importance de générer un mot de passe fort, par exemple en utilisant des sources bien aléatoires, comme documenté dans le RFC 4086) :


<update>
  <domain:update
    xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
    <domain:name>example.test</domain:name>
    <domain:chg>
      <domain:authInfo>
        <domain:pw>LuQ7Bu@w9?%+_HK3cayg$55$LSft3MPP</domain:pw>
      </domain:authInfo>
    </domain:chg>
  </domain:update>
</update>

    

Le BE perdant devra peut-être également supprimer l'état clientTransferProhibited, si le domaine était protégé contre les transferts.

Le BE gagnant peut également vérifier l'information d'autorisation sans déclencher un transfert, avec une requête <info>, qui lui renverra l'information d'autorisation. Pour plusieurs exemples par la suite, j'ai utilisé le logiciel Cocca. Cocca, par défaut, ne stocke pas l'autorisation d'information en clair et ne peut donc pas la renvoyer. Ou bien le client EPP peut envoyer une commande <info> en indiquant l'information d'autorisation. S'il obtient une erreur EPP 2202 (RFC 5730, section 3), c'est que cette information n'était pas correcte. Ici, la réponse EPP de Cocca lorsqu'on lui envoie un <info> avec information d'autorisation correcte :


Client : <info xmlns="urn:ietf:params:xml:ns:epp-1.0"><info xmlns="urn:ietf:params:xml:ns:domain-1.0"><name>foobar.test</name><authInfo xmlns="urn:ietf:params:xml:ns:domain-1.0"><pw xmlns="urn:ietf:params:xml:ns:domain-1.0">tropfort1298</pw></authInfo></info></info>

Serveur : ... <ns1:authInfo><ns1:pw>Authinfo Correct</ns1:pw></ns1:authInfo> ...

  

Et si cette information est incorrecte :


Serveur : ... <ns1:authInfo><ns1:pw>Authinfo Incorrect</ns1:pw></ns1:authInfo>

  

(Mais Cocca répond quand même avec un code EPP 1000, ce qui n'est pas correct.)

Et enfin, bien sûr, voici la demande de transfert elle-même :


<transfer op="request">
  <domain:transfer
    xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
    <domain:name>example1.com</domain:name>
    <domain:authInfo>
      <domain:pw>LuQ7Bu@w9?%+_HK3cayg$55$LSft3MPP</domain:pw>
    </domain:authInfo>
  </domain:transfer>
</transfer>

  

Et si c'est bon :

    
<ns0:response xmlns:ns0="urn:ietf:params:xml:ns:epp-1.0" xmlns:ns1="urn:ietf:params:xml:ns:domain-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ns0:result code="1000"><ns0:msg>Command completed successfully</ns0:msg></ns0:result><ns0:msgQ count="2" id="3" />
<ns1:trStatus>serverApproved</ns1:trStatus> ...

  

Et avec une mauvaise information d'autorisation :


<ns0:response xmlns:ns0="urn:ietf:params:xml:ns:epp-1.0"><ns0:result code="2202"><ns0:msg>Invalid authorization information; (T07) Auth Info Password incorrect</ns0:msg></ns0:result>...

  

La section 6 du RFC décrit le problème de la transition depuis l'ancien modèle d'autorisation vers le nouveau. Notez que certains registres peuvent avoir une partie du nouveau système déjà en place. Le registre qui désire transitionner doit d'abord s'assurer que l'information d'autorisation absente ou vide équivaut à un rejet. Il doit ensuite permettre aux BE de mettre une information d'autorisation vide, permettre que la commande <info> puisse tester une information d'autorisation, s'assurer que l'acceptation d'un transfert supprime l'information d'autorisation, etc.

L'extension à EPP décrite dans ce RFC a été enregistrée dans le registre des extensions EPP. Quelles sont les mises en œuvre de ce RFC ? Cocca, déjà cité, le fait partiellement (par exemple en ne stockant pas les mots de passe en clair). Je n'ai pas testé avec ce logiciel ce qui se passait avec une information d'autorisation vide. Sinon, CentralNic a déjà ce mécanisme en production. Et Verisign l'a mis dans son SDK.


Téléchargez le RFC 9154


L'article seul

RFC 9153: Requirements and Terminology for the Drone Remote Identification Protocol (DRIP)

Date de publication du RFC : Février 2022
Auteur(s) du RFC : S. Card, A. Wiethuechter (AX Enterprize), R. Moskowitz (HTT Consulting), A. Gurtov (Linköping University)
Pour information
Réalisé dans le cadre du groupe de travail IETF drip
Première rédaction de cet article le 11 février 2022


Il y a aujourd'hui beaucoup plus de drones que d'avions pilotés. Ces drones, qui se déplacent dans un espace partagé, soulèvent un certain nombre de questions de sécurité, ce qui justifie des mécanismes obligatoires d'immatriculation et d'identification. Dans tous les pays, des règles imposent aux drones ou au moins à certains d'entre eux de diffuser leur identité. Mais, ensuite, une fois que l'observateur du drone a cette identité, qu'en fait-on ? Le but du projet DRIP à l'IETF est de mettre au point des mécanismes pour utiliser cette identité. Ce RFC est le premier du projet, dédié à établir les exigences pour les futurs protocoles. Ce n'est pas gagné, vu qu'il y a beaucoup de grosses organisations sur le chemin qui vont privilégier des solutions fermées.

Pour aider à comprendre le problème, imaginons le scénario suivant. Un gardien fait sa ronde autour d'une centrale nucléaire. Un drone approche. Sa présence ici est-elle normale ? Est-il encore sous contrôle ou bien a-t-il échappé à son propriétaire ? Peut-on contacter son propriétaire pour vérifier ? S'il s'agissait d'un avion piloté traditionnel, les règles de circulation aérienne imposeraient tout un tas de contraintes au pilote (par exemple d'enregistrement de son vol à l'avance), qui permettraient d'éviter des incertitudes. On ne peut pas imposer des procédures aussi lourdes à un simple drone de loisirs. Mais on ne peut pas laisser non plus l'espace partagé qu'est l'air sans règles minimales. Prenons un autre scénario : un drone se promène au-dessus de votre jardin. Il a sans doute une caméra, comme la plupart des drones. Comment faire en sorte qu'il déguerpisse au lieu de vous filmer tranquillement en train de bronzer ? Peut-on lui tirer dessus, comme ça se fait aux États-Unis (« get off my lawn »), s'il ne répond pas ? Faut-il faire des sommations, et comment ? Comme l'analyse correctement la Quadrature du Net, le danger des drones ne vient pas que d'individus malveillants, une utilisation de ces engins par l'État crée également de nombreux risques.

Depuis quelques années, de nombreuses règles ont été édictées dans divers pays. Ainsi, en France, l'arrêté du 29 décembre 2019 impose la signalisation électronique des appareils pour tous les drones de plus de 800 grammes (cet article du Monde résume les obligations). Le drone doit être enregistré sur le site officiel AlphaTango (uniquement le drone, pas chaque vol, contrairement aux obligations des avions). Aux États-Unis, il existe des règles similaires . La plupart du temps, l'obligation minimale est que le drone diffuse son identité, de la même façon qu'une voiture a une plaque d'immatriculation. Mais cette règle est très insuffisante pour répondre à tous les scénarios. Le drone peut facilement mentir sur son identité. Et puis à quoi sert cette identité si elle ne fournit pas de moyen de retrouver le propriétaire ? Et ne serait-ce pas souhaitable d'avoir un moyen de contacter celui-ci en temps réel ? Ces questions ne sont pas évidentes et, comme toutes les questions liées à la sécurité, soulèvent d'innombrables problèmes politiques, juridiques et techniques. L'IETF se concentre sur une partie limitée de ces problèmes : ne pouvons-nous pas utiliser notre expérience en matière de protocoles réseau pour développer des mécanismes permettant de retrouver des données sur le drone à partir de son identité et, pourquoi pas, permettant de communiquer avec lui ? Voyons d'abord en détail le problème, les exigences de notre RFC pour les futurs protocoles ne seront présentées qu'à la fin de cet article.

Ah, si vous voyez ce drone, c'est le mien : drone.jpg

La section 1 de notre RFC détaille pourquoi les drones posent des problèmes. Il existe plusieurs types de drones (à aile fixe, à aile tournante, capables de décollage vertical ou non, avec un ou plusieurs moteurs…). Le type le plus répandu et le plus connu, notamment grâce aux petits drones de loisir, est le multi-moteur à ailes tournantes (multicoptère). Disposant de systèmes de stabilisation automatiques, ils peuvent être pilotés par des amateurs, contrairement aux avions et hélicoptères. Mais il existe aussi des drones dont la taille et le prix rappelle davantage un avion. Être pilotés à distance par un humain tenant un joystick n'est pas la seule possibilité : doté d'un récepteur GPS, certains drones sont partiellement autonomes et, dans le futur, seront totalement autonomes.

Un petit drone, comme celui que vous achetez pour quelques dizaines d'euros en magasin, a des propriétés importantes pour la sécurité de l'espace aérien :

  • ils sont nombreux,
  • il est difficilement détectable par les radars,
  • s'il est bruyant, voire insupportable, à courte distance, il est en général indétectable au bout de quelques centaines de mètres, alors qu'il peut couvrir cette distance rapidement ; un drone grand public peut faire 500 mètres en 13 secondes,
  • il vole à très basse altitude,
  • il est souvent piloté par un amateur sans vraie formation,
  • très manœuvrant, il peut passer sous les arbres, rentrer dans un immeuble, etc.

Un drone peut donc être très utile pour l'espionnage, voire l'attaque. Il peut porter une caméra pour surveiller, et des explosifs pour attaquer (cf. l'attentat contre le premier ministre irakien en novembre 2021). Même sans explosifs, le drone peut jouer un rôle de projectile, involontaire ou volontaire. Un article de Kaspersky donne une bonne idée des dangers possibles (mais notez qu'il est publié par une entreprise qui vend des solutions et peut donc être soupçonnée de dramatiser).

Il n'y a évidemment pas de solution magique à ces risques mais la plupart des approches nécessitent a priori qu'on découvre un identificateur du drone, qu'on ait une solution RID (Remote IDentification and tracking). Dans le scénario typique de RID, un ou plusieurs drones volent dans un espace restreint, et des observateurs, certains ayant un rôle particulier (policiers, par exemple) et d'autres étant du grand public, veulent obtenir un identificateur, qui va être le point de départ de leurs actions (s'informer sur ce drone, peut-être le contacter). Il est donc prévu un ou plusieurs registres d'identificateurs, et des informations associées. Il existe déjà des normes pour cela, comme la F3411-19, développée par le comité F38 de l'ASTM. Elle coûte 91 dollars étatsuniens. Comme beaucoup de normes développées par des organismes privés, elle n'est pas librement et gratuitement disponible, mais on peut apparemment trouver des brouillons en ligne. F3411-19 est la référence dans ce domaine, normalisant les messages que le drone doit envoyer pour annoncer son identifiateur mais, pour l'instant, c'est à peu près tout, on ne peut pas forcément faire grand'chose de cet identificateur. Elle ne précise pas comment l'observateur se renseigne sur les personnes ou organisations derrière un identificateur. Elle ne traite pas la communication entre drones ou avec l'observateur. Elle n'a pas de mécanisme d'authentification de l'identificateur. Le RFC, étant surtout développé par des Étatsuniens, est très inspiré par le travail de l'ASTM, qui ne s'applique pas forcément partout dans le monde. En Europe, ASD-STAN, associée à CEN, publie une norme DIN EN 4709-002:2021-02 (également chère et pas libre). Un résumé est disponible en ligne. Cette norme décrit un système de DRI (Drone Remote Identification) où le drone diffuse par radio :

  • le numéro d'identification de l'opérateur du drone (qui doit donc s'enregistrer),
  • celui du drone (en suivant CTA2063A - aussi désignée ANSI/CTA/2063-A, enfin une norme gratuitement disponible mais attention, il faut laisser plein de données personnelles pour l'obtenir).
  • l'heure qu'il est, la position du drone (en trois dimensions),
  • la vitesse et le cap du drone,
  • la position du pilote ou, à défaut, la position d'où le drone a décollé.

Un exemple d'identificateur de drone (celui donné par la norme) est MFR1C123456789ABC. (Le C en cinquième position est l'encodage de la longueur du numéro de série, ici 123456789ABC).

Même avec ces normes, de nombreux problème subsistent. Si trois drones sont proches, chacun diffusant son identité, et qu'un seul des trois a un comportement inquiétant, comment distinguer les trois identités ? Et puis les informations envoyées peuvent être erronnées (les drones bon marché n'incluent pas du matériel de mesure perfectionné) voire mensongères, notamment si le drone est malveillant.

Notez aussi que les groupes techniques comme DRIP ne peuvent qu'établir des normes techniques. Définir des règles politiques est une autre affaire. Et les faire respecter sera largement l'affaire des CAA.

Autre problème, comme le drone diffuse des informations en clair, tout le monde peut les capter et certaines informations (comme l'identificateur de l'opérateur) sont des données personnelles, ce qui soulève des questions de vie privée. On a là un beau débat politique en perspective, entre intimité et transparence.

Le RFC note qu'un déploiement massif d'un système de RID (Remote IDentification and Tracking) est à la fois urgent et important. Le déploiement doit être massif car, comme le dit le RFC, « quel serait l'intérêt des plaques minéralogiques si seulement 90 % des voitures en avaient ? ».

Un autre défi des systèmes d'identification réside dans le caractère capricieux des ondes radio. Elles peuvent être bloquées par des bâtiments ou du feuillage. Si le drone émet avec davantage de puissance, il va épuiser rapidement sa batterie. Et il aggravera la congestion dans cet espace partagé (d'autant plus que le drone émet dans des fréquences où une licence n'est pas nécessaire). Bref, d'une manière générale, la liaison radio sera lente et non fiable.

En parlant de batterie, il faut noter que le drone est typiquement un objet contraint (au sens du RFC 8352). Son « budget » en énergie, puissance de calcul et poids (ce qu'on désigne en général par le sigle CSWaP, Cost, Size, Weight, and Power, parfois écrit $SWaP) est très limité. Cela impose des protocoles de communication simples et frugaux, ce qui va rendre difficile l'utilisation de la cryptographie. Actuellement, il y a zéro authentification : un drone mensonger peut annoncer l'identificateur qu'il veut, sans qu'on puisse vérifier. (Dans des réunions, j'ai entendu des affirmations ridicules, comme de confondre la somme de contrôle avec un mécanisme d'authentification. Comme souvent en cybersécurité, on entend beaucoup de n'importe quoi dans les réunions.)

Et il n'y a pas que le drone, il y a aussi la machine de l'observateur. Dans des zones reculées, on ne peut pas compter sur une connectivité Internet permanente, et c'est une des raisons du choix du Broadcast RID (le drone diffuse son identificateur, également appelé Direct ID) plutôt que du Network ID (l'observateur obtient l'identificateur via un réseau public, typiquement l'Internet). Mais si vous voulez en savoir plus sur ce débat Network ID vs. Broadcast ID, voyez l'article « Why Did the FAA Go with Broadcast Remote ID for Drones Over Network? ».

Bref, les mécanismes actuellement normalisés et déployés sont très insuffisants. Le projet DRIP de l'IETF vise, non pas à remplacer les normes existantes, mais à les compléter pour traiter les manques. Ce RFC est le cahier des charges du projet.

Le monde des drones utilise une grande quantité d'acronymes. La section 2 du RFC en donne une liste complète, mais il faut au moins connaitre :

  • CAA (Civil Aviation Authority), l'autorité de régulation de l'aviation comme la DGAC en France ou la FAA aux États-Unis. Le milieu aérien, partagé et complexe, est très régulé dans tous les pays.
  • DRI (Direct Remote Identification), la possibilité d'obtenir l'identificateur d'un drone (le cœur du système).
  • LOS (Line Of Sight), la ligne qui va mener au drone pour le contrôler. Ce n'est pas forcément un lien visuel (voir « VLOS »), le pilote peut obtenir l'information sur le drone par d'autres moyens. Lorsque le drone n'est pas sur la LOS, il est incontrôlable (sauf s'il peut agir de manière autonome).
  • RID Remote IDentification ou UAS RID, le système qui permet aux observateurs d'obtenir l'identificateur du drone et, par extension, l'identificateur du drone.
  • UA (Unmanned Aircraft), un drone.
  • UAS (Unmanned Aircraft System), le drone, et tout ce qui lui permet de voler et d'être piloté (la télécommande, par exemple).
  • UAS ID (Unmanned Aircraft System IDentifier), l'identificateur de l'UAS (en fait, en pratique, c'est plutôt celui de l'UA).
  • VLOS (Visual Line Of Sight), lorsque le pilote voit son drone. Dans certains pays, la reglémentation peut imposer que le vol du drone se fasse en permanence à vue, et que le pilote ait donc une VLOS avec son engin, ce qui interdit par exemple de le faire voler derrière un bâtiment.

Pour les RID, les identificateurs des drones, il existe plusieurs textes normatifs. Le plus répandu est F3411-19, déjà mentionné, qui décrit les notions de Network RID et Broadcast RID. Pour le premier, l'observateur va récolter l'information via le réseau (le drone s'étant au préalable enregistré, on est dans le publish-subscribe), alors que dans le cas du second, il va écouter directement ce que diffuse le drone par ondes radio. Comme l'observateur, le pilote du drone et surtout le drone lui-même n'ont pas forcément d'accès à l'Internet, c'est le Broadcast RID qui est privilégié. Il fonctionne donc indépendamment de l'Internet.

Le Broadcast RID soulève de nombreux problèmes techniques, notamment en raison des caractéristiques physiques des ondes radio. Si on utilise Bluetooth, on a de sérieuses contraintes de taille des données, au cas où on veuille transmettre, en sus de l'identificateur, des informations de position, de vitesse et de route. En outre, les normes existantes ne standardisent pas les couches basses (Bluetooth 4, 5, Wifi NAN, Wifi Beacon…), donc il sera difficile de capter tous les drones, et surtout pas avec n'importe quel ordiphone, le drone émettant peut-être avec une technologie que l'ordiphone ne connait pas. En outre, sur beaucoup d'ordiphones, on n'a pas accès direct à Bluetooth (cf. les problèmes de l'application TousAntiCovid) et/ou pas accès aux paquets bruts, ce qui va compliquer la vie des développeurs. (Le projet AltBeacon vise à traiter ce problème.)

Comment est attribué le RID ? F3411-19 propose plusieurs méthodes :

  • Un identificateur statique attribué par le fabricant du drone, comme prévu par la norme ANSI/CTA-2063-A,
  • Un identificateur statique attribué par une CAA, comme cela se fait aujourd'hui pour les avions,
  • Un UUID (RFC 9562), statique ou dynamique, attribué par un système spécifique aux drones (une nouvelle autorité ?),
  • Un identificateur dynamique spécifique au vol.

Actuellement, l'Union Européenne impose le premier type, les États-Unis le premier ou le troisième. La situation est compliquée, et le choix fait a des implications évidentes en terme de vie privée (un identificateur statique permet de suivre à la trace un drone). C'est d'autant plus important que cette information, étant diffusée unilatéralement, ne peut pas être chiffrée, les protocoles de type défi-réponse étant inutilisables. Pour prendre un exemple, Amazon ne serait sans doute pas ravi qu'un concurrent puisse suivre précisement leurs drones de livraison.

La simple diffusion du RID est donc déjà un problème compliqué. Mais d'autres organismes que l'IETF s'en occupent. Par contre, cette diffusion laisse entier un autre problème : une fois qu'on a récupéré le RID, qu'en fait-on ? Comment, par exemple, communiquer avec le pilote du drone ? Si le message contenait, en sus de l'identificateur, la position du pilote, on peut toujours s'y rendre. Cela peut être lent, voire infaisable si le pilote est, par exemple, de l'autre côté d'une voie ferrée. Dans l'hypothèse où il existe un registre des identificateurs, associé à des informations de contact (comme on fait pour les noms de domaine), on pourrait imaginer que l'observateur regarde dans le registre (avec des protocoles comme RDAP ou whois) et appelle au téléphone le pilote. Reste à savoir si c'est une bonne idée que le pilote réponde alors que son attention devrait être concentrée sur le pilotage…

L'IETF, via son groupe DRIP, va donc tenter de développer des solutions pour :

  • authentifier l'identificateur de drone,
  • permettre l'interrogation d'un registre de ces identificateurs,
  • faciliter la coordination entre observateurs pour qu'ils puissent vérifier collectivement ce que fait un drone,
  • contacter le pilote.

Le but est évidemment que ces solutions reposent sur des normes ouvertes et accessibles. (Un certain nombre d'organisations font déjà du lobbying pour pousser au déploiement et à l'obligation légale de solutions privées et fermées, leur assurant le contrôle, et les revenus associés.)

Les normes existantes comme F3411-19 ne permettent pas cela, d'où ce cahier des charges du projet DRIP, dont les exigences forment la section 4 de notre RFC. Chacune est identifiée par trois lettres indiquant sa catégorie, puis un numéro. Ainsi, la première exigence est dans la catégorie des généralités et se nomme GEN-1 : « DRIP doit permettre d'authentifier les affirmations du drone » (puisque, actuellement, un drone peut raconter ce qu'il veut). Je ne vais pas vous lister toutes les exigences, seulement de quoi vous donner une idée du projet. Ainsi, GEN-2 suit GEN-1 en demandant qu'en outre, tous les messages du drone puissent être reliés à ceux qui prouvent l'identité du drone (et pas uniquement parce que les messages sont émis depuis la même adresse MAC). GEN-3 impose qu'il existe un moyen de vérifier l'enregistrement du drone dans un registre, même en l'absence de connexion Internet (par exemple le drone pourrait diffuser un certificat signé par le registre). GEN-6 concerne la prise de contact, il faut qu'il existe un moyen de contacter le pilote. (Attention, comme tout ce cahier des charges, il s'agit d'une exigence technique, pas politique. L'IETF n'a pas l'autorité pour édicter des règles disant, par exemple, que le pilote doit accepter les communications entrantes. Elle dit juste qu'un moyen technique doit exister, mais son utlisation pourra, par exemple, être restreinte à des appelants identifiés et autorisés.) Un protocole envisagé pour cette communication est HIP.

GEN-8 demande que tout cela fonctionne même si le drone (évidemment), son pilote et l'observateur sont en déplacement, GEN-9 veut un mode multicast, et GEN-10 un moyen de superviser le bon fonctionnement du système (notamment pour le Network RID).

La catégorie suivante concerne plus spécifiquement les identificateurs. ID-1 met une taille maximale à 19 octets (c'est une limite de F3411-19, sauf erreur). ID-2 dit que sur ces 19 octets au maximum, il faut placer un identificateur du registre. ID-4 exige que l'identificateur soit unique (ce qui est du simple bon sens) et ID-5, reprenant GEN-1, demande que le drone ne puisse pas mentir sur son identificateur. ID-6 veut qu'on puisse empêcher de suivre un drone ou son propriétaire à la trace (et il faut donc permettre des identificateurs qui ne soient pas statiques sur le long teme). Là encore, rappelez-vous l'avertissement de la catégorie précédente : DRIP ne définit pas une politique, l'activation ou non de la technique demandée par ID-6 n'est pas une question purement technique.

En parlant de gêner la surveillance, la troisième catégorie concerne la vie privée (voir aussi la section 7). PRIV-1 rappelle l'importance de protéger les données privées (par exemple en ne distribuant pas le nom et l'adresse du pilote publiquement) et PRIV-4 demande pour cela que le système d'enregistrement sépare ce qui est public et ce qui ne l'est pas (données personnelles, par exemple).

Et les registres ? Sur l'Internet, il existe de nombreux types de registres, comme les registres de noms de domaine ou les RIR. Certains problèmes, comme l'arbitrage entre les exigences opérationnelles (où on voudrait obtenir facilement beaucoup d'informations sur les titulaires des noms de domaine) et celles de vie privée, sont bien connues depuis longtemps (le « problème whois » de l'ICANN, par exemple). J'ai pris plusieurs exemples tirés des registres de noms de domaines car c'est là où je travaille. La quatrième et dernière catégorie d'exigences DRIP concerne précisément les futurs registres de drones. En raison des limites de taille très strictes sur ce que le drone peut diffuser, il est essentiel de disposer de registres permettant l'accès à davantage d'informations. REG-1 commence par demander qu'il existe un moyen d'interroger le registre (la solution évidente étant RDAP, RFC 9082). Ce moyen doit (exigence REG-2), contrairement à whois (RFC 3912), permettre l'accès différencié (davantage d'informations pour certains clients). REG-3 réclame qu'on puisse ajouter, retirer et modifier de l'information dans le registre (là, la solution évidente est EPP, RFC 5730). Notez qu'une présentation sur l'utilisation de registres avait été faite lors d'une réunion professionnelle des acteurs du monde des registres : les supports.

Enfin, la section 6 du RFC concerne l'analyse de sécurité du futur système. Quelles que soient les solutions adoptées, il faudra faire attention aux attaques Sybil, aux attaques par déni de service utilisant, par exemple, de nombreux messages incorrectement signés, aux attaques de l'Homme du Milieu, etc. On pourra voir aussi des attaques plus subtiles comme, suggère le RFC, un drone de petite taille, a priori pas très effrayant, et qui s'authentifiera proprement, mais derrière lequel surgira le vrai attaquant.

Pour les curieuses et les curieux, je recommande également l'annexe A du RFC, qui discute les choix effectués dans ce RFC et leurs raisons. Par exemple, l'analogie avec les plaques d'immatriculation des voitures, souvent utilisée dans les débats sur l'identification des drones a ses limites. Dans certains pays, par exemple des États des USA, n'importe qui peut accéder, parfois même gratuitement, aux données du registre, et donc aux informations personnelles sur les propriétaires de véhicules. Faut-il faire pareil pour les drones ? Même en Europe, si le registre n'est pas accessible, en revanche, les voitures ont un identificateur statique stable, qui peut faciliter certaines formes de surveillance. Veut-on cela pour les drones ?

Les avions (civils) pilotés et les bateaux ont l'habitude depuis longtemps de diffuser leur identité à qui veut écouter (le transpondeur pour les avions). D'excellentes et utiles applications comme OpenSky et Flightradar24 exploitent cette information. Souhaite-t-on un même fonctionnement pour les drones ? Outre les questions politiques, cela peut poser des problèmes techniques, par exemple parce que le nombre de codes utilisables est limité et qu'il y a beaucoup plus de drones que d'avions. Avec les 24 bits typiquement utilisés, on ne pourrait traiter que seize millions de drones, ce qui est loin des nombres envisagés.

Les drones qui diffusent leur identité le font en Wi-Fi ou en Bluetooth. Ces techniques ont l'avantage d'être très répandues, aussi bien du côté des drones que des observateurs (tous ont un ordiphone), peu chères, libres d'usage, et elles permettent la diffusion. Par contre, leur portée est faible. Il pourrait être intéressant de travailler sur des technologies conçues pour les grandes distances (comme WiMAX), ou pour les réseaux de capteurs (comme LoRA).

Voilà, le groupe DRIP est évidemment au travail pour mettre au point des protocoles correspondant à ces exigences. Le problème posé par les drones ne peut que devenir de plus en plus sérieux, notamment parce que leur discrétion augmentera. Dans les romans de la série Harry Potter (par J. K. Rowling), une journaliste-sorcière sans scrupule peut se transformer en insecte pour aller espionner les gens chez eux. Un drone suffisamment petit pour être quasiment indétectable serait un cauchemar pour la vie privée…

Pour en savoir plus, quelques ressources utiles :


Téléchargez le RFC 9153


L'article seul

RFC 9151: Commercial National Security Algorithm (CNSA) Suite Profile for TLS and DTLS 1.2 and 1.3

Date de publication du RFC : Avril 2022
Auteur(s) du RFC : D. Cooley (NSA)
Pour information
Première rédaction de cet article le 26 avril 2022


Le choix est vaste parmi les algorithmes de cryptographie. Des protocoles comme TLS offrent de l'agilité cryptographique, c'est-à-dire la possibilité d'avoir différents algorithmes pour un même protocole. Pour l'administrateur système qui n'est pas un ou une expert en cryptographie, lesquels choisir ? Pour l'aider, les agences de sécurité nationales font souvent des documents synthétisant leur analyse des bons algorithmes du moment. C'est le cas du RGS en France, par exemple. Ce RFC définit le profil TLS de CNSA (Commercial National Security Algorithm), les bons algorithmes recommandés par la NSA.

C'est d'ailleurs le deuxième RFC écrit par un employé de cette agence étatsunienne, après le RFC 5903 mais il est évidemment possible que certains auteurs aient été discrets sur leur affiliation.

Ce profil CNSA (Commercial National Security Algorithm) est destiné à être utilisé dans le cadre de TLS, plus exactement ses versions 1.2 (RFC 5246) et 1.3 (RFC 8446). Le cadre réglementaire étatsunien est SP 80059. Un rappel : il s'agit d'un profil, une restriction des algorithmes possibles, il ne définit pas de nouvel algorithme, il liste juste ceux à utiliser, par exemple ceux des RFC 5288 et RFC 5289. C'est en effet une de missions de la NSA que de conseiller l'État et les entreprises en matière de cryptographie. (La NSA a également une mission offensive, qui va en sens inverse, les encourageant par exemple à garder secrètes des vulnérabilités, pour qu'elle puisse les exploiter.) Cette suite CNSA n'a rien de très novateur, elle est dans la continuation des précédentes, le prochain grand changement, estime la NSA, sera le passage aux algorithmes post-quantiques. (Notez que l'analyse de la NSA sur les mesures à prendre en attendant les calculateurs quantiques est très discutée.)

Après ces préliminaires, la suite CNSA elle-même (section 4). Elle inclut les grands classiques, Diffie-Hellman avec les corps finis, les courbes elliptiques du NIST et encore RSA, les signatures avec les courbes elliptiques (ECDSA seulement) et RSA, et l'algorithme de chiffrement symétrique AES en intègre avec GCM (ChaChaRFC 8439, sans doute pas assez officiel, n'est pas cité). CNSA impose des tailles minimales de clés, par exemple 3 072 bits pour une signature avec RSA. Pour la condensation, c'est SHA-384 seulement (j'ignore pourquoi SHA-256 et les autres ne sont pas cités).

Pour la version de TLS, la section 5 de notre RFC impose au minimum 1.2 (c'est un des points où le blog que vous êtes en train de lire n'est pas conforme aux recommandations de la NSA…). Si on utilise une courbe elliptique (cf. RFC 8422), cela doit être la P-384 du NIST. (Saviez-vous d'ailleurs qu'il existe une courbe elliptique française, « FRP256 », publiée au Journal officiel ?) Et au passage, les certificats doivent suivre le profil du RFC 8603.

Pour TLS 1.3 (RFC 8446), CNSA impose de tirer profit de certaines nouveautés de cette version, comme l'extension signature_algorithms. Par contre, il faut refuser early_data (les raisons sont dans la section 2.3 du RFC 8446).


Téléchargez le RFC 9151


L'article seul

RFC 9150: TLS 1.3 Authentication and Integrity-Only Cipher Suites

Date de publication du RFC : Avril 2022
Auteur(s) du RFC : N. Cam-Winget (Cisco Systems), J. Visoky (ODVA)
Pour information
Première rédaction de cet article le 22 avril 2022


Ce nouveau RFC normalise des algorithmes pour le protocole de sécurité TLS, qui ne fournissent pas de chiffrement (seulement authentification et intégrité). C'est évidemment une option très contestée et l'IETF ne suggère pas d'utiliser ces algorithmes mais, bon, certaines personnes y voient une utilité.

La section 1 du RFC décrit des scénarios d'usage (souvent tirés du monde de l'IoT) où cela peut avoir un sens de ne pas chiffrer. Le premier exemple est celui d'un bras robotique commandé via un protocole TCP/IP. L'authentification est importante mais la confidentialité ne l'est peut-être pas, ce qui rendrait acceptable l'absence de chiffrement. Autre exemple, des rapports météo, ou bien l'envoi de signaux d'horloges, qu'il faut évidemment authentifier mais qui ne sont pas secrets. Le RFC cite aussi des exemples de communication avec des trains ou des avions, où l'intégrité des données est cruciale, mais pas leur confidentialité. Mais attention avant de jeter le chiffrement à la poubelle, le RFC dit bien qu'il faut faire une analyse soignée de la menace. Pour reprendre l'exemple du bras robotique, l'écoute des messages pourrait permettre la rétro-ingénierie, ce qu'on ne souhaite pas forcément.

Mais pourquoi se passer de chiffrement, d'ailleurs, alors qu'il est plus simple de l'utiliser systématiquement ? Parce que dans certains cas, il coûte cher, notamment en latence. En outre, certains équipements, notamment du genre objets connectés, ont des capacités de calcul très limitées. Bref, si le chiffrement systématique et par défaut reste la politique de l'IETF, ce RFC présente quelques façons de s'en passer, si on sait ce qu'on fait (si vous ne savez pas, continuez à chiffrer !).

Les algorithmes sans chiffrement sont présentés dans la section 4. Ils se nomment TLS_SHA256_SHA256 et TLS_SHA384_SHA384, et sont évidemment dans le registre IANA. Ils utilisent une fonction de condensation comme SHA-256 pour faire du HMAC, protégeant ainsi l'intégrité des communications.

Rappelez-vous bien : aucun chiffrement n'est fourni. Les certificats client, par exemple, qui sont normalement chiffrés depuis TLS 1.3, seront envoyés en clair.

Publier un tel RFC n'a pas été facile, la crainte de beaucoup étant que des gens qui ne comprennent pas bien les conséquences utilise ces algorithmes sans mesurer les risques. Le RFC demande (section 9) donc qu'ils ne soient pas utilisés par défaut et qu'ils requièrent une configuration explicite. Et rappelez-vous que ce RFC n'est que « pour information » et ne fait pas l'objet d'un consensus à l'IETF.


Téléchargez le RFC 9150


L'article seul

RFC 9147: The Datagram Transport Layer Security (DTLS) Protocol Version 1.3

Date de publication du RFC : Avril 2022
Auteur(s) du RFC : E. Rescorla (RTFM), H. Tschofenig (Arm Limited), N. Modadugu (Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 22 avril 2022


Pour sécuriser vos applications TCP sur l'Internet, vous utilisez TLS, pas vrai ? Et si vos applications préfèrent UDP, par exemple pour faire de la voix sur IP ? La solution est alors DTLS, dont la nouvelle version, la 1.3, est normalisée dans ce RFC 9147. Elle a vocation à remplacer l'ancienne version 1.2, qui était dans le RFC 6347.

DTLS fournit normalement les mêmes services de sécurité que TLS, notamment la confidentialité (via un chiffrement du trafic) et l'authentification (via une signature). Les seuls services TLS que DTLS ne peut pas rendre sont la protection de l'ordre des messages (ce qui est logique pour UDP) et, selon les options choisies, la protection contre le rejeu. DTLS a été conçu pour être aussi proche que possible de TLS, pour pouvoir réutiliser le code et s'appuyer sur des propriétés de sécurité déjà établies. DTLS 1.0 et 1.2 (il n'y a jamais eu de 1.1) avaient été normalisés sous forme de différences avec la version de TLS correspondante. De même, DTLS 1.3, objet de notre RFC, est défini en décrivant les différences avec TLS 1.3. Il faut donc lire le RFC 8446 avant. À propos de lectures, il faut aussi lire le RFC 9146, pour le concept d'identificateur de connexion (Connection ID).

La section 3 du RFC pose le problème : on veut un truc comme TLS mais qui marche sur un service de datagrammes, en général UDP (RFC 768). Un service de datagrammes ne garantit pas l'ordre d'arrivée, ni même que les datagrammes arrivent. DTLS ajoute de la sécurité au service de datagrammes mais ne change pas ses propriétés fondamentales : une application qui utilise DTLS ne doit pas s'étonner que les messages n'arrivent pas tous, ou bien arrivent dans le désordre. C'est un comportement connu des applications de diffusion vidéo ou de jeu en ligne, qui préfèrent sauter les parties manquantes plutôt que de les attendre.

Pourquoi un protocole distinct, DTLS, plutôt que de reprendre TLS ? C'est parce que TLS ne marche que si le service de transport sous-jacent garantit certaines propriétés qu'UDP ne fournit pas. TLS a besoin de :

  • Une numérotation croissante des données. Ces numéros ne sont pas explicites dans TLS, ils sont déduits de l'ordre d'arrivée. DTLS les rend explicites.
  • L'établissement d'une association TLS nécessite un échange fiable, où les messages arrivent dans l'ordre d'émission. Là aussi, DTLS doit ajouter des numéros de séquence explicites pour pouvoir remettre dans l'ordre les messages d'établissement d'association (cf. section 4.2).
  • Certains messages TLS n'ont pas d'accusé de réception explicite, TLS compte sur les messages suivants pour savoir si son message a été reçu. Avec DTLS, ces messages doivent avoir un accusé de réception puisque la couche transport ne nous dit plus si le message a été perdu.
  • Certains messages TLS peuvent être de grande taille (chaines de certificats, par exemple), supérieure à celle du datagramme (qui est typiquement de 1 500 octets) et DTLS doit donc être capable de les fragmenter et de les réassembler (la fragmentation IP étant hélas souvent bloquée par des équipements réseau mal programmés et/ou mal configurés).
  • Tout protocole utilisant les datagrammes doit se méfier des attaques par réflexion, où l'attaquant ment sur son adresse IP. TCP protège contre cela, mais DTLS, ne pouvant compter sur TCP, doit avoir son propre mécanisme de test de réversibilité.

La section 4 présente la couche Enregistrements de DTLS, c'est-à-dire le format des paquets sur le réseau. Les messages sont transportés dans des enregistrements (record) TLS, et il peut y avoir plusieurs messages dans un seul datagramme UDP. Le format des messages est différent de celui de TLS (ajout d'un numéro de séquence) et de celui de DTLS 1.2 :

struct {
           ContentType type;
           ProtocolVersion legacy_record_version;
           uint16 epoch = 0
           uint48 sequence_number;
           uint16 length;
           opaque fragment[DTLSPlaintext.length];
       } DTLSPlaintext;

struct {
           opaque unified_hdr[variable];
           opaque encrypted_record[length];
} DTLSCiphertext;
  

La première structure est en clair, la seconde contient des données chiffrées. Un message dans un datagramme est, soit un DTLSPlaintext (ils sont notamment utilisés pour l'ouverture de connexion, quand on ne connait pas encore le matériel cryptographique à utiliser) ou bien un DTLSCiphertext. La partie chiffrée du DTLSCiphertext est composée d'un :

struct {
            opaque content[DTLSPlaintext.length];
            ContentType type;
            uint8 zeros[length_of_padding];
} DTLSInnerPlaintext;

Comme pour TLS 1.3, le champ legacy_record_version est ignoré (il n'est là que pour tromper les middleboxes). Le unified_hdr contient notamment l'identificateur de connexion (Connection ID), concept expliqué dans le RFC 9146 (comme avec QUIC, ils peuvent augmenter la traçabilité). Les détails de la structure des messages figurent dans l'annexe A du RFC.

À l'arrivée d'un datagramme, c'est un peu compliqué, vu la variété de formats. La section 4.1 suggère un mécanisme de démultiplexage, permettant de déterminer rapidement si le message était du DTLSPlaintext, du DTLSCipherext, ou une erreur. Un datagramme invalide doit être silencieusement ignoré (c'est peut-être une tentative d'un méchant pour essayer d'injecter des données ; couper la connexion au premier datagramme invalide ouvrirait une facile voie d'attaque par déni de service).

Qui dit datagramme dit problèmes de MTU, une des plaies de l'Internet. Normalement, c'est l'application qui doit les gérer, après tout le principe d'un service de datagrammes, c'est que l'application fait tout. Mais DTLS ne lui facilite pas la tâche, car le chiffrement augmente la taille des données, « dans le dos » de l'application. Et, avant même que l'application envoie ses premières données, la poignée de main DTLS peut avoir des datagrammes dépassant la MTU, par exemple en raison de la taille des certificats. Et puis dans certains cas, le système d'exploitation ne transmet pas à l'application les signaux indispensables, comme les messages ICMP Packet Too Big. Bref, pour aider, DTLS devrait transmettre à l'application, s'il la connait, la PMTU (la MTU du chemin complet, que la couche Transport a peut-être indiqué à DTLS). Et DTLS doit gérer la découverte de la PMTU tout seul pour la phase initiale de connexion.

La section 5 du RFC décrit le protocole d'établissement de l'association entre client et serveur (on peut aussi dire connexion, si on veut, mais pas session, ce dernier terme devrait être réservé au cas où on reprend une même session sur une connexion différente). Il est proche de celui de TLS 1.3 mais il a fallu ajouter tout ce qu'UDP ne fournit pas, la détection de la MTU (Path MTU, la MTU du chemin complet), des accusés de réception explicites et la gestion des cas de pertes de paquets. Voici l'allure d'un message DTLS d'établissement de connexion :

 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;

 struct {
     HandshakeType msg_type;    /* handshake type */
     uint24 length;             /* bytes in message */
     uint16 message_seq;        /* DTLS-required field */
     uint24 fragment_offset;    /* DTLS-required field */
     uint24 fragment_length;    /* DTLS-required field */
     select (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;
     } body;
 } Handshake;
 

Le message ClientHello, comme en TLS 1.3, a un champ legacy_version qui sert à faire croire aux middleboxes (et aux serveurs bogués qui ne gèrent pas correctement la négociation de version) qu'il s'agit d'un TLS ancien.

Un établissement de connexion DTLS typique est :

  • Le client envoie un ClientHello,
  • le serveur répond avec un HelloRetryRequest qui contient le cookie,
  • le client renvoie le ClientHello, cette fois avec le cookie (le serveur est désormais certain que le client ne ment pas sur son adresse IP),
  • le serveur peut alors transmettre son ServerHello,
  • le client peut alors envoyer un message Finished et transmettre des données.

Il existe également, comme en TLS 1.3, un mode rapide, quand le client a déjà contacté ce serveur et obtenu du matériel cryptographique qu'il peut ré-utiliser. (C'est ce qu'on appelle aussi le « 0-RTT » ou la « reprise de session », et ça existe aussi dans des protocoles comme QUIC.) Tous les paquets pouvant se perdre, DTLS doit avoir un mécanisme de réémission.

Les risques d'attaques par déni de service sont très élevés dans le cas où on utilise des datagrammes. Un méchant peut envoyer des demandes de connexion répétées, pour forcer le serveur à faire des calculs cryptographiques et surtout à allouer de la mémoire pour les connexions en attente. Pire, il peut utiliser un serveur DTLS pour des attaques par réflexion où le méchant ment sur son adresse IP pour que le serveur dirige ses réponses vers une victime innocente. Les attaques par réflexion sont encore pires lorsqu'elles sont combinées à une amplification, quand la réponse est plus grosse que la question.

Pour éviter ces attaques, DTLS reprend, comme vu plus haut, le principe des cookies sans état de Photuris (RFC 2522) et IKE (RFC 7296).

L'extension connection_id (cf. RFC 9146) peut être mise dans le ClientHello. Notez qu'une nouveauté par rapport au RFC 9146 est la possibilité de changer les identifiants de connexion pendant une association.

En section 7, une nouveauté de DTLS 1.3, et qui n'a pas d'équivalent dans TLS, les accusés de réception (ACK), nécessaires puisqu'on fonctionne au-dessus d'un service de datagrammes, qui ne garantit pas l'arrivée de tous les paquets. Un ACK permet d'indiquer les numéros de séquence qu'on a vu. Typiquement, on envoie des ACK quand on a l'impression que le partenaire est trop silencieux (ce qui peut vouloir dire que ses messages se sont perdus). Ces accusés de réception sont facultatifs, on peut décider que la réception des messages (par exemple un ServerHello quand on a envoyé un ClientHello) vaut accusé de réception. Ils servent surtout à exprimer son impatience « allô ? tu ne dis rien ? »

Dans sa section 11, notre RFC résume les points importants, question sécurité (en plus de celles communes à TLS et DTLS, qui sont traitées dans le RFC 8446). Le principal risque, qui n'a pas vraiment d'équivalent dans TLS, est celui de déni de service via une consommation de ressources déclenchée par un partenaire malveillant. Les cookies à la connexion ne sont pas obligatoires mais fortement recommandés. Pour être vraiment sûrs, ces cookies doivent dépendre de l'adresse IP du partenaire, et d'une information secrète pour empêcher un tiers de les générer.

À noter d'autre part que, si DTLS garantit plusieurs propriétés de sécurité identiques à celle de TLS (confidentialité et authentification du serveur), il ne garantit pas l'ordre d'arrivée des messages (normal, on fait du datagramme…) et ne protège pas parfaitement contre le rejeu (sections 3.4 et 4.5.1 du RFC si vous voulez le faire).

Les sections 12 et 13 résument les principaux changements depuis DTLS 1.2 (seulement les principaux car DTLS 1.3 est très différent de 1.2) :

  • n'accepte désormais que du chiffrement intègre,
  • le mécanisme de reprise de session est différent, et la terminologie a changé (on parle désormais de PSK, Pre-Shared Key pour désigner les données stockées chez le client),
  • la négociation de version a changé, pour tenir compte des nombreuses middleboxes boguées,
  • les numéros de séquence sont chiffrées (sans cela, corréler deux échanges utilisant des adresses IP différentes serait trivial). En fait, c'est plus compliqué que cela, il y a le numéro de séquence complet dans la partie chiffrée, et un numéro réduit à ses derniers bits en clair.

Si jamais vous vous lancez dans la programmation d'une bibliothèque DTLS, lisez l'annexe C, qui vous avertit sur quelques pièges typiques de DTLS.

En octobre 2021, il n'y avait pas encore de DTLS 1.3 dans GnuTLS (ticket en cours), BoringSSL ou dans OpenSSL (ticket en cours).

Merci à Manuel Pégourié-Gonnard pour sa relecture attentive.


Téléchargez le RFC 9147


L'article seul

RFC 9146: Connection Identifiers for DTLS 1.2

Date de publication du RFC : Mars 2022
Auteur(s) du RFC : E. Rescorla (RTFM), H. Tschofenig, T. Fossati (Arm Limited), A. Kraus (Bosch.IO)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 20 mars 2022


Ce RFC ajoute au protocole de sécurité DTLS 1.2 la possibilité d'identificateurs de connexion (connection ID), permettant de relier entre eux des paquets d'une même session DTLS, même si l'adresse IP source change. C'est une solution simple et minimale, DTLS 1.3 (RFC 9147) l'utilise (avec quelques ajouts).

Dans le DTLS habituel, version de TLS qui tourne sur UDP et qui est normalisée dans le RFC 6347, lorsqu'un paquet arrive à une machine, celle-ci trouve l'association de sécurité appropriée (ce qui permet de trouver le matériel crptographique qui permettra de déchiffrer et de vérifier les signatures) en regardant le tuple {protocole, adresse IP source, adresse IP destination, port source, port destination}. Mais si la machine avec qui on correspond a changé d'adresse IP, par exemple parce qu'il s'agit d'un malinphone qui est passé de WiFi à 4G ? Ou bien si elle a changé de port car un routeur NAT a trouvé intelligent de considérer la session terminée, effaçant une entrée dans sa table de correspondance ? Dans ce cas, le paquet entrant va être considéré comme une nouvelle session, il faudra reprendre la négociation TLS, et c'est du temps perdu (la poignée de main cryptographique est coûteuse, et on souhaite amortir ce coût sur la plus longue durée possible).

Le RFC note que le problème est particulièrement sérieux pour les déploiements type Internet des Objets, où les objets peuvent se mettre souvent en sommeil pour économiser leur batterie, amenant le routeur NAT à oublier la session en cours.

Notez aussi qu'outre l'identificateur de connexion, notre RFC apporte quelques changements supplémentaires, notamment pour permettre le remplissage.

La section 3 du RFC spécifie l'extension DTLS connection_id (numéro 54 dans le registre IANA) qui permet de spécifier des identifiants des connexions DTLS, et donc ainsi de construire une connexion qui résistera aux changements d'adresses IP et de ports. Dans son ClientHello, le client DTLS indiquera l'identifiant qu'il utilisera (idem pour le serveur DTLS dans son ServerHello). Par contre, on ne peut pas changer d'identifiant de connexion en cours de connexion (contrairement à ce que permettent, par exemple, QUIC ou, tout simplement, DTLS 1.3). Une fois l'utilisation de connection IDs négociée, les données sont envoyées dans une nouvelle structure, de type tls12_cid (numéro 25 dans le registre IANA). (Pour DTLS 1.3, il faudra regarder le RFC qui lui sera consacré.) On peut ajouter des zéros avant le chiffrement, à des fins de remplissage. Voici la structure de données avant le chiffrement :

struct {
            opaque content[length];
            ContentType real_type;
            uint8 zeros[length_of_padding];
} DTLSInnerPlaintext;    
  

Et une fois chiffrée, on envoie ça sur le réseau :

struct {
            ContentType outer_type = tls12_cid;
            ProtocolVersion version;
            uint16 epoch;
            uint48 sequence_number;
            opaque cid[cid_length];    // L'identifiant de
	    // connexion est là.
            uint16 length;
            opaque enc_content[DTLSCiphertext.length];
} DTLSCiphertext;    
  

Que se passe-t-il quand on reçoit un message pour un identifiant de connexion connu mais une nouvelle adresse IP ? Il ne faut pas lui faire une confiance aveugle (des méchants ont pu usurper l'adresse IP) et envoyer immédiatement des réponses à la nouvelle adresse IP. Il faut d'abord vérifier que le message est correctement signé, qu'il a une epoch plus récente que le précédent message (dans certains cas, comme l'ordre des messages n'est pas garanti, cela peut mener à ignorer un message valide), et l'application doit tester que le pair est toujours d'accord (la méthode dépend de l'application).

Les identifiants de connexion posent évidemment des questions de vie privée (section 8 du RFC). Ils doivent être en clair (puisqu'ils servent au récepteur à découvrir le matériel cryptographique de la connexion, qui servira au déchiffrement) et sont donc observables par quiconque est situé sur le trajet, ce qui améliore la traçabilité (ce qui est mauvais pour la vie privée). Et, contrairement à QUIC, on n'a qu'un identifiant, on ne peut pas en changer (c'est mieux en DTLS 1.3).


Téléchargez le RFC 9146


L'article seul

RFC 9141: Updating References to the IETF FTP Service

Date de publication du RFC : Novembre 2021
Auteur(s) du RFC : R. Danyliw (Software Engineering Institute)
Chemin des normes
Première rédaction de cet article le 19 novembre 2021


L'IETF distribuait un certain nombre de documents en FTP anonyme. Ce service, ftp.ietf.org, va être interrompu. Mais un certain nombre de RFC continuent à le citer comme source. Comme on ne peut pas modifier un RFC après publication, notre RFC 9141 fait la liste des mises à jour à lire pour corriger ces RFC et indiquer la nouvelle source des documents.

FTP, qui avait été le premier protocole de transfert de fichiers de l'Internet, avant même l'adoption de TCP/IP, a été pendant longtemps le principal moyen de copier des fichiers à travers le réseau. Une de ces fonctions, le FTP anonyme (qui n'était en fait pas anonyme du tout) permettait d'accéder aux fichiers, lorsque le serveur le voulait bien, sans avoir de compte sur le serveur. D'immenses archives de logiciels, de documents, d'images, ont été ainsi distribuées pendant des années. Aujourd'hui, FTP n'est guère plus utilisé (entre autres parce qu'il fonctionne en clair, cf. la section 4 sur la sécurité) et maintenir un service FTP anonyme n'a plus guère de sens. D'où la décision de l'IETF en 2020 de fermer le sien (cf. l'annonce). Le plan élaboré à cette occasion (une lecture recommandée sur les détails de cette décision, par exemple les statistiques d'utilisation) notait qu'il y avait trente RFC qui référençaient ce service, le dernier, le RFC 7241, datant de 2014. Tous ces RFC sont donc formellement mis à jour par notre RFC 9141. (Comme le note GuB, « C'est pire que légifrance cette histoire ».)

Par exemple, le RFC 2077 dit « Copies of RFCs are available on: ftp://ftp.isi.edu/in-notes/ » alors qu'il faudra désormais lire « Copies of RFCs are available on: https://www.rfc-editor.org/rfc/ ».

Voici par exemple, pour la nostalgie, le fonctionnement du serveur en novembre 2021, avant sa fermeture. On cherche les archives indiquées par le RFC 5098, et qui sont désormais en https://www.ietf.org/ietf-ftp/ietf-mail-archive/ipcdn/ :

% ncftp ftp.ietf.org    
NcFTP 3.2.5 (Feb 02, 2011) by Mike Gleason (http://www.NcFTP.com/contact/).

Copyright (c) 1992-2011 by Mike Gleason.
All rights reserved.

Connecting to 4.31.198.44...                                                                                                            
FTP server ready
Logging in...                                                                                                                           
Anonymous access granted, restrictions apply
Logged in to ftp.ietf.org.                                                                                                              
ncftp / > ls
charter/                         ietf/                            internet-drafts/                 slides/
concluded-wg-ietf-mail-archive/  ietf-mail-archive/               review/                          status-changes/
conflict-reviews/                ietf-online-proceedings/         rfc/                             yang/
ncftp / > 
ncftp / > cd ietf-mail-archive/ipcdn
ncftp /ietf-mail-archive/ipcdn > ls
...
1996-12               1999-07.mail          2001-09.mail          2003-11.mail          2006-01.mail          2008-03.mail
1997-01               1999-08.mail          2001-10.mail          2003-12.mail          2006-02.mail          2008-04.mail
1997-02               1999-09.mail          2001-11.mail          2004-01.mail          2006-03.mail          2008-05.mail
1997-03               1999-10.mail          2001-12.mail          2004-02.mail          2006-04.mail          2008-06.mail
...
  

Téléchargez le RFC 9141


L'article seul

RFC 9138: Design Considerations for Name Resolution Service in Information-Centric Networking (ICN)

Date de publication du RFC : Novembre 2021
Auteur(s) du RFC : J. Hong, T. You (ETRI, L. Dong, C. Westphal (Futurewei Technologies), B. Ohlman (Ericsson)
Pour information
Réalisé dans le cadre du groupe de recherche IRTF icnrg
Première rédaction de cet article le 1 décembre 2021


L'ICN est l'idée (très contestable) qu'un réseau informatique sert à accéder à du « contenu » et que le réseau doit donc être architecturé autour de cette idée de contenu. Les noms identifient ainsi un contenu donné. Mais il faut bien ensuite trouver le contenu donc résoudre ces noms en quelque chose de plus concret. Ce RFC est le cahier des charges d'un tel système de résolution de noms pour les projets ICN. Comme beaucoup de cahier des charges, il est très « liste au Père Noël », accumulant des desiderata sans se demander s'ils sont réalistes (et compatibles entre eux !).

Comme avec beaucoup de documents qui promeuvent l'ICN, ce RFC donne une description erronée du nommage et de l'adressage dans l'Internet d'aujourd'hui. Passons, et voyons ce que l'ICN propose. L'idée est que le contenu est stocké dans des NDO (Named Data Objects) et que toute activité dans le réseau coniste à récupérer des NDO. Les NDO sont identifiés par un nom. Il ne s'agit pas seulement d'un identificateur mis au-dessus d'un réseau architecturé sur d'autres concepts (comme le sont les URI) mais du concept de base du réseau ; les routeurs ne routent plus selon des adresses mais selon les noms des NDO. Le problème est évidemment qu'il faudra bien, à la fin, trouver l'objet désiré. Cela nécessite (cf. RFC 7927) :

  • Un mécanisme de résolution des noms en de l'information plus concrète (une localisation, un autre nom, etc),
  • un mécanisme de routage vers l'endroit où se trouve le NDO,
  • un mécanisme de récupération du NDO.

Ce RFC se focalise sur le premier point, le NRS (Name Resolution Service), et en est le cahier des charges. Le RFC 9236 a depuis décrit l'architecture envisagée. Si vous voulez apprendre des choses sur les ICN en général et la résolution de noms en particulier, voir par exemple « A Survey of Information-Centric Networking » ou « A Survey of Information-Centric Networking Research ».

Si on compare avec l'Internet actuel, le NRS aura un rôle analogue à celui de BGP (plutôt que du DNS, car le NRS sera au cœur du réseau, et complètement inséparable). Bon, ceci dit, c'est plus compliqué que cela car, derrière l'étiquette « ICN », il y a des tas de propositions différentes. Par exemple, certaines ressemblent plutôt à l'Internet actuel, avec une résolution de noms en localisateurs qui servent ensuite pour le routage (comme dans IDnet, cf. « IDNet: Beyond All-IP Network), alors que d'autres versions du concept d'ICN utilisent les noms pour le routage (comme le NDN ou le CCNx du RFC 8569). La section 2.4 du RFC compare ces approches.

La section 3 du RFC est ensuite le cahier des charges proprement dit. Malheureusement, elle plane au-dessus des réalités quand elle affirme par exemple qu'il faut un NRS qui fonctionnera de la même façon que l'espace de nommage soit plat ou hiérarchique. C'est très irréaliste, il n'y a pas de nette séparation entre la structure de l'espace de nommage et le mécanisme de résolution. Ainsi, ce mécanisme, dans le cas du DNS, est très lié à la structure des noms. Si on la change, tout le DNS serait à refaire (et sans doute en moins efficace). Parmi les systèmes d'ICN qui utilisent un nommage hiérarchique (et réintroduisent donc une forme de « localisation » dans les noms), on trouve NDN et CCNx.

Certains des mécanismes de résolution discutés ont déjà un RFC, par exemple le NI du RFC 6920, utilisé dans NetInf (cf. « Network of Information (NetInf) - An information-centric networking architecture »).

Bref, les principes du NRS :

  • Fonctionner avec des structures d'espaces de noms différentes (cf. ma critique ci-dessus),
  • accepter la mobilité des contenus,
  • passer à l'échelle (ce qui est trivial avec des noms hiérarchiques, beaucoup moins avec des noms plats),
  • permettre la mémorisation (caching),
  • accepter que les objets soient identifiés par un nom choisi ou par un condensat du contenu (ce que le RFC nomme les « objets sans nom », ce qui me semble accroitre encore la confusion),
  • permettre enfin de récupérer des métadonnées et pas seulement le localisateur.

Et le cahier des charges à proprement parler est en section 4. Je ne cite pas tout mais la liste au Père Noël comprend :

  • Des réponses rapides (« en un temps raisonnable »),
  • des réponses correctes (on voit bien le ridicule de l'approche par cahier des charges, quand il faut préciser explicitement des points aussi évidents), et à jour (là encore, « raisonnablement »),
  • des réponses justes, respectant un principe de neutralité (pas de censure ? L'ARCOM n'aimerait pas),
  • un fonctionnement satisfaisant sur des réseaux de grande taille (le RFC mentionne au moins 10^21 objets),
  • un système gérable (pouvant s'adapter avec par exemple l'ajout et le retrait de nœuds),
  • la capacité à être réellement déployé (là encore, on a envie de dire « encore heureux »),
  • la résistance aux pannes (la panne d'une machine ne doit pas arrêter le NRS), et aux attaques par déni de service,
  • la confidentialité des requêtes (un problème pour le DNS, cf. RFC 7626), mais aussi des données (le RFC mentionne des ACL sur les noms, chose qui n'existe pas vraiment dans le DNS)
  • l'authentification des serveurs, des producteurs de noms (par exemple pour que seule l'entité qui a enregistré un nom puisse modifier les données associées), et des données elle-mêmes (ce que DNSSEC fournit au DNS),

Une conclusion ? Les projets regroupés sous le nom d'ICN sont assez anciens, n'ont rien fait de vraiment nouveau récemment, et il y a peu de chances que ce RFC soit suivi de réalisations concrètes.


Téléchargez le RFC 9138


L'article seul

RFC 9137: Considerations for Cancellation of IETF Meetings

Date de publication du RFC : Octobre 2021
Auteur(s) du RFC : M. Duke (F5 Networks)
Réalisé dans le cadre du groupe de travail IETF shmoo
Première rédaction de cet article le 12 octobre 2021


Pour son travail de normalisation technique, l'IETF tient normalement trois réunions physiques par an. La pandémie de Covid-19 a évidemment changé tout cela. Les premières décisions d'annulation ont été prises selon un procédure ad hoc, mais ce nouveau RFC fournit des critères plus rigoureux pour les prochaines décisions d'annulation.

La dernière réunion physique de l'IETF a eu lieu à Singapour en novembre 2019. Depuis, les conditions sanitaires ont forcé à annuler toutes les réunions. Mais notre nouveau RFC ne se limite pas au cas de la Covid-19. Une réunion, après tout, peut devoir être annulée pour d'autres raisons, un problème de dernière minute dans le bâtiment où elle devait se tenir, une catastrophe naturelle dans le pays d'accueil, un brusque changement dans la politique de visa de ce pays, etc. (Le RFC cite même le cas d'une guerre civile, mais ce n'est encore jamais arrivé à une réunion IETF.) Dans ces cas, l'IETF LLC (la direction administrative de l'IETF) et l'IESG vont devoir décider si on maintient la réunion ou pas.

La section 3 du RFC expose les critères de décision. L'IETF LLC (cf. RFC 8711) détermine si la réunion peut se tenir, l'IESG si cela vaut la peine de la tenir. L'IETF LLC doit évidemment travailler en toute transparence, informant l'IETF de la situation, des décisions possibles et d'un éventuel « plan B ». Il n'est pas toujours possible de procéder proprement et démocratiquement si la situation est urgente (tremblement de terre trois jours avant la réunion…). Dans ce cas, l'IETF LLC doit déterminer seule si la réunion peut se tenir (ce qui implique certaines garanties de sécurité pour les participants, de la restauration mais également un accès Internet qui marche ; les participants à l'IETF n'ont pas juste besoin de dormir et de manger, il leur faut aussi du réseau). L'IETF LLC doit aussi intégrer des paramètres internes comme la disponibilité de ses employés et des bénévoles, et bien sûr les conséquences financières du maintien ou de l'annulation (si le lieu de la réunion considère qu'il n'y avait pas force majeure et ne veut pas rembourser…). La section 3 du RFC 8718 contient des indications utiles à cette évaluation.

L'IESG, lui, doit déterminer s'il y aura suffisamment de monde à la réunion pour que ça vaille la peine. Il ne serait pas malin de maintenir une réunion pour que personne ne vienne.

La section 4 du RFC couvre les alternatives. Si on annule, que peut-on proposer à la place ? Il faut évaluer ces alternatives en tenant compte de leur efficacité (une réunion en ligne est moins efficace) et de leur coût (changer les billets d'avion au dernier moment coûte cher). Ces alternatives peuvent être, dans l'ordre décroissant de préférence :

  • Changer le lieu de la réunion. C'est ce qui dérange le moins le travail, surtout si le nouveau lieu est proche de l'ancien. Naturellement, le nouveau lieu doit correspondre aux critères des RFC 8718 et RFC 8719.
  • Passer en ligne, à une réunion « virtuelle », ce qui a été fait pour toutes les réunions annulées en raison de la Covid-19.
  • Repousser la réunion à plus tard. Ce n'est en général pas facilement faisable, puisque cela perturbe les agendas de tout le monde.
  • Annuler complètement, sans alternative. Le RFC note que cela aura des conséquences, non seulement sur le travail de normalisation, mais également sur des processus politiques comme la détermination de qui peut siéger dans des comités comme le NomCom (comité de nomination, cf. RFC 8788 et RFC 8989).

La section 5 de notre RFC couvre les questions financières. En gros, l'IETF ne remboursera pas les dépenses engagées par les participants (avion, hôtel, etc), seulement les frais d'inscription à la réunion, en totalité s'il y a annulation complète et partiellement dans les autres cas.


Téléchargez le RFC 9137


L'article seul

RFC 9132: Distributed Denial-of-Service Open Threat Signaling (DOTS) Signal Channel Specification

Date de publication du RFC : Septembre 2021
Auteur(s) du RFC : M. Boucadair (Orange), J. Shallow, T. Reddy.K (Akamai)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dots
Première rédaction de cet article le 10 octobre 2021


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. Il remplace le RFC 8782, mais les changements sont mineurs.

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 DTLSTCP 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 11 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.

L'annexe A de notre RFC résume les principaux changements depuis le RFC 8782. Le principal changement touche les modules YANG, mis à jour pour réparer une erreur et pour tenir compte du RFC 8791. Il y a aussi une nouvelle section (la 9), qui détaille les codes d'erreur à renvoyer, et l'espace des valeurs des attributs a été réorganisé… Rien de bien crucial, donc.


Téléchargez le RFC 9132


L'article seul

RFC 9124: A Manifest Information Model for Firmware Updates in Internet of Things (IoT) Devices

Date de publication du RFC : Janvier 2022
Auteur(s) du RFC : B. Moran, H. Tschofenig (Arm Limited), H. Birkholz (Fraunhofer SIT)
Pour information
Réalisé dans le cadre du groupe de travail IETF suit
Première rédaction de cet article le 15 janvier 2022


On le sait, la sécurité des gadgets nommés « objets connectés » est abyssalement basse. Une de raisons (mais pas la seule, loin de là !) est l'absence d'un mécanisme de mise à jour du logiciel, mécanisme qui pourrait permettre de corriger les inévitables failles de sécurité. Le problème de la mise à jour de ces machins, souvent contraints en ressources, est très vaste et complexe. Le groupe de travail SUIT de l'IETF se focalise sur un point bien particulier : un format de manifeste permettant de décrire une mise à jour. Ce RFC décrit le modèle de données des informations qui seront placées dans ce manifeste.

Donc, que faut-il indiquer pour permettre une mise à jour du logiciel (et des réglages) d'un objet connecté ? Le groupe de travail est parti des expériences concrètes, de scénarios et de menaces, pour arriver à la liste que contient ce RFC. Certes, cette liste n'est pas exhaustive (cela serait impossible) mais donne quand même un bon point de départ. Attention, ce RFC décrit le modèle d'information (cf. RFC 3444), pas le format du manifeste (qui sera en CBOR, et fera l'objet d'un futur RFC).

Pour suivre le contenu de ce RFC, il vaut mieux avoir déjà lu le premier RFC du groupe de travail, le RFC 9019, qui explique l'architecture générale du système, ainsi que le RFC 8240, qui était le compte-rendu d'un atelier de réflexion sur cette histoire de mise à jour des objets connectés..

La (longue) section 3 du RFC est consacrée à énumérer tous les éléments à mettre dans le manifeste. Je ne vais pas reproduire toute la liste, juste quelques éléments intéressants. Notez que pour chaque élément, le RFC précise s'il doit être présent dans le manifeste ou bien s'il est facultatif. Le premier élément listé est l'obligatoire identificateur de version de la structure du manifeste, qui permettra de savoir à quel modèle ce manifeste se réfère.

Autre élément obligatoire, un numéro de séquence, croissant de façon monotone, et qui permettra d'éviter les reculs accidentels (« mise à jour » avec une version plus ancienne que celle installée). On peut utiliser pour cela une estampille temporelle (si on a une horloge sûre).

Ensuite, le RFC recommande (mais, contrairement aux deux précédents éléments, ce n'est pas obligatoire) de placer dans le manifeste un identificateur du fournisseur de la mise à jour. Il n'est pas prévu pour d'autres comparaisons que la simple égalité. Le format recommandé pour cet identificateur est un UUID (RFC 9562) de « version 5 », c'est-à-dire formé à partir d'un nom de domaine, qui est forcément unique. L'avantage des UUID (surtout par rapport au texte libre) est leur taille fixe, qui simplifie analyse et comparaison. Si on veut un identificateur de fournisseur qui soit lisible par des humains, il faut le placer dans un autre élément.

L'identificateur de la classe (class ID), indique un type de machines, les machines d'une même classe acceptant le même logiciel (cette acceptation dépend du matériel mais aussi d'autres facteurs comme la version du microcode). Il doit être unique par identificateur de fournisseur (et, en cas de vente en marque blanche, il doit être fourni par le vrai fournisseur, pas par le vendeur). Là aussi, un UUID de « version 5 » est recommandé. Il ne doit pas dépendre juste du nom commercial, un même nom commercial pouvant recouvrir des produits qui n'acceptent pas les mêmes mises à jour. Si un objet peut recevoir des mises à jour indépendantes, pour différents composants de l'objet, il faut des identificateurs de classe différents (surtout si certains objets du fournisseur utilisent une partie des mêmes composants, mais pas tous ; l'identificateur doit identifier le composant, pas l'objet).

Le manifeste contient aussi (mais ce n'est pas obligatoire), une date d'expiration, indiquant à partir de quand il cesse d'être valable.

Le manifeste peut (mais ce n'est pas obligatoire) contenir directement l'image utilisée pour la mise à jour du logiciel de l'objet. Cela peut être utile pour les images de petite taille ; plus besoin d'une étape supplémentaire de téléchargement.

Par contre, l'indication du format de l'image, qu'elle soit directement incluse ou non, est obligatoire, ainsi que celle de la taille de la dite image.

Question sécurité, le RFC impose également la présence d'une signature du manifeste. Le manifeste peut aussi contenir des éléments qui vont servir à établir si on a une délégation sûre depuis une autorité reconnue : des Web Tokens en CBOR (RFC 8392), avec peut-être des preuves du RFC 8747.

La sécurité est au cœur des problèmes que traite ce RFC. Après cette liste d'élements facultatifs ou obligatoires dans un manifeste, la section 4 de notre RFC expose le modèle des menaces auxquelles il s'agit de faire face. Le RFC rappelle que la mise à jour elle-même peut être une menace : après tout, mettre à jour du logiciel, c'est exécuter du code distant. Juste répéter « il faut mettre à jour le logiciel de sa brosse à dents connectée » ne suffit pas, si le mécanisme de mise à jour permet d'insérer du code malveillant. (Et encore, le RFC est gentil, tendance bisounours, il ne rappelle pas que l'attaquant peut être le fournisseur, envoyant du code nouveau pour désactiver certaines fonctions ou pour espionner l'utilisateur, voir les exemples de Hewlett-Packard et de Sony. Sans même parler de la possibilité d'une attaque contre la chaine de développement comme celle contre SolarWinds.)

L'analyse part du modèle STRIDE. Je ne vais pas citer toutes les menaces possibles (il y en a beaucoup !), lisez le RFC pour avoir une vue complète. Notez que les attaques physiques contre les objets (ouvrir la boite et bricoler à l'intérieur) ne sont pas incluses.

Évidemment, la première menace est celle d'une mise à jour qui serait modifiée par un attaquant. Le code correspondant serait exécuté, avec les conséquences qu'on imagine. La signature prévient cette attaque.

Ensuite, le cas d'une vieille mise à jour, qui était honnête et signée, mais n'est plus d'actualité. Si elle a une faiblesse connue, un attaquant pourrait essayer de faire réaliser une « mise à jour » vers cette vieille version. Le numéro de séquence dans le manifeste, qui est strictement croissant, est là pour protéger de cette attaque.

Autre risque, celui d'une mise à jour signée mais qui concerne en fait un autre type d'appareil. Appliquer cette mise à jour pourrait mener à un déni de service. La protection contre ce risque est assurée par l'identificateur du type d'objet.

Le RFC liste la menace d'une rétro-ingénierie de l'image. Outre que cela ne s'applique qu'au logiciel privateur, du point de vue de la sécurité, cela n'est pas crucial, puisqu'on ne compte pas sur la STO. Si on y tient quand même, le chiffrement de l'image (qui n'est pas obligatoire) pare ce risque.

Sinon la section 4.4 contient des scénarios typiques d'utilisation, où une histoire décrit les acteurs, les menaces, les solutions possibles, rendant ainsi plus vivants et plus concrets les problèmes de sécurité étudiés dans ce RFC.


Téléchargez le RFC 9124


L'article seul

RFC 9120: Nameservers for the Address and Routing Parameter Area ("arpa") Domain

Date de publication du RFC : Octobre 2021
Auteur(s) du RFC : K. Davies (IANA), J. Arkko (Ericsson)
Pour information
Première rédaction de cet article le 30 octobre 2021
Dernière mise à jour le 4 mai 2022


Le TLD .arpa sert pour différentes fonctions techniques et est géré directement par l'IAB. Ce RFC décrit un changement dans ses serveurs de noms.

Ce TLD est décrit dans le RFC 3172. Le nom de .arpa fait référence à l'ancien nom de la DARPA, l'agence qui avait financé le développement de l'Internet. Mais, aujourd'hui, il veut dire « Address and Routing Parameter Area » et le TLD sert à diverses fonctions techniques comme la résolution d'adresses IP en noms de domaine via des sous-domaines comme ip6.arpa. Par exemple, l'adresse IP du serveur Web de l'IAB a pour nom correspondant :


% dig -x 2001:1900:3001:11::2c
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53150
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
...
;; ANSWER SECTION:
c.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.1.0.0.1.0.0.3.0.0.9.1.1.0.0.2.ip6.arpa. 3600 IN PTR mail.ietf.org.

Comme vous le voyez, dig a inversé l'adresse IP et ajouté ip6.arpa à la fin. .arpa sert également à d'autres fonctions comme le home.arpa du RFC 8375.

Traditionnellement, le domaine .arpa était hébergé sur une partie des serveurs de noms de la racine. Le 22 octobre 2021, voici quels étaient les serveurs faisant autorité pour .arpa :


% dig +nodnssec NS arpa 
...
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43670
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 12, AUTHORITY: 0, ADDITIONAL: 1
...
;; ANSWER SECTION:
arpa.			15910 IN NS b.root-servers.net.
arpa.			15910 IN NS f.root-servers.net.
arpa.			15910 IN NS d.root-servers.net.
arpa.			15910 IN NS l.root-servers.net.
arpa.			15910 IN NS e.root-servers.net.
arpa.			15910 IN NS a.root-servers.net.
arpa.			15910 IN NS c.root-servers.net.
arpa.			15910 IN NS i.root-servers.net.
arpa.			15910 IN NS h.root-servers.net.
arpa.			15910 IN NS g.root-servers.net.
arpa.			15910 IN NS k.root-servers.net.
arpa.			15910 IN NS m.root-servers.net.

;; Query time: 0 msec
;; SERVER: ::1#53(::1)
;; WHEN: Fri Oct 22 20:35:36 CEST 2021
;; MSG SIZE  rcvd: 241

  

Comme vous pouvez le voir, c'était presque tous les serveurs de la racine (si vous aimez les jeux : quel serveur de la racine n'hébergeait pas .arpa ?). La raison pour cela est que .arpa est critique et doit donc bénéficier d'un hébergement solide. Profiter des serveurs de la racine était donc intéressant. Mais le problème est que ça lie .arpa à la racine. On pourrait avoir envie de faire des changements dans les serveurs faisant autorité pour .arpa sans toucher aux serveurs de la racine, celle-ci étant encore plus critique (cf. RFC 7720). Le principe de base de notre nouveau RFC est donc : découpler les serveurs DNS de .arpa de ceux de la racine (ce qui avait été fait pour ip6.arpa il y a dix ans, voir le RFC 5855).

Le changement ne concerne que l'hébergement DNS, pas la gestion de .arpa (section 2 de notre RFC), qui reste un TLD critique, et soumis au RFC 3172. Le choix des sous-domaines et de leur administration est inchangé (par exemple, pour ip6.arpa, c'est décrit dans le RFC 5855).

La section 3 du RFC décrit le nouveau système. Le principe est de commencer par utiliser des noms différents pour les serveurs, mais qui pointeront vers les mêmes machines, au moins au début. Les nouveaux noms sont dans le sous-domaine ns.arpa, on a donc a.ns.arpa, b.ns.arpa, etc. Aucune modification dans les serveurs ne sera nécessaire pour cette première étape, qui n'affectera que la zone racine et la zone .arpa. Comme tous les noms seront dans la zone qu'ils servent, il faudra ajouter de la colle aux réponses DNS (ne pas juste dire « le serveur est c.ns.arpa » mais également indiquer son adresse IP). Il n'y a désormais plus de nom de serveur commun à la racine et à .arpa, et il sera possible dans le futur de migrer vers d'autres serveurs. (Toujours en respectant le RFC 3172.) Voici l'état actuel :

  

% dig +nodnssec NS arpa 

; <<>> DiG 9.16.1-Ubuntu <<>> +nodnssec NS arpa
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15962
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 12, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;arpa.			IN NS

;; ANSWER SECTION:
arpa.			517877 IN NS h.ns.arpa.
arpa.			517877 IN NS i.ns.arpa.
arpa.			517877 IN NS k.ns.arpa.
arpa.			517877 IN NS l.ns.arpa.
arpa.			517877 IN NS m.ns.arpa.
arpa.			517877 IN NS a.ns.arpa.
arpa.			517877 IN NS b.ns.arpa.
arpa.			517877 IN NS c.ns.arpa.
arpa.			517877 IN NS d.ns.arpa.
arpa.			517877 IN NS e.ns.arpa.
arpa.			517877 IN NS f.ns.arpa.
arpa.			517877 IN NS g.ns.arpa.

;; Query time: 3 msec
;; SERVER: 192.168.2.254#53(192.168.2.254)
;; WHEN: Wed May 04 08:57:47 CEST 2022
;; MSG SIZE  rcvd: 228

Vous pouvez également voir cet état actuel de .arpa à l'IANA ou bien dans le DNS.

Notez enfin que certains des serveurs de .arpa autorisent le transfert de zones. Voici une copie faite le 24 octobre 2021.


Téléchargez le RFC 9120


L'article seul

RFC 9117: Revised Validation Procedure for BGP Flow Specifications

Date de publication du RFC : Août 2021
Auteur(s) du RFC : J. Uttaro (AT&T), J. Alcaide, C. Filsfils, D. Smith (Cisco), P. Mohapatra (Sproute Networks)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 28 septembre 2021


Le RFC 8955 spécifie comment utiliser BGP pour diffuser des règles de filtrage (dites « FlowSpec ») aux routeurs. On voit facilement qu'une annonce de règles de filtrage maladroite ou malveillante pourrait faire bien des dégâts et c'est pour cela que le RFC 8955 insiste sur l'importance de valider ces annonces. Mais les règles de validation étaient trop strictes et ce nouveau RFC les adoucit légèrement.

Le changement ? Le RFC 8955 imposait que la machine qui annonce une règle de filtrage pour un préfixe de destination donné soit également le routeur suivant (next hop) pour le préfixe en question. Cela limitait l'annonce aux routeurs situés sur le trajet des données. En iBGP (BGP interne à un AS, où les routeurs qui annoncent ne sont pas toujours ceux qui transmettent les paquets), cette règle était trop restrictive. (Pensez par exemple à un réflecteur de routes interne, par exemple géré par le SOC, réflecteur qui n'est même pas forcément un routeur.)

La règle nouvelle, plus libérale, ne concerne que du iBGP, interne à un AS ou à une confédération d'AS (RFC 5065). Entre des AS gérés par des organisations différentes (par exemple un AS terminal informant son opérateur Internet de ses désirs de filtrage), la règle de validation reste inchangée.

Plus formellement, la section 6 du RFC 8955, sur la validation des annonces de filtrage, est modifiée, l'étape « l'annonceur doit être également l'annonceur de la meilleure route » devient « l'annonceur doit être l'annonceur de la meilleure route ou bien le chemin d'AS doit être vide (ou n'inclure que des AS de la confédération, s'il y en a une) ». Le RFC précise également que cette validation relâchée doit pouvoir être désactivée par l'administrateur réseaux (s'ielle sait qu'il n'y aura pas d'annonces de filtrage par un routeur qui n'est pas sur le chemin des paquets). Vous noterez plus bas que, pour l'instant, seul Huawei le permet.

Il y a également un léger changement des règles de validation du chemlin d'AS, pour les cas des serveurs de route (a priori, ils ne relaient pas ce genre d'annonces et peuvent donc les rejeter). Le RFC note que, même avec une validation stricte, certaines faiblesses de BGP permettent quand même dans certains cas à tort l'envoi de règles de filtrage. C'est un problème fondamental du RFC 8955, qui n'est donc pas nouveau.

Les nouvelles règles de ce RFC sont déjà largement mises en œuvre chez les routeurs.


Téléchargez le RFC 9117


L'article seul

RFC 9116: A File Format to Aid in Security Vulnerability Disclosure

Date de publication du RFC : Avril 2022
Auteur(s) du RFC : E. Foudil, Y. Shafranovich (Nightwatch Cybersecurity)
Pour information
Première rédaction de cet article le 28 avril 2022


Lorsqu'on repère un problème de sécurité sur un site Web ou, plus généralement, dans l'informatique d'une organisation, de deux choses l'une : soit on est un méchant et on exploite l'information à son profit, soit on fait partie des bons et on va essayer de faire en sorte que le problème soit résolu. Une solution évidente est de signaler le problème à l'organisation qui gère le site Web. Mais comment ? Entre le formulaire de contact cassé qui refuse votre adresse de courrier en prétendant qu'elle est illégale, et le message envoyé à un humain sous-payé et sous-qualifié qui cliquera tout de suite sur « Problème résolu » pour faire grimper ses chiffres de résolution, prévenir quelqu'un de compétent et de responsable est souvent un parcours du combattant ! D'où le choix de ce RFC de proposer encore une nouvelle méthode : un fichier à un endroit bien connu sur le site Web, au format structuré, et contenant les informations essentielles, le security.txt.

Comme beaucoup de techniques utilisées sur l'Internet, ce format a été déployé bien avant d'être officiellement décrit dans un RFC. On trouve aujourd'hui de tels fichiers sur plusieurs sites, y compris sur ce blog (regardez-le tout de suite si vous voulez avoir une idée de ce qu'on y met).

La section 1 du RFC décrit plus en détail le problème. Toute personne qui a déjà essayé de signaler un problème de sécurité à une organisation reconnaitra ses propres mésaventures. Le RFC commence par rappeler que trouver un contact, quoique très difficile, n'est pas tout : il faut aussi s'informer sur la politique de traitement des signalements de cette organisation, car plus d'un citoyen ayant voulu signaler une vulnérabilité s'est retrouvé accusé d'être un vilain pirate, et a parfois réellement été poursuivi en justice. Et enfin il faut s'informer sur les moyens de communications sécurisés puisque, par définition, on va transmettre des informations délicates. Est-ce qu'on peut utiliser PGP (RFC 4880), par exemple ? Pour ce qui est des contacts, il existe en théorie plusieurs solutions :

  • Le RFC 2142 exige un certain nombre d'adresses de courrier pour chaque domaine, comme security@LE-DOMAINE pour les signalements de sécurité. Il y a peu d'organisations où cette adresse fonctionne techniquement et, même quand c'est le cas, elle n'est pas forcément lue.
  • Les RFC 3013 (section 2), RFC 2350 (section 3.2) et RFC 2196 (section 5.2) prévoient des adresses de contact pour différents types d'acteurs. (Même remarque que sur le point précédent.)
  • Les bases des registres (de noms de domaine et d'adresses IP) contiennent également des adresses de contacts, en général accessibles via whois (cf. RFC 7485) ou RDAP. (Même remarque que sur le point précédent, en ajoutant que ces bases sont purement déclaratives, que leur exactitude est faible et baisse avec le temps.)
  • Aucune de ces solutions ne donne facilement accès à la politique de divulgation de l'organisation. Et, surtout, même si le RFC garde un silence poli sur la question, toute personne qui a essayé de contacter une organisation sait bien que ces adresses de contact sont souvent erronées, dépassées, ou bien arrivent chez des gens qui s'en f...ent.

Le dernier point est à la fois une motivation important pour créer une nouvelle solution et une des principales critiques qui avaient été formulées à l'encontre du projet security.txt lors de la discussion à l'IETF : pourquoi est-ce que ça serait mieux avec un nouveau format ? Si une organisation est assez négligente pour ne pas tenir à jour les informations présentes chez le registre de son nom de domaine, comment espérer qu'elle tienne à jour son security.txt ? Il n'y a évidemment pas de certitude à ce sujet, juste l'espoir qu'un autre format, et surtout un autre mécanisme de mise à jour augmentera les chances que l'information soit correcte.

Le security.txt est un fichier analysable par un programme (mais quand même lisible par un humain), et qui permet d'indiquer tout ce qui est nécessaire, par exemple au chercheur de vulnérabilités qui a trouvé quelque chose. Il vise à signaler les vulnérabilités (pas encore exploitées), pas les incidents (car un attaquant qui a réussi a peut-être modifié le fichier security.txt).

La spécification, maintenant (section 2 du RFC). Le security.txt est un simple fichier texte qui doit être déposé à un endroit précis du site Web, /.well-known (RFC 8615, et security.txt a été enregistré dans le registre des URL bien connus). Son type doit être text/plain et il doit être accessible en HTTPS, pour d'évidentes raisons de sécurité. Il doit être en Unicode, utilisant le profil du RFC 5198. Analysable par un programme, il doit se conformer à la grammaire spécifiée dans le RFC. Il comporte plusieurs champs, chacun sur une ligne, et ayant un nom et une valeur. La plupart des champs sont optionnels. Le champ le plus connu est Contact, qui est obligatoire, et voici un exemple :

Contact: mailto:stephane%2Bsecurity@bortzmeyer.org
  

(Vous avez reconnu l'encodage de l'URI spécifié dans la section 2.1 du RFC 3986. %2B est le signe plus.) Les lignes commençant par un croisillon sont des commentaires.

Le RFC recommande que les security.txt soient signés avec OpenPGP (RFC 4880). Naturellement, comme avec n'importe quelle signature, sa valeur dépend de si le vérificateur connait de manière certaine la clé publique qui a été utilisée.

Un certain nombre de champs sont définis aujourd'hui, la plupart optionnels. Parmi eux :

  • Acknowledgments : un lien vers une page des remerciements, pour que la personne ayant l'intention de signaler un problème sache qu'elle pourra être remerciée publiquement. Question carotte pour les chercheurs de vulnérabilités, il y a aussi un champ Hiring ☺.
  • Canonical : l'URL officiel de ce security.txt. Cela peut permettre de détecter certaines erreurs de configuration (security.txt copié sur le mauvais site).
  • Contact : sans doute le champ le plus important, et un des rares qui soit obligatoire. Il contient un URI indiquant comment contacter quelqu'un de responsable, à qui confier le problème de sécurité. L'intérêt de définir la valeur de ce champ comme un URI est que cela permet de ne pas se limiter à une méthode de contact particulière. Puisque c'est un URI, les adresses de courrier électronique doivent être mises sous forme d'URI mailto: (