Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Ève

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


RFC 7835: Locator/ID Separation Protocol (LISP) Threat Analysis

Date de publication du RFC : Avril 2016
Auteur(s) du RFC : D. Saucez (INRIA), L. Iannone (Telecom ParisTech), O. Bonaventure (Université catholique de Louvain)
Pour information
Réalisé dans le cadre du groupe de travail IETF lisp
Première rédaction de cet article le 30 avril 2016


Le protocole LISP (à ne pas confondre avec le langage de programmation) est un protocole à séparation de l'identificateur et du localisateur. Ces protocoles présentent des défis de sécurité particuliers et ce RFC est donc consacré à une étude détaillée des menaces et risques associés à LISP.

LISP est normalisé dans le RFC 6830. Il peut être utilisé dans un réseau interne ou bien sur l'Internet public et ce RFC étudie sa sécurité dans ce dernier cas, évidemment plus difficile.

Le RFC est en trois parties : la section 2 détaille le modèle de menaces (qui est l'attaquant), la section 3 expose les techniques que peut utiliser l'attaquant, la section 5 décrit les solutions possibles. Il s'agit bien de regarder les attaques génériques contre LISP, pas celles contre une mise en œuvre particulière de LISP, qui peut évidemment avoir ses bogues spécifiques.

Commençons donc par le modèle de menace (section 2). On suppose un attaquant situé quelque part sur l'Internet, éventuellement en plusieurs points de celui-ci. Il peut être sur le chemin des paquets (on path) ou pas (off path). Le premier est évidemment le plus dangereux. S'il est attaquant actif, il peut modifier des paquets et même faire des attaques de l'Homme du Milieu. Si l'attaquant est en dehors du chemin, cela va être plus difficile pour lui. Il ne peut pas modifier les paquets échangés, il peut par contre envoyer des paquets mais, pour que ceux-ci soient acceptés, il faudra qu'ils correspondent à ce qu'attendent les parties légitimes, ce qui fait que l'attaquant devra souvent deviner des informations et compter sur la chance.

Autre façon de classer les attaquants, il y a ceux qui sont internes à un site LISP et ceux qui sont externes. Les attaquants de l'intérieur sont plus dangereux car la machine qu'ils contrôlent (suite à une trahison ou un piratage) est a priori jugée de confiance.

Il faut aussi distinguer les attaquants permanents (live) des attaquants ponctuels (time-shifted). L'attaquant permanent est celui qui reste actif pendant toute la durée de l'attaque. Dès qu'il est neutralisé, l'attaque stoppe. L'attaquant ponctuel peut lancer une attaque, puis se retirer et les effets de l'attaque vont continuer. Ce genre d'attaques est évidemment bien plus dangereux.

L'attaquant peut viser le système de contrôle (control plane) de LISP ou bien son système de transmission des données (data plane). Un exemple typique est le mécanisme de correspondance (mapping) qui permet aux routeurs LISP de trouver le localisateur (le RLOC) en échange de l'identificateur (l'EID). Ce mécanisme de correspondance fait partie du système de contrôle. Si un attaquant veut détourner les paquets, il n'est pas obligé de trouver une faille dans le routage lui-même : s'il arrive à modifier les correspondances, il obtiendra le même résultat. (Comme le ferait une attaque DNS dans l'Internet classique.)

Plusieurs types d'attaques peuvent être faites contre un protocole réseau comme LISP. Il y a les attaques par rejeu, où un attaquant capture des paquets légitimes pour les rejouer intacts plus tard. Ces attaques marchent souvent, même quand les paquets sont protégés par de la cryptographie (l'attaquant n'a pas besoin de modifier les paquets, ni même de les comprendre). Il y a les manipulations de paquets (l'attaquant prend un paquet légitime et le réinjecte). Il y a la suppression complète des paquets.

Plus complexe et plus vicieux, il y a l'usurpation (spoofing). L'attaquant injecte des paquets prétendant venir d'une autre machine. C'est quelque chose qu'on voit souvent sur l'Internet. LISP est une technologie de tunnel et l'usurpation peut donc porter sur deux endroits : l'adresse externe, celle que voient les routeurs IP ordinaires (c'est le RLOC), et l'adresse interne, celle du paquet encapsulé (c'est l'EID). Identifier un usurpateur est bien plus compliqué lorsque des tunnels sont en jeu !

L'attaquant n'est pas forcément un usurpateur. Il peut très bien dire la vérité sur son adresse (rogue attack) par exemple parce qu'il se moque d'être identifié, ou bien parce qu'il est un zombie.

Autre type d'attaque, évidemment, les attaques par déni de service, où l'attaquant ne cherche pas à prendre le contrôle du système, mais à le paralyser (ou à le ralentir).

Parfois, la cible immédiate de l'attaque n'est pas la principale victime : dans les attaques par réflexion, un attaquant utilise un tiers pour renvoyer ses paquets vers la vraie victime. C'est surtout utilisé en combinaison avec l'amplification : lorsque la réponse est plus grosse que la question, un attaquant peut amplifier son trafic. En outre, pour LISP, le système de transmission est certainement bien plus rapide que celui de contrôle et il y a donc en théorie une possibilité d'attaquer le second avec le premier.

Et, bien sûr, il y a les attaques d'espionnage passif, où des grandes oreilles écoutent votre trafic pour vous surveiller, sans eux-même envoyer de paquets. Grâce à Edward Snowden, l'ampleur de ce type d'attaques par les États est désormais bien connu (RFC 7258).

Bon, assez de théorie, comment fait-on avec LISP quand on est un méchant et qu'on veut effectuer une des attaques en question ? Rentrons désormais dans les détails techniques (qui nécessitent de connaitre un peu LISP : relisez les RFC 6830, RFC 7215, RFC 6832, RFC 6833 et RFC 6834). D'abord, le « glanage » (gleaning), la collecte passive d'informations sur les correspondances identificateur->localisateur (RFC 6830, section 6). Il peut être utilisé pour empoisonner le cache des correspondances d'un routeur : le méchant envoie un paquet LISP fabriqué, les routeurs innocents l'observent et enregistrent la correspondance entre l'EID et le RLOC dans leur cache et paf, le méchant a pu détourner le trafic futur. C'est un exemple d'attaque ponctuelle : l'attaquant envoie juste un paquet puis plus rien, mais l'effet persiste pendant toute la durée de vie de l'information dans le cache (quelques secondes, si on suit fidèlement les conseils du RFC 6830, section 6.2).

Autre exemple d'empoisonnement d'un routeur LISP avec de fausses informations, le champ LSB (Locator Status Bits), qui indique l'état (joignable ou pas) des machines situées sur le site de départ du paquet (RFC 6830, section 6.3). En envoyant un paquet usurpé avec un faux LSB, on peut tromper des routeurs innocents. On peut par exemple mettre tous les bits à zéro (qui signifie que le préfixe est injoignable par ce routeur), menant à une attaque par déni de service ou bien les mettre tous à zéro sauf un, menant à une surcharge de cet unique routeur. C'est également une attaque ponctuelle : ses effets se feront sentir même si l'attaquant est neutralisé.

Un point important de LISP est le test de la « joignabilité » (reachability, RFC 6830, section 6.3). LISP vérifie que la machine qui prétend être joignable, et par tel routeur, l'est effectivement. Un des outils pour cela est un numnique envoyé à l'autre machine et qu'elle doit renvoyer. Un attaquant qui arriverait à tricher avec la joignabilité pourrait pousser un routeur LISP d'entrée du tunnel (un ITR) à changer de routeur de sortie (l'ETR) au profit d'un routeur qui ne marche pas. Cela réaliserait une attaque par déni de service. Même s'il n'arrive pas à faire changer de routeur, il pourrait perturber le routage en envoyant des tas de numniques différents. (Rassurez-vous, cette attaque est plus dure à réussir qu'il ne semble, voyez la section 5.)

LISP permet que plusieurs espaces d'adressage coexistent, différenciés par leur instance ID indiquée dans l'en-tête (RFC 6830, section 5.5). Elle n'est pas authentifiée et un attaquant peut donc envoyer des paquets à une autre instance ID que la sienne.

LISP permet de l'interconnexion avec des réseaux non-LISP (RFC 6832). Ces mécanismes ont des attaques très similaires à celles qu'on peut faire contre LISP lui-même. Par exemple, un paquet avec une adresse source usurpée peut être transmis sur l'Internet par la passerelle LISP.

Mais les pires attaques se situeront sans doute sur le système de correspondance (mapping). Le point central de tous les systèmes à séparation de l'identificateur et du localisateur est de découpler deux fonctions qui, dans IP, sont confondues. Cela suit un grand principe de l'informatique : « tout problème peut être résolu en ajoutant un degré d'indirection ». Sauf que cette séparation se paie en sécurité : il faut bien, à un moment, faire correspondre identificateur et localisateur et, là, cette nouvelle fonction, la correspondance, offre de nouvelles possibilités d'attaque.

Ainsi, les messages Map-Request (RFC 6830, section 6) peuvent être utilisés de plusieurs façons. Envoyés en masse, à un système de contrôle qui est bien moins rapide que le système de transmission, ils peuvent saturer les routeurs. Comme la réponse (Map-Reply) est plus grosse que la question, ils peuvent servir à des attaques par amplification.

Et si un attaquant réussit à fabriquer de fausses réponses (Map-Reply) et à les faire accepter ? Il devra pour cela mettre le numnique correct dans ses paquets. Comme il fait 64 bits, si on a suivi les bons principes du RFC 4086 pour le générer, un attaquant qui n'est pas sur le chemin (et doit donc deviner le numnique) n'a que peu de chances de réussir. Mais attention, le numnique n'est pas une signature : il indique juste que le paquet est bien une réponse à une question posée. Si l'attaquant peut modifier les paquets, il peut empoisonner le cache de correspondance du routeur. Variante de cette attaque, si le méchant est un routeur légitime (rogue attack), il peut répondre, mais avec de fausses réponses. Il ne peut pas annoncer des préfixes quelconques mais il peut annoncer des préfixes plus généraux que ceux qu'il gère réellement (par exemple annoncer qu'il gère 192.0.2.0/24 alors qu'il ne contrôle que 192.0.2.0/26), ce qui lui permet de détourner le trafic.

Enfin, il y a les Map-Register, ces messages envoyés par les routeurs aux serveurs de correspondance pour les informer des préfixes gérés (RFC 6833, section 4.2). Ils sont authentifiés donc, normalement, un attaquant quelconque ne peut pas les usurper. Mais il reste des attaques résiduelles comme l'annonce de préfixes plus généraux que les siens.

Au passage, un problème de sécurité qu'on oublie parfois est celui de la vie privée (section 4). Les correspondances LISP sont publiques (comme les tables BGP dans l'Internet classique) et il ne faut donc pas oublier qu'on ne peut pas participer à LISP discrètement.

Et pour finir, les solutions (section 5). Comment faire pour éviter ces menaces ? Le RFC donne des conseils très généraux (« déployer soigneusement », « appliquer les règles habituelles de sécurité ») mais aussi des indications plus précises et spécifiques à LISP. Clairement, le plan de contrôle est la partie la plus sensible. Il est donc important d'utiliser les techniques d'authentification décrites dans le RFC 6833 (voir notamment sa section 6). Des extensions de sécurisation de LISP sont en cours de développement.

D'autre part, les conseils de sécurité du RFC 6830 doivent être suivis. Par exemple, quand ce RFC écrit qu'il faut limiter le rythme des Map-Request, cela doit être appliqué.

L'information obtenue par examen des paquets (comme le glanage cité plus haut) n'est évidemment pas de confiance : à n'utiliser que pour le flot de données qui contenait cette information, ou alors à vérifier avant. (Voir aussi « How to mitigate the effect of scans on mapping systems ».)


Téléchargez le RFC 7835


L'article seul

RFC 7857: Network Address Translation (NAT) Behavioral Requirements Updates

Date de publication du RFC : Avril 2016
Auteur(s) du RFC : R. Penno (Cisco), S. Perreault (Jive Communications), M. Boucadair (Orange), S. Sivakumar (Cisco), K. Naito (NTT)
Réalisé dans le cadre du groupe de travail IETF tsvwg
Première rédaction de cet article le 29 avril 2016


Le mécanisme de traduction d'adresses IPv4 connu sous le nom de NAT (et qui devrait plutôt être appelé NAPT pour Network Address and Port Translation, car il ne se contente pas de traduire les adresses IP) cause beaucoup de problèmes, car il casse le modèle normal de fonctionnement d'IP, modèle de bout en bout. Pour limiter les problèmes dus à ce mécanisme, plusieurs RFC posent certaines exigences que doivent respecter (en théorie) les routeurs NAT. Ce nouveau document met à jour certaines de ses exigences. Il met donc légèrement à jour les règles des RFC 4787, RFC 5382 et RFC 5508.

Ce RFC de maintenance ne s'applique qu'au « NAT44 » traditionnel, où une adresse IP publique n'est partagée que par les membres d'un même foyer, ou bien les employés d'une même entreprise. Pour le CGN, les exigences sont dans le RFC 6888.

D'abord, le suivi de connexions TCP (section 2 du RFC). Notre RFC formalise rigoureusement la machine à états que doit suivre un routeur NAT (elle est proche de celle du RFC 6146). Le RFC 5382 spécifiait bien des délais pour certains états mais sans décrire précisement la machine complète. Par exemple, l'exigence 5 du RFC 5382 donne un délai pour le temps minimum d'attente avant de considérer une connexion partiellement ouverte, ou fermée, comme abandonnée, mais il n'était pas clair si ces deux cas devaient avoir le même délai. Notre RFC tranche donc : le cas des connexions partiellement ouvertes et celui des connexions fermées sont différents et les délais devraient pouvoir être configurés différemment.

Et les paquets TCP RST (ReSeT), on en fait quoi ? Notre RFC précise clairement (suivant ce que faisait déjà le RFC 6146) qu'un paquet RST doit être considéré come détruisant la connexion TCP et donc comme mettant fin à la correspondance dans le routeur NAT (après un délai, pour tenir compte du fait que le paquet RST a pu être reçu par le routeur NAT mais pas par la machine terminale). Attention, il faut d'abord vérifier que ce paquet RST correspond bien à une connexion existante, sinon, on permet des attaques par déni de service faciles (RFC 5961).

Les RFC 4787 et RFC 5382 précisaient qu'on pouvait utiliser le même couple {adresse IP externe, port externe} pour des connexions allant vers des machines extérieures différentes. Mais ils ne traitaient que du cas où il n'y avait qu'une machine interne qui allait vers ces machines extérieures. Désormais, on précise (section 3 de notre RFC) que cette utilisation est également valables si plusieurs machines internes se connectent. Sans cette règle, il faudrait beaucoup plus de ports externes disponibles, ce qui poserait un problème avec les environnements où le facteur de partage d'adresses est important (cf. RFC 6269).

Est-ce qu'un routeur NAT doit/peut utiliser les mêmes correspondances pour UDP et TCP (section 5 de notre RFC) ? Ce n'est pas précisé dans les RFC 4787 (exigence 1) et RFC 5382 (exigence 1). On fait une connexion TCP sortante, est-ce qu'un paquet UDP sortant va réutiliser la même correspondance ? La règle est désormais explicite : non, il ne faut pas, par défaut, les correspondances doivent être spécifiques à un protocole (donc, différentes pour UDP et TCP).

Autre piège du NAT, le fait qui peut parfois changer une distribution aléatoire des ports en une distribution prévisible. Cela pose un problème pour certaines méthodes de sécurité qui dépendent du caractères imprévisible (par exemple le RFC 5452). Notre RFC reprend (section 9) une recommandation de la section 4 du RFC 6056 : un routeur NAT ne doit pas choisir les ports de sortie de manière prévisible ou régulière.

Comme d'habitude, la fragmentation est une source d'ennuis (section 10). Notre RFC rappelle donc qu'i faut suivre les règles de la section 5.3.1 du RFC 6864, notamment sur le caractère unique et imprévisible de l'identificateur de fragment.

Le routeur NAT voit parfois passer des paquets en « épingle à cheveux » (hairpinning). Cela désigne les paquets (section 12) qui, venus du réseau interne, y retournent, après être passés par le routeur. Si le réseau interne est 172.17.42.0/24, et qu'un serveur HTTP est sur la machine 172.17.41.1 et accessible de l'extérieur en 192.0.2.71, alors, une machine interne qui tente d'aller en http://192.0.2.71/ va envoyer des paquets qui iront au routeur NAT, prendront un virage en épingle à cheveux et reviendront dans le réseau interne, vers 172.17.41.1. Historiquement, pas mal de routeurs NAT étaient incapables de gérer ce cas. La situation est désormais meilleure mais il reste des cas limites. Ainsi, l'exigence 7 du RFC 5508 devait rappeler que le virage en épingle à cheveux était possible même en ICMP. Notre RFC insiste sur le respect de cette règle.

Enfin, la sécurité (section 13). Notre RFC estime que ses exigences renforcées vont améliorer la sécurité. Par exemple, des règles strictes (sections 7 et 11) sur la suppression des correspondances dans la table du routeur NAT évitent qu'un attaquant puisse garder ouvertes des correspondances qui devraient être fermées (et la mémoire récupérée). Les règles sur les ports sources imprévisibles de la section 9 rendront (entre autres) plus difficile le suivi des machines situées derrière un routeur NAT.

Notez enfin qu'il y a des gens qui prétendent avoir un brevet sur certaines de ces recommandations...


Téléchargez le RFC 7857


L'article seul

Le mystère DNS Free rebondit

Première rédaction de cet article le 24 avril 2016


J'ai déjà parlé ici de la bogue DNS de Free affectant certains domaines signés avec DNSSEC. Six mois après, elle n'a pas été réparée et le problème se montre en fait encore plus complexe qu'à première vue.

Le problème a été signalé sur la liste FRnog et analysé par Rémy Duchet. En gros, certains usagers de Free n'arrivent pas à aller en http://test-yann.myshopify.com/. Si on regarde avec dig, on voit bien un problème DNS (ici, j'interroge explicitement un des résolveurs DNS officiels de Free, puisque j'ai un résolveur local) :


% dig @212.27.40.240 test-yann.myshopify.com 

; <<>> DiG 9.9.5-9+deb8u6-Debian <<>> @212.27.40.240 test-yann.myshopify.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 393
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;test-yann.myshopify.com. IN A

;; Query time: 27 msec
;; SERVER: 212.27.40.240#53(212.27.40.240)
;; WHEN: Sun Apr 24 17:00:03 CEST 2016
;; MSG SIZE  rcvd: 52

    

Ah, en effet, nous avons un problème (SERVFAIL = Server Failure). Avec d'autres résolveurs de Free, pas de problème. Et ça varie dans le temps (en d'autres termes, des fois, ça marche, des fois, ça marche pas). On peut également tester avec les sondes RIPE Atlas, dans l'AS de Free, 12322 :

% atlas-resolve -r 500 --as 12322 -t A test-yann.myshopify.com
[ERROR: SERVFAIL] : 61 occurrences 
[23.227.38.68 23.227.38.69 23.227.38.70 23.227.38.71] : 133 occurrences 
[TIMEOUT(S)] : 1 occurrences 
Test #3682131 done at 2016-04-24T14:57:13Z

Cela montre bien que certains résolveurs de Free ont le problème SERVFAIL mais pas tous.

Si on essaie avec l'option +cd (Checking Disabled, couper la validation DNSSEC), tout marche :


% dig +cd @212.27.40.240 test-yann.myshopify.com 

; <<>> DiG 9.9.5-9+deb8u6-Debian <<>> +cd @212.27.40.240 test-yann.myshopify.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18276
;; flags: qr rd ra cd; QUERY: 1, ANSWER: 22, AUTHORITY: 4, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;test-yann.myshopify.com. IN A

;; ANSWER SECTION:
test-yann.myshopify.com. 1800 IN CNAME shops.shopify.com.
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 12663 myshopify.com.
				K3oRwkmXUkzNYbFO1jNLGLrSkhaRRBnKeGvld9YdcZo+
				nUOpJzGUECTzrzOTBGpbGXdJB5M3nJ6pebAGpr46m5Rk
				HfG3+FOdgCRS+CG5lpu0+KC8w80718ywOFO8LROdIjwm
				h4swblM+0Aft4o1lj5ChnCBefWgn2Cs4qSqMT0g= )
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 39852 myshopify.com.
				lBsyv5zrQ4twd4LNzLrdQpHWyxL9SQGM2wfhVR/GfnWE
				TXNV3wyW5bIwRDySbmdRg9RLiz9h1sBFByIeITYEJeKv
				7daLBZSwoI7551mz0jJX5fMgEuW7FEFOP25pYb6p5o1r
				1VFvc47+X8qTLaV5j0Uz+PXJjFBvF7GB4i3gFb0= )
test-yann.myshopify.com. 1800 IN CNAME shops.shopify.com.
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 12663 myshopify.com.
				K3oRwkmXUkzNYbFO1jNLGLrSkhaRRBnKeGvld9YdcZo+
				nUOpJzGUECTzrzOTBGpbGXdJB5M3nJ6pebAGpr46m5Rk
				HfG3+FOdgCRS+CG5lpu0+KC8w80718ywOFO8LROdIjwm
				h4swblM+0Aft4o1lj5ChnCBefWgn2Cs4qSqMT0g= )
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 39852 myshopify.com.
				lBsyv5zrQ4twd4LNzLrdQpHWyxL9SQGM2wfhVR/GfnWE
				TXNV3wyW5bIwRDySbmdRg9RLiz9h1sBFByIeITYEJeKv
				7daLBZSwoI7551mz0jJX5fMgEuW7FEFOP25pYb6p5o1r
				1VFvc47+X8qTLaV5j0Uz+PXJjFBvF7GB4i3gFb0= )
test-yann.myshopify.com. 1800 IN CNAME shops.shopify.com.
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 12663 myshopify.com.
				K3oRwkmXUkzNYbFO1jNLGLrSkhaRRBnKeGvld9YdcZo+
				nUOpJzGUECTzrzOTBGpbGXdJB5M3nJ6pebAGpr46m5Rk
				HfG3+FOdgCRS+CG5lpu0+KC8w80718ywOFO8LROdIjwm
				h4swblM+0Aft4o1lj5ChnCBefWgn2Cs4qSqMT0g= )
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 39852 myshopify.com.
				lBsyv5zrQ4twd4LNzLrdQpHWyxL9SQGM2wfhVR/GfnWE
				TXNV3wyW5bIwRDySbmdRg9RLiz9h1sBFByIeITYEJeKv
				7daLBZSwoI7551mz0jJX5fMgEuW7FEFOP25pYb6p5o1r
				1VFvc47+X8qTLaV5j0Uz+PXJjFBvF7GB4i3gFb0= )
test-yann.myshopify.com. 1800 IN CNAME shops.shopify.com.
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 12663 myshopify.com.
				K3oRwkmXUkzNYbFO1jNLGLrSkhaRRBnKeGvld9YdcZo+
				nUOpJzGUECTzrzOTBGpbGXdJB5M3nJ6pebAGpr46m5Rk
				HfG3+FOdgCRS+CG5lpu0+KC8w80718ywOFO8LROdIjwm
				h4swblM+0Aft4o1lj5ChnCBefWgn2Cs4qSqMT0g= )
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 39852 myshopify.com.
				lBsyv5zrQ4twd4LNzLrdQpHWyxL9SQGM2wfhVR/GfnWE
				TXNV3wyW5bIwRDySbmdRg9RLiz9h1sBFByIeITYEJeKv
				7daLBZSwoI7551mz0jJX5fMgEuW7FEFOP25pYb6p5o1r
				1VFvc47+X8qTLaV5j0Uz+PXJjFBvF7GB4i3gFb0= )
test-yann.myshopify.com. 1800 IN CNAME shops.shopify.com.
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 12663 myshopify.com.
				K3oRwkmXUkzNYbFO1jNLGLrSkhaRRBnKeGvld9YdcZo+
				nUOpJzGUECTzrzOTBGpbGXdJB5M3nJ6pebAGpr46m5Rk
				HfG3+FOdgCRS+CG5lpu0+KC8w80718ywOFO8LROdIjwm
				h4swblM+0Aft4o1lj5ChnCBefWgn2Cs4qSqMT0g= )
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 39852 myshopify.com.
				lBsyv5zrQ4twd4LNzLrdQpHWyxL9SQGM2wfhVR/GfnWE
				TXNV3wyW5bIwRDySbmdRg9RLiz9h1sBFByIeITYEJeKv
				7daLBZSwoI7551mz0jJX5fMgEuW7FEFOP25pYb6p5o1r
				1VFvc47+X8qTLaV5j0Uz+PXJjFBvF7GB4i3gFb0= )
test-yann.myshopify.com. 1800 IN CNAME shops.shopify.com.
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 12663 myshopify.com.
				K3oRwkmXUkzNYbFO1jNLGLrSkhaRRBnKeGvld9YdcZo+
				nUOpJzGUECTzrzOTBGpbGXdJB5M3nJ6pebAGpr46m5Rk
				HfG3+FOdgCRS+CG5lpu0+KC8w80718ywOFO8LROdIjwm
				h4swblM+0Aft4o1lj5ChnCBefWgn2Cs4qSqMT0g= )
test-yann.myshopify.com. 1800 IN RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 39852 myshopify.com.
				lBsyv5zrQ4twd4LNzLrdQpHWyxL9SQGM2wfhVR/GfnWE
				TXNV3wyW5bIwRDySbmdRg9RLiz9h1sBFByIeITYEJeKv
				7daLBZSwoI7551mz0jJX5fMgEuW7FEFOP25pYb6p5o1r
				1VFvc47+X8qTLaV5j0Uz+PXJjFBvF7GB4i3gFb0= )
shops.shopify.com.	1800 IN	A 23.227.38.71
shops.shopify.com.	1800 IN	A 23.227.38.69
shops.shopify.com.	1800 IN	A 23.227.38.70
shops.shopify.com.	1800 IN	A 23.227.38.68

;; AUTHORITY SECTION:
shopify.com.		6458 IN	NS ns4.p19.dynect.net.
shopify.com.		6458 IN	NS ns3.p19.dynect.net.
shopify.com.		6458 IN	NS ns1.p19.dynect.net.
shopify.com.		6458 IN	NS ns2.p19.dynect.net.

;; Query time: 41 msec
;; SERVER: 212.27.40.240#53(212.27.40.240)
;; WHEN: Sun Apr 24 17:44:13 CEST 2016
;; MSG SIZE  rcvd: 2376

    

On a la réponse mais on voit bien qu'il y a un problème : ces enregistrements DNS CNAME (Canonical Name) répétés ne sont pas normaux. Il ne sont pas dans les serveurs faisant autorité pour le domaine :


%  dig @ns1.p19.dynect.net test-yann.myshopify.com  

; <<>> DiG 9.9.5-9+deb8u6-Debian <<>> @ns1.p19.dynect.net test-yann.myshopify.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7499
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 3, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;test-yann.myshopify.com. IN A

;; ANSWER SECTION:
test-yann.myshopify.com. 600 IN	CNAME shops.shopify.com.
test-yann.myshopify.com. 600 IN	RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 12663 myshopify.com.
				K3oRwkmXUkzNYbFO1jNLGLrSkhaRRBnKeGvld9YdcZo+
				nUOpJzGUECTzrzOTBGpbGXdJB5M3nJ6pebAGpr46m5Rk
				HfG3+FOdgCRS+CG5lpu0+KC8w80718ywOFO8LROdIjwm
				h4swblM+0Aft4o1lj5ChnCBefWgn2Cs4qSqMT0g= )
test-yann.myshopify.com. 600 IN	RRSIG CNAME 5 2 600 (
				20160519013040 20160419013040 39852 myshopify.com.
				lBsyv5zrQ4twd4LNzLrdQpHWyxL9SQGM2wfhVR/GfnWE
				TXNV3wyW5bIwRDySbmdRg9RLiz9h1sBFByIeITYEJeKv
				7daLBZSwoI7551mz0jJX5fMgEuW7FEFOP25pYb6p5o1r
				1VFvc47+X8qTLaV5j0Uz+PXJjFBvF7GB4i3gFb0= )

;; AUTHORITY SECTION:
test-winkel.myshopify.com. 60 IN NSEC test1012345.myshopify.com. CNAME RRSIG NSEC
test-winkel.myshopify.com. 60 IN RRSIG NSEC 5 3 60 (
				20160519013040 20160419013040 12663 myshopify.com.
				BHS+QE4Pdu0qFdTW2XHN4Z6mbiqu3eb89UhwO3f5/C7W
				vpQmlNEmuYNlUlAHgUWbXvCwmDr+9k0bVZuiZh0UuSCp
				pveXWvYzr6nJjCRy5CZoUJy6C60Dyt1LfW2kCNTXUdjX
				Vp8HwMlKN0np6jxe2o/ryU/BphzdYA1OEmqp9/0= )
test-winkel.myshopify.com. 60 IN RRSIG NSEC 5 3 60 (
				20160519013040 20160419013040 39852 myshopify.com.
				I8LqnvjOSLhivVteqv6gqeeytZ9YZU9heYIj/hNfYUz+
				vOQ2PQBCk7N0ujbI9vWpFkj+3YSNnsxBshxjVZfWQoLp
				Cpde8Ir+zP8WK95/04VQBpU1HjI7QBAARkPCD0YCnTBY
				U/hTVe6gcB43c9CFAUaJ5qynRmnTZFIsvtcGBz8= )

;; Query time: 22 msec
;; SERVER: 2001:500:90:1::19#53(2001:500:90:1::19)
;; WHEN: Sun Apr 24 17:45:54 CEST 2016
;; MSG SIZE  rcvd: 831

Donc, ce sont les résolveurs de Free qui les ont inventé. La bogue est clairement de la responsabilité de Free.

Dans mon article précédent, j'avais émis l'hypothèse que le problème était lié à l'utilisation de NSEC3 (RFC 5155) et des jokers. Ici, la zone est signée avec NSEC, pas NSEC3 (ce qui est curieux, d'ailleurs, puisque cela permet de connaitre la liste des clients de shopify.com), donc NSEC3 ne semble pas responsable. On retrouve par contre les jokers, dont l'interaction avec DNSSEC est régulièrement une source d'ennuis. D'une façon ou d'une autre, une combinaison de caractéristiques de la zone myshopify.com déclenche la bogue, multiplie les CNAME (la réponse du résolveur de Free a une taille supérieure à la MTU d'Ethernet...), l'enregistrement NSEC obligatoire est supprimé, et paf.

En résumé, je ne sais pas quel logiciel résolveur utilise Free (aucun des logiciels que je connais, comme Unbound ou BIND, ne montre ce comportement) mais il est bogué et devrait impérativement être réparé, si on veut que DNSSEC soit plus largement utilisé.

Et du côté de l'utilisateur ordinaire, que peut-on faire ? Le client final peut utiliser son propre résolveur (ce qui est une bonne idée, de toute façon), le webmestre qui veut publier des choses accessibles aux utilisateurs de Free ne peut pas grand'chose, à part peut-être configurer sa zone DNS de manière plus conservatrice (les jokers sont presque toujours une mauvaise idée).


L'article seul

RFC 7787: Distributed Node Consensus Protocol

Date de publication du RFC : Avril 2016
Auteur(s) du RFC : M. Stenberg, S. Barth (Independent)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF homenet
Première rédaction de cet article le 23 avril 2016


Ce nouveau protocole DNCP (Distributed Node Consensus Protocol) est prévu notamment pour les réseaux non organisés comportant beaucoup de petites machines, comme on peut en trouver en domotique (Internet des Trucs et tout ça). Il s'appuie sur l'algorithme Trickle (RFC 6206) pour distribuer dans le réseau des informations quelconques. Aucune machine ne joue un rôle particulier dans DNCP : pas de maître ou de racine. DNCP est un algorithme générique, et son utilisation effective nécessite la définition d'un profil, comme HNCP (Home Networking Control Protocol, RFC 7788) dans le cas du réseau « Homenet ».

L'information distribuée par DNCP est sous forme de TLV. Chaque tuple TLV fait 64 ko au maximum et, une fois que l'algorithme a convergé, tous les nœuds du réseau ont la même base de tuples : le réseau est synchronisé. L'état d'un nœud (l'ensemble des tuples qu'il connait) est représenté par un condensat des données (nommé network state hash). Au début, chaque nœud ne connait que ses propres tuples et son état reflète cela. Il diffuse cette information avec Trickle et chaque nœud apprend alors que la synchro n'est pas complète (puisqu'il existe des nœuds avec un condensat différent). Puis chaque nœud modifie l'état (le condensat) en intégrant les tuples des autres nœuds. À la fin, tous les nœuds ont le même état et trouvent donc le même condensat. Ils sont alors heureux et synchronisés. En situation stable, DNCP ne transmet que les condensats (pour que les nœuds puissent vérifier qu'il n'y a pas eu de changement), pas les données, ce qui le rend assez frugal.

DNCP est donc utile pour tous les cas complètement répartis, où des machines sans chef veulent se coordonner. Cela peut servir, par exemple, pour l'affectation automatique des préfixes IP dans un réseau non géré (RFC 7695). DNCP est à l'origine issu d'un projet moins général, le projet « Homenet » de l'IETF ; le groupe Homenet s'est aperçu que le protocole de synchronisation pouvait avoir son utilité en dehors de la domotique et a créé DNCP (et un protocole concret pour Homenet, HNCP).

Comme indiqué plus haut, DNCP est abstrait : plusieurs choix techniques essentiels sont laissés à de futurs profils (comme HNCP, décrit dans le RFC 7788). Parmi ces choix :

  • le mécanisme de transport (TCP, UDP, SCTP),
  • la sécurité (utiliser TLS ou pas),
  • le fait de ne fonctionner que sur un seul lien réseau, ou bien d'être capable de passer par des routeurs.

DNCP est très chouette mais le RFC précise qu'il ne peut pas servir à tout. Quelques exemples de cas où DNCP n'est pas adapté :

  • Si on a des données de grande taille (DNCP les limite à 64 ko),
  • Si on a beaucoup de voisins, et que la diffusion n'est pas possible,
  • Si les données changent tout le temps (DNCP est optimisé pour être économe dans l'état stable),
  • Si beaucoup de nœuds ont des contraintes de mémoire (DNCP nécessite de stocker toutes les données).

Si on a des données qui changent très souvent, il peut être préférable d'utiliser DNCP pour publier l'adresse d'un serveur où récupérer ces données.

DNCP est résumé en section 3 du RFC. Les nœuds sont découverts automatiquement (la méthode exacte dépend du profil) ou manuellement, et leur joignabilité est vérifiée. Les TLV peuvent être des demandes d'information ou bien les données elles-mêmes.

Chaque nœud commence par calculer le condensat des données (la fonction utilisée depend du profil) qu'il veut publier. Il l'annonce ensuite à ses voisins. Quand un voisin annonce un condensat différent, le nœud sait que le réseau n'est pas encore synchronisé. Il demande alors au voisin toutes ses données, les ajoute à sa base, met à jour son condensat et recommence.

Pour jouer son rôle, le nœud DNCP a besoin de quelques informations (section 5). Il a un identificateur unique (la façon doit il est choisi dépend du profil), ses données à publier, une ou plusieurs instances Trickle (le terme ne vient pas du RFC 6206 mais est défini ici : c'est un état Trickle autonome, avec les valeurs des paramètres Trickle), etc. Le nœud a également en mémoire un ensemble de voisins avec, pour chacun, son identificateur, son adresse IP, etc.

Les TLV et leur format sont décrits en section 7 : un type de deux octets, une longueur de deux octets et la valeur. Des TLV peuvent être inclus dans les TLV. Quelques exemples de types : REQ-NETWORK-STATE (type 1) sert à demander l'état du réseau (le condensat de tous les tuples), NETWORK-STATE (type 4) sert pour les réponses à ces requêtes et donne l'état du réseau, etc. L'ensemble des valeurs possibles figure dans un registre IANA.

La section 8 discute de la sécurité de DNCP. Le traitement de celle-ci dépend essentiellement du profil, qui peut décider, par exemple, d'utiliser TLS ou DTLS.

La section 9 discute en détail ce qu'on attend des profils, ce que doivent définir les protocoles concrets qui réalisent DNCP :

  • S'ils utilisent l'unicast ou le multicast.
  • Le protocole de transport, UDP ou TCP.
  • S'ils utilisent TLS pour la sécurité.
  • Quels TLV sont acceptés ou refusés.
  • Quelles valeurs ont les paramètres Trickle.
  • Quelle est la fonction de condensation.
  • Etc.

Des indications sur les choix à faire sont présentées dans l'annexe B. Par exemple, si on utilise UDP, il est recommandé que les données restent d'une taille inférieure à une MTU typique, notamment pour éviter la fragmentation. D'un autre côté, UDP donne davantage de contrôle sur l'envoi des données (mais pas forcément sur leur réception).

Un exemple de profil figure dans l'annexe A. On peut aussi regarder un vrai profil, dans le RFC 7788, qui normalise HNCP. Le profil d'exemple est pour un protocole fictif nommé SHSP (il a existé un projet de protocole nommé Simple Home Status Protocol mais qui semble abandonné, et sans lien avec ce SHSP fictif), un protocole pour l'automatisation de la maison. Ses choix sont :

  • Uniquement IPv6,
  • Unicast en TCP et multicast en UDP,
  • Zéro sécurité (ne doit être utilisé que sur des liens sûrs),
  • L'identificateur d'un nœud est un nombre de 32 bits choisi aléatoirement (en cas de collision, les deux machines en choisissent un autre),
  • Les paramètres quantitatifs de Trickle font que, dans l'état stable, au moins un paquet multicast est émis toutes les 25 secondes,
  • La fonction de condensation est une classique SHA-256,
  • Etc.

Téléchargez le RFC 7787


L'article seul

RFC 7788: Home Networking Control Protocol

Date de publication du RFC : Avril 2016
Auteur(s) du RFC : M. Stenberg, S. Barth (Independent), P. Pfister (Cisco)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF homenet
Première rédaction de cet article le 23 avril 2016


Ce nouveau protocole, HNCP (Home Networking Control Protocol), est conçu pour la domotique. Son rôle est de distribuer de l'information de configuration (les services comme celui d'impression, par exemple) à toutes les machines de la maison. C'est un profil (une instance particulière) du protocole DNCP du RFC 7787.

Cet HNCP est issu du projet Homenet, visant à créer à la maison des réseaux non gérés (M. Michu a autre chose à faire) et non limités à un seul lien (donc, avec des routeurs). L'architecture d'Homenet est décrite dans le RFC 7368. Dans ce cadre Homenet, HNCP doit permettre de :

  • découvrir les frontières du réseau (qu'est-ce qui est extérieur et qu'est-ce qui est intérieur), ce qui sera nécessaire pour la sécurité,
  • distribuer les préfixes IP (RFC 7695),
  • fournir l'accès à un service de résolution de noms.

HNCP n'est pas un protocole de routage (quoique on puisse considérer que le routage, c'est distribuer de l'information de configuration, mais HNCP n'est pas prévu pour cela), il faut l'utiliser avec un protocole de routage comme Babel (RFC 6126).

Comme HNCP est un profil de DNCP, il en hérite les caractéristiques (RFC 7787). Par exemple, il n'est pas adapté à de l'information très changeante. C'est pour cela que, pour la résolution de noms, HNCP distribue les adresses des serveurs de noms, mais n'assure pas cette fonction lui-même.

Le RFC sur DNCP décrit les points laissés libres dans DNCP et qu'un profil de DNCP doit spécifier (RFC 7787, section 9). La section 3 de notre RFC sur HNCP précise ces points :

  • HNCP est transporté sur UDP, port 8231, sur IPv6, bien sûr.
  • Le trafic est sécurisé par DTLS, port 8232.
  • HNCP ne marche que sur des réseaux capables de multicast, comme Ethernet (ce qui permet de découvrir les voisins facilement). Il est recommandé que l'identificateur d'interface soit dérivé de l'adresse IPv6.
  • L'identificateur de chaque nœud du réseau est choisi au hasard. (En cas - improbable - de collision, on recommence avec un nouvel identificateur.)
  • La fonction de condensation utilisée est MD5 (RFC 1321).
  • Les paramètres Trickle (RFC 6206) sont k=1, Imin=200ms, Imax=7.

Je n'ai pas d'expérience pratique avec HNCP donc je vais passer rapidement sur le reste du RFC. Parmi les points amusants à noter :

  • HNCP permet la configuration complète des adresses IP (section 6). Les nœuds situés aux frontières du réseau domestique apprennent du FAI les préfixes IP publics à utiliser (par exemple en DHCP, cf. RFC 3633), et les diffusent en HNCP. L'algorithme du RFC 7695 est utilisé pour répartir ces préfixes en sous-préfixes pour les différents liens (rappelez-vous que HNCP permet d'avoir un réseau domestique composé de plusieurs liens séparés par des routeurs.)
  • Les adresses IP des nœuds sont typiquement attribuées par le mécanisme du RFC 7217, qui dérive une adresse d'un certain nombre de paramètres du réseau local et de la machine qui se configure. (Ce qui permet d'avoir des adresses IP stables mais imprévisibles et qui ne permettent pas de traçabilité quand on change de réseau.) Les machines non-HNCP (section 7) sont, elles, configurées par les moyens classiques, comme SLAAC (RFC 4861).
  • La résolution de noms et la découverte des services sont évidemment des fonctions essentielles. L'utilisateur ne s'amuserait pas s'il devait manipuler des adresses IPv6. Chaque routeur HNCP fournit un nom pour les réseaux qu'il gère et le diffuse en HNCP. Il a aussi un nom pour lui-même. Le TLD utilisé est .home (qui, c'est à noter, ne figure pas dans le registre des noms de domaine spéciaux).

Comme tous les protocoles instanciés à partir de DNCP, HNCP transporte l'information sous forme de TLV (section 10). Ces TLV peuvent être emboîtés (un sous-TLV dans la partie Valeur d'un TLV). Parmi les TLV possibles (la liste complète est dans un registre IANA), on trouve :

  • « Version HNCP », type 32, qui permet d'indiquer aux voisins quelle version de HNCP on gère. Cela ne se fait pas sous forme d'un numéro de version mais sous forme d'un groupe de bits décrivant les capacités du nœud HNCP.
  • « Connexion extérieure », type 33, rassemble les informations envoyées par un FAI, et que le réseau local doit connaitre. « Préfixe délégué », type 34, sous-TLV du précédent, indique les préfixes IP.
  • « Nom de domaine », type 40, est le suffixe qui sera utilisé pour nommer les machines et services, alors que « Nom de machine », type 41, indique le nom d'une machine et son adresse IP.
  • Etc (il y en a beaucoup).

La section 11 du RFC résume les exigences qui pèsent sur les nœuds d'un réseau HNCP. En plus de gérer le protocole HNCP lui-même, ils doivent aussi, s'ils sont routeurs, respecter le RFC 7084, qui spécifie les règles (légèrement assouplies dans notre RFC sur HNCP) que doivent suivre les routeurs IPv6 situés chez l'utilisateur.

Enfin, un petit mot sur la sécurité (section 12) : HNCP est conçu pour être simple pour l'utilisateur et pour marcher tout seul. Sa sécurité sera donc forcément très faible : un réseau à la maison sans administrateur système professionnel offrira plein de trous à un éventuel attaquant (le RFC 7368 parle de la sécurité des réseaux domotiques). On ne peut pas avoir à la fois le beurre (l'auto-configuration) et l'argent du beurre (la sécurité).

Quelles sont les mises en œuvre de HNCP aujourd'hui ? Il y a, en logiciel libre, hnetd (qui est accessible également sous forme de paquetages pour OpenWrt, depuis la version Barrier Breaker - 14.07 - en juillet 2014). hnetd fait partie du projet HomeWrt. Complètement indépendant de hnetd, il existe également shncpd. On peut aussi citer pysyma, écrit en Python.

En non-libre, il y a du HNCP en cours de développement dans des environnements aussi différents que les routeurs Cisco ou D-Link, les systèmes Nest, le système Xfinity de Comcast... Il parait même que c'est prévu pour les Freebox mais je n'ai pas trouvé de détails à ce sujet. D'autre part, HNCP est apparemment cité dans la spécification (fermée et non publique) Thread.

Les protocoles Homenet sont en développement depuis longtemps et, si vous allez à des salons ou des conférences, vous avez certainement vu des démonstrations utilisant HNCP.

Et si vous aimez l'histoire des protocoles, notez que HNCP, à l'origine, était prévu pour assurer l'auto-configuration de routeurs dans un réseau OSPF (RFC 7503). Cette idée d'« auto-configuration OSPF » n'a pas rencontré un grand succès car tout le monde n'aime pas OSPF et aurait préféré découpler le routage (OSPF, Babel, etc) de la distribution d'informations. HNCP, protocole limité à cette distribution, était né. Il a ensuite été abstrait dans le protocole DNCP (RFC 7787), dont HNCP n'est qu'un profil.


Téléchargez le RFC 7788


L'article seul

RFC 7806: On Queuing, Marking, and Dropping

Date de publication du RFC : Avril 2016
Auteur(s) du RFC : F. Baker, R. Pan (Cisco)
Pour information
Réalisé dans le cadre du groupe de travail IETF aqm
Première rédaction de cet article le 21 avril 2016


Au cœur de l'Internet se trouvent les routeurs, et la façon dont ils traitent les paquets IP est cruciale pour le bon fonctionnement du réseau. C'est particulièrement important lorsque le trafic augmente et que les files d'attente dans les routeurs se remplissent. Que faire, alors ? Ce nouveau RFC discute des stratégies des routeurs confrontés à des files d'attente bien remplies.

Pas de « bonne » ou de « mauvaise » méthode dans ce RFC mais une discussion des possibilités. Il y a longtemps que le sujet est discuté, souvent avec des termes dangereux, car trop chargés, comme « juste » (qu'est-ce qui est juste ? Quels paquets jeter lorsque la file d'attente est pleine ? Les plus anciens ? Les plus gros ? Ceux venant de certains réseaux ?) Ainsi, le RFC 970 parle de gestion juste de la file d'attente... Le problème (à part la question de philosophie « qu'est-ce que la justice ? ») est que toute politique de gestion de la file d'attente peut entrainer des adaptations non souhaitées de la part des machines connectées au réseau (si on jette en priorité les paquets les plus gros, on encourage les machines à envoyer des paquets plus petits, comme dans le Silly Window Syndrome).

Sur la question de principe de l'équité ou de la justice, il faut toujours lire l'article de référence, « Flow Fairness: Dismantling a Religion ».

Historiquement, des modèles de trafic peu réalistes avaient été utilisés. Par exemple, certains modèles partent du débit moyen d'un flot tout au long de son existence et raisonnent là-dessus. Mais le trafic Internet est tout, sauf moyen. Il est plutôt en quanta, avec des sursauts brusques. Par exemple, une caméra va envoyer trente images par seconde et cela se traduira par beaucoup de données lorsqu'une nouvelle image est disponible, puis un silence d'un trentième de seconde avant l'image suivante.

Au passage, j'ai parlé de flot sans l'expliquer. Le terme est courant dans les discussions réseau mais souvent assez flou. Cela peut désigner une session du protocole de transport (identifiée par le fameux tuple à cinq éléments du RFC 2990), tous les paquets entre deux machines données, tous les paquets vers une machine donnée...

Autre question pratique dès qu'on discute d'équité entre les flots, comment est-ce qu'on la mesure, pour déterminer si elle est respectée ou pas ? Le RFC 7141, entre autres, se penche sur cette question.

Le gros du RFC est formé par la section 3, qui discute des outils habituels de gestion des paquets par les routeurs : mettre en file d'attente (queuing), marquer les paquets (marking) et les jeter (dropping). Tous ces outils sont indispensables (tant que la mémoire des routeurs sera finie, et donc les files d'attente n'auront pas une taille illimitée, il faudra parfois jeter des paquets) et sont interprétés par le reste du réseau comme des signaux indiquant l'approche ou le début de la congestion.

Ce RFC discute de divers concepts, il ne spécifie pas un algorithme. Pour un exemple d'algorithme récent de gestion des files d'attente des routeurs, voir FQ-Codel, même s'il ne suit pas forcément les avis du RFC.


Téléchargez le RFC 7806


L'article seul

RFC 7838: HTTP Alternative Services

Date de publication du RFC : Avril 2016
Auteur(s) du RFC : M. Nottingham (Akamai), P. McManus (Mozilla), J. Reschke (greenbytes)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 15 avril 2016


Une nouveauté dans le monde HTTP : ce RFC décrit un mécanisme permettant d'indiquer des endroits alternatifs où trouver le contenu demandé. Ce n'est pas une classique redirection : l'URI reste le même, on découple simplement l'identificateur (l'URI) et le localisateur (l'endroit où chercher et le protocole à utiliser).

Ce concept est assez nouveau dans le monde HTTP (section 1 du RFC). En effet, le RFC 7230 tend à mélanger l'identificateur d'une ressource (http://www.example.com/foo/bar) et les moyens d'y accéder (la même ressource peut aussi être en https://www.example.com/foo/bar voire sur un miroir à un URL très différent). Le but du mécanisme de ce nouveau RFC 7838 est de séparer plus clairement identificateur et localisateur.

Quelques exemples pratiques sont donnés dans cette section 1 :

  • Le serveur d'origine peut vouloir rediriger vers un serveur de secours si lui-même est sous forte charge, ou bien s'il sait qu'il existe un serveur plus proche de ce client particulier.
  • Le serveur d'origine peut rediriger le client vers une connexion sécurisée (avec HTTPS) ou vers un protocole plus moderne (HTTP 2, cf. RFC 7540).
  • Le serveur d'origine peut renvoyer certains clients à des serveurs spécialisés, par exemple pour le cas de clients ne gérant pas certaines options comme SNI (RFC 6066, section 3).

Actuellement, ce genre de choses est fait par des redirections HTTP explicites (codes 301, 302, 307 ou 308, voir le RFC 7231, section 6.4). L'URL vers lequel on est redirigé est montré par le navigateur (ce qui n'est clairement pas une bonne idée dans le cas de redirections temporaires comme 307). Autre façon de faire, un relais, qui se connecte au serveur effectif, sans le dire du tout au client HTTP. Les « services alternatifs » de ce RFC 7838 sont situés entre les deux façons : annoncés au client HTTP mais pas mémorisés et pas montrés à l'utilisateur (l'URI ne change pas). Le contexte de sécurité n'est pas modifié (si l'URI commençait par le plan https://, le service alternatif devra donc présenter un certificat valide), le champ Host: de la requête HTTP contiendra toujours le nom original (notez qu'il existe désormais un champ Alt-Used: pour indiquer le nom alternatif, cf. section 5). Normalement, l'utilisateur ne verra donc pas qu'on utilise un service alternatif (ceci dit, comme son logiciel client, lui, est au courant, des options de déboguage pourront permettre de l'afficher, contrairement à ce qui se passe avec un relais).

La solution est en deux parties : des concepts abstraits (section 2) puis des mécanismes concrets pour les différents protocoles, dans les sections 3 et 4. Commençons par le cadre abstrait. Un « service alternatif » existe lorsqu'un serveur d'origine (concept lui-même défini dans le RFC 6454, section 4) sait que ses ressources sont accessibles avec une combinaison {protocole, serveur, port} différente. Un tel service alternatif fait autorité (au sens du RFC 7230, section 9.1), ce n'est pas un simple miroir, ni un cache. Notez que la granularité est pour une origine complète (la combinaison {protocole, serveur, port}) : on ne peut pas faire un service alternatif qui couvrirait uniquement le chemin /foo de l'URI.

Un exemple de service alternatif ? Mettons que l'origine soit {http, www.example.com, 80} et que les mêmes ressources soient accessibles en HTTP 2 à {h2, new.example.com, 81}, alors on peut dire que ce {h2, new.example.com, 81} est un service alternatif de l'origine.

Notez que j'ai appelé « protocole » le premier membre du tuple. Ce n'est pas le plan (scheme) dans l'URI (plan que pas mal de gens appellent à tort protocole). En effet, cette valeur est plus spécifique que le plan, elle peut inclure des détails techniques supplémentaires (par exemple, lorsque le plan est http://, on peut y accéder avec les protocoles http ou h2, ce second désignant HTTP 2.) Le protocole, pour les services alternatifs, est un nom ALPN (RFC 7301 et le registre IANA ALPN).

Le tuple {protocole, serveur, port} peut être accompagné d'une durée de vie, indiquant combien de temps le client HTTP peut garder en mémoire l'information sur le service alternatif.

Au passage, un mot de sécurité : le client doit évidemment être conscient que, si la communication n'est pas sécurisée (par exemple par TLS), il peut être envoyé vers un faux service alternatif (section 9). Si le service d'origine n'a pas TLS mais que le service alternatif l'a, et présente un certificat au nom du service d'origine, c'est bon. Si aucun des deux n'a TLS, le risque d'avoir été trompé est bien plus élevé. Il n'est donc pas forcément prudent d'utiliser les services alternatifs sans TLS.

Notons enfin que les services alternatifs sont optionnels. Le client n'est pas obligé de les utiliser (d'autant plus que les clients HTTP actuels ne gèrent pas ces services et n'utiliseront donc pas les alternatives, tant qu'ils n'ont pas été mis à jour). Le serveur d'origine doit donc rester prêt à assurer les requêtes.

Maintenant, les mécanismes pour signaler ces services alternatifs. En HTTP 1, en utilise l'en-tête Alt-Svc: (section 3, et désormais dans le registre IANA). La réponse contient, par exemple :

Alt-Svc: h2="new.example.org:80"      
    

Cela indique que le client devrait plutôt aller voir avec le protocole h2 (HTTP 2), sur la machine new.example.org. Si on ne change que le protocole et le port, on pourrait avoir :

Alt-Svc: h2=":8000"
    

Il peut y avoir plusieurs valeurs, dans l'ordre des préférences décroissantes :

Alt-Svc: h2="alt.example.com:8000", h2=":443"
    

Par défaut, le service alternatif peut être mémorisé par le client pendant 24 h. Si on veut changer cette durée, on utilise le paramètre ma (Maximum Age, en secondes) :

Alt-Svc: h2=":443"; ma=3600
     

Si le serveur veut qu'on oublie tout ce qu'on a mémorisé sur les services alternatifs, et n'a pas envie d'attendre l'expiration des caches, il envoie la valeur spéciale clear :

Alt-Svc: clear
    

À part ce paramètre ma, et un autre paramètre persist (qui indique que, contrairement au comportement par défaut, le client HTTP ne devrait pas vider cette information de son cache si sa configuration réseau change), d'autres paramètres sont possibles dans le futur. Ils devront passer par le procédure « Examen par un expert » du RFC 5226 et seront mis dans le registre IANA.

Et si on utilise HTTP 2, où on est en binaire et plus en texte, et où les en-têtes sont représentés d'une façon complètement différente ? Dans ce cas, on se sert de trames du type ALTSVC (section 4). C'est une extension au protocole original. La trame ALTSVC a le type 0xa (10, dans le registre IANA) et contient un champ Longueur (de l'origine), l'origine, et le service alternatif sous une syntaxe identique à celle de la section précédente.

On l'a vu, le service alternatif fait normalement autorité et doit donc gérer les mêmes ressources. Que se passe-t-il si un serveur maladroit redirige vers une machine qui n'est pas configurée comme service alternatif ? Idéalement, cette machine ne faisant pas autorité doit répondre 421 (RFC 7540, section 9.1.2). Le client doit alors réessayer avec un autre service alternatif, s'il existe, ou bien avec le serveur d'origine. (Aujourd'hui, je crois qu'il est plus fréquent que la machine ne faisant pas autorité renvoie les ressources de sa configuration par défaut.)

Un petit mot d'histoire : ce système des services alternatifs via d'une solution spécifique à Chrome qui se nommait Alternate-Protocol:.


Téléchargez le RFC 7838


L'article seul

RFC 7842: Requirements for Improvements to the IETF Email List Archiving, Web-Based Browsing, and Search Tool

Date de publication du RFC : Avril 2016
Auteur(s) du RFC : R. Sparks (Oracle)
Pour information
Première rédaction de cet article le 13 avril 2016


Un des gros avantages de travailler à l'IETF est qu'il existe un ensemble d'outils de travail en groupe très perfectionnés, permettant de gérer un grand nombre de documents qui changent tout le temps. Dans la plupart des SDO, ou organisations similaires, on a juste des piles de documents Word (ou, dans les dernières années, Google Docs) inexploitables. Comme le gros du travail IETF est fait sur des listes de diffusion, un des outils les plus cruciaux est le moteur de recherche dans ces listes de diffusion. Mais aucun outil n'est parfait et ce nouveau RFC est une liste de points à améliorer dans l'outil actuel, liste fondée sur l'expérience récente.

Ce https://mailarchive.ietf.org/ permet notamment de retrouver un ou plusieurs messages, parmi les centaines de messages échangés sur les listes IETF chaque jour. Voici un exemple de recherche de « tcp encryption eliot » (Eliot = Eliot Lear, auteur entre autres des RFC 1918, RFC 4192, RFC 6557...). On y voit la liste des messages correspondants en haut à droite, à gauche les noms des listes (en général, des groupes de travail IETF) où les messages apparaissent, en bas à droite l'URL stable du message en cours (ce qui permet des références précises lorsqu'on affirme « Brian Trammel a dit que ») :

L'outil actuel avait eu comme cahier des charges le RFC 6778 et avait été déployé en janvier 2014. Notre nouveau RFC liste les améliorations souhaitées.

Par exemple, lorsqu'on choisit « Group by Thread », la liste résultante est plate, sans hiérarchie. Cela rend difficile de voir où un fil de discussion commence et où il finit :

Notre RFC demande que, dans les améliorations de l'outil, l'affichage montre la hiérarchie.

Autre problème, il n'y a pas actuellement de boutons de navigation (message suivant/précédent par date, ou suivant le fil de discussion), que cela soit dans la liste des messages affichés, ou bien lorsqu'on regarde un seul message. Le RFC demande que ces boutons soient ajoutés.

C'est en lisant ce RFC que j'ai appris qu'on pouvait changer la taille de la fenêtre où s'affiche la liste des messages. Je la croyais fixe, et c'était apparemment le cas de bien des utilisateurs. Le RFC demande donc qu'on rende cette possibilité d'agrandissement plus évidente (personnellement, je n'ai pas trouvé). En prime, la façon dont est faite la troncation aujourd'hui ne semble pas rationnelle. Ici, on voit bien que les sujets sont coupés (et la fin remplacée par des ellipses) sans vraiment de raison.

Le service actuel est difficile à utiliser sur un petit écran, comme celui des phono sapiens puisque les divers éléments de l'interface sont toujours présents. Pour une interface riche, il vaudrait mieux être responsive, ce que demande le RFC (par exemple, on pourrait supprimer le menu des filtres quand l'écran est trop petit).

Par contre, sur des grands écrans, l'interface actuelle est au contraire trop économe et sous-utilise l'écran.

Enfin, notre RFC regrette que l'interface actuelle ne marche guère sans JavaScript. La prochaine version doit évidemment être capable de fonctionner sans JavaScript, quitte à accepter quelques fonctions en moins (comme les filtres appliquées dynamiquement).

Reste à développer, maintenant... Je n'ai pas encore vu d'appel d'offres.


Téléchargez le RFC 7842


L'article seul

RFC 7828: The edns-tcp-keepalive EDNS0 Option

Date de publication du RFC : Avril 2016
Auteur(s) du RFC : P. Wouters (Red Hat), J. Abley (Dyn), S. Dickinson (Sinodun), R. Bellis (ISC)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 11 avril 2016
Dernière mise à jour le 12 avril 2016


Historiquement, le DNS utilisait surtout UDP et le transport sur TCP ne servait qu'à des cas particuliers, comme lorsqu'un client DNS ré-essayait avec TCP de récupérer des données trop grosses pour UDP. Depuis le RFC 7766, ce n'est clairement plus le cas : TCP est désormais aussi utilisable qu'UDP pour le DNS. Mais les mises en œuvre typique du DNS sur TCP ont des restrictions, par exemple des délais maximum d'attente avant de « raccrocher » qui sont trop courtes, de l'ordre de quelques secondes lorsqu'il n'y a plus de trafic sur la connexion. Ce nouveau RFC propose un mécanisme EDNS permettant au client DNS de signaler au serveur qu'il souhaite garder la connexion ouverte, si le serveur est d'accord, s'il vous plait.

Utiliser une connexion TCP par requête DNS serait en effet très inefficace, notamment question latence. Il faudrait effectuer la triple poignée de mains TCP à chaque requête. Le DNS sur TCP n'est envisageable que si les connexions restent ouvertes et que la même connexion peut servir pour plusieurs requêtes. Dans ce cas, lorsque la connexion a déjà été ouverte, la latence peut être aussi faible qu'avec UDP, voire meilleure (dans le cas de données de grande taille, par exemple avec DNSSEC, plus besoin d'essayer d'abord sur UDP). Reste une question pour le serveur DNS : faut-il laisser la connexion ouverte dès qu'il n'y a plus de requêtes à traiter ? Cette nouvelle option EDNS edns-tcp-keepalive permet au client DNS d'indiquer que, lui, il est prêt à réutiliser la connexion et qu'il souhaite qu'on la garde ouverte.

Le serveur souhaite économiser ses ressources (mémoire, notamment). Contrairement à UDP, TCP nécessite que le serveur garde un état. Imaginons que chaque connexion TCP prenne une entrée dans un grand tableau des connexions TCP en cours. Si c'était « open bar » pour les clients, qu'ils puissent ouvrir autant de connexions qu'ils veulent, et les garder éternellement ensuite, le tableau serait vite rempli, menant à un déni de service. (Et si vous pensez qu'à la place du tableau, une structure de données dynamique, grossissant automatiquement, résoudrait le problème, rappelez-vous que la mémoire du serveur est finie...) Le serveur choisit donc s'il garde les connexions ouvertes, et combien de temps.

Au passage, la section 1 du RFC rappelle aussi qu'UDP a des problèmes sérieux, notamment parce qu'il permet des attaques par réflexion et parce qu'il implique, pour les grosses données, de la fragmentation, qui est en général une source d'ennuis (voir par exemple «  Fragmentation Considered Poisonous de Herzberg et Shulman). D'où cet encouragement, depuis des années, à utiliser de plus en plus TCP.

La section 3 est la partie centrale du RFC, la spécification de la nouvelle option. Elle utilise EDNS (RFC 6891). Elle permet d'indiquer le délai d'attente avant la fermeture de la connexion, délai qui commence à courir lorsque la connexion devient inactive (« idle », cf. la section 3 du RFC 7766).

L'option se nomme edns-tcp-keepalive et a reçu le code 11 (stocké dans le registre des options EDNS). Elle prend un seul argument, encodé sur deux octets, TIMEOUT qui indique la durée du délai de garde, exprimée en unités de 100 millisecondes. Cette valeur n'apparait que dans les réponses du serveur, pas dans les requêtes du client. Si TIMEOUT vaut 600, cela signifie que les connexions restent ouvertes pendant une minute après la dernière requête reçue.

Ensuite, une fois cette option définie, comment on s'en sert ? Elle ne doit évidemment pas être utilisée sur UDP, où elle n'aurait aucune signification. Le client doit utiliser cette option pour signaler qu'il souhaiterait garder cette connexion ouverte (il ne doit pas indiquer de valeur pour TIMEOUT). Le serveur utilise cette option pour indiquer le temps pendant lequel il va garder la connexion (rappelez-vous que le client peut demander ce qu'il veut, c'est le serveur qui décide). Le client est censé « raccrocher » juste avant l'expiration de ce délai. Une valeur de 0 pour TIMEOUT signifie « raccroche tout de suite » (un serveur l'envoie, par exemple, lorsqu'il est soumis à un fort stress et n'a plus de ressources disponibles.)

Au passage, si le serveur n'a plus envie de supporter cette connexion, pourquoi ne raccroche-t-il pas lui-même, au lieu de demander au client de le faire ? C'est parce que le pair TCP qui ferme la connexion le premier passe ensuite en état TIME-WAIT (RFC 793, section 3.5) et que cela l'oblige à garder un état pendant deux fois la durée de vie maximale d'un segment TCP (soit quatre minutes en tout). Il vaut donc mieux demander au client de le faire. Par contre, après avoir envoyé le TIMEOUT=0, le serveur va avoir un délicat arbitrage à faire entre attendre que le client raccroche et fermer de lui-même la connexion (et donc garder une prise en état TIME-WAIT) quand le client n’obtempère pas suffisamment vite.

La section 5 du RFC recommande d'ailleurs aux serveurs de suivre l'activité de leurs clients et de « punir », d'une façon ou d'une autre, ceux qui violeraient ces règles, par exemple en continuant à envoyer des requêtes alors qu'on leur a demandé de raccrocher.

J'insiste mais cela vaut la peine d'être dit et répété : cette option edns-tcp-keepalive ne diminue pas la liberté des clients et des serveurs DNS. En cas de problème (tableau des connexions TCP en cours presque plein, par exemple), clients et serveurs sont libres de couper les connexions TCP plus tôt, ce qu'on appelle le « TCP session management ».

Comme d'habitude avec toute nouvelle option, on peut s'attendre à des problèmes avec les stupides middleboxes. Une requête contenant cette option peut ainsi être jetée alors qu'elle ne l'aurait pas été sans l'option. Une stratégie de repli comme celle de la section 6.2.2 du RFC 6891 peut donc être utile.

Merci à Kim-Minh Kaplan pour d'utiles précisions sur TCP.


Téléchargez le RFC 7828


L'article seul

RFC 7805: Moving Outdated TCP Extensions and TCP-related Documents to Historic and Informational Status

Date de publication du RFC : Avril 2016
Auteur(s) du RFC : A. Zimmermann (NetApp), W. Eddy (MTI Systems), L. Eggert (NetApp)
Pour information
Réalisé dans le cadre du groupe de travail IETF tcpm
Première rédaction de cet article le 11 avril 2016


Un peu de nettoyage dans l'imposante bibliothèque des RFC sur le protocole TCP. Bien des extensions à TCP n'ont jamais connu de déploiement réel dans la nature et d'autres, qui ont été déployées, ne sont plus utilisées depuis longtemps. Un grand nombre de RFC sont donc reclassifiés « intérêt historique seulement » ou bien « pour information ». Cela concerne surtout des vieux RFC, qui étaient bien oubliés de toute façon.

Le RFC 7414 (le guide des normes sur TCP) avait déjà fait cette classification mais sans mentionner le changement de statut dans la base de l'éditeur des RFC. C'est désormais chose faite. Il y a aussi des RFC très anciens qui n'avaient jamais été classés comme le RFC 675, la première norme TCP, à l'époque où les RFC n'avaient pas de statut clair. Remplacé par le RFC 793 (avec des modifications sérieuses du format des paquets), ce RFC 675 est désormais « intérêt historique seulement ». (À noter que le précédent grand nettoyage de TCP était dans le RFC 6247.)

Dans la liste, le RFC 1078 (TCPMUX) a suscité quelques controverses car il est apparemment mis en œuvre dans certains systèmes d'exploitation (la partie serveur, pas la partie client, qui semble absente). On ne peut donc pas le caractériser comme « non déployé ». Néanmoins, les failles, notamment de sécurité, de TCPMUX font que le RFC 1078 se retrouve lui aussi classé « intérêt historique seulement ». Notre RFC donne la liste des problèmes techniques qu'il pose.

Ce sort touche aussi des RFC récents comme le RFC 6013, proposition d'une ouverture de connexion TCP plus rapide et plus légère, publiée en 2011, implémentée, mais jamais déployée, et remplacée depuis par des RFC qui ont eu plus de succès comme le RFC 7413.

D'autres RFC sont seulement classés « pour information ». C'est le cas du RFC 700 car il ne décrivait pas un protocole mais était une analyse des premiers résultats du déploiement de TCP, ou du RFC 814, un important document de réflexion sur des concepts comme les adresses, les ports et les routes, toujours d'actualité, mais qui n'avait jamais reçu de classement auparavant. Parmi les autres RFC désormais classés, le RFC 889, qui était également un compte-rendu d'expérience sur la variation de certains paramètres numériques de TCP, comme le délai maximal d'attente. Faisons enfin une place à part pour le très utile RFC 1071 qui n'est pas abandonné, bien au contraire, juste classé comme « pour information ».


Téléchargez le RFC 7805


L'article seul

RFC 7808: Time Zone Data Distribution Service

Date de publication du RFC : Mars 2016
Auteur(s) du RFC : M. Douglass (Spherical Cow Group), C. Daboo (Apple)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF tzdist
Première rédaction de cet article le 1 avril 2016


Ah, les fuseaux horaires... Quelle source infinie de complications pour le programmeur, pour l'administrateur système de machines situées partout dans le monde, ou tout simplement pour le voyageur ou l'organisateur de télé-réunions internationales... Comme tout serait plus simple si, comme dans le roman d'Asimov « Les cavernes d'acier », les hommes vivaient en permanence sous terre, utilisant un temps uniforme. Mais, en attendant ce futur béni, il faut bien gérer les fuseaux horaires (time zones). Il existe une base de données officielle de ces fuseaux, il ne reste plus qu'à la distribuer efficacement, pour que toute machine connectée à l'Internet ait la même information. C'est le but du protocole normalisé dans ce RFC.

Que contient une base de données des fuseaux horaires (section 1 du RFC) ? Typiquement une liste de ces fuseaux avec, pour chacun d'entre eux, son décalage par rapport à UTC et ses règles concernant l'heure d'été. Les fuseaux ne suivent pas strictement le méridien, ils collent en général à des frontières inter ou intra-étatiques. Les fuseaux eux-même, et leurs décalages avec UTC, sont très stables. Mais les règles concernant l'heure d'été changent, elles, souvent. Il y a aussi le problème des secondes intercalaires, qui sont ajoutées (ou, en théorie, retirées) par l'IERS de manière imprévisible (car la rotation de la Terre n'est pas prévisible). La base actuelle représente un travail d'érudition formidable.

Au passage, un point de terminologie. Le RFC parle de « time zone » là où on dit en général en français « fuseau horaire ». Le terme français n'est pas idéal car il fait penser à un fuseau allant d'un pôle à l'autre (et englobant donc des pays différents, ayant des heures d'été différentes) alors qu'une time zone est plus restreinte. Il faut donc bien se rappeler qu'on parle ici de zones limitées à un pays, et ayant des règles (décalage avec UTC, heure d'été) uniformes. Il faut donc peut-être mieux dire « zone » ou « zone horaire » en français.

Cette information est essentielle pour les protocoles et les formats qui gèrent le temps, comme iCalendar (RFC 5545), ainsi que pour les systèmes d'exploitation. Il faut donc maintenir à jour la base de données et, jusqu'à ce RFC, il n'existait pas de mécanisme standard pour cela. Par exemple, pour un système d'exploitation comme Debian, la mise à jour se fait typiquement via le mainteneur d'un paquetage « base des fuseaux horaires » (chez Debian, le paquetage se nomme tzdata), qui introduit les changements dans son paquetage, paquetage qui est ensuite installé sur chaque machine lorsque l'administrateur système décide des mises à jour. Si le système est vieux est plus maintenu, ce sera à l'administratreur système local de « patcher » ses fichiers. Parfois, des paquetages viennent avec leur propre base des fuseaux horaires, distincte de celle du système, pour compliquer un peu les choses.

Au passage, signalons que ce RFC normalise le protocole mais qu'il faut également disposer d'une source de données de référence. Il en existe une à l'IANA, décrite dans le RFC 6557.

La section 2 décrit à grands traits l'architecture du système. Tout en amont, des contributeurs envoient des données sur les fuseaux horaires, elles sont agrégées par le fournisseur de référence (root provider, par exemple l'IANA citée plus haut). Ce fournisseur l'envoie ensuite aux clients (par exemple par le protocole de ce RFC). Le schéma du RFC est un peu plus compliqué, supposant que divers intermédiaires pourraient apparaitre. Notez que le protocole décrit ici est entre fournisseur et clients. En amont (entre contributeurs et fournisseur), il n'y a pas de mécanisme spécifié.

La section 3 de notre RFC définit la terminologie. Une zone (time zone) se caractérise donc par un ensemble de règles uniformes. La zone a un nom (utilisé par exemple dans la propriété TZID du RFC 5545) mais il n'existe pas de norme pour cela. Chaque fournisseur pourrait utiliser son schéma de nommage. Ainsi, la base IANA utilise un nom de région, une barre oblique et un nom de pays ou de ville comme Europe/Paris ou Indian/Cocos. En tout cas, il faut se méfier des abréviations ambigues, n'ayant souvent qu'une signification locale, comme PST (qui peut désigner trois zones différentes).

L'information sur les zones change (autrement, on n'aurait pas besoin d'un protocole pour télécharger les mises à jour) et il est donc important d'avoir un numéro de version permettant de savoir si on a bien la dernière version. Il peut être global à la base, ou bien spécifique à chaque zone.

Maintenant, le protocole lui-même (section 4 du RFC). Il repose, comme souvent de nos jours, sur le couple « HTTP (RFC 7230) et JSON (RFC 7159) », ce dernier servant à représenter les méta-données sur les zones. Les ressources auxquelles on accède en HTTP utilisent les gabarits d'URI du RFC 6570. Et les données elle-mêmes, les informations sur une zone horaire, en quoi sont-elles codées ? Le format par défaut est celui d'iCalendar (RFC 5545). Mais on peut aussi utiliser la représentation en XML du RFC 6321 ou celle en JSON du RFC 7265. La classique négociation de contenu de HTTP (RFC 7231, section 5.3.2) sert au client à choisir son format parmi ceux que le serveur veut bien fournir.

La base peut être assez grosse et les clients vouloir la toute dernière version. La charge sur le réseau des fournisseurs risquerait alors de devenir insupportable. C'est pourquoi il est possible de faire des requêtes conditionnelles, de façon à ne récupérer des données que si elles sont nouvelles. Là encore, c'est du classique HTTP avec les Etags (RFC 7232). Un client peut alors interroger le serveur du fournisseur une fois par jour (valeur recommandée par le RFC) sans risque de saturer le réseau.

Si le client reçoit uniquement le décalage avec UTC et les règles pour l'heure d'été, calculer une heure dans le futur peut être délicat car ces règles sont complexes. Le protocole prévoit donc la possibilité pour le client de demander au serveur de faire ces calculs et de lui envoyer les résultats.

Et si ça ne marche pas, que le serveur, pour une raison ou pour une autre, ne peut pas envoyer ce qui lui est demandé ? Il doit alors utiliser les erreurs structurées du RFC 7807 pour signaler ce qui ne va pas. (Les types d'erreurs possibles figurent dans un registre IANA.)

Comment est-ce qu'un client de ce protocole trouve le serveur ? Il doit de toute façon connaître le nom de domaine de son fournisseur, supposons ici example.net. Ensuite, il y a au moins deux méthodes :

  • Utiliser les enregistrements SRV (RFC 2782), en demandant _timezone._tcp.example.net. Il obtiendra ainsi le nom du serveur et le port à utiliser. Un enregistrement supplémentaire, sous le même nom que l'enregistrement SRV, de type TXT, indique un éventuel chemin à ajouter dans l'URI.
  • Ou bien utiliser les URI .well-known du RFC 5785, en faisant une requête HTTP pour la ressource timezone.

La section 5 du RFC décrit ensuite les actions que le serveur peut effectuer à la demande. Elles sont spécifiées en utilisant les gabarits du RFC 6570. Donc, par exemple, un gabarit {/service-prefix}/capabilities indique que le client doit ajouter /capabilities au préfixe de chemin découvert comme indiqué précédemment. Prenons un exemple complet : le client sait que son fournisseur de zones horaires est example.net. Il fait deux requêtes DNS et obtient deux enregistrements :

_timezone._tcp.example.net. SRV 0 1 8081 tz.example.com.
_timezone._tcp.example.net. TXT "path=/timezones"
    

S'il cherche à déterminer les capacités du serveur, il va alors faire une requête à l'URL http://tz.example.com:8081/timezones/capabilities.

C'est quoi, ces capabilities que je viens d'utiliser dans l'exemple ? C'est la première des actions possibles sur un serveur. Comme son nom l'indique, elle renverra un objet JSON contenant un membre actions qui sera la liste des actions possibles sur ce serveur (avec, pour chacune, le gabarit d'URI à utiliser). Une liste des actions standards possibles figure dans un registre IANA.

L'action list, elle, donne la liste des zones horaires connues du serveur. Et l'action get renvoie les données pour une zone précise. Son gabarit est {/service-prefix,data-prefix}/zones{/tzid}{?start,end} (tzid étant l'identificateur de la zone, par exemple Pacific/Noumea) et un exemple d'utilisation est :

[Requête]
      
GET /timezones/zones/America%2FNew_York HTTP/1.1
Host: tz.example.com:8081
Accept:text/calendar

[Réponse]

HTTP/1.1 200 OK
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: text/calendar; charset="utf-8"
Content-Length: xxxx
ETag: "123456789-000-111"

BEGIN:VCALENDAR
...
BEGIN:VTIMEZONE
TZID:America/New_York
...
END:VTIMEZONE
END:VCALENDAR      
    

Les codes d'erreur habituels de HTTP sont utilisés donc, par exemple, si on demande un tzid inconnu, on récupérera un beau 404, mais avec le corps de la réponse en JSON, suivant le RFC 7807 :

[Requête]
      
GET /timezones/zones/Atlantid%2FPlutopolis HTTP/1.1
Host: tz.example.com:8081
Accept:text/calendar

[Réponse]

HTTP/1.1 404 Not Found
Date: Wed, 4 Jun 2008 09:32:12 GMT
Content-Type: application/problem+json; charset="utf-8"
Content-Language: en
Content-Length: xxxx

{
     "type": "urn:ietf:params:tzdist:error:tzid-not-found",
     "title": "Time zone identifier was not found on this server",
     "status": 404
}

Il existe plusieurs autres actions, comme expand qui dit au serveur de faire les calculs d'heure d'été lui-même, ou find, qui permet de chercher une zone par une partie de son nom.

Notez bien qu'il n'y a pas d'URI fixe et pré-déterminé pour les actions : il faut utiliser les gabarits pour les générer.

Les détails des objets JSON qui peuvent être renvoyés en réponse à ces actions figurent en section 6 de notre RFC.

Et la sécurité ? Elle est cruciale car, si on peut changer la connaissance qu'une machine a de l'heure, plein d'attaques deviennent possibles (fausser les estampilles temporelles dans les journaux, activer ou désactiver un certificat, etc). Il faut donc prendre soin d'utiliser un fournisseur fiable, et de récupérer la base de manière sécurisée. (HTTPS, forcément, et avec vérification sérieuse de l'identité du serveur, en suivant le RFC 6125 ou bien le RFC 6698).

Je ne connais pas encore de serveur disponible qui serve la base de données suivant ce protocole (c'est pour cela que je ne montre pas d'exemple réel). Il existe des services qui distribue la base, mais avec un autre protocole, comme https://timezonedb.com/api.


Téléchargez le RFC 7808


L'article seul

RFC 7807: Problem Details for HTTP APIs

Date de publication du RFC : Mars 2016
Auteur(s) du RFC : M. Nottingham (Akamai), E. Wilde
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 1 avril 2016


Lorsqu'on fait une requête HTTP, on récupère un code à trois chiffres qui indique notamment si tout s'est bien passé (si le code commence par 2, c'est bon, s'il commence par 4 ou 5, c'est qu'il y a un problème). Ces codes ne sont pas toujours assez fins et bien des API de services REST (donc reposant sur HTTP) voudraient des précisions. Plutôt que de créer un nouveau code, ce RFC propose un mécanisme qui permet d'envoyer du JSON normalisé indiquant tous les détails sur le problème survenu.

Les codes de statut de HTTP sont définis dans la section 6 du RFC 7231. Parmi les plus célèbres, on note 200 (qui veut dire que tout s'est bien passé) ou 404 (qui indique que le serveur n'a pas trouvé la ressource demandée). Si le serveur veut fournir des détails, il envoie traditionnellement de l'HTML dans le corps de sa réponse. Le navigateur Web peut alors l'afficher. Mais si la requête n'était pas envoyée par un humain derrière son navigateur, si elle venait d'un client REST ? HTML ne convient alors pas et il faut passer à une information analysable par une machine. C'est ce que fait ce RFC, qui définit deux formats, un en JSON (RFC 7159) et un en XML. Le client REST va donc avoir un résumé simple (le code de statut) et tous les détails nécessaires s'il le souhaite. « Comprenant » l'erreur exacte, le client pourra même, dans certains cas, la corriger.

Le RFC utilise surtout des exemples avec l'erreur 403 Forbidden mais j'ai préféré me servir de 402 Payment required. Ce code n'a jamais été clairement documenté (il est marqué « réservé pour un usage futur » dans le RFC 7231, section 6.5.3) et c'est sans doute pour cela que notre RFC ne l'utilise pas, mais je le trouve plus rigolo. Voici par exemple une page Web payante :

% wget https://www.bortzmeyer.org/faut-vraiment-payer.html
--2016-03-26 15:54:00--  https://www.bortzmeyer.org/faut-vraiment-payer.html
Resolving www.bortzmeyer.org (www.bortzmeyer.org)... 2001:4b98:dc0:41:216:3eff:fece:1902, 2605:4500:2:245b::42, 204.62.14.153
Connecting to www.bortzmeyer.org (www.bortzmeyer.org)|2001:4b98:dc0:41:216:3eff:fece:1902|:443... connected.
HTTP request sent, awaiting response... 402 Payment Required
2016-03-26 15:54:00 ERROR 402: Payment Required.

On peut envisager plein de choses dans cette réponse analysable par une machine, comme un URI unique pour ce problème donné, qui pourrait être, par exemple, transmis au support pour faciliter la communication. Ou bien un URI d'un service REST de paiement permettant d'envoyer l'argent souhaité. Mais, naturellement, l'utilisation de cette réponse est facultative : parfois, le code de statut est suffisant et il n'y a rien à ajouter (c'est souvent le cas du 404), et parfois il vaut mieux utiliser un format spécifique à l'application utilisée (c'est d'ailleurs le cas pour toutes les API développées avant ce RFC). Ceci dit, pour les nouvelles applications, le mécanisme décrit dans ce RFC peut être très utile, pour doter toutes les applications d'un mécanisme commun de signalement des erreurs et problèmes.

Le modèle de données utilisé est celui de JSON et le type MIME est application/problem+json. Comme je l'ai dit plus haut, il y a aussi une version XML mais la référence est le JSON. La section 3 du RFC décrit ce modèle. Commençons par un exemple, une API qui demandait entre autres d'indiquer un âge et à qui on a envoyé un nombre négatif :

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Content-Language: en

{
   "type": "https://example.net/validation-error",
   "title": "Your request parameters didn't validate.",
   "invalid-params": [ {
                         "name": "age",
                         "reason": "must be a positive integer"
                       }
                     ]
}
 

Ce message d'erreur est de type https://example.net/validation-error, une explication en langue naturelle est donnée par le membre title, et la liste des paramètres invalides est donnée dans le membre (une extension à la norme) invalid-params.

Quels sont les membres possibles de l'objet JSON renvoyé ?

  • type est un URI (il est donc unique) servant d'identificateur au problème, par exemple lorsqu'on va écrire au support. Il est recommandé qu'il soit déréférençable (c'est-à-dire qu'on puisse le visiter avec un navigateur Web et obtenir des informations sur le problème en question). S'il est absent, sa valeur par défaut est le très vide about:blank (RFC 6694, section 3).
  • title est un titre conçu pour des humains, par exemple pour les messages d'erreurs présentés à l'utilisateur. Étant en langue naturelle, il est, contrairement au type, ambigu. Il peut être adapté à la langue de l'utilisateur, via l'habituelle négociation de contenu de HTTP. detail (absent dans l'exemple ci-dessus) le complète éventuellement. Le RFC précise que detail est fait pour l'utilisateur, pas pour le programmeur qui a conçu le service. Il devrait donc contenir des informations aidant l'utilisateur à corriger sa requête, pas des informations de déboguage (pas de pile d'appels Java, par exemple...) D'ailleurs, envoyer ces informations de déboguage poserait un problème de sécurité (cf. section 5).
  • instance (absent dans l'exemple) est un URI qui, contrairement à type, n'identifie pas la classe du problème mais une instance particulière. Si http://example.com/not-enough-credit indique la classe « pas assez d'argent », https://example.com/account/12345/msgs/abc va indiquer le problème d'argent d'un compte particulier.

Notez que type et instance peuvent être des URI relatifs.

Voici maintenant un exemple sur mon blog (c'est conçu comme une application REST donc les résultats sont toujours en JSON) :


% curl -v http://www.bortzmeyer.org/apps/payme     
...
< HTTP/1.1 402 Payment required
...
< Content-Length: 253
< Content-Type: application/problem+json
< 
{
  "type": "http://errors.bortzmeyer.org/nopay", 
  "detail": "Bitcoin address 1HtNJ6ZFUc9yu9u2qAwB4tGdGwPQasQGax, Ethereum address 0xbe1f2ac71a9703275a4d3ea01a340f378c931740, Flattr https://flattr.com/profile/bortzmeyer", 
  "title": "You must pay"
}

% curl -v http://www.bortzmeyer.org/apps/payme\?pay=30
...
< HTTP/1.1 200 OK
< Content-Length: 36
< Content-Type: application/json
< 
{
  "title": "OK, 30 credits paid"
}

    

Le code Python WSGI correspondant est :

 def payme(start_response, environ):
    form = cgi.parse_qs(environ['QUERY_STRING'])
    response_headers = []
    amount = 0
    if form.has_key("pay"):
        try:
            amount = int(form["pay"][0])
        except ValueError: # Bad syntax
            amount = 0
    if amount > 0:
        status = '200 OK'
        response_headers.append(('Content-type', 'application/json'))
        output = json.dumps({"title": "OK, %i credits paid" % amount}, indent=2)
    else:
        status = '402 Payment required'
        response_headers.append(('Content-type', 'application/problem+json'))
        output = json.dumps({"type": "http://errors.bortzmeyer.org/nopay",
                             "title": "You must pay",
                             "detail": "Bitcoin address 1HtNJ6ZFUc9yu9u2qAwB4tGdGwPQasQGax, Ethereum address 0xbe1f2ac71a9703275a4d3ea01a340f378c931740, Flattr https://flattr.com/profile/bortzmeyer"},
                             indent=2)
    response_headers.append(('Content-Length', str(len(output))))
    start_response(status, response_headers)
    return [output]     
    

Si vous n'avez pas ce que vous voulez dans les membres prévus, vous pouvez étendre l'objet JSON. Les clients doivent donc ignorer les membres inconnus. C'est le cas du invalid-params dans l'exemple, qui n'est pas un membre standard.

Bien, maintenant, vous êtes programmeur dans une start-up, vous créez un nouveau service qui a une API, un nom de domaine en .io, un business plan pipeau et vous vous demandez si vous devez utiliser ce RFC et comment. La section 4 du RFC fournit quelques conseils. D'abord, je le répète, ce n'est pas un outil de déboguage pour vous, ne serait-ce que pour des raisons de sécurité (cf. section 5). C'est un outil pour aider vos utilisateurs. Ensuite, si le problème est un problème classique et standard, il est inutile de ce servir de ce RFC. Si l'utilisateur demande une ressource qui n'existe pas, le traditionnel et générique 404 (RFC 7231, section 6.5.4) convient parfaitement et je ne vois pas de raison d'ajouter des détails analysables par une machine (dans une page HTML d'erreur, c'est différent, on peut fournir des conseils aux visiteurs, mais rappelez-vous que ce RFC est pour les API, quand le client est un programme).

D'autre part, une application peut avoir de très bonnes raisons d'utiliser un format à elle pour décrire en détail les problèmes. (Sans compter les applications existantes qui ne vont évidemment pas modifier la définition de leur API juste pour coller à ce RFC.)

En revanche, une application nouvelle, qui n'a pas de format d'erreur établi, a tout intérêt à utiliser le cadre de ce RFC plutôt que de réinventer la roue. Dans ce cas, vous allez devoir définir :

  • L'URI qui servira de type (le seul type prédéfini est about:blank),
  • Le code de statut HTTP qui l'accompagne,
  • Les éventuelles extensions (comme le membre invalid-params plus haut).

Ces extensions peuvent utiliser les liens de HTTP (RFC 5988).

Le format principal décrit par ce RFC utilise JSON. Mais, comme il y a des goûts différents, il y a aussi une variante XML, décrite dans l'annexe A. Elle est spécifiée en Relax NG. Le modèle de données étant le même, cela donne à peu près :

   start = problem

   problem =
     element problem {
       (  element  type            { xsd:anyURI }?
        & element  title           { xsd:string }?
        & element  detail          { xsd:string }?
        & element  status          { xsd:positiveInteger }?
        & element  instance        { xsd:anyURI }? ),
       anyNsElement
     }
     

Et le résultat serait :


HTTP/1.1 400 Bad Request
Content-Type: application/problem+xml
Content-Language: en

<?xml version="1.0" encoding="UTF-8"?>
<problem xmlns="urn:ietf:rfc:XXXX">
   <type>https://example.net/validation-error</type>
   <title>Your request parameters didn't validate.</title>
   <invalid-params><param><name>age</name><reason>must be a positive integer</reason></param></invalid-params>
</problem>

     

On a presque fini, quelques petits mots sur la sécurité en section 5 : attention à ne pas laisser fuiter de l'information qui pourrait aider un attaquant à préparer son attaque. Il faut notamment se méfier des détails de mise en œuvre (du genre afficher la requête SQL qui a échoué...)

Les deux nouveaux types MIME, application/problem+json et application/problem+xml figurent désormais dans le registre IANA.

Les développeurs d'API n'ont pas attendu ce RFC pour renvoyer des messages d'erreurs structurés, utilisant d'autres schémas (voici, par exemple, les erreurs possibles de l'API Github). Un concurrent sérieux à ce RFC est, par exemple, http://jsonapi.org/ qui a son propre mécanisme de signalement d'erreur.


Téléchargez le RFC 7807


L'article seul

RFC 7801: GOST R 34.12-2015: Block Cipher "Kuznyechik"

Date de publication du RFC : Mars 2016
Auteur(s) du RFC : V. Dolmatov (Research Computer Center MSU)
Pour information
Première rédaction de cet article le 25 mars 2016


Encore un algorithme de cryptographie venu de l'Est, avec cette spécification sous forme de RFC d'un algorithme GOST, connu sous le petit nom de « Kuznyechik » (dans la transcription anglo-saxonne), et, plus formellement, sous celui de GOST R 34.12-2015. C'est un algorithme de chiffrement symétrique par blocs.

Comme d'autres algorithmes de cryptographie normalisés par GOST, Kuznyechik a été développé en partie par le secteur public (Service des communications spéciales et d'information du Service fédéral de protection de la Fédération de Russie) et par le secteur privé (InfoTeCS). Le décret n° 749 du 19 juin 2015, pris par l'Agence fédérale pour la régulation technique et la métrologie en a fait un algorithme russe officiel. C'est donc apparemment un concurrent d'AES.

Les sections 3 et 4 du RFC décrivent l'algorithme en détail. Pour les connaisseurs en cryptographie seulement, d'autant plus que le format actuel (c'est en cours de révision) des RFC n'est pas idéal pour décrire des mathématiques, il vaut mieux utiliser la version en anglais de la norme russe. Des vecteurs de test figurent en section 5.

Merci à André Sintzoff, qui sait utiliser Canard, canard, va.


Téléchargez le RFC 7801


L'article seul

RFC 7742: WebRTC Video Processing and Codec Requirements

Date de publication du RFC : Mars 2016
Auteur(s) du RFC : A.B. Roach (Mozilla)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF rtcweb
Première rédaction de cet article le 24 mars 2016


Ce court RFC expose les exigences portant sur les codecs vidéo de WebRTC. Le point principal était le choix du codec obligatoire. Après une très longue et très amère discussion à l'IETF, le choix a été plus politicien que technique : le codec H.264 est obligatoire au même titre que VP8.

Le système WebRTC (voir le RFC 7478) permet de faire de la vidéoconférence, notamment entre deux navigateurs Web. Il permet aux parties qui communiquent de choisir le codec. Un point important des protocoles IETF est que, lorsqu'il existe un tel choix, au moins un des choix doit être mis en œuvre dans toutes les implémentations. Le but est d'assurer l'interopérabilité, en vérifiant que les programmes ont tous au moins un codec en commun.

Mais quel codec choisir ? Il y avait deux concurrents, un codec de l'UIT, H.264 (normalisé ici), qui avait la faveur des compagnies de télécommunication traditionnelles, et un codec issu de l'Internet, VP8 (RFC 6386), qui était le plus apprécié par les entreprises Internet et par le monde du logiciel libre. Le débat a été très long et très houleux à l'IETF. Le problème n'était pas uniquement sentimental, il portait également sur les innombrables brevets qui verrouillent H.264 (son concurrent VP8 a aussi des brevets, mais avec une licence très libérale pour la plupart). Quant aux critiques sur VP8, elles portaient surtout sur sa relative jeunesse (en fait, certaines entreprises avaient investi pas mal dans H.264 et ne voulaient pas recommencer).

Le débat a donc duré, générant plus de mille messages sur la liste de diffusion du groupe de travail, et occupant une bonne partie des réunions physiques de l'IETF (réunions IETF 81, 82, 85, 86, et 88...), affectant notablement la patience des participants. Des tas de solutions ont été proposées pour sortir de l'affrontement, y compris un inhabituel vote (dans ce message, MTI = Mandatory To Implement). Le sujet est revenu sur le tapis et revenu encore. La solution, adoptée à la réunion IETF 91 à Honolulu, a finalement été de prendre les deux.

Notez bien que ce RFC n'est pas la description complète de WebRTC, loin de là. C'est juste le cahier des charges minimum des codecs (le cahier des charges du transport des données sera dans un autre RFC).

D'abord, les considérations générales, indépendantes du codec (section 3). Par exemple, l'espace de couleur à utiliser est sRGB. Cette section conseille également sur l'utilisation de la caméra de capture : si elle le permet, il est recommandé d'utiliser ses capacités propres, comme la mise au point automatique, ou le réglage automatique de la luminosité. Le RFC rappelle également que la résolution de la source peut changer en cours de route (parce que la source est une fenêtre de l'écran qui change soudain de taille, ou bien parce que la source est la caméra d'un mobile qui change soudain d'orientation, cf. section 4) et que WebRTC doit donc s'y adapter.

La section 5 est le morceau sensible du RFC : la description du, ou plutôt des, codecs obligatoires. Le principe est que toute mise en œuvre de WebRTC doit accepter VP8 et H.264. Une note du RFC précise que, si le problème des brevets s'arrange pour un des codecs et pas pour l'autre, cette décision pourra être reconsidérée et un des codecs devenir optionnel (évidemment, tout le monde pense à H.264, beaucoup plus lardé de brevets avec licences hostiles). À noter que des codecs comme Theora ont été discutés, et sont possibles dans WebRTC, mais sans qu'il ait vraiment été envisagé de les rendre obligatoires.

La section 6 précise ensuite quelques exigences techniques pour chacun des codecs. Notamment, plusieurs options de H.264 ont une valeur fixe en WebRTC, pour simplifier la tâche des programmeurs. (H.264 a bien trop de variables possibles.)


Téléchargez le RFC 7742


L'article seul

RFC 7763: The text/markdown Media Type

Date de publication du RFC : Mars 2016
Auteur(s) du RFC : S. Leonard (Penango)
Pour information
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 23 mars 2016


Le format Markdown est un excellent format léger de marquage de textes, permettant d'enrichir un texte, sans pour autant avoir à apprendre un gros langage de marquage comme LaTeX. Markdown est très bien adapté aux courts rapports, aux articles simples, aux fichiers README, à la génération de pages Web... Markdown a de nombreuses variantes et le but de ce nouveau RFC n'est pas de normaliser Markdown (une tâche probablement impossible) mais juste d'enregistrer un type MIME pour ce format, text/markdown. Donc, on décrit, on ne normalise pas.

Markdown est un langage de marquage du texte, comme ReST (utilisé dans le livre CNP3) ou comme les langages des Wiki. On peut donc éditer du Markdown avec n'importe quel éditeur (mes lecteurs ne seront pas surpris d'apprendre que j'utilise Emacs). Le source Markdown est donc du texte lisible tel quel, sans logiciel spécifique, mais un logiciel peut le traiter pour produire une forme plus agréable et plus efficace, par exemple du HTML pour publication sur le Web.

Voici un exemple de document Markdown n'utilisant, sauf erreur, que les constructions communes à toutes les variantes : test1.md. On peut le traiter, par exemple avec discount, une mise en œuvre simple de Markdown, en C, qui ne peut produire que du HTML :


% markdown  test1.md 
<h1>Début</h1>

<p>Un test très simple
en Markdown.</p>
...
<h2>Suite</h2>
...

Mais on peut aussi utiliser le bien plus riche Pandoc, qui a de nombreuses extensions à Markdown, et peut produire beaucoup de formats de sortie, ici du PDF, via LaTeX :

% pandoc --to latex --latex-engine=xelatex -o test1.pdf test1.md
% evince test1.pdf
...

Un point important de Markdown est qu'il n'y a jamais d'erreur de syntaxe (section 1 du RFC) : tout fichier texte est un fichier Markdown légal (même s'il ne donne pas toujours le résultat attendu par son auteur). C'est donc un monde très différent de LaTeX ou de XML (au passage, ce blog est écrit en XML). Markdown se veut un langage léger, imposant peu de contraintes et peu d'apprentissage. Avec Markdown, on a toujours un résultat, même suboptimal. Ce n'est pas considéré comme un problème. Si on veut du « sérieux », il faut utiliser LaTeX ou DocBook.

La « spécification » de Markdown est très grossière, et laisse le doute sur beaucoup de points mais le choix de l'IETF a été de ne pas essayer d'en écrire une correcte : Markdown est fait ainsi.

Un autre point important est le grand nombre de variantes, souvent incompatibles. L'auteur original, John Gruber, a toujours refusé de permettre des évolutions du langage et chacun a donc ajouté les siennes. Il y a eu plusieurs tentatives de normalisation (la principale étant CommonMark), mais sans résultat clair. Au début du processus long et compliqué qui a abouti à ce RFC, il était question de faire une nouvelle tentative, sous la houlette de l'IETF cette fois. Mais le projet RFC s'est assez vite rabattu sur une ambition plus modeste : enregistrer le type MIME et documenter (partiellement) l'état de Markdown. Un autre RFC, le RFC 7764, décrit les usages de Markdown. La discussion a été d'autant plus chaude que la « communauté » Markdown est largement extérieure à l'IETF.

On pourrait dire que Markdown n'est pas un langage, mais une famille de langages, ayant des éléments communs. Un bon exemple de la variété de Markdown est, par exemple, le cas des métadonnées. Pandoc permet d'écrire en début de fichier :

% Document title
% Document author
% Document date

Mais d'autres variantes de Markdown ne vont pas comprendre ces métadonnées, les laissant telles quelles. Autre exemple de variété, les commentaires, pour lesquels il n'existe pas de solution standard (il y a un bon article sur StackOverflow qui discute les différentes possibilités.)

Cet enregistrement du type MIME de Markdown s'inspire de celui de text/troff, un type pour un autre langage de marquage (RFC 4263). Après tout, beaucoup de gens considèrent que Markdown est « le troff d'aujourd'hui ».

La section 2 de notre RFC décrit formellement l'enregistrement du type MIME text/markdown et de l'extension associée, .md (.markdown est également accepté). Ce type a plusieurs paramètres possibles (les paramètres sont mis après un point-virgule, par exemple text/markdown; charset=UTF-8; variant=Original va désigner du Markdown à la syntaxe originale de Gruber, encodé en UTF-8).

Premier paramètre, et obligatoire (RFC 6838, section 4.2.1), charset, qui désigne l'encodage du texte. Les autres paramètres sont facultatifs.

Le plus important est sans doute variant qui désigne le dialecte Markdown particulier utilisé. Ce fut l'une des plus chaudes discussions à l'IETF avant la sortie de ce RFC. Notamment, fallait-il un registre IANA des variantes (une sorte de catalogue des dialectes) ou bien laisser les auteurs mettre ce qu'ils veulent, ce qui correspondait mieux au côté très peu organisé de la « communauté » Markdown, très informelle ? (Différentes versions du draft qui a mené à ce RFC faisaient des choix différents.) Le paramètre variant a finalement un registre (section 6.1) mais l'émetteur n'est pas obligé de se limiter aux valeurs de ce registre. Il faut considérer la valeur de ce paramètre comme une simple indication. Parmi les valeurs actuellement enregistrées : Original (le Markdown des débuts, celui de Gruber), GFM (celui de GitHub), pandoc (celui de ce logiciel), CommonMark (le candidat à la normalisation) etc. Notez que ce paramètre variant, au gré des évolutions du draft à l'IETF, s'est nommé syntax, flavor, processor...

Des valeurs sont interdites dans ce registre : Standard, Common et Markdown ne peuvent être enregistrés comme variantes. C'est parce que « une partie de la communauté » (en réalité Gruber seul) a protesté contre la volonté de déclarer une variante particulière comme étant « standard » ou « officielle ».

Et si on veut ajouter une entrée à ce registre des variantes de Markdown ? La politique d'enregistrement est le « Premier Arrivé, Premier Servi » du RFC 5226, donc très légère. Il suffit d'indiquer deux-trois trucs sur la variante et roulez, jeunesse. Dans des versions initiales de ce RFC, le gabarit d'enregistrement d'une variante était bien plus complexe, bien trop détaillé, avec des infos très volatiles comme currently maintained ou anticipated output types mais il est maintenant réduit à seulement cinq champs obligatoires.

Un des projets qui avaient été lancés était celui d'enregistrer non pas les noms des variantes mais leurs caractéristiques (« permet les notes de base de page », « permet la création automatique de liens hypertexte »). À la réunion IETF 90, la proposition était de pouvoir écrire des choses comme text/markdown;variations=footnotes,line_blocks pour désigner du texte Markdown utilisant les notes de bas de page et les blocs de lignes considérés comme un paragraphe. Une telle approche aurait nécessité la constitution d'un catalogue compliqué, et aurait imposé aux auteurs de garder trace de quelles extensions ils utilisent. Elle n'a finalement pas été retenue.

L'enregistrement d'un type MIME nécessite une analyse de ses risques de sécurité. Rien d'extraordinaire ici, le RFC note juste que du texte avec du marquage ne présente guère de risque (contrairement à un format comme TrueType qui inclut un langage de Turing). Attention toutefois : ce que le processeur Markdown ne comprend pas, il l'envoie verbatim dans le format de sortie (ce truc est souvent utilisé pour mettre du HTML spécifique, ou du JavaScript, lorsqu'on utilise Markdown pour produire des pages Web). Du logiciel malveillant peut donc être copié ainsi.

Et la section sur l'interopérabilité rappelle ce qui a été dit plus haut : les différents Markdown vont donner des résultats différents, d'autant plus différents qu'ils utilisent des techniques spécifiques d'une variante.

Et si on veut indiquer un point précis d'un document Markdown ? Contrairement à HTML, Markdown n'a pas d'identificateurs de fragments d'un texte (section 3 du RFC). Mais on peut réutiliser une partie de la syntaxe du RFC 5147 : en terminant un URI par #lines=N, on accède (si le logiciel client connait le RFC 5147) à la Nième ligne du document Markdown.

À noter qu'on peut écrire des RFC en Markdown : c'est expliqué dans le RFC 7328 et c'est utilisé, par exemple, pour ceux du groupe Human Rights Protocol Considerations de l'IRTF (ce groupe est typiquement moins geek que la moyenne de l'IETF/IRTF).

Un exemple plus important que l'exemple un peu artificiel test1.md que je donnais au début est mon rapport sur la panne DNS d'Oleane. On peut le traiter avec Pandoc et un Makefile comme :

TARGETS=panne-dns-oleane-2016.pdf panne-dns-oleane-2016.html

all: ${TARGETS}

%.pdf: %.md
	pandoc --latex-engine=xelatex -o $@ $^

%.html: %.md
	pandoc -o $@ $^

clean:
	rm -f ${TARGETS}
    

Markdown est souvent présent dans les « micro-éditeurs » qui permettent de remplir des documents via le Web. C'est le cas à GitHub où les gists dont le nom se termine par .md sont automatiquement convertis. C'est ainsi que j'ai fait un gist de ce rapport (cliquez sur le bouton Raw pour avoir le source).

Quelques lectures sur Markdown :


Téléchargez le RFC 7763


L'article seul

RFC 7764: Guidance on Markdown: Design Philosophies, Stability Strategies, and Select Registrations

Date de publication du RFC : Mars 2016
Auteur(s) du RFC : S. Leonard (Penango)
Pour information
Réalisé dans le cadre du groupe de travail IETF appsawg
Première rédaction de cet article le 23 mars 2016


Ce RFC est un complément du RFC 7763, qui enregistrait le type MIME text/markdown. Il contient tout ce qui ne tenait pas dans le RFC original, c'est donc un pot-pourri de considérations philosophiques, de descriptions des variantes les plus courantes de Markdown, de techniques de stockage local de contenus en Markdown...

Le RFC 7763 était plus sec, contenant uniquement ce qui était nécessaire à l'enregistrement de text/markdown. Cet autre RFC est plus disert, expliquant d'abord (section 1) la philosophie de Markdown, et ses usages. Rappelons donc que Markdown est un format de marquage du texte. Lorsqu'on veut stocker des textes (romans, rapports techniques, articles scientifiques, lettres d'amour, etc) sur un système informatique, on a plusieurs solutions : elles vont du texte brut à un format binaire. Le texte brut est le stockage des seuls caractères Unicode (cf. RFC 6838, section 4.2.1 : le texte est distribué sur l'Internet avec un type MIME text/quelquechose, par exemple text/plain pour le texte brut). Son gros avantage est qu'il est lisible et modifiable avec n'importe quel logiciel. Bon, en fait, le texte brut n'est jamais tout à fait brut. Il contient quelques caractères de contrôle comme le saut de ligne ou la marque d'un nouveau paragraphe (U+2029 en Unicode, mais ce séparateur de paragraphe n'est que rarement géré par les logiciels).

À l'autre extrémité se trouve les formats binaires : lisibles et modifiables uniquement par des applications spécifiques (et c'est pour cela qu'ils sont distribués avec le type MIME application/quelquechose).

Entre les deux se trouvent le texte formaté ou marqué. Il s'agit à première vue de texte brut mais il inclut en fait quelques marques qui ont une signification non prévue par le jeu de caractères. Un exemple classique est la mise en évidence qui, en Markdown, se fait en encadrant le texte avec des étoiles : « il est *très* important de comprendre que... » Le texte reste lisible sans logiciel spécifique (d'autant plus qu'ici, Markdown reprend une ancienne convention d'écriture largement utilisée sur l'Internet). Et n'importe quel éditeur (et n'importe quel clavier) permet de créer un tel texte.

On sépare souvent les formats de texte marqués en formats stricts (markup) et formats légers (lightweight markup). Les premiers sont plus proches des formats binaires, dans la mesure où ils ont des marques bien séparées du texte, et suivent une syntaxe rigide. Ce sont par exemple XML et LaTeX. Cette syntaxe rigide permet de vérifier la qualité technique du document et mène à une meilleure interopérabilité. Mais elle peut être pénible pour les utilisateurs débutants ou occasionnels. Plus proches du texte brut, les formats de texte marqué légers ont des marques plus discrètes, et ont souvent une syntaxe moins formelle, plus tolérante (un cas extrême est Markdown, qui ne connait même pas la notion d'erreur de syntaxe). Ils sont conçus pour être facilement accessibles au nouvel utilisateur (ou à celui qui n'a pas pratiqué depuis longtemps). Jeff Atwood qualifiait ces formats légers d'« humains ».

Markdown est issu d'un langage originellement développé par John Gruber (et Aaron Swartz). Il a été rapidement un grand succès, notamment pour les tâches simples (comme de saisir un commentaire ou un rapport de bogue dans un formulaire Web). Et il a connu de nombreuses extensions. Beaucoup de programmes et de langages savent traiter le Markdown, chacun avec sa variante particulière.

Markdown est clairement dans le camp de l'« informalité ». Un des événements fondateurs fut un problème avec de nombreux flux de syndication rendus invalides juste par un article contenant du code incorrect (la norme XML dit bien qu'une fois qu'une erreur apparait dans un fichier XML, le processeur ne doit pas continuer). Avec Markdown, cela ne risque pas d'arriver, tout texte est du Markdown légal. Cela ressemble au fameux principe de robustesse des protocoles réseau. (À noter que ce principe a été suivi, en pratique, par la plupart des navigateurs Web, lorsqu'il s'agit d'analyser du HTML. Résultat, les auteurs se sont habitués à taper n'importe quoi et le Web est aujourd'hui une poubelle de fichiers HTML bourrés d'erreurs.) Lorsqu'on soumet un fichier Markdown à un processeur, cela « marche » toujours. Si on n'est pas satisfait du résultat, on modifie le source et on réessaie (cf. « The Talk Show: Ep. 88: 'Cat Pictures' (Side 1) ».) Pas de processus de validation, comme en XML ou HTML strict, seul le résultat compte.

Malgré ou bien à cause de ce principe, Markdown est un grand succès. On le trouve utilisé partout et il est probablement le plus populaire des langages de marquage « légers ». On peut l'éditer avec n'importe quel logiciel et lire le source, même sans avoir subi aucun apprentissage de sa syntaxe. Des gens utilisent Markdown pour des articles scientifiques, l'écriture de scénarios, ou pour faire des maths, le domaine qui était autrefois le bastion de LaTeX. (Personnellement, je m'en sers pour les rapports de bogue sur GitHub, les README de logiciels, les rapports techniques internes à mon employeur, LaTeX ou DocBook prenant le relais pour les choses plus « sérieuses ». Markdown avec les sites qui le gèrent permet aussi de faire du mini-blogging très simplement et très rapidement. Regardez aussi le témoignage de Yann Houry.)

Depuis le RFC 7763, il existe un type MIME pour identifier Markdown, text/markdown. (Une extension de nom de fichier comme .md ne suffit pas : tout le contenu n'est pas forcément dans des fichiers.) Un seul type MIME décrit toutes les variantes puisque n'importe quel processeur pourra traiter n'importe quelle variante (même si le résultat ne sera pas toujours celui voulu.) À noter que la plupart des variantes de Markdown n'ont pas de mécanisme pour mettre les métadonnées : il faut les placer à l'extérieur.

Comment préserver ce type MIME lorsque du contenu Markdown est copié sur l'Internet ? Parfois, la copie ne préserve pas cette information et Markdown n'a pas l'équivalent du <?xml version="1.0"> qui, au début des contenus en XML, indique sans ambiguité qu'il s'agit de XML (il a été proposé de l'introduire mais cela peut casser les mécanismes de métadonnées existants). Utiliser le contexte (le répertoire où se trouve un fichier, par exemple...) est casse-gueule car, justement, l'envoi d'un document fait facilement perdre le contexte. La section 2 du RFC propose et discute plusieurs stratégies. Utiliser le nom de fichier, lorsqu'on passe par des fichiers, est possible. mon-roman.md est un texte en Markdown, on le sait par l'extension. Et la variante ? On peut l'indiquer avant, par exemple mon-roman.pandoc.md si le texte est écrit dans la variante Pandoc. Si le système de fichiers permet de stocker des attributs ou métadonnées, c'est une autre bonne solution.

Mais il y en a d'autres. Une solution, suggérée par le RFC 6533, est de stocker les métadonnées et notamment l'en-tête MIME dans un fichier séparé, conventionnellement nommé. Si le fichier texte est mon-roman.md, le fichier des métadonnées pourrait être mon-roman.md.headers.

Ou alors dans le même fichier ? L'idée est de transformer le fichier Markdown en un fichier au format IMF (Internet Message Format, spécifié dans le RFC 5322) en mettant des en-têtes d'abord, puis le contenu en Markdown :

MIME-Version: 1.0
Content-Type: text/markdown; charset=utf-8
Date: Wed, 10 Feb 2016 20:59:06 +0100 (CET)
    
Exemple d'un fichier « armé »
=============================

Notez la ligne vide qui sépare les en-têtes du corps.

Il est *important* que son extension ne soit pas .md ou .markdown, ce
n'est plus du Markdown pur mais de l'IMF, son extension doit donc être
.eml ou .msg.

...
    

Le RFC contient plein d'autres stratégies rigolotes pour résoudre ce problème. Par exemple, on pourrait, lors de la réception, voire de l'envoi, d'un contenu en Markdown, générer/envoyer un script qui contienne les commandes permettant de le traiter convenablement. Ou on pourrait carrément envoyer le résultat du traitement, et pas le Markdown : si on veut juste que le destinataire puisse lire, pas la peine de lui envoyer le « source », on pourrait juste lui transmettre le XHTML (je ne suis pas sûr d'être d'accord avec cette curieuse idée : un des gros avantages de Markdown est qu'il est lisible même si on n'a aucun logiciel spécifique, et si on n'a jamais entendu parler de ce format).

Cette section 2 se termine avec le cas des VCS. Subversion offre un excellent mécanisme de stockage et de versionnement des méta-données. Il suffit donc de définir la métadonnée standard svn:mime-type et on ne risque plus de perdre cette information :

% svn propset svn:mime-type text/markdown mon-roman.md 
property 'svn:mime-type' set on 'mon-roman.md'

% svn commit -m "Type indiqué" mon-roman.md
...
    

Hélas, git n'a, lui, aucun moyen propre de stocker les métadonnées. Il faut donc recourir à une autre des stratégies mentionnées plus haut (fichier d'en-têtes avec un nom conventionnel, extension du fichier, etc).

La section 3 de notre RFC est consacrée aux formulaires d'enregistrement des différentes variantes de Markdown, désormais mémorisées à l'IANA. Cette section est utile si vous voulez vous renseigner sur une variante particulière, mais aussi si vous avez développé votre propre variante (sans doute une mauvaise idée mais les programmeurs adorent réinventer la roue) et que vous voulez l'enregistrer à l'IANA. Vous pouvez alors utiliser les cas de cette section comme exemples concrets, avant d'écrire votre propre demande.

Prenons par exemple la variante que j'utilise le plus souvent, Pandoc. Contrairement au Markdown originel, elle ne vise pas uniquement la génération de HTML mais également des tas d'autres formats de sortie. Elle a des tas d'extensions par rapport à l'original, comme les tableaux ou bien les notes de bas de page. Sa fiche contient :

   Identifier: pandoc

   Name: Pandoc

   ...
   
   Extensions to turn off (on by default):

                   escaped_line_breaks
                   blank_before_header
                   header_attributes
		   ...
		   
   References:
   <http://johnmacfarlane.net/pandoc/README.html#pandocs-markdown>

   Contact Information:
     (individual) Prof. John MacFarlane <jgm@berkeley.edu>
                              <http://johnmacfarlane.net/>
    

Les autres variantes enregistrées via cette section 3 sont MultiMarkdown, GitHub, Fountain, CommonMark, Markdown for RFCs, Pandoc for RFCs (RFC 7328) et PHP Markdown (utilisé par plusieurs CMS comme Drupal).

La section 4 de notre RFC fournit des exemples de ces différentes variantes et de ce qu'elles apportent. Par exemple, la variante GitHub apporte la transformation automatique des URL en liens hypertexte.


Téléchargez le RFC 7764


L'article seul

RFC 7816: DNS query name minimisation to improve privacy

Date de publication du RFC : Mars 2016
Auteur(s) du RFC : S. Bortzmeyer (AFNIC)
Expérimental
Réalisé dans le cadre du groupe de travail IETF dnsop
Première rédaction de cet article le 23 mars 2016


La meilleure façon de protéger les données contre la surveillance, c'est de ne pas avoir de données. Ce principe de base de la protection de la vie privée est souvent oublié. C'est pourtant un des deux piliers de la confidentialité, avec la protection technique des données. Le DNS a pendant longtemps transmis trop de données, et ce RFC décrit une technique qui va limiter les fuites, la QNAME minimisation, ou « réduction de la question posée ». Demandez à votre FAI ou à votre service informatique de l'activer !

Si vous regardez les vidéos sur le fonctionnement du DNS (comme celle-ci) ou lisez les textes expliquant « le DNS pour les nuls », vous y trouverez souvent une fausse explication de la résolution DNS, où les serveurs faisant autorité ne reçoivent que la question strictement nécessaire. Ainsi, dans la vidéo citée ci-dessus, le serveur de la racine reçoit une question où ne figure que le TLD. Mais la réalité du DNS, aujourd'hui, est tout autre : les serveurs faisant autorité reçoivent la totalité de question originale. Ainsi, si vous visitez www.alcooliques-anonymes.fr, la racine voit que vous vous intéressez à l'alcoolisme, alors que ce n'était nullement nécessaire pour son travail (puisqu'elle ne connait que les TLD). Si votre logiciel BitTorrent demande _bittorrent-tracker._tcp.domain.example, les serveurs faisant autorité pour .example sauront que vous faites du BitTorrent, alors qu'ils ne connaissaient que les domaines situés immédiatement sous .example. Le RFC 7626 décrit plus en détail les problèmes de vie privée liés au DNS.

Dans le dernier cas, pour que la résolution se passe bien, il aurait suffit de demander à la racine « quels sont les serveurs de noms de .example » et à ces serveurs « quels sont les serveurs de noms de domain.example ». C'est le principe de base de la QNAME minimisation.

Bien sûr, on pourrait chiffrer le trafic DNS (et le groupe de travail DPRIVE de l'IETF travaille précisément sur ce sujet). Mais cela ne protège que contre un tiers écoutant le réseau : les serveurs faisant autorité verraient quand même les données et pourraient en faire ce qu'ils veulent. C'est pour cela qu'un principe de base en matière de protection de la vie privée est de marcher sur deux jambes (RFC 6973) : minimiser les données envoyées et les protéger. Lorsqu'on parle de vie privée, pas mal d'informaticiens réagissent en criant « cryptographie ! » alors que celle-ci ne protège pas contre tout et notamment pas contre le serveur à qui on parle.

Et pourquoi est-ce que les résolveurs DNS envoyaient la question complète (full QNAMEQNAME veut dire Query NAME) ? Uniquement parce que la protection de la vie privée n'était pas tellement prise en compte à l'époque ? Pas uniquement : il y avait quelques raisons techniques spécifiques à l'époque (le RFC les détaille mais, surtout, il ne faut pas oublier que toutes les décisions concernant le DNS n'ont pas forcément été mûrement réfléchies).

La section 2 décrit la QNAME minimisation. Elle est mise en œuvre dans le résolveur DNS (aucun changement n'est fait dans le serveur faisant autorité, la QNAME minimisation ne change pas le protocole DNS). Avant, lorsqu'il recevait une requête demandant l'adresse IPv6 pour _foobar._tcp.sub.internautique.fr et qu'il connaissait les serveurs faisant autorité pour .fr, mais pas ceux faisant autorité pour internautique.fr, le résolveur envoyait à l'AFNIC une requête avec comme QNAME (Query NAME) le nom complet _foobar._tcp.sub.internautique.fr et comme QTYPE (Query TYPE) AAAA (indiquant une demande d'adresse IPv6). Désormais, le résolveur moderne qui met en œuvre la QNAME minimisation enverra une requête avec le QNAME internautique.fr et le QTYPE NS (demande des serveurs de noms). Plus rigoureusement, la requête est faite avec un QNAME qui est l'original, où on a retiré les premiers composants, jusqu'à un seul composant de plus que celui pour lequel on connait les serveurs faisant autorité.

Les experts en DNS ont noté un problème : il n'y a pas forcément un jeu de serveurs faisant autorité pour chaque composant. Si je prends www.st-cyr.terre.defense.gouv.fr, il n'y a par exemple aujourd'hui pas de serveurs de noms pour gouv.fr, ce sont ceux de .fr. En termes techniques, il n'y a pas de limite de zone (zone cut, cf. RFC 2181, section 6) à chaque composant. Dans le cas de ce dernier nom, il y a une limite de zone entre la racine et .fr, une autre entre fr et defense.gouv.fr mais pas entre .fr et gouv.fr. Un résolveur qui veut faire de la QNAME minimisation doit donc tenir compte des limites de zone. S'il valide avec DNSSEC, pas de problème, il les connait déjà, car leur connaissance est nécessaire au bon fonctionnement de DNSSEC. Sinon, il doit les trouver tout seul, par exemple avec l'algorithme de l'annexe A.

Est-ce un changement « légal » du fonctionnement du résolveur DNS ? La section 4 discute ce problème et conclut que oui. La QNAME minimisation est permise par les RFC existants (RFC 1034, section 5.3.3 et RFC 1035, section 7.2). C'est un changement unilatéral de la part du résolveur, ne nécessitant pas de changement dans les serveurs faisant autorité. Comme c'est un changement unilatéral, différents résolveurs pourront choisir de la mettre en œuvre de façon légèrement différente. L'annexe B décrit certaines de ces alternatives, comme d'utiliser des requêtes « traditionnelles » avec le nom de domaine complet, au démarrage du résolveur, attendant que le cache soit peuplé pour passer à la QNAME minimisation, qui préserve la vie privée mais peut nécessiter davantage de paquets.

La QNAME minimisation ne change pas le protocole DNS. Elle ne pose donc pas de problème avec les vieux serveurs. En théorie car, en pratique, il existe pas mal de serveurs incorrects qui ne suivent pas les règles et poseront quelques problèmes (section 3 du RFC, voir aussi un intéressant exposé et un « storify » d'une discussion.) Le problème n'est en général pas dû aux serveurs en logiciel libre sérieux qui forment l'essentiel de l'infrastructure du DNS (BIND, NSD, Knot...) mais plutôt aux appliances boguées que certains s'obstinent à placer devant des serveurs qui marcheraient parfaitement autrement.

C'est par exemple le cas de certains répartiteurs de charge qui répondent aux requêtes pour certains QTYPE mais qui échouent lorsque le QTYPE vaut NS (répondant, par exemple REFUSED). Pire, certains ne répondent pas du tout, comme ceux de www.ratp.fr. Il s'agit bien d'une bogue, et qui cause plein de problèmes, pas seulement à la QNAME minimisation.

Un autre problème est celui des serveurs bogués (comme djbns) qui ne réagissent pas correctement aux ENT. Qu'est-ce qu'un ENT ? Un Empty Non-Terminal (terme décrit dans le RFC 7719, section 6) est un domaine qui n'a pas d'enregistrements DNS mais qui a des sous-domaines qui en ont. gouv.fr, cité plus haut, est un ENT mais ceux-ci sont particulièrement fréquents sous ip6.arpa. Normalement, la bonne réponse à un ENT est le code NOERROR, sans enregistrements (ce qu'on appelle parfois NODATA, bien que ce dernier ne soit pas un code de retour explicite dans le DNS). Mais certains serveurs boguées répondent à la place NXDOMAIN, code qui indique normalement que le domaine n'existe pas (ce qui est faux). Voici ce que répond djbdns à une requête sur l'ENT temporary.cr.yp.to :


% dig A temporary.cr.yp.to
...
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 23636

    

C'est bien un ENT puisqu'il y a des noms en dessous (par exemple pairings.temporary.cr.yp.to). Le résolveur qui se fierait à ce NXDOMAIN croirait alors que sa recherche est terminée et que le nom demandé n'existe pas. C'est pour cela que les mises en œuvre existantes de la QNAME minimisation ont des comportements spécifiques pour les réponses NXDOMAIN, pour contourner cette bogue.

La protection de la vie privée fait qu'on enverra moins de données. C'est bien le but. Résultat, les serveurs faisant autorité et les sniffeurs recevront moins d'information. Cela peut gêner certains gros silos de données, qui exploitaient cette information.

Et les performances ? Elles seront parfois meilleures et parfois pires, avec la QNAME minimisation. Meilleures car le résolveur enverra moins de requêtes aux serveurs faisant autorité. Aujourd'hui, si un résolveur reçoit trois requêtes successives, pour A.example, B.example et C.example, les trois requêtes seront envoyées aux serveurs racine, et donneront toutes les trois un NXDOMAIN (puisque .example n'est pas délégué). Avec la QNAME minimisation, seule la première requête déclenchera une demande à la racine, pour le nom example. Cela suffira au résolveur.

Par contre, les performances peuvent se dégrader dans certains cas. Si un nom comporte beaucoup de composants (c'est fréquent dans ip6.arpa), la recherche initiale des limites de zone nécessitera bien plus de paquets. Ceci dit, cela ne durera que le temps de remplir le cache, tout ira plus vite après, une fois que le résolveur connaitra les limites de zone.

Ce RFC est issu du projet « DNS privacy », lancé initialement au CENTR, puis passé à l'IETF (le premier RFC de ce projet avait été le RFC 7626 ; comme lui, cette idée de QNAME minimisation était née dans un avion de la KLM).

À noter que Verisign a un brevet dont ils prétendent qu'il couvre la QNAME minimisation. Ils promettent une licence (attention, le diable est dans les détails) gratuite et non-discriminatoire. Ces brevets ont bien perturbé la réflexion du groupe de travail. Personnellement, je pense que ce brevet n'a pas de sens : l'idée de QNAME minimisation est évidente et avait déjà été discuté plusieurs fois, mais sans laisser de trace écrite précise, ce qui permet à Verisign de prétendre qu'il n'y a pas de prior art. Ce n'est sans doute pas un hasard si les deux premières mises en œuvre de la QNAME minimisation ont été faites en Europe, où il n'y a (normalement) pas de brevet logiciel. Ceci dit, lors des discussions sur la licence de ces brevets, en marge de la réunion IETF d'Honolulu, c'est Verisign qui a payé les boissons, reconnaissons-leur ce mérite.

La QNAME minimisation est mise en œuvre dans Unbound et dans le résolveur Knot (ce dernier n'étant pas encore officiellement publié). Pour Knot (qui le fait par défaut), voici le résultat vu par tcpdump d'une requête dig -x d'une adresse IPv6. Par exemple, le serveur racine n'a reçu qu'une demande pour .arpa. Notez aussi que Knot fait varier la casse (une protection contre certains empoisonnements) :

02:36:39.673268 IP6 2400:8900::f03c:91ff:fe69:60d3.54216 > 2001:e30:1c1e:1::333.53: 38773% [1au] NS? aRpA. (33)
02:36:40.114074 IP6 2400:8900::f03c:91ff:fe69:60d3.59934 > 2001:dc3::35.53: 22056% [1au] NS? Ip6.aRPa. (37)
02:36:40.428545 IP6 2400:8900::f03c:91ff:fe69:60d3.47793 > 2001:500:86::86.53: 43002% [1au] NS? 2.ip6.arPA. (39)
...

Pour se protéger contre les serveurs bogués dont je parlais plus haut (ceux qui répondent NXDOMAIN en cas d'ENT), Knot réessaie avec le QNAME complet lorsqu'il reçoit un NXDOMAIN (les deux dernières lignes). Mauvais pour la vie privée mais sans doute nécessaire aujourd'hui (ici, par la faute d'Akamai) :

02:34:45.050913 IP 106.186.29.14.51228 > 128.175.13.17.53: 24014% [1au] A? WwW.UpENn.edU. (42)
02:34:45.227102 IP 128.175.13.17.53 > 106.186.29.14.51228: 24014*- 2/0/1 CNAME www.upenn.edu-dscg.edgesuite.net., RRSIG (270)
02:34:45.228413 IP6 2400:8900::f03c:91ff:fe69:60d3.46525 > 2001:503:231d::2:30.53: 52576% [1au] NS? edGeSUItE.NEt. (42)
02:34:45.297319 IP6 2001:503:231d::2:30.53 > 2400:8900::f03c:91ff:fe69:60d3.46525: 52576- 0/17/15 (1034)
02:34:45.298284 IP 106.186.29.14.45604 > 23.61.199.64.53: 22228 [1au] NS? EdU-DScG.EdGesUITe.nET. (51)
02:34:45.373238 IP 23.61.199.64.53 > 106.186.29.14.45604: 22228 NXDomain*- 0/1/1 (114)
02:34:45.373795 IP 106.186.29.14.34320 > 72.246.46.66.53: 1355 [1au] A? WWW.UPenN.edu-dSCG.EdgESuItE.net. (61)

Un autre exemple de ce repli sur les requêtes classiques est donné ici, lorsque je demande www.long.verylong.detail.example. Comme le TLD .example n'existe pas, Knot débraye hélas la QNAME minimisation :

20:08:49.615421 IP6 2400:8900::f03c:91ff:fe69:60d3.51723 > 2001:1398:1:21::8001.53: 19881% [1au] NS? ExaMpLE. (36)
20:08:49.900009 IP6 2400:8900::f03c:91ff:fe69:60d3.59917 > 2001:6d0:6d06::53.53: 40708% [1au] AAAA? www.LONg.VeRylONG.DEtaIl.eXamPLE. (61)

Même chose avec un ENT où la réponse est pourtant correcte. Knot se méfie et réessaie sans QNAME minimisation :

20:14:15.479872 IP6 2400:8900::f03c:91ff:fe69:60d3.45418 > 2001:67c:1010:11::53.53: 18200% [1au] NS? gOUV.Fr. (36)
20:14:15.740424 IP 106.186.29.14.33850 > 194.0.36.1.53: 54260% [1au] A? www.ST-cYr.TerRE.DeFeNSe.GouV.fR. (61)

Lorsque le cache commence à être rempli, Knot a besoin de moins de requêtes. Ici, je lui ai demandé l'adresse de www.bortzmeyer.org, et il connaissait déjà les serveurs de .org, il passe donc directement à la question « quels sont les serveurs de noms de bortzmeyer.org ? » :

20:08:20.420757 IP 106.186.29.14.41889 > 199.249.120.1.53: 39126% [1au] NS? bOrtzMEYeR.oRg. (43)
20:08:20.941797 IP 106.186.29.14.35536 > 217.70.190.232.53: 33709% [1au] AAAA? Www.bOrtZmEyER.Org. (47)

Unbound a la QNAME minimisation depuis la version 1.5.7 (sortie en décembre 2015, cf. l'historique de ce travail). Ce n'est pas activé par défaut, il faut mettre dans la configuration :

server:
    qname-minimisation: yes

(Un pcap est disponible.) Pour vérifier si votre résolveur met bien en œuvre la QNAME minimisation, vous pouvez tester avec le domaine qnamemintest.internet.nl. Ici, le résolveur est un Unbound récent :

% dig +nodnssec +short TXT qnamemintest.internet.nl
a.b.qnamemin-test.internet.nl.
"HOORAY - QNAME minimisation is enabled on your resolver :)!"

Avec un résolveur traditionnel (ici, Verisign Public DNS, qui utilise Unbound mais une vieille version) :

% dig @64.6.64.6 +nodnssec +short TXT qnamemintest.internet.nl
a.b.qnamemin-test.internet.nl.
"NO - QNAME minimisation is NOT enabled on your resolver :("

À noter qu'à la prochaine réunion de l'OARC à Buenos Aires, Ralph Dolmans présentera un très intéressant exposé technique sur la mise en œuvre de la QNAME minimisation dans Unbound.


Téléchargez le RFC 7816


L'article seul

Exposé « Objets qui communiquent entre eux ou bien esclaves du cloud ? » aux journées FedeRez

Première rédaction de cet article le 19 mars 2016


FedeRez est la fédération nationale des associations d'étudiants qui gèrent des réseaux informatiques. Elle tient des journées annuelles et, à celles de 2016 dans un coin perdu de la grande banlieue parisienne, j'ai fait un exposé sur le thème « Objets qui communiquent entre eux ou bien esclaves du cloud ? ».

Voici les transparents de mon exposé :

Les enregistrements vidéo seront disponibles plus tard.


L'article seul

RFC 7776: IETF Anti-Harassment Procedures

Date de publication du RFC : Mars 2016
Auteur(s) du RFC : P. Resnick (Qualcomm Technologies), A. Farrel (Juniper Networks)
Première rédaction de cet article le 13 mars 2016


La question du harcèlement dans le cadre des activités professionnelles (ou autres, d'ailleurs) est maintenant largement reconnue comme cruciale et toute organisation doit se demander « que dois-je faire ? » L'IETF décrit dans ce RFC ses procédures à ce sujet, avec notamment le rôle de l'ombudsteam, l'équipe de justice privée qui doit traiter les cas de harcèlement signalés.

Tout groupe humain est évidemment confronté au problème des comportements négatifs, les trolls, par exemple. L'IETF a eu sa dose, bien que mon impression soit que le problème est nettement moins grave que dans certains groupes, comme chez certains gamers. Mais c'est difficile à dire, c'est un domaine où on n'a pas facilement des statistiques précises. L'IETF a donc son « guide des bonnes manières » (RFC 7154), et les documents de cuisine procédurale interne (RFC 2418 et RFC 3934) prévoient la gestion des cas les plus sérieux. Ces documents se focalisent sur les comportements négatifs qui impactent directement le travail de l'IETF, et le mettent en péril (par exemple, un troll qui assomme le groupe de travail de longs messages répétés en quantité et n'apportant pas de contenu). Mais il y a aussi, et c'est le sujet de ce nouveau RFC, des pratiques négatives qui ne gênent pas directement l'IETF mais peuvent pourrir la vie des participant·e·s qui en sont victimes, par exemple du harcèlement sexuel lors d'une réunion physique. Ces pratiques sont évidemment interdites (déclaration de l'IESG) mais il restait à préciser quoi faire en cas de violation de cette règle.

Il faut noter (et c'est oublié dans la plupart des ces guides de bonnes manières d'origine états-unienne qui fleurissent dans beaucoup de conférences informatiques) que le harcèlement est déjà interdit par la loi dans la plupart des pays où l'IETF tient ses réunions. Comme le note le RFC, les règles de l'IETF ne remplacent pas la loi. Nul besoin d'une déclaration de l'IESG pour interdire, par exemple, les insultes racistes ou le harcèlement sexuel à une réunion IETF. C'est pour cela que la plupart de ces codes of conduct sont inutiles. Celui de l'IETF est très orienté par ses origines états-uniennes, mêlant la demande d'un comportement « professionnel » (comme si le harcèlement n'était fait que par des amateurs) et le puritanisme (« sexual imagery in public presentations », sachant qu'aux États-Unis, la vue d'un torse nu est déjà de la sexual imagery).

La déclaration de l'IESG citée plus haut donne une définition du harcèlement, définition forcément très large (il y a hélas bien des formes de harcèlement, et bien des raisons pour lesquelles un·e participant·e à l'IETF peut être pris·e pour cible). Le problème est-il fréquent à l'IETF ? Je n'ai jamais entendu parler d'un cas précis (contrairement aux cas documentés qui sont arrivés à DEFCON), mais, évidemment, toutes les victimes ne se signalent pas publiquement et le harcèlement est donc très difficile à mesurer scientifiquement.

En première ligne dans ce RFC est l'ombudsteam. Je n'ai pas essayé de traduire ce terme, d'origine suédoise (ombudsman, devenu en anglais ombudsperson pour éviter d'exclure les femmes, puis ombusteam pour marquer son caractère collectif). Disons que c'est une équipe chargée de gérer les cas d'accusation de harcèlement, et c'est la principale ligne de défense de l'IETF dans ces cas.

L'ombudsteam est composé d'au moins trois personnes. Elles sont désignées - pour deux ans, renouvelable - par le président de l'IETF (actuellement Jari Arkko), éventuellement après consultations. Il est recommandé de suivre une certaine diversité (éviter un ombudsteam qui serait uniquement composé d'hommes blancs d'âge mûr et issus des classes dominantes, comme l'est l'auteur de ces lignes...) et de faire en sorte qu'au moins un des membres soit un participant actif à l'IETF. Il n'est donc pas nécessaire, ni même souhaitable, que l'ombudsteam soit sélectionné uniquement en interne. L'idéal, demande le RFC, est que l'ombudsteam rassemble des compétences variées, par exemple des gens qui connaissent très bien l'IETF, des gens qui sont des experts en médiation et en gestion de conflits, etc.

Les membres de l'ombudsteam ne sont pas payés pour leur activité. (À mon humble avis, cela est en contradiction avec l'objectif d'avoir de la diversité : s'ils ne sont pas payés, on n'aura que des gens favorisés socialement.)

Si quelqu'un n'est pas content de la désignation de telle ou telle personne, il peut en appeler à l'IESG (section 6.5.4 du RFC 2026).

L'ombudsteam dispose d'une page Web officielle. La liste des membres du ombudsteam a été publiée deux semaines après le RFC.

Une fois l'ombudsteam constitué, que se passe-t-il en cas de plainte (section 4) ? La victime, ou un témoin du harcèlement, contacte l'ombudsteam, en présentiel ou bien par courrier (ombuds@ietf.org). Tout participant·e à l'IETF peut contacter l'ombudsteam, soit pour une plainte formelle, soit pour solliciter un avis.

Les pratiques de l'ombudsteam seront en grande partie définies par l'ombudsteam, qui est supposé en discuter avec l'IESG puis publier ses principes de fonctionnement. Au minimum, le RFC demande à l'ombudsteam que :

  • Les membres de l'ombudsteam soient présents aux réunions physiques de l'IETF, pour permettre des prises de contact AFK.
  • L'ombudsteam travaille dans la plus stricte confidentialité. Il s'agit de protéger aussi bien le ou la signaleur d'un harcèlement (qui peut hésiter à se plaindre, par exemple par crainte de représailles) que celui ou celle qui en est accusé, peut-être à tort (la section 8 revient en détail sur cette exigence de confidentialité). C'est une exception à la règle habituelle de l'IETF comme quoi tout doit se dérouler en public (une autre exception est le NomCom, cf. RFC 7437).
  • L'ombudsteam ne doit évidemment prendre aucune décision sans avoir entendu les deux parties - pas seulement celle qui signale le harcèlement.
  • Si les accusations s'avèrent infondées, aucune sanction ne doit être prise contre celui ou celle qui a signalé à tort, mais de bonne foi. Autrement, les problèmes ne seraient jamais remontés.
  • L'ombudsteam doit garder des traces écrites de toute son activité, ce qui n'est pas techniquement trivial, vues les exigences de confidentialité.

Bon, donc, des gens signalent des problèmes à l'ombudsteam, l'ombudsteam investigue, se réunit, discute et à la fin, il peut décider quoi ? La section 5 décrit les solutions disponibles pour l'ombudsteam :

  • Dans certains cas, les processus existants avant ce RFC peuvent être utilisés (voir par exemple les RFC 2418 et RFC 3934).
  • Dans les autres cas, l'ombudsteam doit chercher si une médiation entre les parties est possible, débouchant sur un accord qui empêchera la répétition des faits.
  • Dans les cas les plus graves, l'ombudsteam peut décider de sanctions, comme l'exclusion de tout ou partie des activités de l'IETF. Par exemple, le participant convaincu de harcèlement peut être banni d'une réunion et/ou des futures réunions.

Ces sanctions les plus sérieuses peuvent provoquer des problèmes délicats si le harceleur occupait une position de responsabilité à l'IETF. Bien sûr, ces personnes en position de responsabilité (président d'un groupe de travail, par exemple) n'ont pas d'immunité. Mais il ne faut pas non plus que l'ombudsteam puisse empêcher, de facto, un responsable de tenir son rôle. Une possibilité est, dans ce cas, que l'ombudsteam fasse jouer les procédures de révocation normales de l'IETF (RFC 7437, notamment la section 7). Dans le cas d'un poste qui passe par le NomCom (Nominating Commitee), l'ombudsteam demande alors le lancement de la procédure de révocation, et n'a pas besoin pour cela d'une pétition de 20 participants (RFC 7437, section 7.1). Dans le cas de poste pourvus par l'ISOC, l'IAB ou un autre organisme, l'ombudsteam peut demander à cet organisme la révocation du harceleur. Idem si le poste a été pourvu via un directeur de zone (AD, Area Director) ou un président de groupe de travail.

On le voit, l'ombudsteam dispose d'un large pouvoir, sans contrôle (en raison de l'obligation de confidentialité) et la section 5.1 du RFC dit bien que les organismes à qui l'ombudsteam demande une révocation doivent, sauf raison très sérieuse, donner suite à cette demande. Mais que se passe-t-il si quelqu'un est en désaccord avec l'ombudsteam ? La section 6 traite ce problème.

Après discussion avec l'ombudsteam la question doit être remontée au président de l'IETF. Si celui-ci préfère ne pas être impliqué (voir par exemple la section 7, sur les conflits d'intérêt), il peut demander à l'IESG de désigner un de ses membres pour jouer son rôle (les protocoles réseaux, c'est compliqué, mais la bureaucratie interne de l'IETF, c'est pire). Si la discussion entre le président et l'ombudsteam ne débouche pas sur une solution, un mécanisme formel d'appel est prévu. Le groupe qui étudiera l'appel est composé du président de l'IETF et de deux membres de l'IESG qu'il·elle a choisi. Ce groupe peut évaluer la question sur le fond. S'il y a un problème de forme, il existe un dernier mécanisme d'appel auprès de l'ISOC.

Le RFC se termine par une jolie citation d'Aung San Suu Kyi : « Human beings the world over need freedom and security that they may be able to realize their full potential. »


Téléchargez le RFC 7776


L'article seul

RFC 7707: Network Reconnaissance in IPv6 Networks

Date de publication du RFC : Mars 2016
Auteur(s) du RFC : F. Gont (Huawei Technologies), T. Chown (University of Southampton)
Pour information
Réalisé dans le cadre du groupe de travail IETF opsec
Première rédaction de cet article le 11 mars 2016


Soit un pauvre petit réseau innocent, et un méchant attaquant qui va essayer de faire subir les derniers outrages au réseau en question. L'attaquant commence souvent par une phase de reconnaissance, où il cherche à améliorer sa connaissance du réseau visé. Un outil souvent utilisé dans cette phase est le balayage (scanning) systématique du réseau, à la recherche d'adresses IP de machines à attaquer. En IPv6, contrairement à IPv4, la tâche semble colossale, vu le nombre d'adresses IP possibles. Mais, comme l'avait déjà noté le RFC 5157, cette tâche n'est pas impossible. Ce nouveau RFC fait le point sur le balayage IPv6 et détaille les techniques utilisables.

Comme exemple de la différence de situation entre IPv4 et IPv6, prenons un réseau local typique. La densité de machines (nombre de machines réellement présentes par rapport au nombre d'adresses possibles) est bien inférieure en IPv6. Sur un /26 IPv4, on a 62 adresses théoriquement possibles et la plupart correspondront sans doute effectivement à une machine. Balayer ce /26 est à la fois très rapide et très avantageux. En IPv6, un simple /64 permet d'avoir plus de 10^19 adresses (c'est bien la principale motivation pour déployer IPv6 : avoir davantage d'adresses). Même si on a bien plus de machines, la densité sera infime. Si les adresses étaient attribuées au hasard et que le balayage se fasse également au hasard, les chances de tomber sur une machine réelle sont quasi-nulles. Heureusement pour l'attaquant, les adresses réelles ne sont pas attribuées au hasard, et le balayage n'est pas forcément fait au hasard. Ce nouveau RFC, deux fois plus long que l'ancien (le RFC 5157) fait le point sur ce que les défenseurs et les attaquants doivent savoir sur le balayage en IPv6.

Avant de tester, installons l'outil de balayage scan6, distribué avec le SI6 toolkit (et déjà utilisé dans mon article sur les attaques en IPv6) :

% wget http://www.si6networks.com/tools/ipv6toolkit/ipv6toolkit-v2.0.tar.gz
% tar xzvf ipv6toolkit-v2.0.tar.gz
% cd ipv6toolkit-v2.0
% make

C'est parti, en commençant par les techniques les plus classiques (section 3 de notre RFC). D'abord, lorsque le réseau visé configure les adresses IP avec le SLAAC du RFC 4862. Le SLAAC fonctionne en concaténant un préfixe appris du routeur avec un suffixe, l'IID (Interface IDentifier). Comment la machine choisit-elle son IID ? Il existe plusieurs techniques, qui ont des conséquences sur la facilité de balayage. Par exemple, une technique courante (mais de moins en moins employée) est de fabriquer l'IID à partir de l'adresse MAC de la machine, en mettant juste le bit 6 à 1 et en ajoutant au milieu 0xfffe. Ainsi, mon adresse MAC 38:59:f9:7d:b6:47 me donne actuellement une adresse IPv6 de 2003:88:6c71:c7f8:3a59:f9ff:fe7d:b647 (préfixe 2003:88:6c71:c7f8 chez Deutsche Telekom et IID 3a59:f9ff:fe7d:b647). A priori, il y a 64 bits possibles pour l'IID. Mais ce n'est pas tout à fait vrai. D'abord, 16 sont fixes (le 0xfffe). Ensuite, les 24 premiers bits identifient le vendeur de la carte via son OUI (sauf si, comme dans l'exemple ci-dessus, on a changé son adresse MAC...) Tous les OUI possibles n'étant pas encore affectés, cela réduit le champ de recherche. Et ceux qui sont affectés le sont souvent pour du matériel qui n'est plus fabriqué, réduisant encore ce champ. Enfin, un attaquant malin observera qu'une organisation donnée a souvent un parc matériel homogène (par exemple uniquement des ordinateurs Dell). Cela permet de réduire encore l'espace de recherche (option --tgt-vendor de scan6). En outre, si les machines ont été achetées en même temps, il est tout à fait possible qu'elles fassent partie du même lot et que leurs adresses MAC soient consécutives. Une fois qu'on en a trouvé une, on peut supposer que les autres sont proches.

Un cas particulier est celui des techniques de virtualisation. Par exemple, VirtualBox utilise l'OUI 08:00:27 et, avec le 0xfffe du milieu, cela fait que l'espace de recherche réel n'est que de 24 bits, bien moins que les 64 théoriques. VMware est encore pire, pour ses adresses MAC automatiques : l'OUI est 00:05:69, 16 bits sont tirés de l'adresse IPv4 de la console, 8 bits sont un condensat du nom du fichier de configuration. Il peut donc n'y avoir que 8 bits à chercher ! (Les adresses MAC manuelles de VMware ont 22 bits variables.)

Et les adresses temporaires du RFC 4941, n'avaient-elles pas été conçues justement pour éviter qu'on suive à la trace une machine ? L'IID est cette fois aléatoire, et change souvent (par exemple une fois par jour). Ces adresses temporaires sont une très bonne chose, mais elles ont quelques limites. D'abord, elles viennent en plus des adresses classiques, auxquelles la machine répond toujours. Les adresses temporaires étant utilisées pour les connexions sortantes, le risque de fuite de l'adresse globale permanente est plus faible mais elles n'empêchent pas le balayage, avec les techniques données plus haut.

C'est en partie pour combler les faiblesses des adresses temporaires que les adresses du RFC 7217 ont été développées. L'IID est un condensat de certaines caractéristiques de la machine et du réseau. Ces adresses sont stables tant que la machine ne change pas de réseau (ce qui permet de n'utiliser que ces adresses et d'oublier les adresses globales classiques). Du point de vue de la vie privée, elles représentent la meilleure solution, et devraient être systématiquement utilisées. Pour l'instant, comme l'indique « IPv6 Address Analysis - Privacy In, Transition Out », la grande majorité des clients IPv6 utilisent les adresses temporaires du RFC 4941 (et 7 % utilisent encore les adresses MAC).

Et DHCP ? La politique d'allocation des adresses dépend du serveur DHCP. Parfois, elle mène à des adresses prévisibles, mais pas toujours. Par exemple, si on configure un serveur DHCP pour allouer des adresses dans la plage 2001:db8:a35:b24::/64, et que le serveur DHCP décide qu'une allocation séquentielle (2001:db8:a35:b24::1 puis 2001:db8:a35:b24::2, puis 2001:db8:a35:b24::3...) est la meilleure solution, alors les adresses seront prévisibles. C'est pour cela que le RFC 5157 conseillait aux serveurs DHCP d'allouer au hasard à partir de la plage d'adresses configurée et ce conseil est répété ici.

Et les adresses attribuées manuellement, par l'administrateur système ? C'est utile pour les routeurs (qui ne peuvent pas utiliser SLAAC) et les serveurs (qui ne cherchent pas à se cacher, et pour qui il est très souhaitable d'avoir une adresse IP stable). En théorie, l'administrateur système peut choisir l'IID librement parmi 2^64 valeurs. En pratique, on observe que ces IID ne sont pas choisis au hasard mais que les ingénieurs suivent un de ces quatre schémas, utilisant :

  • Un nombre de faible valeur (schéma dit low byte) : PREFIX::1, PREFIX::2, etc. L'option --tgt-low-byte de scan6 permet de tester en premier ces valeurs. Dans le cas le plus simple, il suffit de tester les 256 valeurs correspondant aux huits bits finaux (certaines variantes de ce schéma utilisent les deux ou trois derniers octets, ce qui rend le balayage un peu plus long). Ce schéma est de loin le plus fréquent pour les serveurs et les routeurs (voir « IPv6 Network Reconnaissance: Theory & Practice », cité en section 3.1.5).
  • Une adresse IPv4, en profitant de ce que la représentation texte d'une adresse IPv6 peut se terminer par une adresse IPv4, comme dans 2001:db8:88:12a::192.0.2.21. Balayer ces adresses revient à balayer l'espace IPv4 correspondant (option --tgt-ipv4, de scan6).
  • Un numéro de port, le serveur DNS sera PREFIX::53, le serveur IMAP PREFIX::993, etc. Le cas le plus simple (numéro de port dans le dernier octet) est très rapide à balayer (option --tgt-port de scan6, il n'y a que quelques dizaines de ports populaires), certaines variantes sont un peu plus lentes.
  • Un terme amusant en hexspeak comme ::dead:beef ou ::a11. Balayer ces termes se fait à partir d'un dictionnaire.

Bon, armés de ces connaissances, voyons maintenant concrètement comment balayer un réseau. D'abord, un réseau distant (section 3.2). Un balayage par la force brute est impossible en IPv6 : un seul /64 peut avoir dans les 10^20 machines, ce qui, même à un million de paquets par seconde, prendrait trois millions d'années à examiner. Un balayeur IPv6 doit donc être plus astucieux que la force brute et exploiter les propriétés des adresses IPv6, qui font que l'espace réel à explorer est plus réduit que l'espace théorique. Ainsi, en balayant dans l'ordre, de PRÉFIXE:: puis PRÉFIXE::1, PRÉFIXE::2, etc, on tire profit des adressses de faible valeur. De même, on peut deviner le plan d'adresssage et utiliser les adresses IPv4 du réseau, les numéros des bâtiments, etc.

À noter que le balayage peut aussi être utilisé pour faire une attaque par déni de service. Certaines mises en œuvres d'IPv6 gèrent mal leur cache NDP et un grand nombre de requêtes pour des machines inexistantes peut planter certains routeurs IPv6 (RFC 6583).

Revenons au balayage fait dans le but de découvrir des adresses actives. Si on balaye le réseau local, celui où on se trouve, il y a encore d'autres possibilités, notamment les adresses multicast. Par exemple, en envoyant un seul paquet à l'adresse multicast ff02::1, on récupère plein de machines :

% ping6 -I eth0 ff02::1
PING ff02::1(ff02::1) from fe80::e349:a3a5:ad58:a21 eth0: 56 data bytes
64 bytes from fe80::e349:a3a5:ad58:a21: icmp_seq=1 ttl=64 time=0.250 ms
64 bytes from fe80::21e:8cff:fe76:29b6: icmp_seq=1 ttl=64 time=1.17 ms (DUP!)
64 bytes from fe80::f6ca:e5ff:fe4d:1f41: icmp_seq=1 ttl=64 time=4.96 ms (DUP!)
64 bytes from fe80::ce0d:dad3:6bc2:6da4: icmp_seq=1 ttl=64 time=4.96 ms (DUP!)
...
 

Ça ne marche pas avec les machines Windows, qui ne répondent pas à ce ping. Mais il y a d'autres astuces comme d'envoyer à cette adresse multicast un paquet IPv6 ayant une option inconnue et dont le type commence par 10 (qui signifie « si cette option n'est pas connue, jeter le paquet et envoyer une erreur ICMP », cf. RFC 2460, section 4.2). Windows répondra alors par un message ICMP Parameter problem. Voici le résultat de scan6 vu avec tcpdump, montrant cette technique :

19:01:06.204431 IP6 fe80::e349:a3a5:ad58:a21 > ff02::1: DSTOPT ICMP6, echo request, seq 34994, length 64
19:01:06.205469 IP6 fe80::21e:8cff:fe76:29b6 > fe80::e349:a3a5:ad58:a21: ICMP6, parameter problem, option - octet 42, length 120
19:01:06.214792 IP6 fe80::ba27:ebff:feaa:78b9 > fe80::e349:a3a5:ad58:a21: ICMP6, parameter problem, option - octet 42, length 120
...

Ces techniques ne sont pas purement théoriques. Certes, un outil comme nmap sait faire du balayage en IPv4 mais pas en IPv6 à distance, mais d'autres outils existent. Ces techniques sont mises en œuvre dans l'outil scan6 du SI6 toolkit, cité plus haut. Une fois compilé et installé, on peut utiliser cet outil pour découvrir ses voisins (sur le même réseau local) :

 % sudo scan6 -L -i eth0
 ...
2a01:f23:a65:6721:21e:8cff:fe76:29b6
2a01:f23:a65:6721::1
2a01:f23:a65:6721:666:6c7c:9bed:b390
2a01:f23:a65:6721:b5a5:dfd6:4e7b:2584
... 
 

Ou bien tester un réseau distant :

% sudo scan6 -d 2001:7b2:dc0:41::/64
2001:7b2:dc0:41::250
...
 

Le dernier exemple ci-dessus est évidemment complètement irréaliste (balayage d'un /64 en force brute), même si ici on a trouvé tout de suite une machine « low-byte ». Ne soyez pas trop optimiste : scan6, livré à lui-même, est très lent et vous trouverez rarement quelque chose. Un balayage d'un site où on connait le vendeur des cartes Ethernet (et donc l'OUI) :

 %  sudo scan6 --tgt-ieee-oui b8:27:eb -d 2001:db8:8469:a30::/64 -i eth0 -e print-global  
 

prend de très nombreuses heures et risque fortement d'être détecté. En pratique, pour utiliser scan6 avec succès, il faut récolter beaucoup d'informations sur le réseau qui vous intéresse, et guider scan6 en lui mettant beaucoup d'options. Un projet intéressant serait de tenter d'automatiser cette phase heuristique.

Une des conséquences de la difficulté de balayer en IPv6 est que la gestion de réseaux devient plus compliquée. L'administrateur réseaux ne peut pas découvrir facilement tout ce qui a été branché et fonctionne sur son réseau. Il doit donc changer ses pratiques. Une des approches, pour connaitre tout son réseau, est l'écoute passive, avec ndpmon, qui permet de se constituer automatiquement une base des machines et de leur adresse MAC.

Et l'administrateur réseaux qui veut défendre son réseau et limiter le balayage, que peut-il faire (section 3.5 du RFC) ? Il existe plusieurs techniques qui aident, en rendant les adresses moins prévisibles :

  • Utiliser les adresses stables mais opaques du RFC 7217. (Au passage, je ne connais pas encore de système d'exploitation où ce soit facile ou même possible.)
  • Utiliser un IDS pour détecter (ou un IPS pour bloquer) : les balayages IPv6 ne peuvent pas être faits discrètement, ils allument les systèmes d'alarme comme un arbre de Noël.
  • Peut-être filtrer certains types de paquets (avec prudence : voir le RFC 4890).
  • Configurer manuellement les adresses MAC (par exemple avec macchanger). Ainsi, même si les adresses IPv6 son dérivées de l'adresse MAC, elles ne seront plus prévisibles.
  • Si on utilise DHCP, configurer le serveur DHCP pour allouer dans un ordre aléatoire, et pas bêtement en partant de 1. (Je n'ai pas regardé les possibilités des serveurs DHCP actuels.)

Ces solutions ne sont pas parfaites. Consolez-vous avec l'idée que la sécurité de vos machines ne doit de toute façon pas dépendre uniquement de la résistance de votre réseau au balayage. Tôt ou tard, l'attaquant découvrira vos adresses IP, et les machines doivent donc être préparées à tout.

Le balayeur n'utilisera pas que des techniques de couche 3 comme décrit dans la précédente section, la section 3 du RFC. Il peut aussi se servir, par exemple, du DNS (section 4). Par exemple, s'il est possible de récupérer la zone DNS, le balayeur obtient beaucoup d'adresses IPv6. Mais il peut aussi tester des noms communs dans le DNS et obtenir ainsi des adresses (et, si votre algorithme d'allocation est prévisible, un petit nombre d'adresses lui permettra d'en déduire d'autres). Un tel balayage est facile à automatiser.

Mieux (du point de vue de l'attaquant), on peut énumérer les enregistrements PTR dans votre sous-arbre de ip6.arpa. (C'est automatisable, avec l'outil dnsrevenum6, qui fait partie de la boîte à outils THC.)

Outre le DNS classique, mDNS (RFC 6762) peut également aider (section 5) : des requêtes envoyées à la cantonade peuvent découvrir des machines qui se signaleraient imprudemment.

L'attaquant en mission de reconnaissance peut aussi utiliser des archives publiques (section 6). Par exemple, une liste de diffusion dont les messages seraient stockées intégralement, y compris les en-têtes Received:, qui contiennent souvent des adresses IP. Difficile d'être discret sur l'Internet !

Certaines applications peuvent également aider l'attaquant à récolter des adresses IP (section 7). Par exemple, BitTorrent ne fait aucun effort pour dissimuler l'adresse du pair avec qui on échange des fichiers.

On trouve également des adresses IP dans les caches NDP (section 8) - et ces caches peuvent parfois être examinés à distance avec SNMP (section 13), dans les journaux (gérer un serveur Web populaire permet d'en récolter beaucoup, cf. section 9), via les protocoles de routage (section 10), via traceroute (section 12)... Au passage, il est recommandé de lire l'excellent article de Bellovin, S., Cheswick, B., et A. Keromytis, « Worm propagation strategies in an IPv6 Internet » dans login: en 2006.

L'annexe A du RFC décrit une mise en œuvre des principales idées expliquées dans ce document. Si vous voulez tous les détails techniques sur le balayage en IPv6, c'est le texte à lire. Il faut par exemple être prudent : un balayage exhaustif en IPv6 peut nécessiter un nombre colossal de paquets et, si on les envoie trop vite, l'augmentation du trafic vous fera repérer (variante : vous surchargerez le réseau, des paquets seront perdsu et vous raterez ainsi des réponses). Cette annexe couvre également les méthodes de dissimulation, pour éviter d'être détecté (avec la plupart des IDS IPv6, il suffit d'ajouter des en-têtes d'extension au paquet). Le tout est évidemment très inspiré de l'expérience de l'un des auteurs du RFC avec l'outil scan6, déjà cité.

Je ne fais pas ici un résumé des différences avec son prédécesseur, le RFC 5157, car il y en a beaucoup trop (notre nouveau RFC est bien plus détaillé).


Téléchargez le RFC 7707


L'article seul

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

Syndication : en HTTP non sécurisé, Flux Atom avec seulement les résumés et Flux Atom avec tout le contenu, en HTTPS, Flux Atom avec seulement les résumés et Flux Atom avec tout le contenu.