Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

echoping

Ève

Recherche dans ce blog :

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


RFC 7336: Framework for Content Distribution Network Interconnection (CDNI)

Date de publication du RFC : Août 2014
Auteur(s) du RFC : L. Peterson (Akamai Technologies), B. Davie (VMware), R. van Brandenburg (TNO)
Pour information
Réalisé dans le cadre du groupe de travail IETF cdni
Première rédaction de cet article le 22 août 2014


Ce RFC est le nouveau cadre technique pour les solutions standard d'interconnexion de CDN. Après le document de description du problème (RFC 6707), une liste de scénarios d'usage (RFC 6770) et un cahier des charges formel (RFC 7337, voici une nouvelle étape du travail du groupe CDNI de l'IETF. Il remplace un ancien travail équivalent, qui n'avait pas débouché, dans le RFC 3466.

Personnellement, je trouve qu'il ne va pas très loin par rapport aux RFC 6707 et RFC 7337. Mais, bon, c'est la description officielle de l'architecture des futurs protocoles d'interconnexion de CDN donc, allons-y.

N'attendez pas de ce RFC des protocoles précis : les spécifications viendront plus tard, ce qui est décrit ici est une architecture de haut niveau, très proche de ce qui avait été écrit dans le RFC 6707. (Par contre, terminologie et architecture du vieil RFC 3466 sont abandonnés.) Le principal ajout est que l'interface de routage des requêtes (Request routing interface) est désormais découpée en deux, une interface de redirection (Request routing indirection Interface) et une interface d'échange d'informations (Footprint & Capabilities Interface). Autrement, je vous renvoie à mon article sur le RFC 6707. Outre la terminologie des RFC 6707 et RFC 7337, quelques concepts nouveaux apparaissent :

  • [Rappel] « CDN amont » : celui sollicité par le client qui veut du contenu, mais qui n'a pas le contenu (ou bien, pour une raison ou pour une autre, qui ne peut pas / ne veut pas servir la requête) et doit donc utiliser les mécanismes CDNI pour déléguer ; le RFC écrit aussi uCDN pour upstream CDN,
  • [Rappel] « CDN aval » : celui qui a le contenu et va donc servir le client ; le RFC écrit aussi dCDN pour downstream CDN,,
  • « Domaine CDN » : le nom de domaine dans l'URL qui identifie le CDN. Par exemple, pour le site Web de l'Élysée, qui utilise le CDN de Level 3, c'est actuellement cdn.cdn-tech.com.c.footprint.net.
  • « Redirection Itérative » : lorsque le CDN amont renvoie simplement au CDN aval, sans essayer de traiter la requête lui-même,
  • « Redirection Récursive » : lorsque le CDN amont fait le travail de redirection lui-même, sans que le client qui accédait au contenu en soit même conscient.

La section 3 du RFC est un survol général du fonctionnement de CDN interconnectés. L'opérateur A gère le CDN amont, celui qui a un lien avec le fournisseur de contenu, qui a choisi A pour servir ses ressources. L'opérateur A a un accord avec l'opérateur B, qui gère un possible CDN aval, qui pourra servir des clients intéressés, par exemple sur une base de proximité géographique (mettons que A est en Europe et B aux États-Unis). Avant que le client n'arrive, A et B auront donc échangé des messages (via les interfaces MI et FCI, cf. RFC 7337) pour que B sache quoi servir (interface MI, envoi de métadonnées) et pour que A sache les capacités de B (interface FCI). Lorsque le client final envoie sa requête, elle est d'abord dirigée (par les techniques habituelles des CDN existants) vers A. Via l'interface RI, le client est redirigé vers B (voir plus loin les techniques de redirection). Le client envoie alors ses requêtes à B. Celui-ci doit contacter A pour obtenir le contenu lui-même (cette opération n'utilise pas les protocoles CDNI) et il sert ce contenu au client. Ensuite, il contactera A pour remplir les statistiques de ce dernier (interface LI).

La section 3 détaille ensuite les mécanismes de redirection récursifs et itératifs. La section 2 du RFC avait déjà résumé ces techniques de base utilisées par les CDN pour la redirection des requêtes. Dans le cas le plus simple, un client (par exemple un navigateur Web) va interroger un CDN qui, soit lui répondra, soit le renverra vers le CDN aval. Pour cette redirection, on peut utiliser le DNS ou bien des mécanismes de redirection du protocole applicatif (par exemple, en RTSP, un code de retour 302). D'abord, le DNS. Le principe est d'envoyer des réponses différentes selon le client. Ce dernier est situé en Europe et interroge un CDN amont qui ne sert de contenu qu'en Amérique ? Pas de problème, aux requêtes sur le domaine CDN, on renvoie l'adresse IP d'un CDN aval qui est présent en Europe. Il y a quelques précautions à prendre (se souvenir que les réponses DNS seront gardées en cache et qu'il faut donc choisir intelligemment le TTL, sans compter le fait que certains résolveurs trichent en gardant en cache plus longtemps, prêter attention à DNSSEC si on génère les redirections au vol, cf. section 3.4.1) mais cette méthode est simple et est déjà utilisée par les CDN actuels, non connectés entre eux. L'avantage de la technique DNS est qu'elle est complètement invisible à l'application, qui ne se rend même pas compte qu'elle a été servie par un autre CDN que celui demandé. L'un de ces inconvénients est que le serveur DNS qui fait autorité ne connait pas le client mais son résolveur DNS. Si le client utilise un résolveur DNS public (comme OpenDNS), le client du contenu et le résolveur peuvent être sur des continents différents et la redirection ne se fera pas vers le CDN optimal. Autre inconvénient, on ne pourra tenir compte, dans l'URL original, que du nom de domaine, le reste de l'URL n'étant pas connu du serveur DNS.

À noter qu'il existe une variante, où on ne renvoie pas directement l'adresse IP mais un nom (technique CNAME ou, mieux, DNAME). C'est le cas de l'Élysée citée plus haut :

% dig A www.elysee.fr
...
;; ANSWER SECTION:
www.elysee.fr.		3600 IN	CNAME cdn.cdn-tech.com.c.footprint.net.

Autre possibilité de redirection, la redirection HTTP : cette fois, on se sert des codes de retour 301 ou 302 qui indiquent au client HTTP qu'il doit aller chercher ailleurs ce qu'il voulait. La nouvelle URL est donnée dans la réponse (en-tête Location:). Cette fois, le serveur peut changer tout l'URL, pas juste le nom de domaine, on connait l'adresse IP du client original (le RFC oublie de mentionner qu'il peut être un relais), ce qui peut permettre de mieux choisir le serveur, et enfin on a plein d'autres informations qui peuvent être utiles pour une redirection intelligente, comme la marque du logiciel client utilisé.

Il y a par contre des inconvénients, le changement de domaine fait que les cookies ne suivront pas, et les caches ne gardent pas en mémoire les redirections (le RFC 7234 le permet mais ce n'est pas fréquemment mis en œuvre).

La section 4 du RFC donne quelques détails sur les différentes interfaces du système, interfaces qui avaient été présentées dans les RFC 6707 et RFC 7337. Dans certains cas, l'interconnexion des CDN se fait à l'intérieur du protocole de communication déjà utilisé (in-band). C'est le cas des redirections HTTP. Dans d'autres cas, on utilise un protocole externe (out-of-band). Il faut également noter, pour comprendre la complexe combinaison d'interfaces de CDNI que, bien que les interfaces soient largement indépendantes, elles reposent sur des conventions communes, par exemple pour le nommage des ressources.

Parmi les détails creusés dans ce RFC 7336 sur les interfaces :

  • Une liste des éléments d'information qui peuvent être intéressants pour l'interface de journalisation, LI (Logging Interface). Conceptuellement, cette interface peut être vue simplement comme une copie des fichiers apache/access.log du CDN aval vers l'amont... Attention, le champ indiquant le nom de domaine demandé (VirtualHost dans Apache) peut être différent de l'original, s'il y a eu redirection HTTP.
  • Certains services de l'interface de communication de métadonnées (MI pour Metadata Interface) peuvent être assurés par le mécanisme de redirection. Par exemple, si le fournisseur de contenu veut limiter l'accès aux utilisateurs européens, il suffit que le CDN amont ne redirige pas vers le CDN américain, pour les requêtes de ce contenu à accès limité. (Le RFC note toutefois que cela manque de souplesse : par exemple, il y a peu de chances que le CDN aval ait une couverture géographique qui coïncide parfaitement avec les exigences de géo-limitation du fournisseur de contenu.)

La section 5 du RFC décrit plusieurs scénarios d'usage de CDNI quand il sera terminé. Par exemple, bien que le modèle de référence (et les exemples que j'ai cité) soient unidirectionnels (le CDN A délègue au CDN B et jamais le contraire), on pourra se servir de CDNI pour les CDN maillés, avec des graphes arbitraires entre les différents CDN.

Déléguer ses fonctions soulève évidemment tout un tas de questions, comme toute sous-traitance. Le CDN amont peut-il faire confiance au CDN aval pour servir le contenu correctement ? Les statistiques de trafic qu'il remonte sont-elles correctes ou bien modifiées dans l'intérêt exclusif du CDN aval ? La section 6 explore le problème de la confiance dans un monde de CDN connectés. Notons que le fournisseur de contenu a déjà aujourd'hui le même problème avec son opérateur CDN : dès qu'on sous-traite, la question de la confiance surgit. Mais CDNI rend les choses un peu plus compliquées : un fournisseur de contenu qui a signé avec l'opérateur de CDN amont peut ne pas être conscient que celui-ci a un accord avec l'opérateur CDN aval. Et si un opérateur CDN modifiait les fichiers avant de les envoyer ? Il existe évidemment des solutions techniques, comme la signature cryptographique des fichiers. Mais, fondamentalement, il faut une combinaison de confiance et de quelques vérifications (par exemple des sondages aléatoires faits par divers clients pour vérifier que le contenu est bien là, et bien conforme à ce qui est demandé).

En parlant de confiance et de sécurité, la section 7 se demande quelles sont les conséquences de l'interconnexion de CDN pour la vie privée (l'existence d'une section nommée Privacy considerations est récente dans les RFC et vient du RFC 6973). Un CDN est bien placé pour observer beaucoup de choses dans le comportement de l'utilisateur, par exemple à quels fichiers il accède, de mécanique_quantique_pour_les_nuls.epub à how_to_make_bombs.avi. L'utilisateur qui accède à un site Web ne sait typiquement pas si celui-ci utilise un CDN (cela se trouve relativement facilement mais tout le monde n'a pas les compétences ou le temps nécessaire). Il fait confiance au site Web, mais ne se rend pas compte qu'il fait également confiance à l'opérateur CDN (qui peut avoir une autre éthique, ou être soumis à d'autres lois : lorsqu'on visite http://www.elysee.fr/, on croit faire confiance à un organisme public français, alors qu'il est en fait servi par un CDN état-unien, soumis au Patriot Act).

Certaines fonctions de CDNI sont justement conçues pour faire circuler de l'information sur l'activité des utilisateurs, notamment l'interface de journalisation (LI). Il faudra donc s'assurer qu'elles disposent de protections appropriées.

Et, toujours sur la sécurité, la section 8 note également d'autres points délicats, qui s'ajoute aux questions de sécurité déjà existantes pour les CDN actuels :

  • Certaines exigences des fournisseurs de contenu (distribution limitée géorgraphiquement, ou bien limitée dans le temps) sont plus difficiles à satisfaire lorsqu'il y a interconnexion des CDN,
  • Par contre, les menottes numériques ne sont pas affectées par CDNI puisqu'elles reposent typiquement sur des mécanismes présents dans le contenu lui-même,
  • Les interfaces entre CDN doivent être protégées, contre l'écoute (cas des journaux d'activité dans la section précédente) mais aussi contre les modifications non autorisées (imaginez qu'un méchant modifie les contenus servis lorsqu'ils passent d'un CDN amont au CDN aval), même si l'interconnexion se fait en utilisant l'Internet public, non sécurisé.

Deux fournisseurs de solutions techniques, Cisco et Alcatel-Lucent, ont annoncé qu'ils travaillaient sur des prototypes d'interconnexion de CDN, utilisant le futur protocole qui sera la concrétisation de ce cadre.


Téléchargez le RFC 7336


L'article seul

RFC 7305: Report from the IAB Workshop on Internet Technology Adoption and Transition (ITAT)

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : E. Lear
Pour information
Première rédaction de cet article le 19 août 2014


En décembre 2013, à Cambridge, l'IAB a tenu un atelier sur l'adoption des nouvelles technologies dans l'Internet. Qu'est-ce qui fait que telle technologie réussit dans le monde réel et que telle autre reste éternellement sur le papier ? Sérieuse question pour l'IETF, qui a a normalisé aussi bien des succès fous que des échecs plus ou moins complets. (Par exemple, le déploiement de la grande œuvre de l'IETF, IPv6, est extrêmement lent.)

Ce RFC résume les contributions à l'atelier. Comme on s'en doute, il n'y a eu aucun consensus, à part sur l'importance d'étudier l'angle de l'économie, et sur la référence à Bitcoin, grande vedette de l'atelier et source de nombreux exemples. Attention, donc, ce RFC n'est pas une prise de position de l'IAB, mais le compte-rendu d'un colloque pluraliste, aussi bien dans ses participants que dans les opinions exprimées. (Le rôle de l'IAB n'est pas de faire des protocoles - c'est la tâche de l'IETF, sous la direction de l'IESG - mais de réfléchir sur le long terme et sur les questions les plus fondamentales.)

On représente souvent l'écosystème de l'Internet sous forme d'un sablier : le protocole standard d'interconnexion est forcément unique et occupe donc la « taille », le tuyau fin du sablier. Au contraire, les protocoles d'application, beaucoup plus nombreux, occupent le bulbe du haut, et les protocoles « physiques », pour lesquels il y a également un grand choix, sont le bulbe du bas. Dans la vision traditionnelle de l'Internet, la taille était IP Dans la biologie, c'est l'ATP qui joue ce rôle central, la brique de base sur laquelle tout le reste est bâti (papier de Meyer).

Aujourd'hui, en raison de l'ossification de l'Internet et de la prévalence de stupides middleboxes qui bloquent tout, la taille (ou faut-il l'appeler le cou ?) du sablier est plutôt HTTP... Il devient de plus en plus difficile pour un protocole de s'imposer, surtout s'il ne tourne pas sur HTTP. En prime, le poids de l'existant est bien plus important qu'aux débuts de l'Internet. Aujourd'hui, un nouveau protocole doit lutter contre des concurrents bien installés.

La section 2 de notre RFC résume les motivations de ce travail, et les travaux équivalents déjà réalisés. On constate souvent que des protocoles, développés soigneusement par l'IETF, ont peu ou pas de succès dans le monde réel et, quand ils se répandent, c'est à un rythme bien trop lent. Trois exemples sont cités par le RFC, IPv6 (RFC 2460), SCTP (RFC 4960) et DNSSEC (RFC 4034). Ce retard peut à son tour retarder d'autres déploiements. Ainsi, DNSSEC est indispensable à DANE (RFC 6698) et le déploiement trop lent de DNSSEC, goulet d'étranglement, affecte donc DANE. Par contre, si les protocoles nouveaux ont du mal, les protocoles existants continuent à évoluer (le RFC cite SMTP et IMAP, qui peuvent être vus comme des réfutations de la théorie comme quoi « HTTP est le nouvel IP, la taille du sablier »).

Un excellent RFC avait déjà étudié les causes du succès ou de l'échec des protocoles, le RFC 5218. Une conséquence de ce RFC 5218 est l'importance plus grande désormais donnée aux facteurs de réussite. Faire un bon protocole ne suffit plus. Les groupes de travail de l'IETF doivent désormais aussi réfléchir à la valeur ajoutée de leurs propositions : le nouveau protocole apporte t-il suffisamment pour surmonter les barrières situées devant l'adoption ?

Ces barrières étant largement financières, il n'est pas étonnant que plusieurs des papiers présentés à l'atelier portaient sur l'économie (section 3). Le RFC en résume quelques uns. D'abord, la théorie de Weber, S., Guerin, R., et J. Oliveira, sur le couplage. Peut-on augmenter les chances de succès d'un protocole en le livrant avec d'autres ? Ou, au contraire, le couplage va t-il ralentir le succès ? Comme cité précédemment, DANE dépend de DNSSEC. Cela simplifie son développement, permettant de s'appuyer sur les propriétés de sécurité de DNSSEC. Mais, du point de vue économique, cela fait dépendre DANE du succès de DNSSEC. Le couplage peut aider quand une technologie à succès en tire une autre, et ralentir si une technologie dépend d'une autre.

Autre exemple, très discuté à l'atelier (et c'est une nouveauté à l'IETF, puisque ce système a été développé en dehors de celle-ci), le succès de Bitcoin. Dans son papier, R. Boehme suggère que les protocoles Internet s'inspirent de Bitcoin, exemple de grand succès (section 3.2 de notre RFC). Bitcoin n'a pas eu la partie facile : des médias hostiles (parfois au point de la propagande, comme dans le mensonge souvent répété comme quoi Bitcoin était une pyramide de Ponzi), des incertitudes juridiques (est-ce bien légal ?), des failles techniques (immédiatement exploitées par des voleurs), plusieurs tentatives de « monnaies Internet » avaient déjà été des échecs, des attaques par des spéculateurs visant le gain à court terme, même au prix de l'existence même du système. Mais Bitcoin a survécu à ces campagnes de presse et à ces problèmes techniques et de sécurité. Selon l'auteur, les raisons principales sont :

  • Un mécanisme de récompense des premiers qui adoptent le système. Au début de Bitcoin, miner (créer de nouveaux bitcoins) est relativement facile et cela se durcit par la suite. Les plus difficiles à convaincre, les premiers adoptants, sont donc récompensés davantage, ce qui est logique et efficace. (J'ajoute personnellement que c'est juste : il est normal que ceux qui ont pris des risques au début reçoivent plus que ceux qui attendaient passivement.)
  • Bitcoin ne vit pas dans son coin, isolé. Dès le début, des mécanismes de conversion ont été mis en place de et vers les monnaies d'État. Ainsi, adopter Bitcoin ne signifiait pas s'enfermer dans un monde à part. Un autre exemple, cité par le RFC, est le succès du courrier électronique : dès le début, le courrier Internet a eu des passerelles de et vers les autres systèmes de courrier. Autrement, il n'aurait pas été choisi.
  • Les bitcoins ont une valeur pour leurs possesseurs, ce n'est pas juste pour le bien de l'humanité qu'on demande aux utilisateurs d'adopter la nouvelle technologie : ils y trouvent un intérêt. (Une remarque personnelle : les discussions à l'atelier supposaient acquis un point de vue égoïste, où les gens n'agissent qu'en fonction d'un intérêt personnel à court terme. Le problème serait très différent si on se plaçait dans une perspective plus volontariste, où on considère que les humains peuvent changer leur mentalité.)

Bref, Bitcoin fournit des motivations à ses adopteurs. (Un problème bien connu des militants pro-IPv6 : lorsqu'on suggère aux gens de migrer vers IPv6, la question est souvent « qu'est-ce que ça m'apporte, à moi ? », et elle n'a guère de réponse.)

Troisième étude de cas, le déploiement de DNSSEC (section 3.3) via le récit de A. Eklund Lowinder et P. Wallstrom sur le déploiement de cette technique en Suède, à commencer par le TLD .se. Le travail d'IIS pour encourager DNSSEC a pris la forme d'une contribution au financement d'OpenDNSSEC (pour abaisser les barrières techniques), et d'incitations financières (auprès des bureaux d'enregistrement).

Une des conclusions de cette partie de l'atelier était qu'il faut éviter d'accrocher une technologie exclusivement à une autre. Le logiciel privateur Skype a été cité comme exemple : il teste plusieurs transports disponibles et choisit celui qui marche. De même, lier une application à SCTP serait probablement une erreur : si SCTP ne réussit pas à se répendre, cette application serait fichue). Il faudrait plutôt essayer plusieurs transports et garder le premier qui marche, un peu comme le fait l'algorithme des globes oculaires heureux dans le RFC 6555.

Et puisqu'on parlait d'économie, il y a eu évidemment des discussions sur le modèles de prix, comme suite au papier de S. Sen. Par exemple, certains protocoles pourraient bénéficier d'un prix dépendant de l'heure (pour encourager le trafic à se déplacer vers les heures creuses).

Ah, une question qui est dans la section 5 mais qui aurait eu sa place dans celle sur l'économie : celle des brevets. Leurs effets néfastes sont bien connus. Le papier de E. Lear et D. Mohlenhoff décrit leurs relations avec la normalisation. Le plus gros problème identifié est celui des « brevets sous-marins », brevets que l'IETF ne connait pas... jusqu'au moment où il est trop tard.

Mais l'économie ne peut pas tout résoudre. Les choix se font aussi, mais si ce n'est pas la seule motivation, sur la qualité technique. De ce point de vue là, pour qu'un protocole réussisse, il faut aussi faire en sorte qu'il soit le « meilleur » possible. Par exemple, est-ce que le processus de normalisation de l'Internet est suffisamment bien connecté au monde de la recherche, où s'élaborent les idées du futur ? Normalement, l'IETF ne normalise que des technologies bien maîtrisées. L'interaction avec la recherche se fait via l'IRTF.

Les chercheurs suggèrent parfois que l'architecture actuelle de l'Internet n'est plus adaptée aux défis futurs, et qu'il faudrait la changer, par exemple dans la direction du Lowest Common Denominator Networking, idée qui a apparemment été mal reçue à l'atelier.

Un atelier n'est pas complet sans une rubrique « Divers » (section 6) et deux autres points ont donc été abordés. D'abord, la résilience de l'Internet, et notamment la sécurité du routage. Le papier d'A. Robachevsky l'abordait en partant de l'éculée référence à la tragédie des terres communales. L'Internet est composé d'un grand nombre de réseaux distincts, sans administration centrale (ce qui est une bonne chose). La tentation peut donc être forte de se servir des communs (comme la table de routage globale) sans souci de leur pérennité, sans gestion collective.

Et un deuxième point « Divers » concernait TLS. TLS est très largement adopté et ne souffre pas, a priori, du syndrome du protocole qu'on n'arrive pas à déployer. Mais la très grande majorité des clients et serveurs existants utilisent des vieilles versions du protocole TLS, ayant des vulnérabilités connues (comme Beast). La version la plus récente de TLS, la 1.2, est normalisée dans le RFC 5246. Comment faire en sorte qu'elle soit déployée partout ? Et le problème ne concerne pas que le protocole lui-même mais aussi les algorithmes de cryptographie utilisés. (Au cours de l'atelier, il a été noté qu'une grande partie du trafic est toujours chiffrée avec Triple DES, algorithme aux faiblesses connues.)

Maintenant, les actions (section 7). La conclusion de ce RFC reconnait l'importance de regarder le succès des systèmes comme Bitcoin, mentionné pour la première fois dans un RFC, et un exemple pour tout nouveau système. Autrement, l'idée la plus intéressante me semble être celle de pousser la collaboration avec le monde académique, par exemple en enrôlant des étudiants pour relire et critiquer les documents IETF. Cela fournirait à ces étudiants une excellente occasion d'apprendre, et à l'IETF plein de regards neufs.


Téléchargez le RFC 7305


L'article seul

RFC 7337: Content Distribution Network Interconnection (CDNI) Requirements

Date de publication du RFC : Août 2014
Auteur(s) du RFC : K. Leung (Cisco), Y. Lee (Comcast)
Pour information
Réalisé dans le cadre du groupe de travail IETF cdni
Première rédaction de cet article le 18 août 2014


Le projet IETF CDNI (Content Delivery Network Interconnection) vise à permettre l'interconnexion de CDN, ces réseaux de serveurs répartis dans le monde qui servent à amortir la charge des serveurs Internet les plus populaires. CDNI a été expliqué dans le RFC 6707 et trois études de cas ont fait l'objet du RFC 6770. Ce troisième RFC du projet décrit les exigences formelles, le cahier des charges du projet. (Le cadre général de la solution technique adoptée est dans le RFC 7336.)

Le projet est important car, aujourd'hui, les CDN ne coopèrent pas, en partie en raison du manque d'interfaces standards. Si un CDN très présent en Amérique veut s'associer avec un autre CDN fort en Europe, à la grande joie de leurs clients respectifs, qui auront ainsi un meilleur service dans les deux cas, ils doivent développer un système ad hoc. Le but de CDNI est de développer cette interface standard (RFC 7336), de manière à ce que plusieurs CDN puissent coopérer et apparaître à l'utilisateur comme un seul service. Dans le futur, une fois le projet complété, le CDN d'origine (« CDN amont ») pourra faire en sorte que le CDN qui l'aide (« CDN aval ») ait accès au même contenu et puisse le servir lui-même aux clients.

Les exigences listées par ce cahier des charges sont classées par priorité : « haute » signifie que cette exigence est impérative, même si elle est compliquée à réaliser, « moyenne » que l'exigence est importante, qu'elle doit être satisfaite sauf si, par sa complexité, elle entraîne un retard dans le projet et, enfin, « basse » est utilisé pour les exigences certes utiles mais pas nécessaires au projet.

Les exigences sont rangées selon l'interface à laquelle elles s'applique. Une interface (RFC 7336) est un ensemble de fonctions du CDN, qu'on peut appeler en utilisant des mécanismes normalisés, et qui correspondent à un ensemble de services proches. Par exemple, LI (Logging Interface) regroupe les services de journalisation, permettant à un CDN amont d'avoir des informations sur l'activité d'un CDN aval associé.

Je ne vais pas ici recopier les nombreuses exigences, me focalisant sur celles de niveau élevé, donc impératives à satisfaire.

D'abord, en section 3, les exigences générales, indépendantes de l'interface. La solution ne doit évidemment pas nécessiter de changer les clients (exigence GEN-2), par exemple les navigateurs Web. Elle ne doit pas nécessiter que le fournisseur de contenu change son système de publication (GEN-3) : si celui-ci permet de publier dans un CDN, il doit permettre de profiter de l'interconnexion. Elle doit être assez abstraite pour que chaque CDN soit une « boîte noire » pour les autres, sans avoir besoin de publier de l'information interne. Elle doit marcher au moins lorsque la délivrance au client est faite en HTTP (exigence GEN-5, il existe d'autres protocoles - voir GEN-7 - mais moins cruciaux), et elle doit éviter de créer des boucles entre CDN et elle doit fonctionner même quand les références à une tierce partie sont cassées (exigence GEN-12), par exemple à cause du NAT ou du split DNS.

Suivent en section 4 les exigences pour la CI. CI (Control Interface) est l'interface qui contrôle les autres interfaces, par exemple pour les opérations de démarrage (ce sera là qu'on indiquera le serveur de statistiques auquel il faudra indiquer l'ampleur du trafic, par exemple). Elle doit permettre au CDN amont de demander le nettoyage (la destruction de contenu, exigence CI-1), et elle doit fournir un mécanisme de rétroaction par lequel le CDN aval informe le CDN amont de ce qu'il a fait (CI-4).

En section 5, la RI (Request routing indirection Interface) est l'interface vers le système de routage des requêtes. Elle doit fonctionner rapidement quelle que soit la taille du contenu (exigence RI-1) ce qui veut dire qu'il faut un mécanisme de redirection ultra-léger pour les objets de petite taille (ceux où le temps de redirection risque de dépasser le temps de transfert des données), et qu'il faut pouvoir choisir le compromis entre fraîcheur des données et rapidité. Pour les gros objets (exigence RI-2) où le temps de transfert sera long, il faut bien choisir le CDN d'où il sera servi et il faut donc que la solution permette un choix précis, lié aux caractéristiques de la requête. Autrement dit, un mode où on passe du temps à sélectionner la source des données, pour que le transfert aille ensuite plus vite.

Il faut aussi que cette redirection puisse être récursive (exigence RI-3), c'est-à-dire que la cible d'une redirection va elle-même suivre les éventuelles nouvelles redirections, et itérative (RI-4), c'est-à-dire que l'initiateur suive lui même les redirections multiples. Cela implique que le CDN aval reçoive du CDN amont toutes les informations nécessaires (exigence RI-8), comme l'origine géographique de la requête, les en-têtes HTTP, etc, et à l'inverse que le CDN aval transmette la réponse complète dans le cas d'une redirection (l'URI complet en HTTP, exigence RI-10).

En section 6, la FCI (Footprint & Capabilities Interface). Elle permet l'échange d'informations entre CDN, de manière à permettre le routage des requêtes (leur transmission au CDN le plus approprié) par l'interface RI. Si on compare RRI (Request Routing Interface, une méta-interface qui regroupe FCI et RI) à IP, FCI est l'équivalent des protocoles d'échange de routes comme OSPF et RI est l'équivalent du lookup dans une table de routage.

La FCI doit au minimum permettre de communiquer au CDN amont que le CDN aval est prêt (exigence FCI-1). Idéalement (mais ces exigences sont seulement au niveau moyen, donc pas indispensables), il faudrait aussi qu'il puisse communiquer des méta-informations comme les formats et protocoles qu'il gère, son extension géographique, les protocoles de redirection, les capacités de journalisation, etc.

En section 7, l'interface MI (Metadata Interface), qui permet l'échange de métadonnées sur les contenus servis, comme les restrictions géographiques, les durées de vie des contenus, les limitations d'accès (par exemple, aux abonnés)... Pour cette interface, l'exigence de base (MI-1) est de permettre l'envoi d'informations depuis le CDN amont. Parmi ces informations, il y a évidemment l'endroit d'où le CDN aval doit tirer le contenu (exigences MI-5 et MI-6). Il faut pouvoir ajouter des métadonnées dans le CDN aval (exigence MI-7) et en retirer (MI-8). La granularité de ces métadonnées doit être au niveau d'un objet (MI-9) mais il faut aussi pouvoir regrouper les objets, de manière à gérer ces métadonnées plus facilement (MI-10), avec un système d'héritage entre les groupes d'objets (MI-11).

L'industrie du contenu étant ce qu'elle est, il n'est pas étonnant qu'une longue exigence MI-13 décrive toutes les conditions d'accès au contenu, filtrage selon les pays, selon les adresses IP, dans le temps, etc.

Enfin, en section 8, la LI (Logging Interface), qui permet de transmettre les informations sur l'activité du CDN, de manière à avoir des journaux d'activité. Le transports des informations de journalisation doit être fiable (exigence LI-1, pas question de se contentet de syslog sur UDP), doit être exhaustif (le CDN amont veut connaître tous les téléchargements faits à partir du CDN aval, exigence LI-2), le transferts des journaux doit pouvoir se faire en batch (LI-4), et utiliser un format standard (LI-6) et un transport standard (LI-7).

Il n'y a plus qu'à normaliser la sécurité (section 9). Bien sûr, la première exigence, SEC-1, est que toutes les opérations entre CDN soient sécurisables, même quand elles s'effectuent au dessus de l'Internet normal, non sécurisé. Il faut donc pouvoir fournir authentification, intégrité, et confidentialité. Mais il faut aussi se défendre contre les attaques par déni de service, dont les CDN sont souvent victimes (SEC-2).


Téléchargez le RFC 7337


L'article seul

RFC 7328: Writing I-Ds and RFCs using Pandoc and a bit of XML

Date de publication du RFC : Août 2014
Auteur(s) du RFC : R. Gieben (Google)
Pour information
Première rédaction de cet article le 13 août 2014


Vous voulez écrire un RFC ? Il faut commencer par faire un Internet-Draft (I-D) et il existe pour cela deux formats, en XML ou bien en Word. Ce RFC présente une troisième solution, utilisant Markdown, ou plutôt sa variante définie par Pandoc.

Le format d'écriture de RFC le plus répandu est sans doute XML, tel que décrit dans le RFC 2629. Le format Word est dans le RFC 5385. (Il existe aussi le format historique, utilisant nroff mais je ne suis pas sûr qu'il soit encore accepté aujourd'hui.) Certaines personnes trouvent que les tags XML les distraient pendant qu'ils écrivent leur Internet-Draft (I-D) et ils préfèrent donc utiliser un autre format « texte », avec les marquages de Markdown, jugés moins intrusifs. Markdown est très populaire et est utilisé à plein d'endroits différents (par exemple, dans les formulaires du système de suivi de bogues de GitHub). Markdown a plein de dialectes et de variantes et celle utilisée ici est celle du logiciel Pandoc, un sur-ensemble de Markdown. (Par abus de langage, j'écris ensuite « Pandoc » pour désigner ce format.)

(Au passage, vous pouvez regarder la page officielle de Markdown et celle de Pandoc.)

le document en Pandoc peut ensuite être traduit dans d'autres formats, HTML, EPUB, DocBook, etc, d'où on peut ensuite tirer le futur RFC dans un format de publication officiel (comme le texte seul). L'outil Pandoc2rfc convertit le Pandoc en Docbook, qui est ensuite traduit en XML-RFC2629, puis ensuite dans les différents formats de publication souhaités (on passe donc d'un texte quasi-brut avec instructions de formatage, à XML, pour revenir à du texte brut...) Dans le futur, peut-être existera t-il une chemin plus rapide, allant directement du Pandoc au RFC final. En attendant, ce chemin impose quelques contraintes, notamment qu'on ne peut pas complètement éviter d'éditer du XML. L'auteur doit éditer quatre documents :

  • Un document Markdown abstract.mkd contenant le résumé,
  • Un document Markdown middle.mkd contenant le gros du RFC,
  • Un document Markdown facultatif back.mkd contenant les annexes,
  • Un document XML template.xml contenant auteurs et références bibliographiques.

Un exemple du gabarit template.xml figure dans la section 2 de notre RFC.

Pour installer l'outil Pandoc2rfc, vous allez avoir besoin du processeur XSLT xsltproc (la transformation du Docbook en XML-RFC2629 est faite avec XSLT) et bien sûr de Pandoc lui-même.

Pandoc2rfc contient un script shell qui fait les transformations nécessaires (voir la section 3 du RFC).

Pour apprendre la syntaxe de Pandoc, il faut regarder le guide. Pandoc2rfc traduit via xml2rfc (l'outil du RFC 2629) et met en correspondance les concepts Markdown avec ceux de XML-RFC2629. Par exemple, une énumération avec * va être traduite en une liste (<list> XML) style="symbols" et une énumération avec des numéros (comme "1.", "2.", etc) va être traduite en style="numbers" (RFC 2629, section 2.3.1.2). La section 4 de notre RFC donne une liste complète des équivalences Pandoc<->XML, résumées sous forme d'un tableau dans l'annexe B.

Il est d'ailleurs difficile de faire du Pandoc2rfc sans connaître xml2rfc. Par exemple, pour afficher une table des matières, il faut inclure l'invocation xml2rfc <?rfc toc="yes"?>.

La section 5 liste quelques limitations de Pandoc2rfc par rapport à Pandoc : pas de gestion des index, les commentaires doivent être faits avec des commentaires HTML dans le source Pandoc, les citations se font avec les références XML, le système de citation de Pandoc n'est pas utilisé, et enfin les auteurs doivent se résigner à voir apparaître des erreurs de xml2rfc, la traduction Pandoc vers Docbook ne gère pas toutes les erreurs possibles. De mon expérience, il faut pas mal bricoler pour faire marcher le tout. Écrire des RFC en Markdown est sympa mais on n'y arrive pas en cinq minutes.

Si vous voulez un exemple concret, la distribution de Pandoc2rfc contient un répertoire test/ avec un exemple de RFC bidon utilisant toutes les fonctions de Pandoc2rfc. J'ai un peu modifié le Makefile pour faire des versions texte, XML et HTML du RFC :

% make all
pandoc2rfc -t template.xml back.mkd middle.mkd
pandoc2rfc -H -t template.xml back.mkd middle.mkd
pandoc2rfc -X -t template.xml back.mkd middle.mkd

Et cela produit les trois versions, à partir du source Markdown (essentiellement en middle.mkd) et du gabarit template.xml.


Téléchargez le RFC 7328


L'article seul

RFC 7194: Default Port for IRC via TLS/SSL

Date de publication du RFC : Août 2014
Auteur(s) du RFC : Richard Hartmann
Pour information
Première rédaction de cet article le 8 août 2014


Le protocole de messagerie instantanée IRC est toujours très utilisé, malgré la concurrence de XMPP. Il n'existait pas de port « officiel » pour les connexions IRC sécurisées par TLS, ce qui est fait avec ce RFC, qui décrit l'usage, déjà très répandu, du port 6697.

IRC est officiellement standardisé dans les RFC 1459, RFC 2810, RFC 2811, RFC 2812 et RFC 2813 mais il ne faut pas s'y fier : la pratique réelle est loin de ces RFC, qu'il faut plutôt utiliser comme point de départ d'une description du protocole que comme une « Bible » définitive. IRC avait depuis longtemps deux ports standards, 194 pour le trafic en clair et 994 pour le trafic chiffré avec TLS. Ces deux ports sont inférieurs à 1 024 et nécessitent donc que le serveur soit lancé par root sur une machine Unix. Comme ce n'est pas toujours possible, ou souhaitable, en pratique, les serveurs IRC écoutent en général sur 6667 en clair et 6697 en chiffré. Le port 6667 a été documenté pour cet usage mais ce n'était pas encore le cas du 6697. Les clients IRC n'avaient donc pas de moyen standard fiable de découvrir si un serveur gérait les connexions IRC sur TLS.

La section 2 du RFC décrit le fonctionnement d'IRC sur TLS : le client se connecte à un serveur IRC. Une fois la connexion établie, la négociation TLS a lieu. Le serveur est censé présenter un certificat d'une autorité reconnue, dont le nom est le nom du serveur. Le client peut s'authentifier, aussi comme décrit dans la documentation d'OFTC . Une fois la négociation terminée avec succès, les identités vérifiées et tout, le trafic passe alors par cette connexion protégée.

Le nouveau numéro est désormais dans le registre IANA :

ircs-u             6697        tcp    Internet Relay Chat via    TLS/SSL               2014-02-11

À noter que ce mécanisme ne protège que la liaison client<->serveur. IRC a aussi des communications serveur<->serveur (coordination non standardisée entre les différentes machines qui mettent en œuvre un service IRC) qui ne sont pas traitées ici, même si le RFC recommande aux opérateurs IRC de systématiquement chiffrer le trafic entre machines d'un même service. À noter aussi qu'IRC ne fait pas de liaison directe en pair à pair. Quand Alice et Bob se parlent en IRC, cela passe toujours par un serveur et, même si TLS sécurise la connexion avec le serveur, cela ne protège pas Alice et Bob d'un espionnage par le serveur (par exemple si celui-ci participe au programme PRISM), comme le rappelle la section 3 du RFC. Le chiffrement qui n'est pas de bout en bout a des pouvoirs de protection limités ! Seul OTR fournirait une protection de bout en bout (mais nécessite un peu plus d'action de la part d'Alice et Bob alors que TLS est largement invisible).

L'annexe A du RFC rappelle qu'en 2010, une étude avait montré que dix des vingt principaux services IRC géraient TLS.

La plupart des grands serveurs IRC aujourd'hui gèrent TLS. Parmi les exceptions, j'ai trouvé ircnet.nerim.fr mais aussi le serveur IRC du W3C qui n'est hélas accessible en TLS que par les membres du W3C. Avec un serveur qui gère TLS, et avec Pidgin comme client, il faut cocher la case Use SSL [sic] mais aussi changer manuellement le port, pour 6697 :

On verra alors des connexions TLS :

(09:00:20) gandalf.geeknode.org: (notice) *** You are connected to gandalf.geeknode.org with TLSv1-AES256-SHA-256bits

À noter que Geeknode est signé par CAcert comme beaucoup de serveurs IRC. Freenode, par contre, est signé par Gandi :

% openssl s_client -connect chat.freenode.net:6697
CONNECTED(00000003)
depth=1 C = FR, O = GANDI SAS, CN = Gandi Standard SSL CA
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/OU=Domain Control Validated/OU=Gandi Standard Wildcard SSL/CN=*.freenode.net
   i:/C=FR/O=GANDI SAS/CN=Gandi Standard SSL CA
 1 s:/C=FR/O=GANDI SAS/CN=Gandi Standard SSL CA
   i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware
---
...

Téléchargez le RFC 7194


L'article seul

delv, le futur outil principal de déboguage de DNSSEC ?

Première rédaction de cet article le 26 juillet 2014


La version 9.10 de BIND vient avec un nouvel outil de déboguage en ligne de commande, delv. Qu'apporte-t-il par rapport à dig ? C'est surtout pour ce qui concerne DNSSEC qu'il est utile.

L'outil de base de celui qui débogue un problème DNS, l'équivalent de ce que sont ping et traceroute pour l'ingénieur réseaux, c'est dig. Cet outil permet de récupérer des données DNS et de les afficher. Avec le DNS traditionnel, on n'avait pas besoin de plus. Mais avec DNSSEC, un problème supplémentaire survient : les données obtenues sont-elles vérifiées cryptographiquement ? dig avait bien une option +sigchase pour tester cela mais, très boguée, elle est restée peu connue. On n'avait donc pas d'outil simple, en ligne de commande, pour valider du DNSSEC. On a bien sûr drill (livré avec ldns) mais son option -S est très bavarde.

Depuis la version 9.10 de BIND, est apparue avec ce logiciel un nouvel outil, nommé delv. delv permet de faire la même chose que dig (en moins bavard par défaut) :

% delv  www.ssi.gouv.fr
; unsigned answer
www.ssi.gouv.fr.	21600	IN	A	86.65.182.120

% delv -t mx  laposte.net
; unsigned answer
laposte.net.		600	IN	MX	10 smtp4.laposte.net.

Mais c'est surtout lorsque le domaine est signé que delv est utile. (Autrement, comme vous l'avez vu plus haut, il affiche un triste unsigned answer.) Testons avec un nom correctement signé :

% delv www.bortzmeyer.org
; fully validated
www.bortzmeyer.org.	52712	IN	A	204.62.14.153
www.bortzmeyer.org.	52712	IN	RRSIG	A 8 3 86400 20140813162126 20140724102037 37573 bortzmeyer.org. ZU9fgjc34O2xfBoN1crbGpMsw8Ov56Jex/6yuSHiAEUFSYTK1H4kBrcK H6MIoeUPrA6/4gmtPTBrc6+KWv765ioWgvHiGPPa8v85T2w0zLsX24NT 4Zz0XVsfybu1re/Nr6V//BOnVF3cZ2hFvdI1Yp9EiKLnRFfPrYX316Hx aJI=

Cette fois, il a procédé à une validation DNSSEC de la réponse (correcte, dans ce cas, fully validated).

Vous allez le dire « mais il suffit d'utiliser un résolveur DNS validant et on sait, via le bit AD (Authentic Data) que les données sont correctement signées ». Mais delv donne des informations supplémentaires. Par exemple, si le domaine est signé mais pointe vers un domaine non-signé :

% delv www.paypal.co.uk           
; fully validated
www.paypal.co.uk.	3600	IN	CNAME	www.intl-paypal.com.edgekey.net.
www.paypal.co.uk.	3600	IN	RRSIG	CNAME 5 4 3600 20140816050243 20140717044122 22776 paypal.co.uk. TU8EubeTmR0NCU+bLlhWPTJL91fUkoExM/86ZIL6qr1kJU7g69lqRzLJ N33TzZdIOgj7g1O4eEyUUgBKxe8GpH5dLisXlHhi64aluT4DkL3PwqDi W9enJobC660aPSp0vebasOnJlCZ2rRwKKqmWUiRfXhhkKmu1U1l4IOMV wmY=

; unsigned answer
www.intl-paypal.com.edgekey.net. 120 IN	CNAME	e493.a.akamaiedge.net.
e493.a.akamaiedge.net.	20	IN	A	23.58.178.37

Dans ce cas (aucun CDN ne gère aujourd'hui DNSSEC, hélas), delv indique bien quelle partie de la réponse est signée et laquelle ne l'est pas.

Et en cas de problème ? Si on teste avec des domaines délibérement cassés :

% delv -t mx servfail.nl 
;; resolution failed: failure

Eh oui, on utilise un résolveur validant, il ne nous donne donc pas les données liées à ce domaine. Il faut donc (paradoxalement !) utiliser un résolveur non-validant ou bien, tout simplement, ajouter l'option +cd, Checking Disabled, à delv :

% delv +cd -t mx servfail.nl
;; validating servfail.nl/DNSKEY: verify failed due to bad signature (keyid=25594): RRSIG has expired
;; validating servfail.nl/DNSKEY: no valid signature found (DS)
;; no valid RRSIG resolving 'servfail.nl/DNSKEY/IN': 127.0.0.1#53
;;   validating R6K26LDO0GS7N66JPQALLM0JIDU6PHML.servfail.nl/NSEC3: bad cache hit (servfail.nl/DNSKEY)
;; broken trust chain resolving 'servfail.nl/MX/IN': 127.0.0.1#53
;; resolution failed: broken trust chain

Voilà, on connait désormais la raison du problème, l'enregistrement DNSKEY de servfail.nl a une signature expirée. Avec un autre domaine, comportant une autre erreur (une signature délibérement modifiée) :

% delv +cd -t mx dnssec-failed.org
;; validating dnssec-failed.org/DNSKEY: no valid signature found (DS)
;; no valid RRSIG resolving 'dnssec-failed.org/DNSKEY/IN': 127.0.0.1#53
;;   validating dnssec-failed.org/NSEC: bad cache hit (dnssec-failed.org/DNSKEY)
;; broken trust chain resolving 'dnssec-failed.org/MX/IN': 127.0.0.1#53
;; resolution failed: broken trust chain

Et, ici, avec une date de signature dans le futur :

% delv +cd -t aaaa futuredate-aaaa.test.dnssec-tools.org
;; validating futuredate-aaaa.test.dnssec-tools.org/AAAA: verify failed due to bad signature (keyid=19442): RRSIG validity period has not begun
;; validating futuredate-aaaa.test.dnssec-tools.org/AAAA: no valid signature found
;; RRSIG validity period has not begun resolving 'futuredate-aaaa.test.dnssec-tools.org/AAAA/IN': 127.0.0.1#53
;; resolution failed: RRSIG validity period has not begun

Comme vous avez vu, delv est parfait pour du premier déboguage, donc, quitte à utiliser des outils plus complexes par la suite. Au fur et à mesure que les versions récentes de BIND se diffuseront, et que DNSSEC sera plus courant, dig sera sans doute souvent remplacé par delv.


L'article seul

RFC 7301: Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : S. Friedl (Cisco Systems), A. Popov (Microsoft), A. Langley (Google), E. Stephan (Orange)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 24 juillet 2014


On le sait, désormais, tout l'Internet tourne sur le port 443 (et aussi sur 80 mais de moins en moins, en raison des révélations de Snowden). C'est en effet l'un des rares ports dont on peut être sûr qu'il est à peu près tout le temps ouvert, même sur les réseaux les plus affreux genre hôtel ou aéroport. En prime, les communications sur ce port sont chiffrées avec TLS (RFC 5246). Si seul HTTP se servait de ce port, cela marcherait mais de plus en plus de protocoles applicatifs se bousculent pour l'utiliser. Comment leur permettre de partager le port 443 sans émeutes ? En ajoutant une extension à TLS qui permet d'indiquer le protocole applicatif qui va utiliser ce port, une fois la session TLS démarrée.

Cette extension avait surtout été conçue pour les besoins de HTTP/2 (autrefois nommée SPDY), version qui est loin d'être terminée. ALPN (Application-Layer Protocol Negotiation), objet de notre RFC, sert à indiquer la version de HTTP, et aussi désigner les services qui vont tourner au dessus de HTTP, comme WebRTC. Mais elle pourra servir à d'autres protocoles. Comme, par exemple, faire coexister IMAP et HTTP sur le même port. Vous allez peut-être me dire « mais pourquoi ne pas faire cela dans une couche entre TLS et l'application, de manière à éviter de complexifier TLS ? » et le RFC vous répond que le but est de performance (éviter les allers-retours entre client et serveur) et de souplesse (choisir un certificat différent selon le protocole d'application).

À noter qu'il s'agit d'une extension TLS : si on veut faire coexister sur un même port des applications TLS comme HTTP et non-TLS comme SSH, il faut une autre solution (par exemple sslh).

Comment fonctionne ALPN (section 3 de notre RFC) ? Le client envoie la liste des applications qu'il gère dans son message TLS ClientHello (section 7.4.1.2 du RFC 5246), le serveur en choisit une et la renvoie dans son ServerHello. Tout se fait donc dans la poignée de mains de TLS, sans imposer d'allers-retours supplémentaires. Si le client ne veut qu'une seule application, il envoie un message avec une liste ne comportant que cette application.

L'extension se nomme application_layer_protocol_negotiation et a le numéro 16 (désormais dans le registre IANA). Elle a un contenu (de type ProtocolNameList) qui contient la liste des protocoles applicatifs, en ordre de préférence décroissante. En termes TLS, cela s'écrit :


enum {
       application_layer_protocol_negotiation(16), (65535)
   } ExtensionType;

opaque ProtocolName<1..2^8-1>;

struct {
       ProtocolName protocol_name_list<2..2^16-1>
   } ProtocolNameList;

Les valeurs possibles pour les protocoles applicatifs sont dans un registre IANA. Des nouveaux protocoles pourront être ajoutés après un examen par un expert (RFC 5226, section 4.1). Pour l'instant, on y trouve le HTTP actuel, 1.1 (RFC 7230) et les variantes de SPDY. HTTP/2 sera placé dans ce registre après sa normalisation.

Cette extension sert aussi bien au client (liste des applications que le client veut utiliser) qu'au serveur (application - au singulier - choisie, après avoir ignoré celles que le serveur ne connait pas). Si le serveur ne met pas cette extension dans sa réponse, c'est ce qu'est un vieux serveur, qui ne la connait pas. S'il répond avec une alerte no_application_protocol, c'est que l'intersection de la liste du client avec celle configurée dans le serveur est hélas vide.

Si vous utilisez une application qui peut attirer l'attention de la NSA, attention : la négociation ALPN est en clair, avant que le chiffrement ne démarre (sections 4 et 5). C'était un des points les plus controversés lors des discussions à l'IETF mais cela a été décidé pour être cohérent avec le modèle d'extension de TLS. Si vous tenez à cacher le protocole applicatif utilisé, vous devez faire une renégociation une fois le chiffrement commencé.

Des mises en œuvre de cette extension ? C'est apparemment fait pour NSS (voir #996250 et #959664). Et Wireshark va pouvoir le décoder. Pour les autres, voir le tableau synthétique.


Téléchargez le RFC 7301


L'article seul

RFC 7290: Test Plan and Results for Advancing RFC 2680 on the Standards Track

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : L. Ciavattone (AT&T Labs), R. Geib (Deutsche Telekom), A. Morton (AT&T Labs), M. Wieser (Technical University Darmstadt)
Pour information
Réalisé dans le cadre du groupe de travail IETF ippm
Première rédaction de cet article le 23 juillet 2014


Le RFC 2680 normalise une métrique utile à l'analyse des performances des réseaux informatiques, le taux de perte de paquets (mesuré sur un aller simple). Pour faire avancer ce RFC sur le chemin des normes, pour qu'il accède au statut de « norme tout court », il faut une évaluation : la description du RFC est-elle claire et complète ? Les mises en œuvre logicielles sont-elles équivalentes ? C'est ce travail d'évaluation que ce nouveau RFC documente.

Évaluer la norme d'une métrique est un peu plus compliqué qu'évaluer un protocole. La méthode traditionnelle à l'IETF pour évaluer un protocole est de regarder si deux mises en œuvre différentes peuvent interopérer. Pour une métrique, ce n'est pas possible et on utilise donc une autre méthode, regarder si deux mises en œuvre différentes d'une métrique, confrontées au même trafic, produisent des résultats compatibles (pas forcément identiques : les mesures ne sont jamais parfaitement précises).

Le groupe de travail IPPM de l'IETF travaille depuis longtemps sur ce problème d'avancement des métriques sur le chemin des normes. Les principes de l'évaluation d'une métrique ont été décrits dans le RFC 6576 et ont déjà été mis en pratique dans le RFC 6808, pour la métrique « délai d'acheminement d'un paquet (en aller simple) ». L'idée de base est que, si deux mesures, par des mises en œuvre différentes, donnent des résultats suffisamment proches, c'est que la métrique était correctement définie, claire et sans ambiguité. Cette évaluation permet aussi de voir quelles options de la métrique n'ont jamais été implémentées, et sont donc candidates à être supprimées dans une future révision de la norme.

En suivant le même plan que le RFC 6808, voici donc l'évaluation du RFC 2680 en pratique. La configuration de test est décrite en section 3 : les deux mises en œuvre testées étaient un NetProbe (ce qu'utilise WIPM), utilisant des paquets UDP avec une distribution périodique (RFC 3432) ou de Poisson (RFC 2330), et un Perfas+ (description en allemand), également avec UDP. Dans les deux cas, on passait par un tunnel L2TP (RFC 3931), pour dissimuler aux équipements intermédiaires les adresses IP et ports des équipements de tests (autrement, le trafic risquerait de passer par des chemins différents pour les deux tests). À l'extrémité du tunnel, le trafic est renvoyé donc cela permet d'avoir l'émetteur et le récepteur au même endroit, tout en faisant une mesure unidirectionnelle sur une longue distance. Pour introduire des phénomènes rigolos comme des pertes de paquet, un émulateur Netem est installé sur une machine Fedora, qui assure la communication entre le tunnel et un VLAN où se trouve l'équipement de test, via un lien FastEthernet. Les équipements testés ont envoyé des flots à 1 et 10 p/s, avec des centaines de paquets, de tailles variables (de 64 à 500 octets). Le type-P (cf. RFC 2680, section 2.8.1) des paquets était « UDP avec DSCP best effort » (de toute façon, l'encapsulation dans le tunnel L2TP fait que ces bits DSCP ont probablement été ignorés).

La section 4 de notre RFC décrit l'étalonnage (vérifier les horloges, par exemple).

On l'a dit, c'est bien joli de comparer deux programmes mais que doit être le résultat de la comparaison ? Une égalité rigoureuse ? Ce n'est pas possible. La section 5 indique les critères de comparaison : on fait de l'ADK avec un facteur de confiance à 0,95 (et de l'adéquation à la loi de distribution).

Enfin, après toutes ces précautions méthodologiques, la section 6 contient les résultats. Par exemple, en périodique, les deux systèmes mesurés donnent des résultats largement compatibles mais, en distribution de Poisson, les résultats sont tout juste acceptables. Les mesures ont bien résisté à des tests comme celui du délai maximal d'attente (avant de déclarer un paquet perdu) ou comme celui du désordonnement des paquets (lorsqu'un paquet est tellement en retard qu'il arrive après un paquet parti après).

Conclusions (section 7) : les deux mises en œuvre sont compatibles, ce qui indique que le RFC 2680 est suffisamment clair et précis. Le processus de test a toutefois noté quelques points où le RFC 2680 pouvait être rendu plus clair (sans compter les erreurs connues du RFC).

Pour les fanas, la section 10 rassemble toutes les commandes de configuration des équipements qui ont été utilisées. Par exemple, Netem a été configuré avec tc (ici, pour ajouter un retard variable et en prime 10 % de perte de paquets) :

tc qdisc change dev eth1 root netem delay 2000ms 1000ms loss 10%

Téléchargez le RFC 7290


L'article seul

Un petit galop d'essai avec le système Haka (filtrage, sécurité, paquets, protocoles, etc)

Première rédaction de cet article le 20 juillet 2014


Le 10 juillet dernier, lors des RMLL à Montpellier, j'ai eu le plaisir de participer à l'« Atelier HAKA : un langage open source [sic] de sécurité réseau ». Haka est un langage permettant d'analyser les paquets, de réagir à certaines caractéristiques des paquets et de générer des réponses diverses. Cela permet entre autres de faire des pare-feux très souples car complètement programmables.

Haka est fondé sur Lua et il faut donc un peu réviser ses connaissances Lua avant de pratiquer l'atelier. (J'ai déjà écrit ici et sur Lua et réalisé un tout petit programme avec.) Haka étend Lua en ajoutant au langage de base des extensions permettant de tripoter les paquets réseau facilement. Je ne vais pas vous faire un cours sur Haka (il y en a un en ligne et les transparents de l'atelier sont disponibles en ligne), juste documenter mon expérience.

L'atelier se faisait dans une machine virtuelle Debian dans VirtualBox. La machine virtuelle utilisée pouvait être téléchargée en https://hakasecurity.files.wordpress.com/2014/07/haka-live-iso.zip. (Je n'ai pas encore essayé d'installer Haka moi-même.) Une machine plus récente est, depuis, en http://www.haka-security.org/resources.html. Une fois la machine démarrée, il faut faire un setxkbmap fr dans un terminal pour passer en AZERTY, le clavier par défaut étant QWERTY. Si vous éditez les sources Lua avec Emacs, notez qu'il n'y a pas de mode Lua pour Emacs dans la machine virtuelle. Bon, on s'en passe, sinon, on le télécharge avec apt-get. Les pcap d'exemple sont livrés avec la machine virtuelle, dans /opt/haka/share/haka/sample/hellopacket.

Premier exercice, s'entraîner aux bases de Haka et apprendre à afficher certains paquets. Allons-y en Lua :

-- On charge le dissecteur IPv4 (pas encore de dissecteur IPv6,
-- malheureusement ; un volontaire pour l'écrire ?)
local ip = require('protocol/ipv4')

-- Haka fonctionne en écrivant des *règles* qui réagissent à des
-- *évènements*, ici, l'évènement est le passage d'un paquet IPv4
haka.rule{
    hook = ip.events.receive_packet,
    -- Suit une série d'actions, qui reçoivent un paramètre qui dépend
    -- du dissecteur. Le dissecteur IPv4 passe à l'évènement
    -- receive_packet un paquet.
    eval = function (pkt)
        haka.log("Hello", "packet from %s to %s", pkt.src, pkt.dst)
    end
}

Et comment on a trouvé que les champs de pkt qui contenaient les adresses IP source et destination se nommaient src et dst ? On a lu la doc (également disponible dans la machine virtuelle en /lib/live/mount/medium/haka/manual/modules/protocol/ipv4/doc/ipv4.html#dissector). Il existe aussi un mode interactif de Haka, permettant d'explorer les paquets, pas montré ici.

Si on lance ce script Haka sur un pcap existant, il affiche :

% hakapcap hellopacket.lua hellopcaket.pcap

info  core: load module 'packet/pcap.ho', Pcap Module
info  core: load module 'alert/file.ho', File alert
info  core: setting packet mode to pass-through

info  core: loading rule file 'hellopacket.lua'
info  core: initializing thread 0
info  dissector: register new dissector 'raw'
info  pcap:      opening file 'hellopacket.pcap'
info  dissector: register new dissector 'ipv4'
info  core:      1 rule(s) on event 'ipv4:receive_packet'
info  core:      1 rule(s) registered

info  core:      starting single threaded processing

info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  Hello:     packet from 192.168.10.99 to 192.168.10.1
info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  Hello:     packet from 192.168.10.99 to 192.168.10.1
info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  Hello:     packet from 192.168.10.99 to 192.168.10.1
info  Hello:     packet from 192.168.10.1 to 192.168.10.99
info  core:      unload module 'Pcap Module'
info  core:      unload module 'File alert'

Ça a bien marché, chaque paquet du pcap a été affiché.

Deuxième exercice, filtrage des paquets qui ne nous plaisent pas, en l'occurrence, ceux qui viennent du méchant réseau 192.168.10.0/27 :

local ip = require('protocol/ipv4')

local bad_network = ip.network("192.168.10.0/27")

haka.rule{
    hook = ip.events.receive_packet,
    eval = function (pkt)
      -- On teste si le paquet appartient au méchant réseau
      if bad_network:contains(pkt.src) then
        -- Si oui, on le jette et on journalise
        haka.log("Dropped", "packet from %s to %s", pkt.src, pkt.dst)
	pkt:drop()
      -- Si non (pas de 'else') le paquet suit son cours
      end
   end
}

Une fois lancé le script sur un pcap, on obtient :

...
info  Dropped:   packet from 192.168.10.1 to 192.168.10.99
info  Dropped:   packet from 192.168.10.1 to 192.168.10.99
info  Dropped:   packet from 192.168.10.1 to 192.168.10.99
info  Dropped:   packet from 192.168.10.1 to 192.168.10.99
info  Dropped:   packet from 192.168.10.10 to 192.168.10.99
info  Dropped:   packet from 192.168.10.10 to 192.168.10.99
info  Dropped:   packet from 192.168.10.10 to 192.168.10.99

Si vous regardez le pcap avec tcpdump, vous verrez qu'il y a d'autres paquets, en provenance de 192.168.10.99 qui n'ont pas été jetés, car pas envoyés depuis le méchant réseau :

14:49:27.076486 IP 192.168.10.1 > 192.168.10.99: ICMP echo request, id 26102, seq 1, length 64
14:49:27.076536 IP 192.168.10.99 > 192.168.10.1: ICMP echo reply, id 26102, seq 1, length 64
14:49:28.075844 IP 192.168.10.1 > 192.168.10.99: ICMP echo request, id 26102, seq 2, length 64
14:49:28.075900 IP 192.168.10.99 > 192.168.10.1: ICMP echo reply, id 26102, seq 2, length 64
14:49:31.966286 IP 192.168.10.1.37542 > 192.168.10.99.80: Flags [S], seq 3827050607, win 14600, options [mss 1460,sackOK,TS val 2224051087 ecr 0,nop,wscale 7], length 0
14:49:31.966356 IP 192.168.10.99.80 > 192.168.10.1.37542: Flags [R.], seq 0, ack 3827050608, win 0, length 0
14:49:36.014035 IP 192.168.10.1.47617 > 192.168.10.99.31337: Flags [S], seq 1811320530, win 14600, options [mss 1460,sackOK,TS val 2224052099 ecr 0,nop,wscale 7], length 0
14:49:36.014080 IP 192.168.10.99.31337 > 192.168.10.1.47617: Flags [R.], seq 0, ack 1811320531, win 0, length 0
14:49:46.837316 ARP, Request who-has 192.168.10.99 tell 192.168.10.10, length 46
14:49:46.837370 ARP, Reply 192.168.10.99 is-at 52:54:00:1a:34:60, length 28
14:49:46.837888 IP 192.168.10.10.35321 > 192.168.10.99.8000: Flags [S], seq 895344097, win 14600, options [mss 1460,sackOK,TS val 2224054805 ecr 0,nop,wscale 7], length 0
14:49:46.837939 IP 192.168.10.99.8000 > 192.168.10.10.35321: Flags [R.], seq 0, ack 895344098, win 0, length 0
14:49:50.668580 IP 192.168.10.10 > 192.168.10.99: ICMP echo request, id 26107, seq 1, length 64
14:49:50.668674 IP 192.168.10.99 > 192.168.10.10: ICMP echo reply, id 26107, seq 1, length 64
14:49:51.670446 IP 192.168.10.10 > 192.168.10.99: ICMP echo request, id 26107, seq 2, length 64
14:49:51.670492 IP 192.168.10.99 > 192.168.10.10: ICMP echo reply, id 26107, seq 2, length 64
14:49:51.841297 ARP, Request who-has 192.168.10.10 tell 192.168.10.99, length 28
14:49:51.841915 ARP, Reply 192.168.10.10 is-at 52:54:00:30:b0:bd, length 46

Bon, jusqu'à présent, on n'a rien fait d'extraordinaire, Netfilter en aurait fait autant. Mais le prochain exercice est plus intéressant. On va faire du filtrage TCP. La documentation du dissecteur TCP nous apprend que le champ qui indique le port de destination est dstport :

local ip = require('protocol/ipv4')

-- On charge un nouveau dissecteur
local tcp = require ('protocol/tcp_connection')

haka.rule{
    -- Et on utilise un nouvel évènement, qui signale un nouveau flot TCP
    hook = tcp.events.new_connection,
    eval = function (flow, pkt)
      -- Si le port n'est ni 22, ni 80...
      if flow.dstport ~= 22 and flow.dstport ~= 80 then
        haka.log("Dropped", "flow from %s to %s:%s", pkt.ip.src, pkt.ip.dst, flow.dstport)
	flow:drop()
      else	
        haka.log("Accepted", "flow from %s to %s:%s", pkt.ip.src, pkt.ip.dst, flow.dstport)
      end
   end
}

Et on le teste sur un pcap :

info  Accepted:  flow from 192.168.10.1 to 192.168.10.99:80
info  Dropped:   flow from 192.168.10.1 to 192.168.10.99:31337
info  Dropped:   flow from 192.168.10.10 to 192.168.10.99:8000

Il y avait trois flots (trois connexions TCP) dans le pcap, une seule a été acceptée.

Maintenant, fini de jouer avec des pcap. Cela peut être intéressant (analyse de pcap compliqués en ayant toute la puissance d'un langage de Turing) mais je vous avais promis un pare-feu. On va donc filtrer des paquets et des flots vivants, en temps réel. On écrit d'abord le fichier de configuration du démon Haka :

[general]
# Select the haka configuration file to use
configuration = "tcpfilter.lua"

# Optionally select the number of thread to use. 
#thread = 4

# Pass-through mode
# If yes, haka will only inspect packet
# If no, it means that haka can also modify and create packet
pass-through = no

[packet]
# Select the capture model, nfqueue or pcap
module = "packet/nfqueue"

# Select the interfaces to listen to
interfaces = "lo"
#interfaces = "eth0"

# Select packet dumping for nfqueue
#dump = yes
#dump_input = "/tmp/input.pcap"
#dump_output = "/tmp/output.pcap"

[log]
# Select the log module
module = "log/syslog"

[alert]
# Select the alert module
module = "alert/syslog"

Le script est le même que dans l'essai précédent, il accepte uniquement les connexions TCP vers les ports 22 ou 80. On lance le démon (notez que celui-ci fera appel à Netfilter pour lui passer les paquets) :

% sudo haka -c haka.conf --no-daemon
info  core: load module 'log/syslog.ho', Syslog logger
info  core: load module 'alert/syslog.ho', Syslog alert
info  core: load module 'alert/file.ho', File alert
info  core: load module 'packet/nfqueue.ho', nfqueue
info  nfqueue: installing iptables rules for device(s) lo
info  core:    loading rule file 'tcpfilter.lua'
info  core:    initializing thread 0
info  dissector: register new dissector 'raw'
info  dissector: register new dissector 'ipv4'
info  dissector: register new dissector 'tcp'
info  dissector: register new dissector 'tcp_connection'
info  core:      1 rule(s) on event 'tcp_connection:new_connection'
info  core:      1 rule(s) registered

info  core:      starting single threaded processing

Et on tente quelques connexions (143 = IMAP) :

info  Accepted:  flow from 127.0.0.1 to 127.0.0.1:80
info  Dropped:   flow from 127.0.0.1 to 127.0.0.1:143
info  Dropped:   flow from 127.0.0.1 to 127.0.0.1:143
info  Dropped:   flow from 127.0.0.1 to 127.0.0.1:143

Et en effet, les clients IMAP vont désormais timeouter. Dès qu'on arrête le démon, avec un Control-C, IMAP remarche :

% telnet 127.0.0.1 imap2
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE STARTTLS AUTH=PLAIN] Dovecot ready.

J'ai beaucoup aimé la possibilité de journaliser ou de jeter un flot TCP entier, pas en agissant paquet par paquet. Pour la partie IP au début de cet atelier, Haka a un concurrent évident, Scapy (mêmes concepts mais Python au lieu de Lua). Mais Scapy ne sait pas gérer les flots TCP, il ne travaille que par paquet.

Exercice suivant, le protocole du Web, HTTP. On va modifier les pages HTML en vol (et vous comprendrez pourquoi il faut toujours utiliser HTTPS). Il faut changer haka.conf pour indiquer le nouveau script, blurring-the-web.lua. D'abord, on se contente d'afficher les requêtes HTTP :

local ip = require('protocol/ipv4')

-- On charge un nouveau dissecteur
local http = require ('protocol/http')

-- Pas d'indication du protocole supérieur dans TCP, seulement du
-- numéro de port, donc il faut être explicite
http.install_tcp_rule(80)

haka.rule{
    -- Et on utilise un nouvel évènement
    hook = http.events.request,
    eval = function (connection, request)
      haka.log("HTTP request", "%s %s", request.method, request.uri)
      -- Il faut être sûr que les données ne soient pas comprimées,
      -- pour que la modification ultérieure marche. On modifie donc
      -- la requête.
      request.headers['Accept-Encoding'] = nil
      request.headers['Accept'] = "*/*"
    end
}

Haka fait beaucoup de choses mais il ne décomprime pas les flots HTTP. Comme la compression est souvent utilisée sur le Web, on modifie les en-têtes de la requête pour prétendre qu'on n'accepte pas la compression (cf. RFC 7231, sections 5.3.4 et 5.3.2).

On lance maintenant le démon Haka avec ce nouveau script. Pour tester que les requêtes du navigateur Web ont bien été modifiées, on peut aller sur un site qui affiche les en-têtes de la requête comme http://www.bortzmeyer.org/apps/env. Par défaut, le Firefox de la machine virtuelle envoie :

HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_ENCODING: gzip, deflate

alors qu'une fois le démon en route, on n'a plus que (mettre une entrée de tableau à nil la détruit) :

HTTP_ACCEPT: */*

Attention au passage : Firefox peut maintenir des connexions TCP persistantes avec le serveur HTTP. Si vous lancez Firefox avant Haka, vous risquez de récupérer fréquemment des :

alert: id = 8
	time = Sat Jul 19 16:31:32 2014
	severity = low
	description = no connection found for tcp packet
	sources = {
		address: 127.0.0.1
		service: tcp/56042
	}
	targets = {
		address: 127.0.0.1
		service: tcp/80
	}

C'est parce que Haka a vu passer des paquets d'une connexion TCP antérieure à son activation et qu'il ne sait donc pas rattacher à un flot qu'il suit. (C'est le problème de tous les filtres à état.)

Attention aussi, Haka ne gère pas IPv6. Si on se connecte à un serveur Web en indiquant son nom, on peut utiliser IPv6 (c'est le cas de http://localhost/ sur la Debian de la machine virtuelle) et Haka ne verra alors rien. D'où l'option -4 de wget, pour tester :

% wget -v -4 http://localhost/
...
# Haka affiche
info  HTTP request: GET /

Maintenant, on ne va pas se contenter d'observer, on modifie la réponse HTTP en ajoutant du CSS rigolo :


local ip = require('protocol/ipv4')
local http = require ('protocol/http')

local re = require('regexp/pcre')

-- Ce CSS rend tout flou
local css = '<style type="text/css" media="screen"> * { color: transparent !important; text-shadow: 0 0 3px black !important; } </style>'

http.install_tcp_rule(80)

-- Bien garder cette règle, pour couper la compression, qui est
-- activée par défaut sur beaucoup de serveurs
haka.rule{
    hook = http.events.request,
    eval = function (connection, request)
      haka.log("HTTP request", "%s %s", request.method, request.uri)
      request.headers['Accept-Encoding'] = nil
      request.headers['Accept'] = "*/*"
    end
}

-- Deuxième règle, pour changer la réponse
haka.rule{
    hook = http.events.response_data,
    options = {
        streamed = true,
    },
    eval = function (flow, iter)
      -- Chercher la fin de l'élément <head>
      local regexp = re.re:compile("</head>", re.re.CASE_INSENSITIVE)
      local result = regexp:match(iter, true)
      -- Si on a bien trouvé un <head>
      if result then 
         result:pos('begin'):insert(haka.vbuffer_from(css))
      end
    end
}

Et re-testons :


% wget -O - -v -4 http://localhost/
--2014-07-19 16:41:48--  http://localhost/
Resolving localhost (localhost)... 127.0.0.1, 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 190 [text/html]
Saving to: `STDOUT'

 0% [                                               ] 0           --.-K/s              <html><head><style type="text/css" media="screen"> * { color: transparent !important; text-shadow: 0 0 3px black !important; } </style></head><body><h1>It works!</h1>
100%[==============================================>] 190         --.-K/s   in 0s      

2014-07-19 16:41:48 (45.0 MB/s) - written to stdout [190/190]

Le CSS a bien été inséré ! Et, avec un vrai site Web, on voit bien l'effet :

Attention, le haka.conf indiqué ici n'écoute que sur l'interface lo. Pour tester sur le Web en grand, pensez à changer le haka.conf (et rappelez-vous que Haka n'aura aucun effet sur les sites Web accessibles en IPv6 comme http://www.ietf.org/, si la machine virtuelle a une connectivité IPv6).

Voilà, sur ce truc spectaculaire, ce fut la fin de l'atelier. À noter que nous n'avons pas testé les performances de Haka, question évidemment cruciale pour des filtrages en temps réel. En première approximation, Haka semble bien plus rapide que Scapy pour les tâches d'analyse de pcap mais il faudra un jour mesurer sérieusement.


L'article seul

RFC 7314: Extension Mechanisms for DNS (EDNS) EXPIRE Option

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : M. Andrews (ISC)
Expérimental
Première rédaction de cet article le 18 juillet 2014


L'enregistrement DNS SOA (Start Of Authority) a un champ expire qui indique au bout de combien de temps un serveur esclave qui ne réussit pas à contacter le maître peut arrêter de servir la zone. Si l'esclave a eu les données, non pas directement du maître, mais d'un autre esclave, la durée d'expiration peut ne pas être correcte. C'est ce (rare) problème que règle l'option EDNS EXPIRE.

En effet, il faut se rappeler que la distribution d'une zone DNS depuis un serveur maître (le RFC utilise l'ancien terme de « serveur primaire ») n'est pas forcément directe. On peut avoir un maître qui envoie des données à un esclave, qui à son tour les envoie à un autre esclave. Cela permet notamment davantage de robustesse (si le maître n'est temporairement pas joignable depuis certains esclaves). Notez que l'esclave ne sait même pas si la machine qui lui a envoyé les données était un maître ou pas.

Si la période de validité donnée par le champ expire du SOA (RFC 1035, section 3.3.13) vaut V et que le transfert de l'esclave à un autre esclave prend place S secondes après le transfert depuis le maître (les transferts ne sont pas forcément instantanés, par exemple en cas de coupure réseau), la zone sera servie pendant V + S secondes et pas seulement pendant V secondes. Pire, s'il existe une boucle dans le graphe de distribution, la zone risque de ne jamais expirer, les esclaves se rafraichissant mutuellement.

Personnellement, le problème me semble rare et de peu d'importance. Il y a des tas d'autres choses plus urgentes à régler dans le DNS. Mais, bon, c'est juste une expérience.

La nouvelle option de ce RFC dépend de EDNS (RFC 6891). Elle a le numéro 9. Mise dans une requête (a priori une requête de type SOA, AXFR ou IXFR), elle indique que le client DNS connait cette option et voudrait sa valeur. Un serveur qui ne connait pas l'option EXPIRE ne mettra pas l'option dans sa réponse.

Par contre, un serveur qui connait cette option répondra avec une option EXPIRE dans sa réponse. Elle comporte quatre octets de données (comme pour le champ expire du SOA), indiquant le nombre de secondes de la période de validité. Si le serveur est un maître, il met toujours dans cette option la valeur du champ expire du SOA. S'il est un esclave, il met la valeur actuelle de son compteur d'expiration. Ainsi, si le champ expire du SOA vaut 7 200 secondes (2 heures) et que le serveur secondaire reçoit une demande 30 minutes après le transfert depuis le maître, il mettra 5 400 dans l'option EDNS EXPIRE (120 minutes de validité moins les 30 minutes écoulées). C'est ainsi qu'on évite l'accumulation des périodes de validité en cas de transfert indirect.

Un serveur esclave (le RFC utilise l'ancien terme de « serveur secondaire ») qui utilise l'option, et qui reçoit une réponse lors d'un transfert initial de la zone, devrait utiliser comme durée de validité la valeur de l'option EXPIRE (et pas le champ expire du SOA, sauf si ce champ a une valeur inférieure à celle de l'option EXPIRE). Cette durée de validité est ensuite mise dans un compteur d'expiration qui décroît avec le temps. Pour les rafraichissements ultérieurs, le serveur esclave doit également utiliser comme durée de validité la valeur de l'option, sauf si le compteur actuel a une valeur plus élevée. Par exemple, si le compteur du secondaire dit que la zone est encore valable pour 4 500 secondes, et qu'une réponse IXFR (RFC 1995) contient une option EXPIRE de valeur 9 300 secondes, alors le compteur est mis à 9 300 secondes. Si l'option EXPIRE avait valu 2 400 secondes, le compteur n'aurait pas été modifié.

Une conséquence amusante de cette option est qu'elle permet de savoir quand a eu lieu le dernier transfert de zone réussi, juste en interrogeant un esclave. Cela peut être vu comme indiscret, mais cela peut aussi être un outil de supervision très pratique.

Cette option est utilisable depuis dig, à partir de la version 9.10. Wireshark a récemment été modifié pour reconnaître cette option.


Téléchargez le RFC 7314


L'article seul

RFC 7291: DHCP Options for the Port Control Protocol (PCP)

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : M. Boucadair (France Telecom), R. Penno, D. Wing (Cisco)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF pcp
Première rédaction de cet article le 15 juillet 2014


Le protocole PCP, normalisé dans le RFC 6887 permet à la machine de M. Michu de configurer un routeur/traducteur, par exemple pour lui demander de laisser passer le trafic entrant vers un port donné. La norme PCP ne précisait pas tellement comment le client était censé trouver le serveur PCP. Dans certains cas, c'est trivial, c'est le routeur par défaut. Dans d'autres, il faut d'autres mécanismes et c'est là que notre RFC 7291 est utile : il normalise un moyen de découvrir le serveur PCP grâce à une nouvelle option DHCP.

Le mécanisme est le même pour DHCP IPv6 (RFC 3315) ou DHCP IPv4 (RFC 2131). Pour DHCP IPv6, l'option est décrite en section 3. Elle est nommée OPTION_V6_PCP_SERVER et a le code 86 (mis dans le registre IANA). Suit une liste d'adresses IPv6, qui sont celles d'un serveur PCP. S'il y a plusieurs serveurs PCP, on met plusieurs occurrences de l'option OPTION_V6_PCP_SERVER.

On note que c'est une adresse IP et pas un nom de domaine qui est utilisée pour identifier le ou les serveur(s) PCP. Un nom de domaine aurait permis un niveau d'indirection supplémentaire. Le sujet a donc fait l'objet d'une certaine controverse au sein du groupe de travail PCP à l'IETF avant que le consensus se fasse sur l'adresse IP.

Pour obtenir cette liste d'adresses, le client DHCP doit mettre l'option OPTION_V6_PCP_SERVER dans sa requête et, si le serveur DHCP connait des serveurs PCP, il mettra leur(s) adresse(s) dans sa réponse.

Le mécanisme est quasiment le même en IPv4 (section 4), avec l'option OPTION_V4_PCP_SERVER de code 158 (registre IANA). La principale différence est le mécanisme utilisé pour mettre plusieurs serveurs PCP (pas juste plusieurs adresses IP pour un serveur, mais plusieurs serveurs) : en IPv6, on répétait l'option. En IPv4, on envoie plusieurs listes dans la même réponse.

Donc, attention, aussi bien côté client que côté serveur, ce RFC permet d'avoir plusieurs serveurs PCP, ayant chacun plusieurs adresses IP. Par exemple, côté serveur, le mécanisme de configuration du serveur (mettons son fichier de configuration) doit permettre de distinguer les deux cas, afin de choisir le bon encodage. Si le fichier de configuration permet de mettre un nom de domaine pour le serveur PCP, et que le serveur DHCP, en résolvant ce nom, trouve plusieurs adresses IP, il doit bien les mettre dans une seule option (en IPv6) ou une seule liste (IPv4).

Notez une différence entre IPv6 et IPv4 : un serveur DHCP IPv6 peut retourner des adresses IPv4 (encodées selon la section 2.5.5.2 du RFC 4291) dans sa réponse (cas d'un réseau purement IPv6 mais qui reçoit une connectivité IPv4 via un mécanisme spécifique), l'inverse n'étant pas vrai.

À noter que, dans le cas de multi-homing, on peut recevoir plusieurs serveurs PCP via des réponses DHCP différentes, sur des liens réseau différents (par exemple un lien WiFi et un 3G). Dans ce cas, la machine doit bien faire attention à associer chaque serveur PCP à son réseau, pour ne configurer que le bon serveur PCP correspondant à l'adresse qu'on veut utiliser.

Les praticiens noteront avec plaisir qu'il existe au moins une mise en œuvre documentée (sur Fedora).


Téléchargez le RFC 7291


L'article seul

RFC 7320: URI Design and Ownership

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : M. Nottingham
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 12 juillet 2014


Ah, les URI... Comme ces identificateurs sont très souvent vus et manipulés par un grand nombre d'utilisateurs, ils suscitent forcément des passions et des discussions sans fin. Ce RFC de bonne pratique s'attaque à un problème fréquent : les applications ou extensions qui imposent des contraintes sur l'URI, sans que cela soit justifié par le format de ces URI. Par exemple, on voit des CMS imposer, au moment de l'installation, que le CMS soit accessible par un URI commençant par le nom du logiciel. S'il s'appelle Foobar, on voit parfois des logiciels qui ne marchent que si l'URI commence par http://www.example.org/Foobar/. Pourquoi est-ce une mauvaise idée et que faudrait-il faire à la place ?

D'abord, l'argument d'autorité qui tue : la norme des URI, le RFC 3986, dit clairement que la structure d'un URI est déterminé par son plan (scheme en anglais) et que donc l'application n'a pas le droit d'imposer des règles supplémentaires. Les deux seuls « propriétaires » sont la norme décrivant le plan (qui impose une syntaxe, et certains éléments, par exemple le nom du serveur avec le plan http://) et l'organisation qui contrôle cet URI particulier (par exemple, toujours pour http://, l'organisation qui contrôle le domaine dans l'URI). Imposer des règles, pour une application ou une extension, c'est violer ce principe de propriété. (Il est formalisé dans une recommandation du W3C, section 2.2.2.1.)

Un exemple de structure dans les URI est l'interprétation du chemin (path en anglais) comme étant un endroit du système de fichiers. Ainsi, bien des serveurs HTTP, en voyant l'URI http://www.example.org/foo/bar/toto.html, chercheront le fichier en $DOCUMENT_ROOT/foo/bar/toto.html. C'est une particularité de la mise en œuvre de ce serveur, pas une obligation du plan d'URI http://. Un autre exemple est l'utilisation de l'extension du nom de fichier comme moyen de trouver le type de média de la ressource. (« Ça se termine en .png ? On envoie le type image/png. ») Ces deux cas ne violent pas forcément le principe de propriété des URI car l'utilisateur, après tout, choisit son serveur.

Mais, dans certains cas, l'imposition d'une structure (autre que celle déjà imposée par la norme du plan d'URI) a des conséquences néfastes :

  • Risque de collisions entre deux conventions différentes.
  • Risque d'instabilité si on met trop d'informations dans l'URI (voir la section 3.5.1 du document « Architecture of the World Wide Web, Volume One », cité plus haut). Pour reprendre l'exemple de l'extension du fichier, si on change le format de l'image de PNG en JPEG, l'URI changera, ce qui est néfaste.
  • Rigidité accrue. Si un logiciel impose d'être installé en /Foobar comme dans l'exemple au début, il contraint les choix pour l'administrateur système (et si je veux des URI avec un chemin vide, genre http://foobar.example.org/, je fais quoi ?)
  • Risque que le client fasse des suppositions injustifiées. Si une spécification décrit le paramètre sig comme étant forcément une signature cryptographique, il y a une possibilité qu'un logiciel client croit, dès qu'il voit un paramètre de ce nom, que c'est une signature.

Donc, pour toutes ces raisons, notre RFC déconseille fortement de rajouter des règles de structure dans les URI. Ces règles diminuent la liberté du propriétaire de l'URI.

Qui risque de violer ce principe ? Les auteurs d'applications, comme dans l'exemple Foobar plus haut, mais aussi des gens qui font des extensions aux URI, par le biais de nouvelles spécifications (par exemple pour mettre des signatures ou autres métadonnées dans l'URI). En revanche, ce principe ne s'applique pas au propriétaire lui-même, qui a évidemment le droit de définir ses règles pour la gestion de ses URI (exemple : le webmestre qui crée un schéma de nommage des URI de son site Web). Et cela ne s'applique pas non plus au cas où le propriétaire de l'URI reçoit lui-même une délégation pour gérer ce site (par exemple, un RFC qui crée un registre IANA et spécifie la structure des URI sous http://www.iana.org/ est dans son droit, l'IANA agissant sur délégation de l'IETF). Le principe « bas les pattes » de ce RFC n'est pas technique : on n'interdit pas de mettre de la structure dans les URI, on dit juste qui a le droit de le faire.

Notre RFC reconnait que certaines normes IETF (non citées...) violent ce principe, et appelle à profiter de la prochaine révision pour les corriger.

La partie normative de notre RFC est la section 2. Elle explicite le principe « bas les pattes » (« get off my lawn » dans le nom original du document en anglais...) D'abord, éviter de contraindre l'usage d'un plan particulier. Par exemple, imposer http:// peut être trop contraignant, certaines applications ou extensions pourraient marcher avec d'autres plans d'URI comme par exemple file://.

D'autre part, si on veut une structure dans les URI d'un plan particulier, cela doit être spécifié dans le document qui définit le plan (RFC 7230 pour http://, RFC 6920 pour ni:, etc) pas dans une extension faite par d'autres (« touche pas à mon plan »).

Les URI comprennent un champ « autorité » juste après le plan. Les extensions ou applications ne doivent pas imposer de contraintes particulières sur ce champ. Ainsi, pour http://, l'autorité est un nom de domaine et notre RFC ne permet pas qu'on lui mette des contraintes (du genre « le premier composant du nom de domaine doit commencer par foobar-, comme dans foobar-www.example.org »).

Même chose pour le champ « chemin », qui vient après l'autorité. Pas question de lui ajouter des contraintes (comme dans l'exemple du CMS qui imposerait /Foobar comme préfixe d'installation). La seule exception est la définition des URI bien connus du RFC 5785. Le RFC 6415 donne un exemple d'URI bien connus, avec une structure imposée.

Autre conséquence du principe « bas les pattes » et qui est, il me semble, plus souvent violée en pratique, le champ « requête » (query). Optionnel, il se trouve après le point d'interrogation dans l'URI. Notre RFC interdit aux applications d'imposer l'usage des requêtes, car cela empêcherait le déploiement de l'application dans d'autres contextes où, par exemple, on veut utiliser des URI sans requête (je dois dire que mes propres applications Web violent souvent ce principe). Quant aux extensions, elles ne doivent pas contraindre le format des requêtes. L'exemple cité plus haut, d'une extension hypothétique, qui fonctionnerait par l'ajout d'un paramètre sig aux requêtes pour indiquer une signature est donc une mauvaise idée. Une telle extension causerait des collisions (applications ou autres extensions qui voudraient un paramètre de requête nommé sig) et des risques de suppositions injustifié (un logiciel qui se dirait « tiens, un paramètre sig, je vais vérifier la signature, ah, elle est invalide, cet URI est erroné »). Au passage, les préfixes n'aident pas. Supposons qu'une extension, voulant limiter le risque de collisions, décide que tous les paramètres qu'elle définit commencent par myapp_ (donc, la signature serait myapp_sig). Cela ne supprime pas le risque de collisions puisque le préfixe lui-même ne serait pas enregistré.

Sauf erreur, Dotclear gère bien cela, en permettant, via les « méthodes de lecture » PATH_INFO ou QUERY_STRING d'avoir les deux types d'URI (sans requête ou bien avec).

Pourtant, HTML lui-même fait cela, dans la norme 4.01, en restreignant la syntaxe lors de la soumission d'un formulaire. Mais c'était une mauvaise idée et les futures normes ne devraient pas l'imiter.

Comme précédemment, les URI bien connus ont, eux, droit à changer la syntaxe ou contraindre les paramètres puisque, d'une certaine façon, l'espace sous .well-known est délégué à l'IETF et n'est plus « propriété » de l'autorité.

Dernier champ de l'URI à étudier, l'identificateur de fragment (ce qui est après le croisillon). Les définitions d'un type de média (RFC 6838) ont le droit de spécifier la syntaxe d'un identificateur de fragment spécifique à ce type de média (comme ceux du texte brut, dans le RFC 5147). Les autres extensions doivent s'en abstenir.

Bon, assez de négativité et d'interdiction. Après les « faites pas ci » et les « faites pas ça », la section 3 de notre RFC expose les alternatives, les bonnes pratiques pour remplacer celles qui sont interdites ici. D'abord, si le but est de faire des liens, il faut se rappeler qu'il existe un cadre complet pour cela, décrit dans le RFC 5988. Une application peut utiliser ce cadre pour donner la sémantique qu'elle veut à des liens. Autre technique rigolote et peu connue, les gabarits du RFC 6570, qui permettent de gérer facilement le cas de données spécifiques à l'application dans un URI.

Et, comme cité plusieurs fois, les URI bien connus du RFC 5785 sont un moyen de déclarer sa propre structure sur des URI. Par contre, ils sont censés avoir un usage limité (accéder à des métadonnées avant de récupérer une ressource) et ne sont pas un moyen générique d'échapper aux règles qui nous dérangent !


Téléchargez le RFC 7320


L'article seul

RFC 7303: XML Media Types

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : H. Thompson (University of Edinburgh), C. Lilley (W3C)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 11 juillet 2014


Voici la nouvelle norme décrivant les types MIME XML, comme application/xml. Elle remplace le RFC 3023. Cette norme décrit aussi l'utilisation de +xml comme suffixe pour des formats de données fondés sur XML comme TEI, avec son application/tei+xml.

XML est une norme du W3C et n'est donc pas dans un RFC mais dans un document du W3C. Lorsque des documents sont envoyés sur l'Internet, ils sont souvent étiquetés avec un type de média, dit aussi, pour des raisons historiques, type MIME. Pour XML, il existe cinq types, application/xml, text/xml, application/xml-external-parsed-entity, text/xml-external-parsed-entity et application/xml-dtd. Il existe aussi une convention, formalisée dans le RFC 6838, pour les formats bâtis au-dessus de XML, comme Atom (RFC 4287). On écrit les types de média pour ces formats avec un +xml à la fin, indiquant ainsi qu'un processeur XML généraliste pourra toujours en faire quelque chose, même s'il ne connait pas ce format spécifique. Ainsi, Atom est application/atom+xml. (Ces suffixes sont désormais banals mais, à l'époque du RFC 3023, ils étaient contestés et l'annexe A du RFC 3023 contient une justification détaillée, qui n'a pas été reprise dans ce nouveau RFC 7303.)

Pour normaliser quelque chose à propos de XML, il faut d'abord se pencher sur les problèmes d'encodage de caractères. Le modèle de caractères de XML est forcément Unicode.Les encodages de documents peuvent être très divers, mais la norme Unicode n'en définit que trois, UTF-8, UTF-16 et UTF-32. UTF-8 (RFC 3629) représente les caractères par une suite d'octets de longueur variable. Il n'a qu'une seule sérialisation en octets possible. UTF-16 en a deux, car il représente les caractères (enfin, ceux du PMB) par des seizets. Selon qu'on mette l'octet de poids fort du seizet en premier ou en dernier, on parle d'UTF-16 gros-boutien (utf-16be dans une déclaration XML) ou d'UTF-16 petit-boutien (utf-16le). Quant à UTF-32, qui est le plus simple des encodages, car le plus uniforme (tout caractère Unicode est représenté par quatre octets, codant son point de code), il a quatre sérialisations possibles en théorie mais deux seulement sont définies, UTF-32BE, gros-boutien, et UTF32-LE, petit-boutien. UTF-32 est malheureusement peu déployé (qu'on ne me dise pas que c'est parce qu'il prend plus de place : quatre octets par caractère, à l'époque où on s'échange des vidéos HD, ce n'est rien). Ce RFC déconseille désormais son usage. (Une bonne discussion des problèmes avec UTF-32 est dans ce rapport de bogue chez Mozilla.)

La section 3 du RFC décrit toutes les recommandations actuelles pour l'encodage des documents XML envoyés sur l'Internet. UTF-8 est recommandé (et sans BOM : il faut le retirer si, par exemple, on convertit de l'UTF-16 en UTF-8). Les producteurs/envoyeurs de XML devraient mettre un paramètre charset (pour les protocoles qui ont ce paramètre) et utiliser la déclaration XML (le truc qui commence par <?xml version="1.0" encoding=...) lorsqu'ils utilisent autre chose qu'UTF-8.

Cela suppose qu'ils sachent exactement quel est l'encodage. Si on envoie de l'XML et qu'on ne peut pas déterminer son encodage, il vaut mieux ne rien dire que de raconter n'importe quoi. Le RFC note que c'est particulièrement important pour les serveurs Web : avoir un paramètre global pour tout le site qui indique un encodage est dangereux, à moins qu'on puisse être absolument sûr que tous les documents du site auront cet encodage. Le RFC recommande que cet étiquetage soit configurable par l'utilisateur, pour qu'il puisse l'adapter à ses fichiers (AddDefaultCharset et AddCharset dans Apache.)

Quant aux consommateurs de documents XML, ils doivent considérer le BOM comme faisant autorité, et utiliser le paramètre charset si le BOM est absent. Les consommateurs qui comprennent le XML peuvent, s'il n'y a ni BOM, ni charset, utiliser les techniques purement XML, exposées dans la norme de ce format (en gros, utiliser la déclaration par exemple <?xml version="1.0" encoding="ISO 8859-1"?>. Il y a plein d'exemples en section 8 du RFC. Par exemple, ce cas-ci :


Content-Type: application/xml; charset=utf-8
...
<?xml version="1.0" encoding="utf-8"?>

est trivial : tout le monde est d'accord, le document est en UTF-8 et c'est explicite. En revanche, ce cas :


Content-Type: application/xml; charset=iso-8859-1
...
<?xml version="1.0" encoding="utf-8"?>

est pathologique : le type MIME dans l'en-tête HTTP et la déclaration XML se contredisent. Ici, un logiciel respectueux des normes doit traiter le document comme de l'ISO 8859-1, le type MIME ayant priorité (il n'y a pas de BOM, qui aurait une priorité supérieure). Mais, évidemment, l'envoyeur n'aurait pas dû générer ces informations incohérentes. Il vaut mieux ne pas envoyer de charset que d'envoyer un incorrect (voir aussi annexe C.2). Ainsi, dans ce troisième exemple :


Content-Type: application/xml
...
<?xml version="1.0"?>

il n'y a rien d'explicite : pas de BOM, pas de charset dans le type MIME et pas d'encodage indiqué dans la déclaration XML. Dans ce cas, puisque MIME ne dit rien, on applique la règle XML : l'encodage par défaut en XML est UTF-8.

À noter que le transport le plus utilisé pour XML, HTTP, avait un encodage de caractères par défaut, ISO 8859-1 mais ce n'est plus le cas depuis le RFC 7230. Voir aussi le RFC 6657 pour l'encodage par défaut des types text/*.

Assez fait d'encodage, place aux types MIME, sujet principal de ce RFC. La section 4 donne leur liste :

  • Les documents XML eux-mêmes auront le type application/xml ou text/xml. Le RFC ne tranche pas entre les deux possibilités, alors que ce choix a fait se déplacer beaucoup d'électrons, lors des innombrables discussions sur le « meilleur » type. (Le prédécesseur, le RFC 3023 recommandait text/xml.)
  • Les DTD auront le type application/xml-dtd.
  • Les « entités externes analysées » (external parsed entities), sont de type application/xml-external-parsed-entity ou text/xml-external-parsed-entity. Si elles sont par ailleurs des documents XML bien formés (ce qui n'est pas toujours le cas), elles peuvent avoir les types application/xml ou text/xml.
  • Les paramètres externes (external parameter entities) ont le même type que les DTD, application/xml-dtd.

Ils sont tous mis dans le registre IANA (cf. section 9 du RFC).

Il y a aussi le suffixe +xml (section 4.2), utilisant la notion de suffixe du RFC 6839. Il a vocation à être utilisé pour tous les formats XML. Si les concepteurs de ce format ne veulent pas d'un traitement générique XML (un cas probablement rare), ils doivent choisir un type MIME sans ce suffixe.

À noter que, si on envoie du XML en HTTP, le système de négociation de contenu de HTTP ne prévoit pas de mécanisme pour dire « j'accepte le XML, quel que soit le format ». Pas question de dire Accept: text/*+xml, donc.

La section 5 décrit ensuite le cas des identificateurs de fragment (le texte après le # dans un URI, pour désigner une partie d'un document). Ils sont décrits dans la section 3.5 du RFC 3986. Pour le cas particulier de XML, on se sert de la syntaxe XPointer pour écrire ces identificateurs (pas exemple, http://www.example.org/data.html#xpointer(/foo/bar)).

Le document XML envoyé peut utiliser l'attribut xml:base (section 6) pour spécifier un URI de référence, utilisé pour construire des URI absolus à partir des relatifs qu'on peut trouver dans le document XML (par exemple, lors de l'inclusion d'un autre document XML).

Et les versions de XML (il existe actuellement deux versions normalisées, 1.0 et 1.1) ? Voyez la section 7, qui précise que les types MIME sont exactement les mêmes pour toutes les versions : un analyseur XML doit utiliser les techniques XML (le champ version dans la déclaration) pour identifier les versions, pas les types MIME.

Enfin, si vous vous intéressez à la sécurité, voyez la section 10 qui résume rapidement les grandes questions de sécurité de XML. D'abord, les documents XML étant souvent modulaires (un document déclenche le chargement de paramètres ou d'entités XML externes, ou encore de feuilles de style), la sécurité doit prendre en compte tous les documents extérieurs et leur technique de chargement. Si un document XML est chargé en HTTPS mais qu'il contient une référence HTTP à une feuille de style CSS, la sécurité de HTTPS ne protégera pas cette dernière, alors qu'elle pourrait, par exemple, faire disparaître certains des éléments XML (display: none;...) Ainsi, beaucoup de documents XML se réfèrent à des entités XML qui sont sur les serveurs du W3C, sans se préoccuper d'analyser la sécurité desdits serveurs. Si la définition de l'entité mdash est remplacée par le texte de Winnie l'ourson, de drôles de résultats peuvent survenir.

Il y a aussi des attaques par déni de service : un document XML qui contient une référence à une entité qui contient à son tour de nombreuses autres références, qui contiennent chacune de nombreuses autres références, et ainsi de suite, jusqu'au dépassement de pile. (La forme la plus triviale de l'attaque, où l'entité se référence elle-même, est normalement empêchée par la prohibition de ces auto-références.)

Également d'un grand intérêt pratique, l'annexe C, sur les questions opérationnelles. La situation actuelle, notamment pour trouver l'encodage des caractères d'un document, n'est pas idéale. La nouvelle règle sur les BOM (priorité sur le paramètre charset) casse, en théorie, la compatibilité mais, en pratique, n'aggrave pas la situation et devrait l'améliorer à terme. Si on veut se simplifier la vie lors de l'envoi de XML, on le met en UTF-8 : c'est l'encodage recommandé par défaut, et il se passe de BOM. Si on n'utilise pas UTF-8, alors, il faut mettre un BOM.

Et le consommateur de XML ? Il lui suffit de regarder le BOM sinon le charset sinon la déclaration XML. En suivant ces règles simples, tout le monde devrait être heureux.

Depuis le RFC 3023, la précédente norme, que s'est-il passé ? L'annexe D résume les nombreux et sérieux changements. Les sous-types de text comme text/xml ont vu leur définition alignée avec celle du sous-type d'application. text/xml est donc désormais un simple synonyme de application/xml, ce qui reflète la réalité des mises en œuvre logicielles de XML. Le BOM est désormais prioritaire sur le charset. D'autre part, XPointer est désormais intégré. Et UTF-32 est maintenant officiellement déconseillé, au nom de l'interopérabilité.


Téléchargez le RFC 7303


L'article seul

RFC 7300: Reservation of Last Autonomous System (AS) Numbers

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : J. Haas (Juniper Networks), J. Mitchell (Microsoft Corporation)
Réalisé dans le cadre du groupe de travail IETF idr
Première rédaction de cet article le 11 juillet 2014


Ce très court RFC réserve formellement deux numéros d'AS, 65535 et 4294967295, qui ne doivent donc pas être alloués à un opérateur réseau.

Ces deux AS étaient déjà réservés en pratique mais cela n'avait pas été documenté (au sens du RFC 6996). Ils sont les derniers de leur plage (65535 est le dernier des AS traditionnels sur 16 bits et 4294967295 le dernier des AS sur 32 bits du RFC 6793), d'où le titre de ce RFC.

Pourquoi les réserver ? Pour 65535, c'est parce qu'il est déjà utilisé dans des communautés BGP bien connues (RFC 1997 et registre IANA, c'est le cas, par exemple, de NO_EXPORT, alias 65535:65281). Par contre, 4294967295 n'a pas de telles communautés mais il est réservé au cas où on ait besoin d'un AS qui ne soit jamais annoncé en BGP.

Donc, ces deux AS ne doivent pas être utilisés sur l'Internet et ne seront donc jamais (si tout le monde respecte les règles) dans un attribut BGP comme AS_PATH. Mais ils peuvent servir à des usages pas encore définis et les programmes qui mettent en œuvre BGP ne doivent donc pas interdire leur usage. Ils sont désormais dans le registre IANA des AS et dans celui des AS spéciaux.


Téléchargez le RFC 7300


L'article seul

RFC 7304: A method for mitigating namespace collisions

Date de publication du RFC : Juillet 2014
Auteur(s) du RFC : W. Kumari (Google)
Pour information
Première rédaction de cet article le 7 juillet 2014


Il arrive parfois, lors de l'utilisation d'un nom de domaine, qu'il y ait plusieurs noms qui correspondent à l'intention exprimée par l'utilisateur humain (dans les milieux ICANN, c'est souvent appelé une collision, terme brutal conçu pour faire peur). Par exemple, si l'utilisateur dans une entreprise example.com tape « www.foobar » dans son navigateur Web, peut-être comptait-il aller en http://www.foobar.example.com/ en comptant sur le fait que le logiciel complètera (ce qu'on nomme une search list) mais peut-être aussi voulait-il aller en http://www.foobar/ (le TLD .foobar n'existe pas aujourd'hui mais, au rythme où des TLD sont créés, cela pourrait arriver un jour). Dans ces conditions, comment faire pour désambiguer ? Ce très court RFC décrit une méthode... et explique pourquoi elle est fortement déconseillée.

Ce problème n'est pas spécifique aux search lists. On peut aussi l'avoir lorsque plusieurs espaces de nommage sont utilisés, par exemple en cas de « racines alternatives ». Les partisans desdites racines écartent souvent le problème de « collision » entre deux racines en disant « il suffira de demander à l'utilisateur son choix ». Pourquoi est-ce une mauvaise idée (mon intention était d'écrire « une idée idiote » mais le RFC dont je rends compte est plus prudent) ?

La section 2 de notre RFC décrit la méthode et ses défauts : si le nom n'a qu'une signification possible, on y va, sinon on présente à l'utilisateur une liste des possibilités « vouliez-vous aller en http://www.foobar.example.com/ ou en http://www.foobar/ ? » et on lui demande de choisir. On peut mémoriser ce choix, pour éviter de demander trop souvent à l'utilisateur. Mes lecteurs techniques voient sans doute immédiatement pourquoi cela ne peut pas marcher, mais ce RFC est conçu à des fins pédagogiques, pour tordre le cou une bonne fois pour toutes à cette fausse bonne idée, qui resurgit de temps en temps.

Quels sont les problèmes avec cette approche (si vous êtes enseignant en informatique, vous pouvez faire de cette question un sujet d'interrogation, pour voir si les étudiants ont bien compris les réseaux) ?

  • Il n'y a pas toujours un humain disponible devant l'écran. Il y a des serveurs fonctionnant automatiquement, et des applications qui s'exécutent en arrière-plan (comme les mises à jour périodiques).
  • L'humain peut ne pas savoir, notamment si le choix découle d'une tentative de hameçonnage ou autre tentative délibérée de l'induire en erreur.
  • Le délai supplémentaire pendant lequel l'utilisateur réfléchit peut conduire l'application à renoncer.
  • Ces demandes perpétuelles peuvent être pénibles pour l'utilisateur (songez qu'une seule page Web peut comporter des dizaines de noms de domaine).
  • Mémoriser les choix résoudrait partiellement le problème précédent, mais créerait d'autres ennuis, par exemple en cas de déplacement (si l'utilisateur se servait d'un portable et qu'il quitte les locaux de example.com).

Bref, c'est une mauvaise solution et notre RFC la déconseille. Par contre, il ne propose pas d'alternative (il n'y en a pas de sérieuse).

Pous les informaticiens, notez que, question mise en œuvre technique d'une telle solution, on aurait plusieurs possibilités : intercepter les requêtes de résolution de noms dans un shim, une mince couche logicielle entre l'application et la bibliothèque de résolution de noms (c'est ainsi que sont souvent mis en œuvre des TLD « alternatifs » comme .bit ou .onion) ou bien en remplaçant complètement la bibliothèque de résolution de noms. On pourrait encore mettre ce service dans le navigateur Web (ignorant les problèmes des applications non-Web), ou dans un relais sur le trajet.


Téléchargez le RFC 7304


L'article seul

RFC 7288: Reflections On Host Firewalls

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : D. Thaler (Microsoft)
Pour information
Première rédaction de cet article le 2 juillet 2014


Aujourd'hui, on met des pare-feux partout et leur présence est en général considérée, sans aucune réflexion supplémentaire, comme indispensable. Il existe des pare-feux qui se branchent sur le réseau et protègent ainsi toutes les machines du réseau (network firewall). Et il existe des pare-feux sur les machines terminales, des host firewalls, qui font l'objet de ce RFC. En présence d'un tel pare-feu, on peut avoir une application qui tourne, et donc consomme des ressources, mais ne peut pas communiquer avec l'extérieur puisqu'elle est bloquée par le pare-feu. Le même résultat de sécurité pourrait être atteint en ne faisant pas tourner l'application, ou en la configurant pour limiter ses possibilités. Cette approche ne serait-elle pas meilleure ?

Le RFC 2979 discutait des pare-feux sur l'Internet et des propriétés qu'on attendait d'eux (et les RFC 4949 et RFC 4948 discutaient de la terminologie). Le RFC 2979 pointait notamment le risque de « faux positif », lorsqu'un pare-feu bloque accidentellement une communication légitime. Les vendeurs de pare-feux oublient toujours de mentionner ce risque. Pourtant, nombreux sont les pare-feux qui bloquent stupidement des connexions parfaitement légitimes, par exemple parce que les paquets IP contiennent des options, ou des en-têtes IPv6 (cf. RFC 7045). Le RFC 4924 revient sur cette question des pare-feux en notant qu'ils sont largement responsables de l'ossification de l'Internet, et que de plus en plus d'applications consacrent beaucoup d'effort à contourner ces obstacles. En outre, les pare-feux contribuent souvent à un faux sentiment de sécurité, qui peut mener à baisser sa garde sur les machines supposées « protégées par le pare-feu » (section 6 de notre RFC). Enfin, la section 1.1 du RFC rappelle que le NAT n'est pas un vrai pare-feu.

Le problème des pare-feux est d'autant plus aigu que la réponse classique à la question de l'utilité d'un pare-feu est « bloquer le trafic non souhaité », mais que cette réponse ne dit pas « non souhaité par qui ». Par l'utilisateur final ? Par son employeur ? Par l'administrateur réseau ? Par le développeur ? Leurs intérêts sont souvent en conflit et on ne peut pas espérer un consensus simple entre tous ces acteurs (le RFC reprend l'excellent terme de « tussle »). Pas étonnant qu'on voit souvent des « courses aux armements » entre ceux qui bloquent et ceux qui contournent le blocage.

Le pare-feu typique est souvent configuré par des règles allow/deny où on indique que tel type de trafic est autorisé ou interdit (section 2 du RFC). Voici par exemple des règles Shorewall où tout est interdit par défaut mais où on accepte quelques services :

# SMTP 
ACCEPT               net       fw       tcp      25
ACCEPT               net       fw       tcp      587
# IMAP, IMAPS et POPS
ACCEPT               net        fw      tcp      143
ACCEPT               net        fw      tcp      993
ACCEPT               net        fw      tcp      995
# Nagios Remote Execution (NRPE)
ACCEPT               net:192.0.2.187  fw  TCP     5767
# SNMP
ACCEPT               net:192.0.2.187,203.0.113.170   fw  udp   161

Pour un pare-feu sur une machine terminale, l'utilisateur n'a pas le droit de modifier ces règles sauf dans le cas (fréquent avec les machines personnelles) où l'utilisateur est en même temps administrateur système. Par contre, des applications peuvent configurer le pare-feu, pour les systèmes où celui-ci fournit une API aux applications, ou bien s'il existe un protocole standard de changement de la configuration du pare-feu comme UPnP ou son successeur potentiel PCP (RFC 6887). Ces règles sont classées par notre RFC en deux catégories : réduction de la surface d'attaque (empêcher l'application de faire des choses que le développeur ne voulait pas qu'elle fasse) et politique de sécurité (empêcher l'application de faire des choses que l'administrateur système ne voulait pas qu'elle fasse).

La section 3 décrit la première catégorie, la réduction de la surface d'attaque. On peut se demander si cette catégorie est utile : après tout, si on ne veut pas qu'une application, par exemple, détruise des fichiers en réponse à une requête venue par le réseau, le mieux est de ne pas mettre en œuvre cette fonction. On sera alors bien plus en sécurité. Sauf qu'on n'a pas toujours le luxe d'avoir uniquement des applications bien conçues et bien écrites. Souvent, le développeur a fait des impasses (ou, en termes plus savants, a pris de la dette technique) et réécrire l'application serait trop coûteux. C'est par exemple le cas de beaucoup d'applications Web programmées à la va-vite sans souci de sécurité, en PHP ou VB.NET. Coller un pare-feu devant l'application est en général moins coûteux et plus efficace que de se plonger dans le plat de spaghettis.

Il y a aussi des cas où la dette prise par le développeur (reporter le problème à plus tard...) était justifiée par un manque de support de la part du système. Par exemple, une règle aussi simple et courante que « communication uniquement avec le lien local » est difficile à mettre en œuvre de façon portable, sur Unix, dans tous les langages de programmation. (Notez au passage que n'autoriser que les adresses IP source du même réseau que celui du serveur n'est pas suffisant, en raison de la possibilité d'usurpation d'adresse. Pour UDP, il faudrait aussi tester le TTL, cf. RFC 5082.) Il y a aussi le cas où la machine se déplace d'un réseau sûr à un réseau non-sûr. Le développeur est donc tenté de déléguer la mise en œuvre de cette politique à un pare-feu.

Même chose avec des politiques complexes, comme de ne communiquer que sur les liens les moins coûteux, sachant que l'application n'a pas forcément connaissance des coûts (sur Android, cette politique est en général purement binaire : on considère que la 3G est coûteuse et le Wifi gratuit, ce qui n'est pas toujours vrai.) Avoir une API permettant à l'application d'exprimer ses souhaits, que le système d'exploitation traduirait en règles de pare-feu concrètes, simplifierait beaucoup les choses.

Quelles sont donc les solutions pour l'utilisateur, qui se retrouve avec une application « imparfaite », pleine de failles de sécurité ? D'abord, réparer le logiciel en corrigeant ces failles. Les partisans des pare-feux feront évidemment remarquer qu'il est plus rapide et plus efficace d'utiliser un pare-feu : déboguer un logiciel mal écrit est long et coûteux et on n'est jamais sûr d'avoir bouché tous les trous de sécurité. Il y a d'ailleurs des cas où réparer le logiciel est quasi-impossible : pas les sources pour le faire ou bien pas de mécanisme de mise à jour pour les engins qui font tourner ce logiciel. En général, un changement des règles du pare-feu peut être déployé plus facilement qu'un patch.

Autre idée, ne pas utiliser le logiciel. Solution brutale mais qui résoud tout. Notez que l'idée n'est pas si violente qu'elle en a l'air : certains systèmes font tourner par défaut des logiciels qui ne devraient pas tourner. Arrêter ce logiciel inutile est une solution réaliste.

Enfin, il y a le pare-feu, qui va protéger contre le logiciel mal fait. Le logiciel tournera toujours, consommant des ressources (personnellement, je ne trouve pas l'argument très convaincant : si le logiciel est juste en attente d'événements, il ne consomme pas grand'chose). Et, surtout, si un utilisateur tente de se servir du logiciel, il aura des surprises, car beaucoup de programmes ne sont pas conçus de manière robuste : ils réagissent mal quand, par exemple, certains services réseau ne leur sont pas accessibles. Parfois, cela aggrave la consommation de ressources (par exemple, le logiciel, n'ayant pas compris qu'il était bloqué par le pare-feu, réessaie en boucle). Autre problème avec les pare-feux : ils sont souvent surbloquants. Par exemple, ils bloquent tout ce qu'il ne connaissent pas et gênent donc considérablement l'innovation. Certains analysent les paquets de manière très sommaire (oubliant par exemple certaines fonctions avancées du protocole) et prennent donc de mauvaises décisions. C'est ainsi qu'ICMP est souvent bloqué, menant à des problèmes de découverte de la MTU du chemin (RFC 4890 et RFC 2979).

Deuxième catégorie de règles dans un pare-feu, celles qui visent à faire respecter une politique de sécurité. L'administrateur a décidé qu'on ne regarderait pas YouTube, les règles bloquent YouTube. Cette catégorie peut en théorie être mise en œuvre à trois endroits. D'abord, dans les applications elles-mêmes. Ça ne passe évidemment pas à l'échelle : il faudrait ajouter du code à toutes les applications (faites un peu une liste du nombre de clients HTTP sur votre machine, par exemple ; rappelez vous qu'il n'y a pas que les navigateurs Web) et les configurer. En pratique, il y aura toujours une application oubliée. Cette solution n'est donc pas réaliste.

Deuxième endroit possible, le pare-feu. Outre les problèmes discutés plus haut (application qui réagit mal au blocage de certains services), cela mène facilement à une course aux armements : comme les désirs de l'utilisateur final et de l'administrateur du pare-feu ne coïncident pas forcément, les applications, à la demande des utilisateurs, vont évoluer pour contourner le filtrage, qui va devenir de plus en plus intrusif et ainsi de suite jusqu'à ce que tout le monde, pris dans cette course, oublie le but originel de la politique de sécurité. Comme le disait la section 2.1 du RFC 4924, le filtrage sans le consentement (au moins passif) de l'utilisateur est difficile, et va mener à des solutions compliquées, coûteuses et fragiles, violant largement les bons principes d'architecture réseau (par exemple en faisant tout passer au-dessus de HTTP, seul protocole autorisé).

Reste le troisième endroit, qui a plutôt les faveurs du RFC : faire des politiques de sécurité un service auquel s'inscrivent les applications. L'application utilise une bibliothèque (comme les TCP wrappers) ou bien parle un protocole réseau (comme PCP, RFC 6887), décrit ce qu'elle veut, et apprend si c'est autorisé ou pas, de manière propre. Cela nécessite évidemment des standards (comme PCP, qui est pour l'instant très peu déployé, et qui est de toute façon sans doute insuffisant pour gérer toutes les politiques possibles) car on n'imagine pas chaque application ajouter du code pour l'API de chaque marque de pare-feu.

Cette technique est très originale par rapport à l'approche habituelle sur les pare-feux mais c'est celle recommandée par notre RFC. Pour le résumer en une phrase, on fait trop travailler les pare-feux et pas assez les applications.


Téléchargez le RFC 7288


L'article seul

RFC 7252: Constrained Application Protocol (CoAP)

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : Z. Shelby (Sensinode), K. Hartke, C. Bormann (Universitaet Bremen TZI)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF core
Première rédaction de cet article le 2 juillet 2014


Le protocole CoAP n'est pas destiné aux ordinateurs, ni même aux smartphones mais principalement aux petits engins, aux machines qui n'ont parfois qu'un microcontrôleur 8 bits pour tout processeur, très peu de mémoire (cf. RFC 6574) et qui, en prime, sont connectées par des liens radio lents et peu fiables (les « LowPAN » des RFC 4919 et RFC 4944), allant parfois à seulement quelques dizaines de kb/s. Pour de tels engins, les protocoles comme HTTP et TCP sont trop contraignants. CoAP est un « HTTP-like » (il reprend pas mal de termes et de concepts de HTTP, comme le modèle REST) mais spécialement conçu pour des applications M2M (machine-to-machine) dans des environnements à fortes limitations matérielles.

Le succès de HTTP et notamment de REST a sérieusement influencé les concepteurs de CoAP. Le modèle de CoAP est celui de HTTP : protocole requête/réponse, verbes GET, POST, DELETE, etc, des URI et des types de media comme sur le Web (application/json...) Cette proximité facilitera notamment le développement de passerelles entre le monde des objets CoAP et le Web actuel (section 10). Une des pistes explorées pour l'Internet des Objets (terme essentiellement marketing mais répandu) avait été de prendre de l'HTTP normal et de comprimer pour gagner des ressources réseau mais CoAP suit une autre voie : définir un protocole qui est essentiellement un sous-ensemble de HTTP. Si vous connaissez HTTP, vous ne serez pas trop perdu avec CoAP.

CoAP tournera sur UDP (RFC 768), TCP étant trop consommateur de ressources, et, si on veut de la sécurité, on ajoutera DTLS (RFC 6347). CoAP fonctionne donc de manière asynchrone (on lance la requête et on espère une réponse un jour). On pourrait décrire CoAP comme composé de deux parties, un transport utilisant UDP avec des extensions optionnelles si on veut de la fiabilité (accusés de réception), et un langage des messages (requêtes et réponses en HTTP-like).

Et les messages ? L'en-tête CoAP (section 3 du RFC) a été conçu pour être facile à analyser par des programmes tournant sur de petites machines. Au lieu du texte lisible de HTTP, l'en-tête de CoAP commence par une partie fixe de quatre octets, qui comprend un Message ID de deux octets. Ce Message ID permet de détecter les duplicatas et d'associer un accusé de réception à un message précis. À noter qu'avec ces deux octets, on est limité à environ 250 messages par seconde (en raison du paramètre EXCHANGE_LIFETIME qui est à 247 secondes par défaut). Ce n'est pas une limite bien grave : les ressources (en énergie, en capacité réseau, etc) des machines CoAP ne leur permettront pas d'être trop bavardes, de toute façon.

Le message contient également un champ Token (utilisé pour mettre en correspondance les requêtes et les réponses, à un niveau supérieur à celui des messages, qui sont, eux, identifiés par le Message ID) et un code sur huit bits. Ce code s'écrit sous forme d'un groupe de trois chiffres, le premier indiquant la classe (2 : succès, 4 : erreur du client, 5 : erreur du serveur, etc) et suivi d'un point, et les deux autres chiffres fournissant des détails. Comme vous connaissez HTTP, vous ne serez pas étonné d'apprendre que le code 4.03 indique un problème de permission... Un registre de ces codes existe.

Notez bien que l'encodage, lui, est radicalement différent de celui de HTTP. Il est en binaire et le code 4.03 sera transmis sous forme d'un octet (10000011, avec 100 pour la classe et 11 pour le détail), pas sous forme de la chaîne de caractères qu'utilise HTTP.

Le message contient ensuite des options, qui jouent le rôle des en-têtes de requête de HTTP (leur liste est également dans un registre, on y trouve des trucs connus comme If-Match - section 5.10.8.1, Accept - section 5.10.4 - ou ETag - section 5.10.6). Pour économiser quelques bits, leur encodage est assez spécial. Le numéro de l'option, normalement sur 16 bits, n'est pas transmis tel quel mais sous forme d'une différence avec l'option précédente. Ainsi, si on utilise les options 4 (ETag) et 5 (If-None-Match), la première option indiquera bien 4 mais la seconde indiquera 1... De même, les valeurs de l'option sont violemment comprimées. Si on veut envoyer un entier, on ne doit transmettre que le nombre d'octets nécessaire (rien si l'entier vaut zéro, un seul octet s'il est inférieur à 255, etc).

Le transport utilisé par CoAP, UDP, ne fournit aucune garantie de remise : un paquet UDP peut être perdu, et UDP ne s'en occupera pas, ce sera à l'application de gérer ce cas. CoAP fournit un service pour faciliter la vie aux applications : on peut marquer un message CoAP comme nécessitant un accusé de réception (type CON pour Confirmable). Si ce type est présent dans l'en-tête CoAP, l'expéditeur recevra un accusé de réception (type ACK) portant le Message ID du message reçu. Le RFC fournit l'exemple d'un capteur de température interrogé en CoAP :

Client -> Serveur: CON [0xbc90] "GET /temperature" 
Serveur -> Client: ACK [0xbc90] "2.05 Content" "22.5 °C"

Ici, le second message, la réponse du serveur, contient un accusé de réception pour 0xbc90 (le Message ID). La requête est un GET et la réponse a le code 2.05 (un succès). Notez que, contrairement à HTTP, les codes sont écrits avec un point entre le premier chiffre (la classe) et les deux autres (le détail, cf. section 5.9). Ainsi, 4.04 a la classe 4 (erreur du client) et le détail 04 (ressource non trouvée). Comme indiqué plus haut, les classes sont les mêmes qu'en HTTP : 2 pour un succès, 4 pour une erreur du client (qui devrait changer sa requête avant de réessayer) et 5 pour une erreur du serveur (réessayer à l'identique plus tard peut marcher).

Le Message ID et ces types CON et NON (son contraire, Non-confirmable) permettent de mettre en œuvre des mécanismes simples de retransmission. L'émetteur envoie un message de type CON, s'il ne reçoit pas de message de type ACK à temps, il réessaie, avec croissance exponentielle du délai. Le Message-ID permet aussi de détecter les éventuels duplicatas.

On n'est pas obligé d'utiliser le type CON. Si un message n'a pas besoin de fiabilité (par exemple les lectures périodiques qu'envoie un capteur, que quelqu'un écoute ou pas), il sera du type NON.

Le Message ID doit évidemment être unique, pendant la durée de vie possible des échanges (variable EXCHANGE_LIFETIME, quatre minutes par défaut). Il peut être généré de manière incrémentale (l'émetteur garde un compteur indiquant le dernier Message ID généré) mais, dans ce cas, le RFC recommande (section 4.4) que la valeur initiale soit aléatoire, pour limiter les possibilités d'attaque par un méchant non situé sur le chemin des données (c'est un problème de sécurité analogue à celui des identificateurs de fragments IP - voir l'option --frag-id-policy de l'outil frag6 - ou celui des numéros de séquences initiaux de TCP - voir le RFC 6528).

Autre problème pour l'émetteur CoAP : quelle taille de messages ? Les machines pour qui CoAP est conçu peuvent avoir du mal à gérer parfaitement la fragmentation. Et puis des gros paquets ont davantage de chances d'être corrompus que des petits, sur les liens radios typiques des machines CoAP. Le RFC recommande donc de ne pas dépasser la MTU et, si elle est inconnue, de ne pas dépasser 1 280 octets en IPv6. (C'est plus compliqué pour IPv4 où le RFC recommande 576 octets et le bit DF - Don't Fragment - en notant que, pour les usages typiques de CoAP, le chemin sera simple, avec peu de routeurs et pas de liens à grande distance, donc peu de chances que la fragmentation soit nécessaire.)

Comme CoAP repose sur UDP, il n'y a pas de mécanisme de contrôle de la congestion par défaut. CoAP doit donc s'auto-limiter : réémissions avec croissance exponentielle des délais, et au maximum une requête en attente pour chaque machine avec qui on communique (paramètre NSTART). Ces règles sont pour le client. Si tous les clients les respectent, tout ira bien. Mais un serveur CoAP prudent doit considérer que certains clients seront bogués ou malveillants. Les serveurs ont donc tout intérêt à avoir une forme ou une autre de limitation de trafic.

La sémantique des requêtes et réponses CoAP figure en section 5. On l'a dit, elle ressemble à celle de HTTP, un client qui envoie une requête et un serveur qui transmet une réponse. L'une des plus grosses différences avec HTTP est le caractère asynchrone de l'échange.

Les requêtes sont les classiques (section 5.8) GET, POST, PUT et DELETE, agissant sur une ressource donnée (identifiée par un URI), comportant des données optionnelles (surtout utilisées pour POST et PUT) et utilisant les types de média. CoAP exige que GET, PUT et DELETE soient idempotents (une bonne pratique mais pas toujours respectée sur le Web). Une requête non reconnue et la réponse sera un 4.05. La liste des requêtes possibles est dans un registre IANA.

Du fait du caractère asynchrone de l'échange, mettre en correspondance une requête et une réponse est un peu plus compliqué qu'en HTTP, où toutes les deux empruntaient la même connexion TCP. Ici, la section 5.3.2 résume les règles de correspondance : une réponse correspond à une requête si l'adresse IP source de la réponse était l'adresse IP de destination de la requête et que le Message ID et le Token correspondent.

Comme HTTP, CoAP permet d'utiliser des relais. C'est d'autant plus important que les machines CoAP sont souvent indisponibles (hibernation pour économiser la batterie, par exemple) et un relais pourra donc répondre à la place d'une machine injoignable. Les relais sont traités plus en détail en section 5.7. Comme en HTTP, on pourra avoir des forward proxies sélectionnés par les clients ou des reverse proxies placés devant un serveur.

On l'a vu, CoAP, comme HTTP, utilise des URI, détaillés en section 6. Ils commencent par les plans coap: ou coaps:. On verra ainsi, par exemple, coap://example.com/~sensors/temp.xml ou coaps://[2001:db8::2:1]/measure/temperature/. Attention, tout le monde croit bien connaître les URI car ils sont massivement utilisés pour le Web mais le RFC met en garde les programmeurs : il y a plein de pièges dans le traitement des URI, notamment dans le décodage des valeurs encodées en pour-cent.

Si le port n'est pas indiqué dans l'URI (ce qui est le cas des deux exemples ci-dessus), le port par défaut est 5683 pour coap: et 5684 pour coaps:.

CoAP utilise, comme HTTP, la convention du /.well-known/ (RFC 5785) pour placer des ressources « bien connues », facilitant ainsi leur découverte.

Justement, à propos de découverte, la section 7 détaille comment un client CoAP va pouvoir trouver les URI des ressources intéressantes. CoAP est conçu pour des communications de machine à machine, où aucun humain n'est présent dans la boucle pour deviner, faire une recherche Google, utiliser son intuition, etc. La méthode privilégiée dans CoAP est celle du RFC 6690 : un fichier au format normalisé situé en /.well-known/core.

Les section 9 et 11 sont consacrées à la sécurité de CoAP. Évidemment, vu les ressources très limitées des machines CoAP typiques, il ne faut pas s'attendre à une grande sécurité. Par exemple, une protection par DTLS (RFC 6347) est possible mais pas obligatoire, juste conseillée. En outre, les ressources matérielles limitées des machines CoAP compliquent la tâche du programmeur : des processeurs très lents qui facilitent les attaques par mesure du temps écoulé, et pas de bon générateur aléatoire. En l'absence d'un tel générateur, on ne peut donc pas générer des clés cryptographiques localement, il faut le faire en usine et les enregistrer sur la machine. Et pas question de mettre les mêmes clés à tout le monde : comme les machines CoAP seront souvent dispersées dans des endroits ayant peu de sécurité physique (capteurs placés un peu partout), et n'auront en général pas de protection contre les manipulations physiques, pour des raisons de coût, un attaquant pourra facilement ouvrir une machine et en extraire ses clés.

La complexité est l'une des ennemies principales de la sécurité. Le RFC rappelle que le traitement des URI (bien plus compliqué qu'il n'en a l'air) impose des analyseurs relativements complexes et donc augmente la probabilité de bogues, pouvant mener à des failles de sécurité. Même chose pour le format des liens du RFC 6690.

Question réseau, CoAP souffrira sans doute d'attaques rendues possibles par l'usurpation d'adresses IP. Comme UDP ne nécessite pas d'échange préalable, il est trivial d'envoyer un paquet depuis une adresse usurpée et de le voir accepté. Cela permet des tas d'attaques comme :

  • Envoyer un RST (Reset) aux demandes de connexion vers un serveur, rendant ainsi ce serveur inutilisable (déni de service),
  • Envoyer une fausse réponse à une requête GET.

Pour cette deuxième attaque, il faut pouvoir deviner le Token utilisé. Si l'attaquant est aveugle (parce que situé en dehors du chemin des paquets), sa tâche sera difficile si le Token est bien aléatoire (mais ces machines contraintes en ressources n'ont souvent pas de vrai générateur aléatoire, au sens du RFC 4086).

En pratique, il est probable que les déploiements réels de CoAP aient lieu dans des réseaux fermés, ne contenant que des machines de confiance, et où l'accès à l'extérieur passera par une passerelle qui s'occupera de sécurité. Autrement, un simple ping à destination d'une machine CoAP pourrait suffir à vider sa batterie.

CoAP introduit aussi une autre menace, celle d'attaques par amplification (section 11.3). Comme SNMP et le DNS (tous les deux déjà utilisés pour ces attaques), CoAP a des réponses plus grandes que les questions (permettant l'amplification) et repose sur UDP et non pas TCP (permettant l'usurpation d'adresse IP).

La seule limite à ces attaques semble devoir être les faibles ressources des machines CoAP, qui devraient les rendre relativement peu intéressantes comme amplificateurs contre des serveurs Internet. En revanche, à l'intérieur d'un réseau de machines CoAP, le risque demeure élevé et le RFC ne propose aucune vraie solution.

CoAP est, d'une certaine façon, un vieux protocole (voir l'article « CoAP: An Application Protocol for Billions of Tiny Internet Nodes » de Carsten Bormann, Angelo Paolo Castellani et Zach Shelby publié en 2012 dans dans Internet Computing, IEEE (Volume:16 , Issue: 2) et malheureusement pas disponible en ligne, il faut demander une copie à l'auteur). Il existe déjà plusieurs mise en œuvre, qui ont été souvent testées dans des essais d'interopérabilité formels. Un exemple de mise en œuvre est celle du système Contiki.

Des exemples de requêtes CoAP décrites en détail figurent dans l'annexe A du RFC. Grâce à Renzo Navas, voici quelques traces CoAP au format pcap :

Si tcpdump ne connait pas encore CoAP, Wireshark, par contre, sait bien l'analyser. Ici, une la première requête GET du premier fichier pcap et sa réponse :


Constrained Application Protocol, TID: 27328, Length: 21
    01.. .... = Version: 1
    ..00 .... = Type: Confirmable (0)
    .... 0000 = Option Count: 0
    Code: GET (1)
    Transaction ID: 27328
    Payload Content-Type: text/plain (default), Length: 17, offset: 4
        Line-based text data: text/plain
            \273.well-known\004core

Constrained Application Protocol, TID: 27328, Length: 521
    01.. .... = Version: 1
    ..10 .... = Type: Acknowledgement (2)
    .... 0000 = Option Count: 0
    Code: 2.05 Content (69)
    Transaction ID: 27328
    Payload Content-Type: text/plain (default), Length: 517, offset: 4
        Line-based text data: text/plain
            \301(\261\r
            [truncated] \377</careless>;rt="SepararateResponseTester";title="This resource will ACK anything, but never send a separate response",</helloWorld>;rt="HelloWorldDisplayer";title="GET a friendly greeting!",</image>;ct="21 22 23 24";rt="Ima

Merci à Laurent Toutain pour sa relecture attentive.


Téléchargez le RFC 7252


L'article seul

RFC 7284: The Profile URI Registry

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : M. Lanthaler
Pour information
Première rédaction de cet article le 1 juillet 2014


Le RFC 6906 définit la notion de profil, une variante (en général restrictive) d'un format. Les profils du RFC 6906 sont identifiés par un URI et ce nouveau RFC crée un registre de ces URI, afin de faciliter la tâche des développeurs.

Les conditions d'inscription dans le registre sont spécifiées dans la section 2 : il faut indiquer l'URI qui sert d'identificateur, un nom court pour le profil, la description et un lien vers une documentation stable du profil, accessible publiquement. Ensuite (section 4), c'est du premier arrivé, premier servi (politique décrite dans la section 4.1 du RFC 5226). La section 3 donne un exemple en utilisant un profil bidon, http://example.com/profiles/example.

Le registre est désormais en ligne et comprend au début cinq profils :

  • urn:example:profile-uri, prévu pour être utilisé dans les exemples, documentations, etc,
  • Dublin Core http://dublincore.org/documents/2008/08/04/dc-html/, un moyen pour mettre les méta-données en HTML,
  • http://www.w3.org/ns/json-ld#expanded, http://www.w3.org/ns/json-ld#compacted et http://www.w3.org/ns/json-ld#flattened, des profils du monde JSON décrits dans une norme du W3C.

Téléchargez le RFC 7284


L'article seul

RFC 7250: Using Raw Public Keys in Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : P. Wouters (Red Hat), H. Tschofenig, J. Gilmore, S. Weiler (SPARTA), T. Kivinen (AuthenTec)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tls
Première rédaction de cet article le 1 juillet 2014


Le système de sécurité TLS, normalisé dans le RFC 5246 est souvent confondu avec la norme de certificats X.509. On lit souvent des choses comme « le serveur TLS envoie alors un certificat X.509 au client », voire, pire, on appelle ces certificats « certificats TLS » (ou, encore plus inexact, « certificats SSL »). Dans la réalité, les deux systèmes, TLS et X.509, sont indépendants. X.509 peut servir à autre chose qu'à TLS (et c'est pourquoi parler de « certificats SSL » est une grosse erreur), et TLS peut utiliser d'autres techniques d'authentification que X.509. Une de ces techniques était déjà spécifiée dans le RFC 6091, l'utilisation de clés PGP. Une autre vient d'être normalisée dans ce RFC, l'utilisation de « clés brutes » (raw keys), sans certificat autour. Comme une clé seule ne peut pas être authentifiée, ces clés brutes n'ont de sens que combinées avec un mécanisme d'authentification externe, comme DANE.

Rappelons en effet qu'un certificat, c'est une clé publique (champ SubjectPublicKeyInfo dans X.509) plus un certain nombre de métadonnées (comme la date d'expiration) et une signature par une autorité qui certifie la clé. Pour la session TLS, la seule partie indispensable est la clé (dans un certificat « auto-signé », seule la clé a un sens, le reste du certificat ne pouvant pas être vérifié). Mais, en pratique, le chiffrement sans l'authentification est limité : il ne protège que contre un attaquant purement passif. Dès que l'attaquant est actif (pensons par exemple à un hotspot Wifi tentant... mais tenu par l'attaquant), il peut rediriger le client TLS vers un serveur intermédiaire qui va faire croire au client qu'il est le vrai serveur, avant de retransmettre vers le serveur authentique. Pour contrer cette attaque de l'homme du milieu, on authentifie le serveur. Sur l'Internet, la méthode la plus courante est l'utilisation de certificats X.509, ou plus précisement de leur profil PKIX (RFC 5280). Cette méthode est d'une fiabilité très douteuse : des centaines d'organisations dans le monde peuvent émettre un faux certificat pour gmail.com et il ne peut donc pas y avoir de confiance rationnelle dans ce système (cf. « New Tricks for Defeating SSL in Practice »).

Il existe d'autres méthodes d'authentification que X.509, permettant de faire un lien entre le serveur qu'on veut contacter, et l'entité qui nous envoie une clé publique en TLS :

  • Les certificats ou clés publiés dans le DNS et sécurisés par DNSSEC, à savoir la technique DANE du RFC 6698,
  • Les certificats ou clés publiés dans un annuaire LDAP,
  • Les certificats ou clés stockés en dur chez le client. Cela manque de souplesse (il est difficile de les changer) et ce n'est donc pas très recommandé pour l'Internet public mais c'est envisagé pour des déploiements plus fermés comme ceux du protocole CoAP (RFC 7252), où les objets peuvent sécuriser la communication avec DTLS. (Cette troisième méthode est sans doute la seule possible pour l'authentification du client TLS par le serveur, cf. section 4.3.)

La section 6 de notre RFC revient en détail sur l'authentification de ces clés brutes. Sans cette authentification, une session TLS utilisant ces clés est très vulnérable à l'homme du milieu.

La section 3 décrit les détails de format des clés brutes. Il faut deux extensions au protocole TLS, client_certificate_type et server_certificate_type (enregistrées dans le registre des extensions), qui permettent d'indiquer (lors de la négociation TLS) séparément le type de certificat utilisé par le serveur et par le client. Par exemple, pour le serveur, cela s'écrit ainsi :

 
struct {
           select(ClientOrServerExtension) {
               case client:
                 CertificateType server_certificate_types<1..2^8-1>;
               case server:
                 CertificateType server_certificate_type;
           }
   } ServerCertTypeExtension;

Lorsque ces extensions TLS sont utilisées, la structure Certificate est modifiée ainsi :

   

struct {
       select(certificate_type){

           // certificate type defined in this document.
           case RawPublicKey:
             opaque ASN.1_subjectPublicKeyInfo<1..2^24-1>;

           // X.509 certificate defined in RFC 5246
           case X.509:
             ASN.1Cert certificate_list<0..2^24-1>;

           // Additional certificate type based on TLS
           //   Certificate Type Registry 
           //   http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-3
           // for instance PGP keys
       };
   } Certificate;

(On notera que le RFC 6091 utilisait un autre mécanisme, l'extension cert_type, numéro 9, mais qui n'a pas été réutilisée ici, le RFC 6091 n'ayant pas le statut « Chemin des Normes » et le mécanisme cert_type ne permettant pas des types différents pour le client et le serveur.) Le champ subjectPublicKeyInfo contient un encodage DER de la clé et d'un identificateur d'algorithme (un OID comme 1.2.840.10045.2.1 pour ECDSA) :

     SubjectPublicKeyInfo  ::=  SEQUENCE  {
           algorithm               AlgorithmIdentifier,
           subjectPublicKey        BIT STRING  }

L'omission d'une grande partie du certificat permet de se contenter d'un analyseur ASN.1 plus réduit. Mais les clés brutes ne sont malheureusement pas réellement brutes, c'est quand même une structure décrite en ASN.1 et il faut donc un analyseur minimal.

La section 4 décrit les modifications à la négociation initiale de TLS (les autres parties de la communication ne sont pas modifiées par ce RFC). Le client ajoute une ou deux des extensions TLS (la plupart du temps, en TLS, le client ne s'authentifie pas, et on n'aura donc que l'extension server_certificate_type). La ou les extensions indiquent les types de certificat reconnus par le pair TLS (si l'extension est absente, seul X.509 est reconu). Ainsi, un serveur nouveau, intégrant les clés brutes, sait tout de suite si un client pourrait les traiter (si le client n'a pas mis server_certificate_type, ou bien si son server_certificate_type n'inclut pas les clés brutes, ce n'est pas la peine d'en envoyer). Si c'est le client qui est nouveau et le serveur ancien, la réponse du serveur ne contiendra pas les extensions et le client saura donc qu'il ne faut pas s'attendre aux clés brutes. Si le serveur reconnait l'extension, mais aucun des types de certificats indiqués, il termine la session. À noter que ces clés brutes marchent avec tous les algorithmes de cryptographie déjà reconnus.

La section 5 du RFC fournit des exemples de sessions TLS avec cette nouvelle possibilité. Par exemple, ce dialogue montre un client qui gère les clés brutes du serveur (et uniquement celles-ci, ce n'est donc pas un navigateur Web typique) et un serveur qui accepte d'envoyer sa clé ainsi. Le client n'est pas authentifié (il n'envoie pas sa clé ou son certificat et ne se sert donc pas de client_certificate_type) :


client_hello,
server_certificate_type=(RawPublicKey) // [1]
                       ->
                       <- server_hello,
                          server_certificate_type=(RawPublicKey), // [2]
                          certificate, // [3]
                          server_key_exchange,
                          server_hello_done

client_key_exchange,
change_cipher_spec,
finished               ->

                       <- change_cipher_spec,
                          finished

Application Data       <-------> Application Data

Ce cas d'un client ne comprenant que les clés brutes est un cas qui peut se produire pour l'Internet des objets, par exemple avec CoAP. Mais un client différent, par exemple un navigateur Web, va accepter d'autres types de certificats. Le dialogue pourrait ressembler à :


client_hello,
server_certificate_type=(X.509, RawPublicKey)
client_certificate_type=(RawPublicKey) // [1]
                         ->
                         <-  server_hello,
                             server_certificate_type=(X.509)//[2]
                             certificate, // [3]
                             client_certificate_type=(RawPublicKey)//[4]
                             certificate_request, // [5]
                             server_key_exchange,
                             server_hello_done
certificate, // [6]
client_key_exchange,
change_cipher_spec,
finished                  ->

                          <- change_cipher_spec,
                             finished

Application Data        <------->     Application Data

Ici, le serveur reconnait la nouvelle extension mais ne sait envoyer que des certificats X.509 (ce qui est donc fait par la suite). Le client, lui, s'authentifie en envoyant une clé brute (même si ce message est marqué certificate dans le dialogue).

Il existe apparemment déjà plusieurs mises en œuvre de TLS qui utilisent ces extensions.


Téléchargez le RFC 7250


L'article seul

RFC 7282: On Consensus and Humming in the IETF

Date de publication du RFC : Juin 2014
Auteur(s) du RFC : P. Resnick (Qualcomm)
Pour information
Première rédaction de cet article le 1 juillet 2014


L'IETF, organisme de normalisation des protocoles de l'Internet, fonctionne normalement par « consensus ». Il n'y a pas de vote formel, pas de majorité qui décide, on discute jusqu'à ce que presque tout le monde soit d'accord. À cause du « presque », on parle de « consensus approximatif » (rough consensus). Ça, c'est la théorie. En pratique, les évolutions récentes font que de plus en plus de décisions sont prises suite à des opérations qui ressemblent pas mal à des votes majoritaires, justement ce que les règles de l'IETF voulaient éviter. D'où ce RFC d'opinion, écrit pour défendre le principe du consensus approximatif, et pour appeler à se méfier de ces dérives.

Ce RFC a le statut « pour information » et représente le point de vue de son auteur : ce n'est pas un document officiel. En outre, il ne définit pas de nouvelles règles, il explique juste le fonctionnement idéal. Pour résumer ce RFC, revenons à la théorie. Le fonctionnement normal de l'IETF a été traduit par une percutante phrase de Dave Clark (dans « A Cloudy Crystal Ball - Visions of the Future »), que tous les participants à l'IETF connaissent par cœur : « We reject kings, presidents and voting. We believe in rough consensus and running code. ». Les décisions à l'IETF ne peuvent pas être prises par un seul individu (roi ou président), mais elles ne doivent pas non plus être prises par un vote, pour les raisons expliquées plus loin. Au contraire, elles doivent s'appuyer sur le consensus approximatif, lui-même nourri d'expérience concrète (le « running code », par opposition à des organisations comme l'ISO qui ont développé des usines à gaz impossibles à programmer). La pratique doit toujours l'emporter : le code qui marche a raison.

Notez que le consensus est approximatif : avoir un consensus tout court (tout le monde est d'accord ou, au moins, se tait) serait l'idéal mais ce n'est pas réaliste. Demander un consensus complet serait donner un droit de veto à n'importe quel individu négatif qui s'oppose à tout (l'IETF, comme toute organisation humaine, a quelques trolls de ce calibre...).

Comme il n'y a pas de vote, l'IETF a développé une autre pratique inhabituelle, le mmmmmmmm (humming). Lorsqu'on veut avoir une idée de l'opinion des gens, lors d'une réunion physique, on demande à chacun de se manifester par ce bruit produit en gardant la bouche fermée (ce qui assure un certain secret, contrairement aux mains levées). En faisant des mmmmmmmmm plus ou moins forts, on peut même avoir un vote non-binaire. Ce rituel est souvent pratiqué pendant les réunions IETF.

Mais tous les participants à l'IETF ne comprennent pas forcément ces subtilités, surtout lorsqu'ils viennent d'arriver. « Pourquoi un mmmmmm et pas un vote normal ? » ou « Il y avait une majorité de mmmmmm en faveur de la proposition B, pourquoi continue-t-on de discuter ? » Même quand le groupe de travail est arrivé à un résultat, bien des participants ne comprennent pas comment il a été obtenu et pensent que le processus a produit un mauvais résultat. Paradoxe : il n'y a pas de consensus à l'IETF sur l'importance du consensus. Ou plutôt, il y en a un en parole (la phrase de Clark est très souvent citée) mais pas dans la réalité du travail.

Donc, premier point expliqué par ce RFC, en section 2, l'important n'est pas que tout le monde soit d'accord, mais qu'il n'y ait pas trop de désaccord. Imaginons un groupe qui doit trancher entre deux formats pour un protocole (mettons JSON et XML, pour prendre un exemple réel...). Il y a des gens pour et des gens contre chacune des deux propositions. Cela veut-il dire qu'il n'y aura jamais de consensus ? Sauf que tout travail d'ingéniérie (et la création d'une norme technique en est un) nécessite des compromis. Si on demande un accord de tous, on ne l'aura jamais (surtout avec le goût des ingénieurs pour les discussions sur la couleur du garage à vélos). En revanche, si on demande qu'il n'y ait pas d'objections bloquantes, on peut arriver à un résultat. Les partisans de XML ne vont pas tout à coup préférer JSON. Par contre, ils peuvent considérer que JSON, sans être leur solution préférée, est acceptable, qu'il n'a pas de défaut qui mettrait vraiment en péril le nouveau protocole. Il ne faut donc pas demander aux participants « êtes-vous heureux avec ce choix ? » (il y aura forcément des râleurs) mais plutôt « ce choix a t-il un défaut rédhibitoire (et, si vous répondez oui, argumentez) ? » Bien sûr, il faut quand même qu'il y ait un certain nombre de gens qui soutiennent activement la proposition. Une solution technique qui ne rencontrerait aucune objection, mais pas non plus de soutien actif ne serait sans doute pas intéressante !

Autre point où il faut entrer dans les nuances, la différence entre compromis et compromission. Le mot « compromis » (en français, comme en anglais avec « compromise ») est utilisé dans deux sens différents. Le compromis est une bonne chose : en ingéniérie, on a rarement des solutions idéales. Il faut en général arbitrer et, par exemple, décider de sacrifier du temps CPU pour consommer moins de ressources réseau ou le contraire, ou bien mettre de la redondance pour assurer l'intégrité alors qu'elle va diminuer les performances, ou encore préférer un format texte qui facilite le déboguage à un format binaire plus efficace mais plus dur à traiter par les humains. Ce genre de compromis est un travail normal et essentiel de l'ingénieur, et l'IETF le fait.

En revanche, les compromissions sont néfastes : une compromission est quand on tient compte des opinions et pas des principes d'ingéniérie. Par exemple qu'on accepte telle option, qui semble inutile, voire dangereuse, parce qu'une partie du groupe de travail trépigne et y tient absolument. On ajoute alors l'option pour obtenir leur accord (ou leur non-désaccord), faisant ainsi une compromission (ou un « design by committee », où le résultat n'enthousiasme personne, à force d'avoir intégré plein de demandes contradictoires). Ce genre de compromission (qu'il faudrait plutôt appeler capitulation) n'a pas réglé les problèmes techniques. Pire, on peut voir ce que le RFC désigne sous le nom de « horse trading » (comportement de maquignon ?) « Je ne suis pas d'accord avec ta proposition sur l'algorithme de traitement de la congestion, tu n'es pas d'accord avec ma proposition sur le format des données, mettons-nous d'accord pour chacun soutenir la proposition de l'autre. » Cela permet d'arriver à un accord mais au prix de la qualité : les objections contre les deux propositions ont été ignorées, alors qu'elles étaient peut-être sérieuses techniquement et devaient être traitées comme telles. Ce comportement donne l'apparence du consensus alors qu'il est de la pure magouille.

Deuxième point important, en section 3, le fait qu'il faut traiter toutes les questions mais pas forcément les résoudre. Parfois, une objection technique va être résolue à la satisfaction de tous : celui qui l'a soulevée est convaincu que son objection était erronée, ou bien le groupe de travail a été convaincu qu'elle était sérieuse et a modifié le protocole en cours de discussion. Mais, parfois, le groupe de travail ne change pas de direction et celui qui a soulevé l'objection est frustré. Est-ce un échec du consensus ? Non, car il y a toujours « consensus approximatif ». Celui-ci est une solution de repli : l'idéal est le consensus complet mais, comme le dit le conte « Le meunier, son fils et l'âne », on ne peut pas toujours plaire à tout le monde et, de toute façon, il y a des gens qui ne sont jamais contents. La notion de consensus approximatif est faite pour ces cas-là : l'objection n'a pas été ignorée, elle a été sérieusement étudiée et le groupe de travail a délibérement décidé de continuer dans la voie originale. Compte-tenu de la forte personnalité de pas mal de participants à l'IETF, le consensus seulement approximatif, qui devrait être une exception à la règle du consensus, est souvent la norme.

Notez que ce consensus approximatif vu comme un repli lorsque le consensus idéal n'est pas atteint est une description nouvelle. Le RFC 1603 voyait plutôt le consensus approximatif comme exprimant le « point de vue dominant » du groupe de travail IETF. Cette vision quantitative (exprimée de manière encore plus directe dans son successeur, le RFC 2418) n'est pas partagée par ce nouveau RFC 7282, qui met l'accent sur le fait que les 0,1 % qui sont en désaccord ont peut-être raison (comme le disait un de mes enseignants à la fac, « la science n'est pas une démocratie »).

Et le mmmmmmm ? La section 4 revient sur son rôle. D'abord, pourquoi ne vote-t-on pas, à l'IETF (certainement la première question que posent tous les nouveaux venus) ? D'abord, l'IETF n'a pas de membres : il n'existe aucun processus formel d'adhésion. On s'inscrit à la liste de diffusion d'un groupe de travail et, hop, on est un participant à l'IETF. Impossible donc, de compter le corps électoral. Ensuite, le vote peut être une mauvaise chose : comme indiqué plus haut (même si l'expression n'est pas dans le RFC), la technique n'est pas démocratique. 51 % ou même 99 % d'un groupe de travail peut toujours voter X, c'est peut-être quand même Y qui est justifié techniquement. Le vote pourrait être un moyen de glisser sous le tapis les problèmes gênants, qui doivent au contraire être traités en pleine lumière. Une des raisons de l'utilisation du mmmmmmm est donc, précisément par le caractère folkorique de la procédure, de rappeler qu'on ne vote pas, à l'IETF (par comparaison avec, disons, un vote à main levée).

Le mmmmmmm sert donc à jauger la situation et à ordonner la discussion. Par exemple, s'il y a beaucoup plus de mmmmmmm objectant contre X que contre Y, partir des objections contre Y peut permettre d'optimiser le temps de discussion (sans pour autant considérer que Y a « gagné »).

Malgré cela, un effet pervers s'est développé, avec des gens considérant le mmmmmmm comme une sorte de vote anonyme. Ce n'est pas son but, le mmmmmmm devrait être utilisé au début (pour avoir une idée des points de vue en présence) pas à la fin pour clore le bec aux minoritaires. Le mmmmmmm n'est pas une méthode magique, et, s'il est mal utilisé, il a souvent les mêmes inconvénients que le vote classique (par exemple une extrême sensibilité à la rédaction des questions, ou même simplement à leur ordre).

Donc, le but, c'est le consensus ? Pas du tout. La section 5 insiste sur le fait que le consensus est un moyen et pas une fin. Le consensus approximatif est un moyen d'obtenir la meilleure décision technique possible, en s'assurant qu'aucune objection, aucun problème, n'a été négligé. Au contraire, l'expérience des SDO qui votent a largement montré que cela mène à des mauvaises décisions, notamment suite à des compromissions pour obtenir une majorité, et suite au mépris des remarques parfaitement valides des minoritaires.

Alors, évidemment, la méthode IETF prend du temps et est souvent exaspérante, même pour les plus patients des participants. Et il ne faut donc pas se faire d'illusions : les magouilles politiciennes existent aussi à l'IETF. Simplement, passer à un système plus rapide comme le vote permettrait d'avoir plus vite des normes de moins bonne qualité.

La section 6 revient sur la notion de majorité et sur l'approche quantitative, qu'appuyait le RFC 2418 (« Note that 51% of the working group does not qualify as "rough consensus" and 99% is better than rough. ») S'il y a 100 participants dans un groupe qui trouvent la proposition en cours excellente, et la soutiennent activement, et 5 participants qui sont en désaccord, prédisant, par exemple, « ce protocole va créer de la congestion et n'a aucun mécanisme de réaction en cas de congestion » et que personne ne répond de manière argumentée à ce point, alors, il n'y a pas de consensus, même pas de consensus approximatif. Une objection a été ignorée, pas traitée, et pas rejetée. Le faible nombre d'opposants n'y change rien, on ne peut pas parler d'un consensus, même pas approximatif. (Le RFC note que c'est pareil si la ou les personnes qui ont soulevé l'objection ne sont pas des participants réguliers, et qu'ils n'insistent pas après avoir fait leurs remarques ; les remarques en question doivent quand même être traitées.)

En sens inverse, et même si cela peut sembler anti-intuitif, on peut avoir 5 personnes qui soutiennent un projet et 100 qui sont contre, et qu'il y ait quand même un consensus en faveur du projet (section 7). Comment est-ce possible ? Le RFC décrit une telle situation (et des cas analogues se sont effectivement produits) : un groupe de travail a peu de participants actifs. Ils sont arrivés à un consensus malgré les objections de l'un d'entre eux, objection qui a été étudiée et rejetée. Le « dissident » fait appel à une centaine de personnes, parfois des participants à d'autres groupes de travail mais souvent des employés de la même société que lui, qui ne connaissent rien au sujet mais font ce qu'on leur dit. Ils envoient tous un message sur la liste de diffusion, disant qu'ils soutiennent l'objection. Une bonne façon de traiter ces soutiens vides (sans contenu nouveau) est de leur demander « Nous avons déjà discuté de ce point et abouti à un consensus. Avez-vous des éléments techniques nouveaux qui justifieraient de rouvrir la discussion ? » En général, les commerciaux et marketeux qui avaient été recrutés pour faire masse dans la discussion ne répondent pas... Ils croyaient que l'IETF fonctionnait sur un système de vote et sont surpris de découvrir qu'on leur demande d'argumenter. Bien sûr, ce cas est un peu extrême mais la même méthode peut servir dans des cas moins dramatiques. Le principe du consensus sert à traiter une des plus grosses failles des systèmes de vote : le bourrage des urnes par des ignorants.


Téléchargez le RFC 7282


L'article seul

Articles des différentes années : 2014  2013  2012  2011  2010  2009  2008  Précédentes années

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